1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * lpc_sch.c - LPC interface for Intel Poulsbo SCH
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * LPC bridge function of the Intel SCH contains many other
6*4882a593Smuzhiyun * functional units, such as Interrupt controllers, Timers,
7*4882a593Smuzhiyun * Power Management, System Management, GPIO, RTC, and LPC
8*4882a593Smuzhiyun * Configuration Registers.
9*4882a593Smuzhiyun *
10*4882a593Smuzhiyun * Copyright (c) 2010 CompuLab Ltd
11*4882a593Smuzhiyun * Copyright (c) 2014 Intel Corp.
12*4882a593Smuzhiyun * Author: Denis Turischev <denis@compulab.co.il>
13*4882a593Smuzhiyun */
14*4882a593Smuzhiyun
15*4882a593Smuzhiyun #include <linux/kernel.h>
16*4882a593Smuzhiyun #include <linux/module.h>
17*4882a593Smuzhiyun #include <linux/errno.h>
18*4882a593Smuzhiyun #include <linux/acpi.h>
19*4882a593Smuzhiyun #include <linux/pci.h>
20*4882a593Smuzhiyun #include <linux/mfd/core.h>
21*4882a593Smuzhiyun
22*4882a593Smuzhiyun #define SMBASE 0x40
23*4882a593Smuzhiyun #define SMBUS_IO_SIZE 64
24*4882a593Smuzhiyun
25*4882a593Smuzhiyun #define GPIO_BASE 0x44
26*4882a593Smuzhiyun #define GPIO_IO_SIZE 64
27*4882a593Smuzhiyun #define GPIO_IO_SIZE_CENTERTON 128
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun #define WDTBASE 0x84
30*4882a593Smuzhiyun #define WDT_IO_SIZE 64
31*4882a593Smuzhiyun
32*4882a593Smuzhiyun enum sch_chipsets {
33*4882a593Smuzhiyun LPC_SCH = 0, /* Intel Poulsbo SCH */
34*4882a593Smuzhiyun LPC_ITC, /* Intel Tunnel Creek */
35*4882a593Smuzhiyun LPC_CENTERTON, /* Intel Centerton */
36*4882a593Smuzhiyun LPC_QUARK_X1000, /* Intel Quark X1000 */
37*4882a593Smuzhiyun };
38*4882a593Smuzhiyun
39*4882a593Smuzhiyun struct lpc_sch_info {
40*4882a593Smuzhiyun unsigned int io_size_smbus;
41*4882a593Smuzhiyun unsigned int io_size_gpio;
42*4882a593Smuzhiyun unsigned int io_size_wdt;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun static struct lpc_sch_info sch_chipset_info[] = {
46*4882a593Smuzhiyun [LPC_SCH] = {
47*4882a593Smuzhiyun .io_size_smbus = SMBUS_IO_SIZE,
48*4882a593Smuzhiyun .io_size_gpio = GPIO_IO_SIZE,
49*4882a593Smuzhiyun },
50*4882a593Smuzhiyun [LPC_ITC] = {
51*4882a593Smuzhiyun .io_size_smbus = SMBUS_IO_SIZE,
52*4882a593Smuzhiyun .io_size_gpio = GPIO_IO_SIZE,
53*4882a593Smuzhiyun .io_size_wdt = WDT_IO_SIZE,
54*4882a593Smuzhiyun },
55*4882a593Smuzhiyun [LPC_CENTERTON] = {
56*4882a593Smuzhiyun .io_size_smbus = SMBUS_IO_SIZE,
57*4882a593Smuzhiyun .io_size_gpio = GPIO_IO_SIZE_CENTERTON,
58*4882a593Smuzhiyun .io_size_wdt = WDT_IO_SIZE,
59*4882a593Smuzhiyun },
60*4882a593Smuzhiyun [LPC_QUARK_X1000] = {
61*4882a593Smuzhiyun .io_size_gpio = GPIO_IO_SIZE,
62*4882a593Smuzhiyun .io_size_wdt = WDT_IO_SIZE,
63*4882a593Smuzhiyun },
64*4882a593Smuzhiyun };
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun static const struct pci_device_id lpc_sch_ids[] = {
67*4882a593Smuzhiyun { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH },
68*4882a593Smuzhiyun { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC },
69*4882a593Smuzhiyun { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON },
70*4882a593Smuzhiyun { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 },
71*4882a593Smuzhiyun { 0, }
72*4882a593Smuzhiyun };
73*4882a593Smuzhiyun MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
74*4882a593Smuzhiyun
75*4882a593Smuzhiyun #define LPC_NO_RESOURCE 1
76*4882a593Smuzhiyun #define LPC_SKIP_RESOURCE 2
77*4882a593Smuzhiyun
lpc_sch_get_io(struct pci_dev * pdev,int where,const char * name,struct resource * res,int size)78*4882a593Smuzhiyun static int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name,
79*4882a593Smuzhiyun struct resource *res, int size)
80*4882a593Smuzhiyun {
81*4882a593Smuzhiyun unsigned int base_addr_cfg;
82*4882a593Smuzhiyun unsigned short base_addr;
83*4882a593Smuzhiyun
84*4882a593Smuzhiyun if (size == 0)
85*4882a593Smuzhiyun return LPC_NO_RESOURCE;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun pci_read_config_dword(pdev, where, &base_addr_cfg);
88*4882a593Smuzhiyun base_addr = 0;
89*4882a593Smuzhiyun if (!(base_addr_cfg & (1 << 31)))
90*4882a593Smuzhiyun dev_warn(&pdev->dev, "Decode of the %s I/O range disabled\n",
91*4882a593Smuzhiyun name);
92*4882a593Smuzhiyun else
93*4882a593Smuzhiyun base_addr = (unsigned short)base_addr_cfg;
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun if (base_addr == 0) {
96*4882a593Smuzhiyun dev_warn(&pdev->dev, "I/O space for %s uninitialized\n", name);
97*4882a593Smuzhiyun return LPC_SKIP_RESOURCE;
98*4882a593Smuzhiyun }
99*4882a593Smuzhiyun
100*4882a593Smuzhiyun res->start = base_addr;
101*4882a593Smuzhiyun res->end = base_addr + size - 1;
102*4882a593Smuzhiyun res->flags = IORESOURCE_IO;
103*4882a593Smuzhiyun
104*4882a593Smuzhiyun return 0;
105*4882a593Smuzhiyun }
106*4882a593Smuzhiyun
lpc_sch_populate_cell(struct pci_dev * pdev,int where,const char * name,int size,int id,struct mfd_cell * cell)107*4882a593Smuzhiyun static int lpc_sch_populate_cell(struct pci_dev *pdev, int where,
108*4882a593Smuzhiyun const char *name, int size, int id,
109*4882a593Smuzhiyun struct mfd_cell *cell)
110*4882a593Smuzhiyun {
111*4882a593Smuzhiyun struct resource *res;
112*4882a593Smuzhiyun int ret;
113*4882a593Smuzhiyun
114*4882a593Smuzhiyun res = devm_kzalloc(&pdev->dev, sizeof(*res), GFP_KERNEL);
115*4882a593Smuzhiyun if (!res)
116*4882a593Smuzhiyun return -ENOMEM;
117*4882a593Smuzhiyun
118*4882a593Smuzhiyun ret = lpc_sch_get_io(pdev, where, name, res, size);
119*4882a593Smuzhiyun if (ret)
120*4882a593Smuzhiyun return ret;
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun memset(cell, 0, sizeof(*cell));
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun cell->name = name;
125*4882a593Smuzhiyun cell->resources = res;
126*4882a593Smuzhiyun cell->num_resources = 1;
127*4882a593Smuzhiyun cell->ignore_resource_conflicts = true;
128*4882a593Smuzhiyun cell->id = id;
129*4882a593Smuzhiyun
130*4882a593Smuzhiyun return 0;
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun
lpc_sch_probe(struct pci_dev * dev,const struct pci_device_id * id)133*4882a593Smuzhiyun static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun struct mfd_cell lpc_sch_cells[3];
136*4882a593Smuzhiyun struct lpc_sch_info *info = &sch_chipset_info[id->driver_data];
137*4882a593Smuzhiyun unsigned int cells = 0;
138*4882a593Smuzhiyun int ret;
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus",
141*4882a593Smuzhiyun info->io_size_smbus,
142*4882a593Smuzhiyun id->device, &lpc_sch_cells[cells]);
143*4882a593Smuzhiyun if (ret < 0)
144*4882a593Smuzhiyun return ret;
145*4882a593Smuzhiyun if (ret == 0)
146*4882a593Smuzhiyun cells++;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun ret = lpc_sch_populate_cell(dev, GPIO_BASE, "sch_gpio",
149*4882a593Smuzhiyun info->io_size_gpio,
150*4882a593Smuzhiyun id->device, &lpc_sch_cells[cells]);
151*4882a593Smuzhiyun if (ret < 0)
152*4882a593Smuzhiyun return ret;
153*4882a593Smuzhiyun if (ret == 0)
154*4882a593Smuzhiyun cells++;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt",
157*4882a593Smuzhiyun info->io_size_wdt,
158*4882a593Smuzhiyun id->device, &lpc_sch_cells[cells]);
159*4882a593Smuzhiyun if (ret < 0)
160*4882a593Smuzhiyun return ret;
161*4882a593Smuzhiyun if (ret == 0)
162*4882a593Smuzhiyun cells++;
163*4882a593Smuzhiyun
164*4882a593Smuzhiyun if (cells == 0) {
165*4882a593Smuzhiyun dev_err(&dev->dev, "All decode registers disabled.\n");
166*4882a593Smuzhiyun return -ENODEV;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
170*4882a593Smuzhiyun }
171*4882a593Smuzhiyun
lpc_sch_remove(struct pci_dev * dev)172*4882a593Smuzhiyun static void lpc_sch_remove(struct pci_dev *dev)
173*4882a593Smuzhiyun {
174*4882a593Smuzhiyun mfd_remove_devices(&dev->dev);
175*4882a593Smuzhiyun }
176*4882a593Smuzhiyun
177*4882a593Smuzhiyun static struct pci_driver lpc_sch_driver = {
178*4882a593Smuzhiyun .name = "lpc_sch",
179*4882a593Smuzhiyun .id_table = lpc_sch_ids,
180*4882a593Smuzhiyun .probe = lpc_sch_probe,
181*4882a593Smuzhiyun .remove = lpc_sch_remove,
182*4882a593Smuzhiyun };
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun module_pci_driver(lpc_sch_driver);
185*4882a593Smuzhiyun
186*4882a593Smuzhiyun MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
187*4882a593Smuzhiyun MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
188*4882a593Smuzhiyun MODULE_LICENSE("GPL");
189