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/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 38 #define GPIO_IO_SIZE_CENTERTON 128 39 40 #define WDTBASE 0x84 41 #define WDT_IO_SIZE 64 42 43 static struct resource smbus_sch_resource = { 44 .flags = IORESOURCE_IO, 45 }; 46 47 static struct resource gpio_sch_resource = { 48 .flags = IORESOURCE_IO, 49 }; 50 51 static struct resource wdt_sch_resource = { 52 .flags = IORESOURCE_IO, 53 }; 54 55 static struct mfd_cell lpc_sch_cells[3]; 56 57 static struct mfd_cell isch_smbus_cell = { 58 .name = "isch_smbus", 59 .num_resources = 1, 60 .resources = &smbus_sch_resource, 61 .ignore_resource_conflicts = true, 62 }; 63 64 static struct mfd_cell sch_gpio_cell = { 65 .name = "sch_gpio", 66 .num_resources = 1, 67 .resources = &gpio_sch_resource, 68 .ignore_resource_conflicts = true, 69 }; 70 71 static struct mfd_cell wdt_sch_cell = { 72 .name = "ie6xx_wdt", 73 .num_resources = 1, 74 .resources = &wdt_sch_resource, 75 .ignore_resource_conflicts = true, 76 }; 77 78 static const struct pci_device_id 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 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, cells = 0; 92 int ret; 93 94 pci_read_config_dword(dev, SMBASE, &base_addr_cfg); 95 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; 100 101 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 } 108 109 pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg); 110 base_addr = 0; 111 if (!(base_addr_cfg & (1 << 31))) 112 dev_warn(&dev->dev, "Decode of the GPIO I/O range disabled\n"); 113 else 114 base_addr = (unsigned short)base_addr_cfg; 115 116 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 } 126 127 if (id->device == PCI_DEVICE_ID_INTEL_ITC_LPC 128 || id->device == PCI_DEVICE_ID_INTEL_CENTERTON_ILB) { 129 pci_read_config_dword(dev, WDTBASE, &base_addr_cfg); 130 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; 141 } 142 } 143 144 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 161 return ret; 162 } 163 164 static void lpc_sch_remove(struct pci_dev *dev) 165 { 166 mfd_remove_devices(&dev->dev); 167 } 168 169 static struct pci_driver lpc_sch_driver = { 170 .name = "lpc_sch", 171 .id_table = lpc_sch_ids, 172 .probe = lpc_sch_probe, 173 .remove = lpc_sch_remove, 174 }; 175 176 module_pci_driver(lpc_sch_driver); 177 178 MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); 179 MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); 180 MODULE_LICENSE("GPL"); 181