打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
gpio: Add support for Intel ICHx/3100/Series[...

gpio: Add support for Intel ICHx/3100/Series[56] GPIO

From:  Aaron Sierra <asierra@xes-inc.com>
To:  Jean Delvare <khali@linux-fr.org>
Subject:  [PATCH V5 2/3] gpio: Add support for Intel ICHx/3100/Series[56] GPIO
Date:  Mon, 20 Feb 2012 19:11:04 -0600 (CST)
Message-ID:  <e46cd35e-bde3-49ef-9dc9-98fe7b8354e1@zimbra>
Cc:  Guenter Roeck <guenter@roeck-us.net>, Peter Tyser <ptyser@xes-inc.com>, Grant Likely <grant.likely@secretlab.ca>, LKML <linux-kernel@vger.kernel.org>
Archive-link:  Article, Thread

From: Peter Tyser <ptyser@xes-inc.com>This driver works on many Intel chipsets, including the ICH6, ICH7,ICH8, ICH9, ICH10, 3100, Series 5/3400 (Ibex Peak), Series 6/C200(Cougar Point), and NM10 (Tiger Point).Additional Intel chipsets should be easily supported if needed, eg theICH1-5, EP80579, etc.Tested on QM67 (Cougar Point), QM57 (Ibex Peak), 3100 (Whitmore Lake),and NM10 (Tiger Point).Includes work from Jean Delvare:	- Resource leak removal during module load/unload	- GPIO API bit value enforcementAlso includes code cleanup from Guenter Roeck.Signed-off-by: Peter Tyser <ptyser@xes-inc.com>Signed-off-by: Aaron Sierra <asierra@xes-inc.com>--- MAINTAINERS             |    6 + drivers/gpio/Kconfig    |   13 ++ drivers/gpio/Makefile   |    1 + drivers/gpio/gpio-ich.c |  432 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 452 insertions(+), 0 deletions(-) create mode 100644 drivers/gpio/gpio-ich.cdiff --git a/MAINTAINERS b/MAINTAINERSindex a1fce9a..9a0b59f 100644--- a/MAINTAINERS+++ b/MAINTAINERS@@ -3284,6 +3284,12 @@ W:	http://www.developer.ibm.com/welcome/netfinity/serveraid.... S:	Supported F:	drivers/scsi/ips.* +ICH LPC DRIVER+M:	Peter Tyser <ptyser@xes-inc.com>+S:	Maintained+F:	drivers/mfd/lpc_ich.c+F:	drivers/gpio/gpio-ich.c+ IDE SUBSYSTEM M:	"David S. Miller" <davem@davemloft.net> L:	linux-ide@vger.kernel.orgdiff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfigindex d0c4118..3359f1e 100644--- a/drivers/gpio/Kconfig+++ b/drivers/gpio/Kconfig@@ -178,6 +178,19 @@ config GPIO_SCH 	  The Intel Tunnel Creek processor has 5 GPIOs powered by the 	  core power rail and 9 from suspend power supply. +config GPIO_ICH+	tristate "Intel ICH GPIO"+	depends on PCI && X86+	select MFD_CORE+	select LPC_ICH+	help+	  Say yes here to support the GPIO functionality of a number of Intel+	  ICH-based chipsets.  Currently supported devices: ICH6, ICH7, ICH8+	  ICH9, ICH10, Series 5/3400 (eg Ibex Peak), Series 6/C200 (eg+	  Cougar Point), NM10 (Tiger Point), and 3100 (Whitmore Lake).++	  If unsure, say N.+ config GPIO_VX855 	tristate "VIA VX855/VX875 GPIO" 	depends on PCIdiff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefileindex fa10df6..b538830 100644--- a/drivers/gpio/Makefile+++ b/drivers/gpio/Makefile@@ -44,6 +44,7 @@ obj-$(CONFIG_GPIO_PXA)		+= gpio-pxa.o obj-$(CONFIG_GPIO_RDC321X)	+= gpio-rdc321x.o obj-$(CONFIG_PLAT_SAMSUNG)	+= gpio-samsung.o obj-$(CONFIG_ARCH_SA1100)	+= gpio-sa1100.o+obj-$(CONFIG_GPIO_ICH)		+= gpio-ich.o obj-$(CONFIG_GPIO_SCH)		+= gpio-sch.o obj-$(CONFIG_GPIO_STMPE)	+= gpio-stmpe.o obj-$(CONFIG_GPIO_SX150X)	+= gpio-sx150x.odiff --git a/drivers/gpio/gpio-ich.c b/drivers/gpio/gpio-ich.cnew file mode 100644index 0000000..e2c737c--- /dev/null+++ b/drivers/gpio/gpio-ich.c@@ -0,0 +1,432 @@+/*+ * Intel ICH6-10, Series 5 and 6 GPIO driver+ *+ * Copyright (C) 2010 Extreme Engineering Solutions.+ *+ * This program is free software; you can redistribute it and/or modify+ * it under the terms of the GNU General Public License as published by+ * the Free Software Foundation; either version 2 of the License, or+ * (at your option) any later version.+ *+ * 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; if not, write to the Free Software+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.+ */++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt++#include <linux/module.h>+#include <linux/pci.h>+#include <linux/gpio.h>+#include <linux/platform_device.h>+#include <linux/mfd/lpc_ich.h>++#define DRV_NAME "gpio_ich"++/* PCI config register offsets into LPC I/F - D31:F0 */+#define PCI_ICHX_ACPI_BAR	0x40+#define PCI_ICHX_GPIO_BAR	0x48+#define PCI_ICHX_GPIO_CTRL	0x4C++/*+ * GPIO register offsets in GPIO I/O space.+ * Each chunk of 32 GPIOs is manipulated via its own USE_SELx, IO_SELx, and+ * LVLx registers.  Logic in the read/write functions takes a register and+ * an absolute bit number and determines the proper register offset and bit+ * number in that register.  For example, to read the value of GPIO bit 50+ * the code would access offset ichx_regs[2(=GPIO_LVL)][1(=50/32)],+ * bit 18 (50%32).+ */+enum GPIO_REG {+	GPIO_USE_SEL = 0,+	GPIO_IO_SEL,+	GPIO_LVL,+};++static const u8 ichx_regs[3][3] = {+	{0x00, 0x30, 0x40},	/* USE_SEL[1-3] offsets */+	{0x04, 0x34, 0x44},	/* IO_SEL[1-3] offsets */+	{0x0c, 0x38, 0x48},	/* LVL[1-3] offsets */+};++#define ICHX_WRITE(val, reg, base_res)	outl(val, (reg) + (base_res)->start)+#define ICHX_READ(reg, base_res)	inl((reg) + (base_res)->start)++struct ichx_desc {+	/* Max GPIO pins the chipset can have */+	uint ngpio;++	/* Whether the chipset has GPIO in GPE0_STS in the PM IO region */+	bool uses_gpe0;++	/* USE_SEL is bogus on some chipsets, eg 3100 */+	u32 use_sel_ignore[3];++	/* Some chipsets have quirks, let these use their own request/get */+	int (*request)(struct gpio_chip *chip, unsigned offset);+	int (*get)(struct gpio_chip *chip, unsigned offset);+};++static struct {+	spinlock_t lock;+	struct platform_device *dev;+	struct gpio_chip chip;+	struct resource *gpio_base;	/* GPIO IO base */+	struct resource *pm_base;	/* Power Mangagment IO base */+	struct ichx_desc *desc;	/* Pointer to chipset-specific description */+	u32 orig_gpio_ctrl;	/* Orig CTRL value, used to restore on exit */+} ichx_priv;++static int modparam_gpiobase = -1;	/* dynamic */+module_param_named(gpiobase, modparam_gpiobase, int, 0444);+MODULE_PARM_DESC(gpiobase, "The GPIO number base. -1 means dynamic, "+			   "which is the default.");++static int ichx_write_bit(int reg, unsigned nr, int val, int verify)+{+	unsigned long flags;+	u32 data, tmp;+	int reg_nr = nr / 32;+	int bit = nr & 0x1f;+	int ret = 0;++	spin_lock_irqsave(&ichx_priv.lock, flags);++	data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);+	if (val)+		data |= 1 << bit;+	else+		data &= ~(1 << bit);+	ICHX_WRITE(data, ichx_regs[reg][reg_nr], ichx_priv.gpio_base);+	tmp = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);+	if (verify && data != tmp)+		ret = -EPERM;++	spin_unlock_irqrestore(&ichx_priv.lock, flags);++	return ret;+}++static int ichx_read_bit(int reg, unsigned nr)+{+	unsigned long flags;+	u32 data;+	int reg_nr = nr / 32;+	int bit = nr & 0x1f;++	spin_lock_irqsave(&ichx_priv.lock, flags);++	data = ICHX_READ(ichx_regs[reg][reg_nr], ichx_priv.gpio_base);++	spin_unlock_irqrestore(&ichx_priv.lock, flags);++	return data & (1 << bit) ? 1 : 0;+}++static int ichx_gpio_direction_input(struct gpio_chip *gpio, unsigned nr)+{+	/*+	 * Try setting pin as an input and verify it worked since many pins+	 * are output-only.+	 */+	if (ichx_write_bit(GPIO_IO_SEL, nr, 1, 1))+		return -EINVAL;++	return 0;+}++static int ichx_gpio_direction_output(struct gpio_chip *gpio, unsigned nr,+					int val)+{+	/* Set GPIO output value. */+	ichx_write_bit(GPIO_LVL, nr, val, 0);++	/*+	 * Try setting pin as an output and verify it worked since many pins+	 * are input-only.+	 */+	if (ichx_write_bit(GPIO_IO_SEL, nr, 0, 1))+		return -EINVAL;++	return 0;+}++static int ichx_gpio_get(struct gpio_chip *chip, unsigned nr)+{+	return ichx_read_bit(GPIO_LVL, nr);+}++static int ich6_gpio_get(struct gpio_chip *chip, unsigned nr)+{+	unsigned long flags;+	u32 data;++	/*+	 * GPI 0 - 15 need to be read from the power management registers on+	 * a ICH6/3100 bridge.+	 */+	if (nr < 16 && ichx_priv.pm_base) {+		spin_lock_irqsave(&ichx_priv.lock, flags);++		/* GPI 0 - 15 are latched, write 1 to clear*/+		ICHX_WRITE(1 << (16 + nr), 0, ichx_priv.pm_base);+		data = ICHX_READ(0, ichx_priv.pm_base);++		spin_unlock_irqrestore(&ichx_priv.lock, flags);++		return (data >> 16) & (1 << nr) ? 1 : 0;+	} else {+		return ichx_gpio_get(chip, nr);+	}+}++static int ichx_gpio_request(struct gpio_chip *chip, unsigned nr)+{+	/*+	 * Note we assume the BIOS properly set a bridge's USE value.  Some+	 * chips (eg Intel 3100) have bogus USE values though, so first see if+	 * the chipset's USE value can be trusted for this specific bit.+	 * If it can't be trusted, assume that the pin can be used as a GPIO.+	 */+	if (ichx_priv.desc->use_sel_ignore[nr / 32] & (1 << (nr & 0x1f)))+		return 1;++	return ichx_read_bit(GPIO_USE_SEL, nr) ? 0 : -ENODEV;+}++static int ich6_gpio_request(struct gpio_chip *chip, unsigned nr)+{+	/*+	 * Fixups for bits 16 and 17 are necessary on the Intel ICH6/3100+	 * bridge as they are controlled by USE register bits 0 and 1.  See+	 * "Table 704 GPIO_USE_SEL1 register" in the i3100 datasheet for+	 * additional info.+	 */+	if (nr == 16 || nr == 17)+		nr -= 16;++	return ichx_gpio_request(chip, nr);+}++static void ichx_gpio_set(struct gpio_chip *chip, unsigned nr, int val)+{+	ichx_write_bit(GPIO_LVL, nr, val, 0);+}++static void __devinit ichx_gpiolib_setup(struct gpio_chip *chip)+{+	chip->owner = THIS_MODULE;+	chip->label = DRV_NAME;+	chip->dev = &ichx_priv.dev->dev;++	/* Allow chip-specific overrides of request()/get() */+	chip->request = ichx_priv.desc->request ?+		ichx_priv.desc->request : ichx_gpio_request;+	chip->get = ichx_priv.desc->get ?+		ichx_priv.desc->get : ichx_gpio_get;++	chip->set = ichx_gpio_set;+	chip->direction_input = ichx_gpio_direction_input;+	chip->direction_output = ichx_gpio_direction_output;+	chip->base = modparam_gpiobase;+	chip->ngpio = ichx_priv.desc->ngpio;+	chip->can_sleep = 0;+	chip->dbg_show = NULL;+}++/* ICH6-based, 631xesb-based */+static struct ichx_desc ich6_desc = {+	/* Bridges using the ICH6 controller need fixups for GPIO 0 - 17 */+	.request = ich6_gpio_request,+	.get = ich6_gpio_get,++	/* GPIO 0-15 are read in the GPE0_STS PM register */+	.uses_gpe0 = true,++	.ngpio = 50,+};++/* Intel 3100 */+static struct ichx_desc i3100_desc = {+	/*+	 * Bits 16,17, 20 of USE_SEL and bit 16 of USE_SEL2 always read 0 on+	 * the Intel 3100.  See "Table 712. GPIO Summary Table" of 3100+	 * Datasheet for more info.+	 */+	.use_sel_ignore = {0x00130000, 0x00010000, 0x0},++	/* The 3100 needs fixups for GPIO 0 - 17 */+	.request = ich6_gpio_request,+	.get = ich6_gpio_get,++	/* GPIO 0-15 are read in the GPE0_STS PM register */+	.uses_gpe0 = true,++	.ngpio = 50,+};++/* ICH7 and ICH8-based */+static struct ichx_desc ich7_desc = {+	.ngpio = 50,+};++/* ICH9-based */+static struct ichx_desc ich9_desc = {+	.ngpio = 61,+};++/* ICH10-based - Consumer/corporate versions have different amount of GPIO */+static struct ichx_desc ich10_cons_desc = {+	.ngpio = 61,+};+static struct ichx_desc ich10_corp_desc = {+	.ngpio = 72,+};++/* Intel 5 series, 6 series, 3400 series, and C200 series */+static struct ichx_desc intel5_desc = {+	.ngpio = 76,+};++static int __devinit ichx_gpio_probe(struct platform_device *pdev)+{+	struct resource *res_base, *res_pm;+	int err;+	struct lpc_ich_info *ich_info = pdev->dev.platform_data;++	if (!ich_info)+		return -ENODEV;++	ichx_priv.dev = pdev;++	switch (ich_info->gpio_version) {+	case ICH_I3100_GPIO:+		ichx_priv.desc = &i3100_desc;+		break;+	case ICH_V5_GPIO:+		ichx_priv.desc = &intel5_desc;+		break;+	case ICH_V6_GPIO:+		ichx_priv.desc = &ich6_desc;+		break;+	case ICH_V7_GPIO:+		ichx_priv.desc = &ich7_desc;+		break;+	case ICH_V9_GPIO:+		ichx_priv.desc = &ich9_desc;+		break;+	case ICH_V10CORP_GPIO:+		ichx_priv.desc = &ich10_corp_desc;+		break;+	case ICH_V10CONS_GPIO:+		ichx_priv.desc = &ich10_cons_desc;+		break;+	default:+		return -ENODEV;+	}++	res_base = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPIO);+	if (!res_base || !res_base->start || !res_base->end)+		return -ENODEV;++	if (!request_region(res_base->start, resource_size(res_base),+				pdev->name))+		return -EBUSY;++	ichx_priv.gpio_base = res_base;++	/*+	 * If necessary, determine the I/O address of ACPI/power management+	 * registers which are needed to read the the GPE0 register for GPI pins+	 * 0 - 15 on some chipsets.+	 */+	if (!ichx_priv.desc->uses_gpe0)+		goto init;++	res_pm = platform_get_resource(pdev, IORESOURCE_IO, ICH_RES_GPE0);+	if (!res_pm || !res_pm->start || !res_pm->end) {+		pr_warn("ACPI BAR is unavailable, GPI 0 - 15 unavailable\n");+		goto init;+	}++	if (!request_region(res_pm->start, resource_size(res_pm),+			pdev->name)) {+		pr_warn("ACPI BAR is busy, GPI 0 - 15 unavailable\n");+		goto init;+	}++	ichx_priv.pm_base = res_pm;++init:+	ichx_gpiolib_setup(&ichx_priv.chip);+	err = gpiochip_add(&ichx_priv.chip);+	if (err) {+		pr_err("Failed to register GPIOs\n");+		goto add_err;+	}++	pr_info("GPIO from %d to %d on %s\n", ichx_priv.chip.base,+	       ichx_priv.chip.base + ichx_priv.chip.ngpio - 1, DRV_NAME);++	return 0;++add_err:+	release_region(ichx_priv.gpio_base->start,+			resource_size(ichx_priv.gpio_base));+	if (ichx_priv.pm_base)+		release_region(ichx_priv.pm_base->start,+				resource_size(ichx_priv.pm_base));+	return err;+}++static int __devexit ichx_gpio_remove(struct platform_device *pdev)+{+	int err;++	err = gpiochip_remove(&ichx_priv.chip);+	if (err) {+		dev_err(&pdev->dev, "%s failed, %d\n",+				"gpiochip_remove()", err);+		return err;+	}++	release_region(ichx_priv.gpio_base->start,+				resource_size(ichx_priv.gpio_base));+	if (ichx_priv.pm_base)+		release_region(ichx_priv.pm_base->start,+				resource_size(ichx_priv.pm_base));++	return 0;+}++static struct platform_driver ichx_gpio_driver = {+	.driver		= {+		.owner	= THIS_MODULE,+		.name	= DRV_NAME,+	},+	.probe		= ichx_gpio_probe,+	.remove		= __devexit_p(ichx_gpio_remove),+};++static int __devinit ichx_gpio_init_module(void)+{+	return platform_driver_register(&ichx_gpio_driver);+}++static void __devexit ichx_gpio_exit_module(void)+{+	platform_driver_unregister(&ichx_gpio_driver);+}++subsys_initcall(ichx_gpio_init_module);+module_exit(ichx_gpio_exit_module);++MODULE_AUTHOR("Peter Tyser <ptyser@xes-inc.com>");+MODULE_DESCRIPTION("GPIO interface for Intel ICH series");+MODULE_LICENSE("GPL");+MODULE_ALIAS("platform:"DRV_NAME);-- 1.7.0.4--To unsubscribe from this list: send the line "unsubscribe linux-kernel" inthe body of a message to majordomo@vger.kernel.orgMore majordomo info at  http://vger.kernel.org/majordomo-info.htmlPlease read the FAQ at  http://www.tux.org/lkml/
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
很好的linux下GPIO驱动详解文章
Kernel 中的 GPIO 定义和控制
一文搞懂 | Linux pinctrl/gpio子系统
broadcom网卡驱动 代码分析注释
gpio 设置
linux e2prom 驱动代码
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服