linux/drivers/mfd/lpc_ich.c
Linus Torvalds 2dfea3803d This is the MFD patch set for the 3.8 merge window.
We have several new drivers, most of the time coming with their sub devices
 drivers:
 
 - Austria Microsystem's AS3711
 - Nano River's viperboard
 - TI's TPS80031, AM335x TS/ADC,
 - Realtek's MMC/memstick card reader
 - Nokia's retu
 
 We also got some notable cleanups and improvements:
 
 - tps6586x got converted to IRQ domains.
 - tps65910 and tps65090 moved to the regmap IRQ API.
 - STMPE is now Device Tree aware.
 - A general twl6040 and twl-core cleanup, with moves to the regmap I/O and IRQ
   APIs and a conversion to the recently added PWM framework.
 - sta2x11 gained regmap support.
 
 Then the rest is mostly tiny cleanups and fixes, among which we have Mark's
 wm5xxx and wm8xxx patchset.
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1.4.12 (GNU/Linux)
 
 iQIcBAABAgAGBQJQznPnAAoJEIqAPN1PVmxKuA8P/0nOJduXFM1c0Gy+DD5DnJnG
 cXzzeSTV8iO3a3sHIye43QPJ5V2YUR5uxLTUEOo/G7my/MoZ/azeNidkUD3qLVlm
 wVIq35lcS8dWTZaY7nlpBcWc6e39UB0sEueuJNxyhOv5lnMKdi2tAow5f4vIRQnd
 Q67/EbrgqdltcOpGmVuCdQcvphvWgy+K65jzbJG5zXs7hGX13Q+M5RnYhx76kc8f
 TDd0APZ71n5/RyISFSBSu2vfl2kES6o47aMgqqXMEHri6d3puAaXM0rFoMzXg/4G
 eBdxndN25H7rW7xvt9tuUod2rn1AO7tif5d7jal3Cfj61y3iqKY30yb3OzS9XQXH
 9WZ2qDst11zvzQivxIkMGvfRXRfncNLWR4DrBSqVfSbYV2uQj2eS8C6ONwKVMXsQ
 5tjNp91PFqN19sWQjIjSMcrNswxgpvdQ9mqFTyOGmISbqrpPSTi+MuO8r9+xTfUF
 PnzUX2nVOW/i9NcI7uotjzh8jiw6t8XMVHhkehiSYR9hzCb6MaPsFPN4jWq9XA2m
 1htCHylNpHqHQ3Mup7Is6j0Li1ahdwfm4lbrgiVEA4t4Mqs5E/Ka+3V8laNAKylW
 PfCP/VmnJYzmgVTK/qobFNeKzRqR0i4WTL6T7oAxGL87Q4TJaqKpEkXWne8UXV+Q
 yIbN0fmWfCveCetM+vaf
 =F790
 -----END PGP SIGNATURE-----

Merge tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6

Pull MFS update from Samuel Ortiz:
 "This is the MFD patch set for the 3.8 merge window.

  We have several new drivers, most of the time coming with their sub
  devices drivers:

   - Austria Microsystem's AS3711
   - Nano River's viperboard
   - TI's TPS80031, AM335x TS/ADC,
   - Realtek's MMC/memstick card reader
   - Nokia's retu

  We also got some notable cleanups and improvements:

   - tps6586x got converted to IRQ domains.
   - tps65910 and tps65090 moved to the regmap IRQ API.
   - STMPE is now Device Tree aware.
   - A general twl6040 and twl-core cleanup, with moves to the regmap
     I/O and IRQ APIs and a conversion to the recently added PWM
     framework.
   - sta2x11 gained regmap support.

  Then the rest is mostly tiny cleanups and fixes, among which we have
  Mark's wm5xxx and wm8xxx patchset."

Far amount of annoying but largely trivial conflicts.  Many due to
__devinit/exit removal, others due to one or two of the new drivers also
having come in through another tree.

* tag 'mfd-3.8-1' of git://git.kernel.org/pub/scm/linux/kernel/git/sameo/mfd-2.6: (119 commits)
  mfd: tps6507x: Convert to devm_kzalloc
  mfd: stmpe: Update DT support for stmpe driver
  mfd: wm5102: Add readback of DSP status 3 register
  mfd: arizona: Log if we fail to create the primary IRQ domain
  mfd: tps80031: MFD_TPS80031 needs to select REGMAP_IRQ
  mfd: tps80031: Add terminating entry for tps80031_id_table
  mfd: sta2x11: Fix potential NULL pointer dereference in __sta2x11_mfd_mask()
  mfd: wm5102: Add tuning for revision B
  mfd: arizona: Defer patch initialistation until after first device boot
  mfd: tps65910: Fix wrong ack_base register
  mfd: tps65910: Remove unused data
  mfd: stmpe: Get rid of irq_invert_polarity
  mfd: ab8500-core: Fix invalid free of devm_ allocated data
  mfd: wm5102: Mark DSP memory regions as volatile
  mfd: wm5102: Correct default for LDO1_CONTROL_2
  mfd: arizona: Register haptics devices
  mfd: wm8994: Make current device behaviour the default
  mfd: tps65090: MFD_TPS65090 needs to select REGMAP_IRQ
  mfd: Fix stmpe.c build when OF is not enabled
  mfd: jz4740-adc: Use devm_kzalloc
  ...
2012-12-16 18:55:20 -08:00

914 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
* document number TBD : Lynx Point-LP
*/
#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 */
LPC_LPT_LP, /* Lynx Point-LP */
};
struct lpc_ich_info lpc_chipset_info[] = {
[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,
},
[LPC_LPT_LP] = {
.name = "Lynx Point_LP",
.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},
{ PCI_VDEVICE(INTEL, 0x9c40), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c41), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c42), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c43), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c44), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c45), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c46), LPC_LPT_LP},
{ PCI_VDEVICE(INTEL, 0x9c47), LPC_LPT_LP},
{ 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 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 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 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 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 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_notice(&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_notice(&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 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_notice(&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 == 1) {
/* Don't register iomem for TCO ver 1 */
lpc_ich_cells[LPC_WDT].num_resources--;
} else {
pci_read_config_dword(dev, RCBABASE, &base_addr_cfg);
base_addr = base_addr_cfg & 0xffffc000;
if (!(base_addr_cfg & 1)) {
dev_notice(&dev->dev, "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 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) {
dev_warn(&dev->dev, "No MFD cells added\n");
lpc_ich_restore_config_space(dev);
return -ENODEV;
}
return 0;
}
static void 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 = 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");