linux/drivers/mfd/lpc_ich.c
Jean Delvare 4f600ada70 gpio: gpio-ich: Share ownership of GPIO groups
The ICH chips have their GPIO pins organized in 2 or 3 independent
groups of 32 GPIO pins. It can happen that the ACPI BIOS wants to make
use of pins in one group, preventing the OS to access these. This does
not prevent the OS from accessing the other group(s).

This is the case for example on my Asus Z8NA-D6 board. The ACPI BIOS
wants to control GPIO 18 (group 1), while I (the OS) need to control
GPIO 52 and 53 (group 2) for SMBus multiplexing.

So instead of checking for ACPI resource conflict on the whole I/O
range, check on a per-group basis, and consider it a success if at
least one of the groups is available for the OS to use.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Cc: Peter Tyser <ptyser@xes-inc.com>
Cc: Aaron Sierra <asierra@xes-inc.com>
Cc: Grant Likely <grant.likely@secretlab.ca>
Acked-by: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
2012-09-14 09:52:13 +02:00

896 lines
24 KiB
C

/*
* 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 ACPIBASE_SMI_OFF 0x30
#define ACPIBASE_SMI_END 0x33
#define ACPIBASE_TCO_OFF 0x60
#define ACPIBASE_TCO_END 0x7f
#define ACPICTRL 0x44
#define ACPIBASE_GCS_OFF 0x3410
#define ACPIBASE_GCS_END 0x3414
#define GPIOBASE 0x48
#define GPIOCTRL 0x4C
#define RCBABASE 0xf0
#define wdt_io_res(i) wdt_res(0, i)
#define wdt_mem_res(i) wdt_res(ICH_RES_MEM_OFF, i)
#define wdt_res(b, i) (&wdt_ich_res[(b) + (i)])
static int lpc_ich_acpi_save = -1;
static int lpc_ich_gpio_save = -1;
static struct resource wdt_ich_res[] = {
/* ACPI - TCO */
{
.flags = IORESOURCE_IO,
},
/* ACPI - SMI */
{
.flags = IORESOURCE_IO,
},
/* GCS */
{
.flags = IORESOURCE_MEM,
},
};
static struct resource gpio_ich_res[] = {
/* GPIO */
{
.flags = IORESOURCE_IO,
},
/* ACPI - GPE0 */
{
.flags = IORESOURCE_IO,
},
};
enum lpc_cells {
LPC_WDT = 0,
LPC_GPIO,
};
static struct mfd_cell lpc_ich_cells[] = {
[LPC_WDT] = {
.name = "iTCO_wdt",
.num_resources = ARRAY_SIZE(wdt_ich_res),
.resources = wdt_ich_res,
.ignore_resource_conflicts = true,
},
[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",
.iTCO_version = 1,
},
[LPC_ICH0] = {
.name = "ICH0",
.iTCO_version = 1,
},
[LPC_ICH2] = {
.name = "ICH2",
.iTCO_version = 1,
},
[LPC_ICH2M] = {
.name = "ICH2-M",
.iTCO_version = 1,
},
[LPC_ICH3] = {
.name = "ICH3-S",
.iTCO_version = 1,
},
[LPC_ICH3M] = {
.name = "ICH3-M",
.iTCO_version = 1,
},
[LPC_ICH4] = {
.name = "ICH4",
.iTCO_version = 1,
},
[LPC_ICH4M] = {
.name = "ICH4-M",
.iTCO_version = 1,
},
[LPC_CICH] = {
.name = "C-ICH",
.iTCO_version = 1,
},
[LPC_ICH5] = {
.name = "ICH5 or ICH5R",
.iTCO_version = 1,
},
[LPC_6300ESB] = {
.name = "6300ESB",
.iTCO_version = 1,
},
[LPC_ICH6] = {
.name = "ICH6 or ICH6R",
.iTCO_version = 2,
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH6M] = {
.name = "ICH6-M",
.iTCO_version = 2,
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH6W] = {
.name = "ICH6W or ICH6RW",
.iTCO_version = 2,
.gpio_version = ICH_V6_GPIO,
},
[LPC_631XESB] = {
.name = "631xESB/632xESB",
.iTCO_version = 2,
.gpio_version = ICH_V6_GPIO,
},
[LPC_ICH7] = {
.name = "ICH7 or ICH7R",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7DH] = {
.name = "ICH7DH",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7M] = {
.name = "ICH7-M or ICH7-U",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH7MDH] = {
.name = "ICH7-M DH",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_NM10] = {
.name = "NM10",
.iTCO_version = 2,
},
[LPC_ICH8] = {
.name = "ICH8 or ICH8R",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8DH] = {
.name = "ICH8DH",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8DO] = {
.name = "ICH8DO",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8M] = {
.name = "ICH8M",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH8ME] = {
.name = "ICH8M-E",
.iTCO_version = 2,
.gpio_version = ICH_V7_GPIO,
},
[LPC_ICH9] = {
.name = "ICH9",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9R] = {
.name = "ICH9R",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9DH] = {
.name = "ICH9DH",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9DO] = {
.name = "ICH9DO",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9M] = {
.name = "ICH9M",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH9ME] = {
.name = "ICH9M-E",
.iTCO_version = 2,
.gpio_version = ICH_V9_GPIO,
},
[LPC_ICH10] = {
.name = "ICH10",
.iTCO_version = 2,
.gpio_version = ICH_V10CONS_GPIO,
},
[LPC_ICH10R] = {
.name = "ICH10R",
.iTCO_version = 2,
.gpio_version = ICH_V10CONS_GPIO,
},
[LPC_ICH10D] = {
.name = "ICH10D",
.iTCO_version = 2,
.gpio_version = ICH_V10CORP_GPIO,
},
[LPC_ICH10DO] = {
.name = "ICH10DO",
.iTCO_version = 2,
.gpio_version = ICH_V10CORP_GPIO,
},
[LPC_PCH] = {
.name = "PCH Desktop Full Featured",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_PCHM] = {
.name = "PCH Mobile Full Featured",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_P55] = {
.name = "P55",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_PM55] = {
.name = "PM55",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_H55] = {
.name = "H55",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_QM57] = {
.name = "QM57",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_H57] = {
.name = "H57",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_HM55] = {
.name = "HM55",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_Q57] = {
.name = "Q57",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_HM57] = {
.name = "HM57",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_PCHMSFF] = {
.name = "PCH Mobile SFF Full Featured",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_QS57] = {
.name = "QS57",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_3400] = {
.name = "3400",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_3420] = {
.name = "3420",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_3450] = {
.name = "3450",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_EP80579] = {
.name = "EP80579",
.iTCO_version = 2,
},
[LPC_CPT] = {
.name = "Cougar Point",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_CPTD] = {
.name = "Cougar Point Desktop",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_CPTM] = {
.name = "Cougar Point Mobile",
.iTCO_version = 2,
.gpio_version = ICH_V5_GPIO,
},
[LPC_PBG] = {
.name = "Patsburg",
.iTCO_version = 2,
},
[LPC_DH89XXCC] = {
.name = "DH89xxCC",
.iTCO_version = 2,
},
[LPC_PPT] = {
.name = "Panther Point",
.iTCO_version = 2,
},
[LPC_LPT] = {
.name = "Lynx Point",
.iTCO_version = 2,
},
};
/*
* 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);
}
/*
* We don't check for resource conflict globally. There are 2 or 3 independent
* GPIO groups and it's enough to have access to one of these to instantiate
* the device.
*/
static int __devinit lpc_ich_check_conflict_gpio(struct resource *res)
{
int ret;
u8 use_gpio = 0;
if (resource_size(res) >= 0x50 &&
!acpi_check_region(res->start + 0x40, 0x10, "LPC ICH GPIO3"))
use_gpio |= 1 << 2;
if (!acpi_check_region(res->start + 0x30, 0x10, "LPC ICH GPIO2"))
use_gpio |= 1 << 1;
ret = acpi_check_region(res->start + 0x00, 0x30, "LPC ICH GPIO1");
if (!ret)
use_gpio |= 1 << 0;
return use_gpio ? use_gpio : ret;
}
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 = lpc_ich_check_conflict_gpio(res);
if (ret < 0) {
/* this isn't necessarily fatal for the GPIO */
acpi_conflict = true;
goto gpio_done;
}
lpc_chipset_info[id->driver_data].use_gpio = ret;
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, NULL);
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_init_wdt(struct pci_dev *dev,
const struct pci_device_id *id)
{
u32 base_addr_cfg;
u32 base_addr;
int ret;
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");
ret = -ENODEV;
goto wdt_done;
}
res = wdt_io_res(ICH_RES_IO_TCO);
res->start = base_addr + ACPIBASE_TCO_OFF;
res->end = base_addr + ACPIBASE_TCO_END;
res = wdt_io_res(ICH_RES_IO_SMI);
res->start = base_addr + ACPIBASE_SMI_OFF;
res->end = base_addr + ACPIBASE_SMI_END;
lpc_ich_enable_acpi_space(dev);
/*
* Get the Memory-Mapped GCS register. To get access to it
* we have to read RCBA from PCI Config space 0xf0 and use
* it as base. GCS = RCBA + ICH6_GCS(0x3410).
*/
if (lpc_chipset_info[id->driver_data].iTCO_version == 2) {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
pr_err("RCBA is disabled by hardware/BIOS, "
"device disabled\n");
ret = -ENODEV;
goto wdt_done;
}
res = wdt_mem_res(ICH_RES_MEM_GCS);
res->start = base_addr + ACPIBASE_GCS_OFF;
res->end = base_addr + ACPIBASE_GCS_END;
}
lpc_ich_finalize_cell(&lpc_ich_cells[LPC_WDT], id);
ret = mfd_add_devices(&dev->dev, -1, &lpc_ich_cells[LPC_WDT],
1, NULL, 0, NULL);
wdt_done:
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_wdt(dev, id);
if (!ret)
cell_added = true;
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");