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