Blame view
drivers/mfd/lpc_sch.c
5.14 KB
e82c60ae7
|
1 2 3 4 5 6 7 8 9 |
/* * lpc_sch.c - LPC interface for Intel Poulsbo SCH * * LPC bridge function of the Intel SCH contains many other * functional units, such as Interrupt controllers, Timers, * Power Management, System Management, GPIO, RTC, and LPC * Configuration Registers. * * Copyright (c) 2010 CompuLab Ltd |
85de80e8d
|
10 |
* Copyright (c) 2014 Intel Corp. |
e82c60ae7
|
11 12 13 14 15 16 17 18 19 20 |
* Author: Denis Turischev <denis@compulab.co.il> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License 2 as published * by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. |
e82c60ae7
|
21 |
*/ |
e82c60ae7
|
22 23 24 25 26 27 28 29 30 31 32 33 |
#include <linux/kernel.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/acpi.h> #include <linux/pci.h> #include <linux/mfd/core.h> #define SMBASE 0x40 #define SMBUS_IO_SIZE 64 #define GPIOBASE 0x44 #define GPIO_IO_SIZE 64 |
8ee3c2a79
|
34 |
#define GPIO_IO_SIZE_CENTERTON 128 |
e82c60ae7
|
35 |
|
ec689a8a8
|
36 37 |
/* Intel Quark X1000 GPIO IRQ Number */ #define GPIO_IRQ_QUARK_X1000 9 |
19921ef61
|
38 39 |
#define WDTBASE 0x84 #define WDT_IO_SIZE 64 |
b24512c86
|
40 41 42 43 |
enum sch_chipsets { LPC_SCH = 0, /* Intel Poulsbo SCH */ LPC_ITC, /* Intel Tunnel Creek */ LPC_CENTERTON, /* Intel Centerton */ |
ec689a8a8
|
44 |
LPC_QUARK_X1000, /* Intel Quark X1000 */ |
e82c60ae7
|
45 |
}; |
b24512c86
|
46 47 48 49 |
struct lpc_sch_info { unsigned int io_size_smbus; unsigned int io_size_gpio; unsigned int io_size_wdt; |
ec689a8a8
|
50 |
int irq_gpio; |
e82c60ae7
|
51 |
}; |
b24512c86
|
52 53 54 55 |
static struct lpc_sch_info sch_chipset_info[] = { [LPC_SCH] = { .io_size_smbus = SMBUS_IO_SIZE, .io_size_gpio = GPIO_IO_SIZE, |
ec689a8a8
|
56 |
.irq_gpio = -1, |
b24512c86
|
57 58 59 60 61 |
}, [LPC_ITC] = { .io_size_smbus = SMBUS_IO_SIZE, .io_size_gpio = GPIO_IO_SIZE, .io_size_wdt = WDT_IO_SIZE, |
ec689a8a8
|
62 |
.irq_gpio = -1, |
b24512c86
|
63 64 65 66 67 |
}, [LPC_CENTERTON] = { .io_size_smbus = SMBUS_IO_SIZE, .io_size_gpio = GPIO_IO_SIZE_CENTERTON, .io_size_wdt = WDT_IO_SIZE, |
ec689a8a8
|
68 69 70 71 72 |
.irq_gpio = -1, }, [LPC_QUARK_X1000] = { .io_size_gpio = GPIO_IO_SIZE, .irq_gpio = GPIO_IRQ_QUARK_X1000, |
b24512c86
|
73 |
}, |
19921ef61
|
74 |
}; |
36fcd06c4
|
75 |
static const struct pci_device_id lpc_sch_ids[] = { |
b24512c86
|
76 77 78 |
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC), LPC_SCH }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ITC_LPC), LPC_ITC }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_CENTERTON_ILB), LPC_CENTERTON }, |
ec689a8a8
|
79 |
{ PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_QUARK_X1000_ILB), LPC_QUARK_X1000 }, |
e82c60ae7
|
80 81 82 |
{ 0, } }; MODULE_DEVICE_TABLE(pci, lpc_sch_ids); |
b24512c86
|
83 84 85 86 87 |
#define LPC_NO_RESOURCE 1 #define LPC_SKIP_RESOURCE 2 static int lpc_sch_get_io(struct pci_dev *pdev, int where, const char *name, struct resource *res, int size) |
e82c60ae7
|
88 89 90 |
{ unsigned int base_addr_cfg; unsigned short base_addr; |
b24512c86
|
91 92 93 94 |
if (size == 0) return LPC_NO_RESOURCE; pci_read_config_dword(pdev, where, &base_addr_cfg); |
5829e9b64
|
95 96 |
base_addr = 0; if (!(base_addr_cfg & (1 << 31))) |
b24512c86
|
97 98 99 |
dev_warn(&pdev->dev, "Decode of the %s I/O range disabled ", name); |
5829e9b64
|
100 101 |
else base_addr = (unsigned short)base_addr_cfg; |
e82c60ae7
|
102 |
|
e82c60ae7
|
103 |
if (base_addr == 0) { |
b24512c86
|
104 105 106 |
dev_warn(&pdev->dev, "I/O space for %s uninitialized ", name); return LPC_SKIP_RESOURCE; |
e82c60ae7
|
107 |
} |
b24512c86
|
108 109 110 |
res->start = base_addr; res->end = base_addr + size - 1; res->flags = IORESOURCE_IO; |
e967f77d9
|
111 |
|
b24512c86
|
112 113 |
return 0; } |
19921ef61
|
114 |
|
b24512c86
|
115 |
static int lpc_sch_populate_cell(struct pci_dev *pdev, int where, |
ec689a8a8
|
116 117 |
const char *name, int size, int irq, int id, struct mfd_cell *cell) |
b24512c86
|
118 119 120 |
{ struct resource *res; int ret; |
19921ef61
|
121 |
|
ec689a8a8
|
122 |
res = devm_kcalloc(&pdev->dev, 2, sizeof(*res), GFP_KERNEL); |
b24512c86
|
123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
if (!res) return -ENOMEM; ret = lpc_sch_get_io(pdev, where, name, res, size); if (ret) return ret; memset(cell, 0, sizeof(*cell)); cell->name = name; cell->resources = res; cell->num_resources = 1; cell->ignore_resource_conflicts = true; cell->id = id; |
ec689a8a8
|
137 138 139 140 141 142 143 144 145 146 147 |
/* Check if we need to add an IRQ resource */ if (irq < 0) return 0; res++; res->start = irq; res->end = irq; res->flags = IORESOURCE_IRQ; cell->num_resources++; |
b24512c86
|
148 149 150 151 152 153 154 155 156 157 158 |
return 0; } static int lpc_sch_probe(struct pci_dev *dev, const struct pci_device_id *id) { struct mfd_cell lpc_sch_cells[3]; struct lpc_sch_info *info = &sch_chipset_info[id->driver_data]; unsigned int cells = 0; int ret; ret = lpc_sch_populate_cell(dev, SMBASE, "isch_smbus", |
ec689a8a8
|
159 |
info->io_size_smbus, -1, |
b24512c86
|
160 161 162 163 164 165 166 |
id->device, &lpc_sch_cells[cells]); if (ret < 0) return ret; if (ret == 0) cells++; ret = lpc_sch_populate_cell(dev, GPIOBASE, "sch_gpio", |
ec689a8a8
|
167 |
info->io_size_gpio, info->irq_gpio, |
b24512c86
|
168 169 170 171 172 173 174 |
id->device, &lpc_sch_cells[cells]); if (ret < 0) return ret; if (ret == 0) cells++; ret = lpc_sch_populate_cell(dev, WDTBASE, "ie6xx_wdt", |
ec689a8a8
|
175 |
info->io_size_wdt, -1, |
b24512c86
|
176 177 178 179 180 |
id->device, &lpc_sch_cells[cells]); if (ret < 0) return ret; if (ret == 0) cells++; |
19921ef61
|
181 |
|
5829e9b64
|
182 183 184 185 |
if (cells == 0) { dev_err(&dev->dev, "All decode registers disabled. "); return -ENODEV; |
19921ef61
|
186 |
} |
bde3e706a
|
187 |
return mfd_add_devices(&dev->dev, 0, lpc_sch_cells, cells, NULL, 0, NULL); |
e82c60ae7
|
188 |
} |
4740f73fe
|
189 |
static void lpc_sch_remove(struct pci_dev *dev) |
e82c60ae7
|
190 191 192 193 194 195 196 197 |
{ mfd_remove_devices(&dev->dev); } static struct pci_driver lpc_sch_driver = { .name = "lpc_sch", .id_table = lpc_sch_ids, .probe = lpc_sch_probe, |
84449216b
|
198 |
.remove = lpc_sch_remove, |
e82c60ae7
|
199 |
}; |
38a36f5a6
|
200 |
module_pci_driver(lpc_sch_driver); |
e82c60ae7
|
201 202 203 204 |
MODULE_AUTHOR("Denis Turischev <denis@compulab.co.il>"); MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH"); MODULE_LICENSE("GPL"); |