mfd: Add LPC driver for Intel ICH chipsets

This driver currently creates resources for use by a forthcoming ICH
chipset GPIO driver. It could be expanded to create the resources for
converting the esb2rom (mtd) and iTCO_wdt (wdt), and potentially more,
drivers to use the mfd model.

Signed-off-by: Aaron Sierra <asierra@xes-inc.com>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Aaron Sierra 2012-03-28 09:43:10 -05:00 committed by Samuel Ortiz
parent f4bf7cf4ca
commit 4630b130b3
4 changed files with 770 additions and 0 deletions

View file

@ -748,6 +748,15 @@ config LPC_SCH
LPC bridge function of the Intel SCH provides support for
System Management Bus and General Purpose I/O.
config LPC_ICH
tristate "Intel ICH LPC"
depends on PCI
select MFD_CORE
help
The LPC bridge function of the Intel ICH provides support for
many functional units. This driver provides needed support for
other drivers to control these functions, currently GPIO.
config MFD_RDC321X
tristate "Support for RDC-R321x southbridge"
select MFD_CORE

View file

@ -99,6 +99,7 @@ obj-$(CONFIG_MFD_DB5500_PRCMU) += db5500-prcmu.o
obj-$(CONFIG_MFD_TIMBERDALE) += timberdale.o
obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
obj-$(CONFIG_LPC_SCH) += lpc_sch.o
obj-$(CONFIG_LPC_ICH) += lpc_ich.o
obj-$(CONFIG_MFD_RDC321X) += rdc321x-southbridge.o
obj-$(CONFIG_MFD_JANZ_CMODIO) += janz-cmodio.o
obj-$(CONFIG_MFD_JZ4740_ADC) += jz4740-adc.o

719
drivers/mfd/lpc_ich.c Normal file
View file

@ -0,0 +1,719 @@
/*
* lpc_ich.c - LPC interface for Intel ICH
*
* LPC bridge function of the Intel ICH contains many other
* functional units, such as Interrupt controllers, Timers,
* Power Management, System Management, GPIO, RTC, and LPC
* Configuration Registers.
*
* This driver is derived from lpc_sch.
* Copyright (c) 2011 Extreme Engineering Solution, Inc.
* Author: Aaron Sierra <asierra@xes-inc.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This driver supports the following I/O Controller hubs:
* (See the intel documentation on http://developer.intel.com.)
* document number 290655-003, 290677-014: 82801AA (ICH), 82801AB (ICHO)
* document number 290687-002, 298242-027: 82801BA (ICH2)
* document number 290733-003, 290739-013: 82801CA (ICH3-S)
* document number 290716-001, 290718-007: 82801CAM (ICH3-M)
* document number 290744-001, 290745-025: 82801DB (ICH4)
* document number 252337-001, 252663-008: 82801DBM (ICH4-M)
* document number 273599-001, 273645-002: 82801E (C-ICH)
* document number 252516-001, 252517-028: 82801EB (ICH5), 82801ER (ICH5R)
* document number 300641-004, 300884-013: 6300ESB
* document number 301473-002, 301474-026: 82801F (ICH6)
* document number 313082-001, 313075-006: 631xESB, 632xESB
* document number 307013-003, 307014-024: 82801G (ICH7)
* document number 322896-001, 322897-001: NM10
* document number 313056-003, 313057-017: 82801H (ICH8)
* document number 316972-004, 316973-012: 82801I (ICH9)
* document number 319973-002, 319974-002: 82801J (ICH10)
* document number 322169-001, 322170-003: 5 Series, 3400 Series (PCH)
* document number 320066-003, 320257-008: EP80597 (IICH)
* document number 324645-001, 324646-001: Cougar Point (CPT)
* document number TBD : Patsburg (PBG)
* document number TBD : DH89xxCC
* document number TBD : Panther Point
* document number TBD : Lynx Point
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/init.h>
#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>
#include <linux/mfd/lpc_ich.h>
#define ACPIBASE 0x40
#define ACPIBASE_GPE_OFF 0x28
#define ACPIBASE_GPE_END 0x2f
#define ACPICTRL 0x44
#define GPIOBASE 0x48
#define GPIOCTRL 0x4C
static int lpc_ich_acpi_save = -1;
static int lpc_ich_gpio_save = -1;
static struct resource gpio_ich_res[] = {
/* GPIO */
{
.flags = IORESOURCE_IO,
},
/* ACPI - GPE0 */
{
.flags = IORESOURCE_IO,
},
};
enum lpc_cells {
LPC_GPIO = 0,
};
static struct mfd_cell lpc_ich_cells[] = {
[LPC_GPIO] = {
.name = "gpio_ich",
.num_resources = ARRAY_SIZE(gpio_ich_res),
.resources = gpio_ich_res,
.ignore_resource_conflicts = true,
},
};
/* chipset related info */
enum lpc_chipsets {
LPC_ICH = 0, /* ICH */
LPC_ICH0, /* ICH0 */
LPC_ICH2, /* ICH2 */
LPC_ICH2M, /* ICH2-M */
LPC_ICH3, /* ICH3-S */
LPC_ICH3M, /* ICH3-M */
LPC_ICH4, /* ICH4 */
LPC_ICH4M, /* ICH4-M */
LPC_CICH, /* C-ICH */
LPC_ICH5, /* ICH5 & ICH5R */
LPC_6300ESB, /* 6300ESB */
LPC_ICH6, /* ICH6 & ICH6R */
LPC_ICH6M, /* ICH6-M */
LPC_ICH6W, /* ICH6W & ICH6RW */
LPC_631XESB, /* 631xESB/632xESB */
LPC_ICH7, /* ICH7 & ICH7R */
LPC_ICH7DH, /* ICH7DH */
LPC_ICH7M, /* ICH7-M & ICH7-U */
LPC_ICH7MDH, /* ICH7-M DH */
LPC_NM10, /* NM10 */
LPC_ICH8, /* ICH8 & ICH8R */
LPC_ICH8DH, /* ICH8DH */
LPC_ICH8DO, /* ICH8DO */
LPC_ICH8M, /* ICH8M */
LPC_ICH8ME, /* ICH8M-E */
LPC_ICH9, /* ICH9 */
LPC_ICH9R, /* ICH9R */
LPC_ICH9DH, /* ICH9DH */
LPC_ICH9DO, /* ICH9DO */
LPC_ICH9M, /* ICH9M */
LPC_ICH9ME, /* ICH9M-E */
LPC_ICH10, /* ICH10 */
LPC_ICH10R, /* ICH10R */
LPC_ICH10D, /* ICH10D */
LPC_ICH10DO, /* ICH10DO */
LPC_PCH, /* PCH Desktop Full Featured */
LPC_PCHM, /* PCH Mobile Full Featured */
LPC_P55, /* P55 */
LPC_PM55, /* PM55 */
LPC_H55, /* H55 */
LPC_QM57, /* QM57 */
LPC_H57, /* H57 */
LPC_HM55, /* HM55 */
LPC_Q57, /* Q57 */
LPC_HM57, /* HM57 */
LPC_PCHMSFF, /* PCH Mobile SFF Full Featured */
LPC_QS57, /* QS57 */
LPC_3400, /* 3400 */
LPC_3420, /* 3420 */
LPC_3450, /* 3450 */
LPC_EP80579, /* EP80579 */
LPC_CPT, /* Cougar Point */
LPC_CPTD, /* Cougar Point Desktop */
LPC_CPTM, /* Cougar Point Mobile */
LPC_PBG, /* Patsburg */
LPC_DH89XXCC, /* DH89xxCC */
LPC_PPT, /* Panther Point */
LPC_LPT, /* Lynx Point */
};
struct lpc_ich_info lpc_chipset_info[] __devinitdata = {
[LPC_ICH] = {
.name = "ICH",
},
[LPC_ICH0] = {
.name = "ICH0",
},
[LPC_ICH2] = {
.name = "ICH2",
},
[LPC_ICH2M] = {
.name = "ICH2-M",
},
[LPC_ICH3] = {
.name = "ICH3-S",
},
[LPC_ICH3M] = {
.name = "ICH3-M",
},
[LPC_ICH4] = {
.name = "ICH4",
},
[LPC_ICH4M] = {
.name = "ICH4-M",
},
[LPC_CICH] = {
.name = "C-ICH",
},
[LPC_ICH5] = {
.name = "ICH5 or ICH5R",
},
[LPC_6300ESB] = {
.name = "6300ESB",
},
[LPC_ICH6] = {
.name = "ICH6 or ICH6R",
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH6M] = {
.name = "ICH6-M",
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH6W] = {
.name = "ICH6W or ICH6RW",
.gpio_version = ICH_V6_GPIO,
},
[LPC_631XESB] = {
.name = "631xESB/632xESB",
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH7] = {
.name = "ICH7 or ICH7R",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7DH] = {
.name = "ICH7DH",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7M] = {
.name = "ICH7-M or ICH7-U",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7MDH] = {
.name = "ICH7-M DH",
.gpio_version = ICH_V7_GPIO,
},
[LPC_NM10] = {
.name = "NM10",
},
[LPC_ICH8] = {
.name = "ICH8 or ICH8R",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8DH] = {
.name = "ICH8DH",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8DO] = {
.name = "ICH8DO",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8M] = {
.name = "ICH8M",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8ME] = {
.name = "ICH8M-E",
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH9] = {
.name = "ICH9",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9R] = {
.name = "ICH9R",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9DH] = {
.name = "ICH9DH",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9DO] = {
.name = "ICH9DO",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9M] = {
.name = "ICH9M",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9ME] = {
.name = "ICH9M-E",
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH10] = {
.name = "ICH10",
.gpio_version = ICH_V10CONS_GPIO,
},
[LPC_ICH10R] = {
.name = "ICH10R",
.gpio_version = ICH_V10CONS_GPIO,
},
[LPC_ICH10D] = {
.name = "ICH10D",
.gpio_version = ICH_V10CORP_GPIO,
},
[LPC_ICH10DO] = {
.name = "ICH10DO",
.gpio_version = ICH_V10CORP_GPIO,
},
[LPC_PCH] = {
.name = "PCH Desktop Full Featured",
.gpio_version = ICH_V5_GPIO,
},
[LPC_PCHM] = {
.name = "PCH Mobile Full Featured",
.gpio_version = ICH_V5_GPIO,
},
[LPC_P55] = {
.name = "P55",
.gpio_version = ICH_V5_GPIO,
},
[LPC_PM55] = {
.name = "PM55",
.gpio_version = ICH_V5_GPIO,
},
[LPC_H55] = {
.name = "H55",
.gpio_version = ICH_V5_GPIO,
},
[LPC_QM57] = {
.name = "QM57",
.gpio_version = ICH_V5_GPIO,
},
[LPC_H57] = {
.name = "H57",
.gpio_version = ICH_V5_GPIO,
},
[LPC_HM55] = {
.name = "HM55",
.gpio_version = ICH_V5_GPIO,
},
[LPC_Q57] = {
.name = "Q57",
.gpio_version = ICH_V5_GPIO,
},
[LPC_HM57] = {
.name = "HM57",
.gpio_version = ICH_V5_GPIO,
},
[LPC_PCHMSFF] = {
.name = "PCH Mobile SFF Full Featured",
.gpio_version = ICH_V5_GPIO,
},
[LPC_QS57] = {
.name = "QS57",
.gpio_version = ICH_V5_GPIO,
},
[LPC_3400] = {
.name = "3400",
.gpio_version = ICH_V5_GPIO,
},
[LPC_3420] = {
.name = "3420",
.gpio_version = ICH_V5_GPIO,
},
[LPC_3450] = {
.name = "3450",
.gpio_version = ICH_V5_GPIO,
},
[LPC_EP80579] = {
.name = "EP80579",
},
[LPC_CPT] = {
.name = "Cougar Point",
.gpio_version = ICH_V5_GPIO,
},
[LPC_CPTD] = {
.name = "Cougar Point Desktop",
.gpio_version = ICH_V5_GPIO,
},
[LPC_CPTM] = {
.name = "Cougar Point Mobile",
.gpio_version = ICH_V5_GPIO,
},
[LPC_PBG] = {
.name = "Patsburg",
},
[LPC_DH89XXCC] = {
.name = "DH89xxCC",
},
[LPC_PPT] = {
.name = "Panther Point",
},
[LPC_LPT] = {
.name = "Lynx Point",
},
};
/*
* This data only exists for exporting the supported PCI ids
* via MODULE_DEVICE_TABLE. We do not actually register a
* pci_driver, because the I/O Controller Hub has also other
* functions that probably will be registered by other drivers.
*/
static DEFINE_PCI_DEVICE_TABLE(lpc_ich_ids) = {
{ PCI_VDEVICE(INTEL, 0x2410), LPC_ICH},
{ PCI_VDEVICE(INTEL, 0x2420), LPC_ICH0},
{ PCI_VDEVICE(INTEL, 0x2440), LPC_ICH2},
{ PCI_VDEVICE(INTEL, 0x244c), LPC_ICH2M},
{ PCI_VDEVICE(INTEL, 0x2480), LPC_ICH3},
{ PCI_VDEVICE(INTEL, 0x248c), LPC_ICH3M},
{ PCI_VDEVICE(INTEL, 0x24c0), LPC_ICH4},
{ PCI_VDEVICE(INTEL, 0x24cc), LPC_ICH4M},
{ PCI_VDEVICE(INTEL, 0x2450), LPC_CICH},
{ PCI_VDEVICE(INTEL, 0x24d0), LPC_ICH5},
{ PCI_VDEVICE(INTEL, 0x25a1), LPC_6300ESB},
{ PCI_VDEVICE(INTEL, 0x2640), LPC_ICH6},
{ PCI_VDEVICE(INTEL, 0x2641), LPC_ICH6M},
{ PCI_VDEVICE(INTEL, 0x2642), LPC_ICH6W},
{ PCI_VDEVICE(INTEL, 0x2670), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2671), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2672), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2673), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2674), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2675), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2676), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2677), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2678), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x2679), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267a), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267b), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267c), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267d), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267e), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x267f), LPC_631XESB},
{ PCI_VDEVICE(INTEL, 0x27b8), LPC_ICH7},
{ PCI_VDEVICE(INTEL, 0x27b0), LPC_ICH7DH},
{ PCI_VDEVICE(INTEL, 0x27b9), LPC_ICH7M},
{ PCI_VDEVICE(INTEL, 0x27bd), LPC_ICH7MDH},
{ PCI_VDEVICE(INTEL, 0x27bc), LPC_NM10},
{ PCI_VDEVICE(INTEL, 0x2810), LPC_ICH8},
{ PCI_VDEVICE(INTEL, 0x2812), LPC_ICH8DH},
{ PCI_VDEVICE(INTEL, 0x2814), LPC_ICH8DO},
{ PCI_VDEVICE(INTEL, 0x2815), LPC_ICH8M},
{ PCI_VDEVICE(INTEL, 0x2811), LPC_ICH8ME},
{ PCI_VDEVICE(INTEL, 0x2918), LPC_ICH9},
{ PCI_VDEVICE(INTEL, 0x2916), LPC_ICH9R},
{ PCI_VDEVICE(INTEL, 0x2912), LPC_ICH9DH},
{ PCI_VDEVICE(INTEL, 0x2914), LPC_ICH9DO},
{ PCI_VDEVICE(INTEL, 0x2919), LPC_ICH9M},
{ PCI_VDEVICE(INTEL, 0x2917), LPC_ICH9ME},
{ PCI_VDEVICE(INTEL, 0x3a18), LPC_ICH10},
{ PCI_VDEVICE(INTEL, 0x3a16), LPC_ICH10R},
{ PCI_VDEVICE(INTEL, 0x3a1a), LPC_ICH10D},
{ PCI_VDEVICE(INTEL, 0x3a14), LPC_ICH10DO},
{ PCI_VDEVICE(INTEL, 0x3b00), LPC_PCH},
{ PCI_VDEVICE(INTEL, 0x3b01), LPC_PCHM},
{ PCI_VDEVICE(INTEL, 0x3b02), LPC_P55},
{ PCI_VDEVICE(INTEL, 0x3b03), LPC_PM55},
{ PCI_VDEVICE(INTEL, 0x3b06), LPC_H55},
{ PCI_VDEVICE(INTEL, 0x3b07), LPC_QM57},
{ PCI_VDEVICE(INTEL, 0x3b08), LPC_H57},
{ PCI_VDEVICE(INTEL, 0x3b09), LPC_HM55},
{ PCI_VDEVICE(INTEL, 0x3b0a), LPC_Q57},
{ PCI_VDEVICE(INTEL, 0x3b0b), LPC_HM57},
{ PCI_VDEVICE(INTEL, 0x3b0d), LPC_PCHMSFF},
{ PCI_VDEVICE(INTEL, 0x3b0f), LPC_QS57},
{ PCI_VDEVICE(INTEL, 0x3b12), LPC_3400},
{ PCI_VDEVICE(INTEL, 0x3b14), LPC_3420},
{ PCI_VDEVICE(INTEL, 0x3b16), LPC_3450},
{ PCI_VDEVICE(INTEL, 0x5031), LPC_EP80579},
{ PCI_VDEVICE(INTEL, 0x1c41), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c42), LPC_CPTD},
{ PCI_VDEVICE(INTEL, 0x1c43), LPC_CPTM},
{ PCI_VDEVICE(INTEL, 0x1c44), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c45), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c46), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c47), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c48), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c49), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4a), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4b), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4c), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4d), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4e), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c4f), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c50), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c51), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c52), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c53), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c54), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c55), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c56), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c57), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c58), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c59), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5a), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5b), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5c), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5d), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5e), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1c5f), LPC_CPT},
{ PCI_VDEVICE(INTEL, 0x1d40), LPC_PBG},
{ PCI_VDEVICE(INTEL, 0x1d41), LPC_PBG},
{ PCI_VDEVICE(INTEL, 0x2310), LPC_DH89XXCC},
{ PCI_VDEVICE(INTEL, 0x1e40), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e41), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e42), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e43), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e44), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e45), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e46), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e47), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e48), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e49), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4a), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4b), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4c), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4d), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4e), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e4f), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e50), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e51), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e52), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e53), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e54), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e55), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e56), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e57), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e58), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e59), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5a), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5b), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5c), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5d), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5e), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x1e5f), LPC_PPT},
{ PCI_VDEVICE(INTEL, 0x8c40), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c41), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c42), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c43), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c44), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c45), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c46), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c47), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c48), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c49), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4a), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4b), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4c), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4d), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4e), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c4f), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c50), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c51), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c52), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c53), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c54), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c55), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c56), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c57), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c58), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c59), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5a), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5b), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5c), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5d), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5e), LPC_LPT},
{ PCI_VDEVICE(INTEL, 0x8c5f), LPC_LPT},
{ 0, }, /* End of list */
};
MODULE_DEVICE_TABLE(pci, lpc_ich_ids);
static void lpc_ich_restore_config_space(struct pci_dev *dev)
{
if (lpc_ich_acpi_save >= 0) {
pci_write_config_byte(dev, ACPICTRL, lpc_ich_acpi_save);
lpc_ich_acpi_save = -1;
}
if (lpc_ich_gpio_save >= 0) {
pci_write_config_byte(dev, GPIOCTRL, lpc_ich_gpio_save);
lpc_ich_gpio_save = -1;
}
}
static void __devinit lpc_ich_enable_acpi_space(struct pci_dev *dev)
{
u8 reg_save;
pci_read_config_byte(dev, ACPICTRL, &reg_save);
pci_write_config_byte(dev, ACPICTRL, reg_save | 0x10);
lpc_ich_acpi_save = reg_save;
}
static void __devinit lpc_ich_enable_gpio_space(struct pci_dev *dev)
{
u8 reg_save;
pci_read_config_byte(dev, GPIOCTRL, &reg_save);
pci_write_config_byte(dev, GPIOCTRL, reg_save | 0x10);
lpc_ich_gpio_save = reg_save;
}
static void __devinit lpc_ich_finalize_cell(struct mfd_cell *cell,
const struct pci_device_id *id)
{
cell->platform_data = &lpc_chipset_info[id->driver_data];
cell->pdata_size = sizeof(struct lpc_ich_info);
}
static int __devinit lpc_ich_init_gpio(struct pci_dev *dev,
const struct pci_device_id *id)
{
u32 base_addr_cfg;
u32 base_addr;
int ret;
bool acpi_conflict = false;
struct resource *res;
/* Setup power management base register */
pci_read_config_dword(dev, ACPIBASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_err(&dev->dev, "I/O space for ACPI uninitialized\n");
lpc_ich_cells[LPC_GPIO].num_resources--;
goto gpe0_done;
}
res = &gpio_ich_res[ICH_RES_GPE0];
res->start = base_addr + ACPIBASE_GPE_OFF;
res->end = base_addr + ACPIBASE_GPE_END;
ret = acpi_check_resource_conflict(res);
if (ret) {
/*
* This isn't fatal for the GPIO, but we have to make sure that
* the platform_device subsystem doesn't see this resource
* or it will register an invalid region.
*/
lpc_ich_cells[LPC_GPIO].num_resources--;
acpi_conflict = true;
} else {
lpc_ich_enable_acpi_space(dev);
}
gpe0_done:
/* Setup GPIO base register */
pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0x0000ff80;
if (!base_addr) {
dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
ret = -ENODEV;
goto gpio_done;
}
/* Older devices provide fewer GPIO and have a smaller resource size. */
res = &gpio_ich_res[ICH_RES_GPIO];
res->start = base_addr;
switch (lpc_chipset_info[id->driver_data].gpio_version) {
case ICH_V5_GPIO:
case ICH_V10CORP_GPIO:
res->end = res->start + 128 - 1;
break;
default:
res->end = res->start + 64 - 1;
break;
}
ret = acpi_check_resource_conflict(res);
if (ret) {
/* this isn't necessarily fatal for the GPIO */
acpi_conflict = true;
goto gpio_done;
}
lpc_ich_enable_gpio_space(dev);
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_GPIO], id);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_GPIO],
1, NULL, 0);
gpio_done:
if (acpi_conflict)
pr_warn("Resource conflict(s) found affecting %s\n",
lpc_ich_cells[LPC_GPIO].name);
return ret;
}
static int __devinit lpc_ich_probe(struct pci_dev *dev,
const struct pci_device_id *id)
{
int ret;
bool cell_added = false;
ret = lpc_ich_init_gpio(dev, id);
if (!ret)
cell_added = true;
/*
* We only care if at least one or none of the cells registered
* successfully.
*/
if (!cell_added) {
lpc_ich_restore_config_space(dev);
return -ENODEV;
}
return 0;
}
static void __devexit lpc_ich_remove(struct pci_dev *dev)
{
mfd_remove_devices(&dev->dev);
lpc_ich_restore_config_space(dev);
}
static struct pci_driver lpc_ich_driver = {
.name = "lpc_ich",
.id_table = lpc_ich_ids,
.probe = lpc_ich_probe,
.remove = __devexit_p(lpc_ich_remove),
};
static int __init lpc_ich_init(void)
{
return pci_register_driver(&lpc_ich_driver);
}
static void __exit lpc_ich_exit(void)
{
pci_unregister_driver(&lpc_ich_driver);
}
module_init(lpc_ich_init);
module_exit(lpc_ich_exit);
MODULE_AUTHOR("Aaron Sierra <asierra@xes-inc.com>");
MODULE_DESCRIPTION("LPC interface for Intel ICH");
MODULE_LICENSE("GPL");

View file

@ -0,0 +1,41 @@
/*
* linux/drivers/mfd/lpc_ich.h
*
* Copyright (c) 2012 Extreme Engineering Solution, Inc.
* Author: Aaron Sierra <asierra@xes-inc.com>
*
* 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.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef LPC_ICH_H
#define LPC_ICH_H
/* GPIO resources */
#define ICH_RES_GPIO 0
#define ICH_RES_GPE0 1
/* GPIO compatibility */
#define ICH_I3100_GPIO 0x401
#define ICH_V5_GPIO 0x501
#define ICH_V6_GPIO 0x601
#define ICH_V7_GPIO 0x701
#define ICH_V9_GPIO 0x801
#define ICH_V10CORP_GPIO 0xa01
#define ICH_V10CONS_GPIO 0xa11
struct lpc_ich_info {
char name[32];
unsigned int gpio_version;
};
#endif