Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
lpc_sch.c
Go to the documentation of this file.
1 /*
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 <[email protected]>
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 
26 #include <linux/init.h>
27 #include <linux/kernel.h>
28 #include <linux/module.h>
29 #include <linux/errno.h>
30 #include <linux/acpi.h>
31 #include <linux/pci.h>
32 #include <linux/mfd/core.h>
33 
34 #define SMBASE 0x40
35 #define SMBUS_IO_SIZE 64
36 
37 #define GPIOBASE 0x44
38 #define GPIO_IO_SIZE 64
39 #define GPIO_IO_SIZE_CENTERTON 128
40 
41 #define WDTBASE 0x84
42 #define WDT_IO_SIZE 64
43 
44 static struct resource smbus_sch_resource = {
45  .flags = IORESOURCE_IO,
46 };
47 
48 
49 static struct resource gpio_sch_resource = {
50  .flags = IORESOURCE_IO,
51 };
52 
53 static struct mfd_cell lpc_sch_cells[] = {
54  {
55  .name = "isch_smbus",
56  .num_resources = 1,
57  .resources = &smbus_sch_resource,
58  },
59  {
60  .name = "sch_gpio",
61  .num_resources = 1,
62  .resources = &gpio_sch_resource,
63  },
64 };
65 
66 static struct resource wdt_sch_resource = {
67  .flags = IORESOURCE_IO,
68 };
69 
70 static struct mfd_cell tunnelcreek_cells[] = {
71  {
72  .name = "ie6xx_wdt",
73  .num_resources = 1,
74  .resources = &wdt_sch_resource,
75  },
76 };
77 
78 static DEFINE_PCI_DEVICE_TABLE(lpc_sch_ids) = {
82  { 0, }
83 };
84 MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
85 
86 static int __devinit lpc_sch_probe(struct pci_dev *dev,
87  const struct pci_device_id *id)
88 {
89  unsigned int base_addr_cfg;
90  unsigned short base_addr;
91  int i;
92  int ret;
93 
94  pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
95  if (!(base_addr_cfg & (1 << 31))) {
96  dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
97  return -ENODEV;
98  }
99  base_addr = (unsigned short)base_addr_cfg;
100  if (base_addr == 0) {
101  dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
102  return -ENODEV;
103  }
104 
105  smbus_sch_resource.start = base_addr;
106  smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
107 
108  pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
109  if (!(base_addr_cfg & (1 << 31))) {
110  dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
111  return -ENODEV;
112  }
113  base_addr = (unsigned short)base_addr_cfg;
114  if (base_addr == 0) {
115  dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
116  return -ENODEV;
117  }
118 
119  gpio_sch_resource.start = base_addr;
120 
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 
126  for (i=0; i < ARRAY_SIZE(lpc_sch_cells); i++)
127  lpc_sch_cells[i].id = id->device;
128 
129  ret = mfd_add_devices(&dev->dev, 0,
130  lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL,
131  0, NULL);
132  if (ret)
133  goto out_dev;
134 
137  pci_read_config_dword(dev, WDTBASE, &base_addr_cfg);
138  if (!(base_addr_cfg & (1 << 31))) {
139  dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n");
140  ret = -ENODEV;
141  goto out_dev;
142  }
143  base_addr = (unsigned short)base_addr_cfg;
144  if (base_addr == 0) {
145  dev_err(&dev->dev, "I/O space for WDT uninitialized\n");
146  ret = -ENODEV;
147  goto out_dev;
148  }
149 
150  wdt_sch_resource.start = base_addr;
151  wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1;
152 
153  for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++)
154  tunnelcreek_cells[i].id = id->device;
155 
156  ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells,
157  ARRAY_SIZE(tunnelcreek_cells), NULL,
158  0, NULL);
159  }
160 
161  return ret;
162 out_dev:
163  mfd_remove_devices(&dev->dev);
164  return ret;
165 }
166 
167 static void __devexit lpc_sch_remove(struct pci_dev *dev)
168 {
169  mfd_remove_devices(&dev->dev);
170 }
171 
172 static struct pci_driver lpc_sch_driver = {
173  .name = "lpc_sch",
174  .id_table = lpc_sch_ids,
175  .probe = lpc_sch_probe,
176  .remove = __devexit_p(lpc_sch_remove),
177 };
178 
179 module_pci_driver(lpc_sch_driver);
180 
181 MODULE_AUTHOR("Denis Turischev <[email protected]>");
182 MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
183 MODULE_LICENSE("GPL");