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 <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 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) = { 79 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) }, 80 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC) }, 81 { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB) }, 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 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 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, 0); 131 if (ret) 132 goto out_dev; 133 134 if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC 135 || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { 136 pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); 137 if (!(base_addr_cfg & (1 << 31))) { 138 dev_err(&dev->dev, "Decode of the WDT I/O range disabled\n"); 139 ret = -ENODEV; 140 goto out_dev; 141 } 142 base_addr = (unsigned short)base_addr_cfg; 143 if (base_addr == 0) { 144 dev_err(&dev->dev, "I/O space for WDT uninitialized\n"); 145 ret = -ENODEV; 146 goto out_dev; 147 } 148 149 wdt_sch_resource.start = base_addr; 150 wdt_sch_resource.end = base_addr + WDT_IO_SIZE - 1; 151 152 for (i = 0; i < ARRAY_SIZE(tunnelcreek_cells); i++) 153 tunnelcreek_cells[i].id = id->device; 154 155 ret = mfd_add_devices(&dev->dev, 0, tunnelcreek_cells, 156 ARRAY_SIZE(tunnelcreek_cells), NULL, 0); 157 } 158 159 return ret; 160 out_dev: 161 mfd_remove_devices(&dev->dev); 162 return ret; 163 } 164 165 static void __devexit lpc_sch_remove(struct pci_dev *dev) 166 { 167 mfd_remove_devices(&dev->dev); 168 } 169 170 static struct pci_driver lpc_sch_driver = { 171 .name = "lpc_sch", 172 .id_table = lpc_sch_ids, 173 .probe = lpc_sch_probe, 174 .remove = __devexit_p(lpc_sch_remove), 175 }; 176 177 module_pci_driver(lpc_sch_driver); 178 179 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 180 MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); 181 MODULE_LICENSE("GPL"); 182