blob: 4ee755034f3b2893899370ec1756d7e69018a8b8 [file] [log] [blame]
Denis Turischeve82c60a2010-02-19 11:26:25 +01001/*
2 * lpc_sch.c - LPC interface for Intel Poulsbo SCH
3 *
4 * LPC bridge function of the Intel SCH contains many other
5 * functional units, such as Interrupt controllers, Timers,
6 * Power Management, System Management, GPIO, RTC, and LPC
7 * Configuration Registers.
8 *
9 * Copyright (c) 2010 CompuLab Ltd
10 * Author: Denis Turischev <denis@compulab.co.il>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License 2 as published
14 * by the Free Software Foundation.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; see the file COPYING. If not, write to
23 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24 */
25
Denis Turischeve82c60a2010-02-19 11:26:25 +010026#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/errno.h>
29#include <linux/acpi.h>
30#include <linux/pci.h>
31#include <linux/mfd/core.h>
32
33#define SMBASE 0x40
34#define SMBUS_IO_SIZE 64
35
36#define GPIOBASE 0x44
37#define GPIO_IO_SIZE 64
Seth Heasley8ee3c2a2012-04-17 14:09:22 -070038#define GPIO_IO_SIZE_CENTERTON 128
Denis Turischeve82c60a2010-02-19 11:26:25 +010039
Alexander Stein19921ef2011-06-16 13:05:49 +020040#define WDTBASE 0x84
41#define WDT_IO_SIZE 64
42
Denis Turischeve82c60a2010-02-19 11:26:25 +010043static struct resource smbus_sch_resource = {
44 .flags = IORESOURCE_IO,
45};
46
Denis Turischeve82c60a2010-02-19 11:26:25 +010047static struct resource gpio_sch_resource = {
48 .flags = IORESOURCE_IO,
49};
50
Alexander Stein19921ef2011-06-16 13:05:49 +020051static struct resource wdt_sch_resource = {
52 .flags = IORESOURCE_IO,
53};
54
Darren Hart5829e9b2013-02-08 15:20:36 -080055static struct mfd_cell lpc_sch_cells[3];
56
57static struct mfd_cell isch_smbus_cell = {
58 .name = "isch_smbus",
59 .num_resources = 1,
60 .resources = &smbus_sch_resource,
Johannes Thumshirn6bfd1e62013-10-23 13:31:00 +020061 .ignore_resource_conflicts = true,
Darren Hart5829e9b2013-02-08 15:20:36 -080062};
63
64static struct mfd_cell sch_gpio_cell = {
65 .name = "sch_gpio",
66 .num_resources = 1,
67 .resources = &gpio_sch_resource,
Johannes Thumshirn6bfd1e62013-10-23 13:31:00 +020068 .ignore_resource_conflicts = true,
Darren Hart5829e9b2013-02-08 15:20:36 -080069};
70
71static struct mfd_cell wdt_sch_cell = {
72 .name = "ie6xx_wdt",
73 .num_resources = 1,
74 .resources = &wdt_sch_resource,
Johannes Thumshirn6bfd1e62013-10-23 13:31:00 +020075 .ignore_resource_conflicts = true,
Alexander Stein19921ef2011-06-16 13:05:49 +020076};
77
Jingoo Han36fcd062013-12-03 08:15:39 +090078static const struct pci_device_id lpc_sch_ids[] = {
Denis Turischeve82c60a2010-02-19 11:26:25 +010079 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
Denis Turischeve967f772011-03-13 17:28:59 +020080 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) },
Seth Heasley8ee3c2a2012-04-17 14:09:22 -070081 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) },
Denis Turischeve82c60a2010-02-19 11:26:25 +010082 { 0, }
83};
84MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
85
Bill Pembertonf791be42012-11-19 13:23:04 -050086static int lpc_sch_probe(struct pci_dev *dev,
Denis Turischeve82c60a2010-02-19 11:26:25 +010087 const struct pci_device_id *id)
88{
89 unsigned int base_addr_cfg;
90 unsigned short base_addr;
Darren Hart5829e9b2013-02-08 15:20:36 -080091 int i, cells = 0;
Alexander Stein19921ef2011-06-16 13:05:49 +020092 int ret;
Denis Turischeve82c60a2010-02-19 11:26:25 +010093
94 pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
Darren Hart5829e9b2013-02-08 15:20:36 -080095 base_addr = 0;
96 if (!(base_addr_cfg & (1 << 31)))
97 dev_warn(&dev->dev, "Decode of the SMBus I/O range disabled\n");
98 else
99 base_addr = (unsigned short)base_addr_cfg;
Denis Turischeve82c60a2010-02-19 11:26:25 +0100100
Darren Hart5829e9b2013-02-08 15:20:36 -0800101 if (base_addr == 0) {
102 dev_warn(&dev->dev, "I/O space for SMBus uninitialized\n");
103 } else {
104 lpc_sch_cells[cells++] = isch_smbus_cell;
105 smbus_sch_resource.start = base_addr;
106 smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
107 }
Denis Turischeve82c60a2010-02-19 11:26:25 +0100108
109 pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
Darren Hart5829e9b2013-02-08 15:20:36 -0800110 base_addr = 0;
111 if (!(base_addr_cfg & (1 << 31)))
112 dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n");
Seth Heasley8ee3c2a2012-04-17 14:09:22 -0700113 else
Darren Hart5829e9b2013-02-08 15:20:36 -0800114 base_addr = (unsigned short)base_addr_cfg;
Denis Turischeve82c60a2010-02-19 11:26:25 +0100115
Darren Hart5829e9b2013-02-08 15:20:36 -0800116 if (base_addr == 0) {
117 dev_warn(&dev->dev, "I/O space for GPIO uninitialized\n");
118 } else {
119 lpc_sch_cells[cells++] = sch_gpio_cell;
120 gpio_sch_resource.start = base_addr;
121 if (id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB)
122 gpio_sch_resource.end = base_addr + GPIO_IO_SIZE_CENTERTON - 1;
123 else
124 gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
125 }
Alexander Stein19921ef2011-06-16 13:05:49 +0200126
Seth Heasley8ee3c2a2012-04-17 14:09:22 -0700127 if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC
Darren Hart5829e9b2013-02-08 15:20:36 -0800128 || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) {
Alexander Stein19921ef2011-06-16 13:05:49 +0200129 pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
Darren Hart5829e9b2013-02-08 15:20:36 -0800130 base_addr = 0;
131 if (!(base_addr_cfg & (1 << 31)))
132 dev_warn(&dev->dev, "Decode of the WDT I/O range disabled\n");
133 else
134 base_addr = (unsigned short)base_addr_cfg;
135 if (base_addr == 0)
136 dev_warn(&dev->dev, "I/O space for WDT uninitialized\n");
137 else {
138 lpc_sch_cells[cells++] = wdt_sch_cell;
139 wdt_sch_resource.start = base_addr;
140 wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
Alexander Stein19921ef2011-06-16 13:05:49 +0200141 }
Alexander Stein19921ef2011-06-16 13:05:49 +0200142 }
143
Darren Hart5829e9b2013-02-08 15:20:36 -0800144 if (WARN_ON(cells > ARRAY_SIZE(lpc_sch_cells))) {
145 dev_err(&dev->dev, "Cell count exceeds array size");
146 return -ENODEV;
147 }
148
149 if (cells == 0) {
150 dev_err(&dev->dev, "All decode registers disabled.\n");
151 return -ENODEV;
152 }
153
154 for (i = 0; i < cells; i++)
155 lpc_sch_cells[i].id = id->device;
156
157 ret = mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL);
158 if (ret)
159 mfd_remove_devices(&dev->dev);
160
Alexander Stein19921ef2011-06-16 13:05:49 +0200161 return ret;
Denis Turischeve82c60a2010-02-19 11:26:25 +0100162}
163
Bill Pemberton4740f732012-11-19 13:26:01 -0500164static void lpc_sch_remove(struct pci_dev *dev)
Denis Turischeve82c60a2010-02-19 11:26:25 +0100165{
166 mfd_remove_devices(&dev->dev);
167}
168
169static struct pci_driver lpc_sch_driver = {
170 .name = "lpc_sch",
171 .id_table = lpc_sch_ids,
172 .probe = lpc_sch_probe,
Bill Pemberton84449212012-11-19 13:20:24 -0500173 .remove = lpc_sch_remove,
Denis Turischeve82c60a2010-02-19 11:26:25 +0100174};
175
Axel Lin38a36f52012-04-03 09:09:19 +0800176module_pci_driver(lpc_sch_driver);
Denis Turischeve82c60a2010-02-19 11:26:25 +0100177
178MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>");
179MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
180MODULE_LICENSE("GPL");