LPC bridge controller of Intel SCH contains several functions.
Currently, LPC bridge controller PCI device is claimed by SMBus driver
thus disabling ability to have an additional driver for the LPC bridge
PCI device. This patch series introduces and MFD driver that creates
platform_device for both SMBus and GPIO controller, converts SMBus
driver to platform_driver and adds the driver for GPIO controller
Intel Poulsbo (SCH) chipset LPC bridge controller contains several
functions. Creating and MFD driver for the LPC bridge controller allows
simultaneous use of SMBus and GPIO interfaces on the SCH.
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
--- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-10 15:15:40.000000000 +0200
@@ -348,6 +348,14 @@
read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well.
+config LPC_SCH
+ tristate "Intel SCH LPC"
+ default m
+ depends on PCI
+ help
+ LPC bridge function of the Intel SCH provides support for
+ System Management Bus and General Purpose I/O.
+
endmenu
menu "Multimedia Capabilities Port drivers"
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
--- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * 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
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#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>
+
+#define SMBASE 0x40
+#define SMBUS_IO_SIZE 64
+
+#define GPIOBASE 0x44
+#define GPIO_IO_SIZE 64
+
+static struct resource smbus_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+
+static struct resource gpio_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell lpc_sch_cells[] = {
+ {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+ },
+ {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+ },
+};
+
+static struct pci_device_id lpc_sch_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
+
+static int __devinit lpc_sch_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ unsigned int base_addr_cfg;
+ unsigned short base_addr;
+
+ pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
+ return -ENODEV;
+ }
+
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+ return -ENODEV;
+ }
+
+ gpio_sch_resource.start = base_addr;
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+
+ return mfd_add_devices(&dev->dev, -1,
+ lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+}
+
+static void __devexit lpc_sch_remove(struct pci_dev *dev)
+{
+ mfd_remove_devices(&dev->dev);
+}
+
+static struct pci_driver lpc_sch_driver = {
+ .name = "lpc_sch",
+ .id_table = lpc_sch_ids,
+ .probe = lpc_sch_probe,
+ .remove = __devexit_p(lpc_sch_remove),
+};
+
+static int __init lpc_sch_init(void)
+{
+ return pci_register_driver(&lpc_sch_driver);
+}
+
+static void __exit lpc_sch_exit(void)
+{
+ pci_unregister_driver(&lpc_sch_driver);
+}
+
+module_init(lpc_sch_init);
+module_exit(lpc_sch_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
--- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
@@ -55,4 +55,5 @@
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
-obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
\ No newline at end of file
+obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+obj-$(CONFIG_LPC_SCH) += lpc_sch.o
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/i2c/busses/i2c-isch.c linux-2.6.33-rc7/drivers/i2c/busses/i2c-isch.c
--- linux-2.6.33-rc7.orig/drivers/i2c/busses/i2c-isch.c 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/i2c/busses/i2c-isch.c 2010-02-10 15:45:23.000000000 +0200
@@ -27,7 +27,7 @@
*/
#include <linux/module.h>
-#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
@@ -46,12 +46,6 @@
#define SMBHSTDAT1 (7 + sch_smba)
#define SMBBLKDAT (0x20 + sch_smba)
-/* count for request_region */
-#define SMBIOSIZE 64
-
-/* PCI Address Constants */
-#define SMBBA_SCH 0x40
-
/* Other settings */
#define MAX_TIMEOUT 500
@@ -63,7 +57,6 @@
#define SCH_BLOCK_DATA 0x05
static unsigned short sch_smba;
-static struct pci_driver sch_driver;
static struct i2c_adapter sch_adapter;
/*
@@ -256,37 +249,26 @@
.algo = &smbus_algorithm,
};
-static struct pci_device_id sch_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, sch_ids);
-
-static int __devinit sch_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int __devinit smbus_sch_probe(struct platform_device *dev)
{
+ struct resource *res;
int retval;
- unsigned int smba;
- pci_read_config_dword(dev, SMBBA_SCH, &smba);
- if (!(smba & (1 << 31))) {
- dev_err(&dev->dev, "SMBus I/O space disabled!\n");
- return -ENODEV;
- }
+ res = platform_get_resource(dev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
- sch_smba = (unsigned short)smba;
- if (sch_smba == 0) {
- dev_err(&dev->dev, "SMBus base address uninitialized!\n");
+ if (acpi_check_region(res->start, resource_size(res), dev->name))
return -ENODEV;
- }
- if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
- return -ENODEV;
- if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
+
+ if (!request_region(res->start, resource_size(res), dev->name)) {
dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
sch_smba);
return -EBUSY;
}
+
+ sch_smba = res->start;
+
dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
/* set up the sysfs linkage to our parent device */
@@ -298,37 +280,43 @@
retval = i2c_add_adapter(&sch_adapter);
if (retval) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
- release_region(sch_smba, SMBIOSIZE);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
return retval;
}
-static void __devexit sch_remove(struct pci_dev *dev)
+static int __devexit smbus_sch_remove(struct platform_device *pdev)
{
+ struct resource *res;
if (sch_smba) {
i2c_del_adapter(&sch_adapter);
- release_region(sch_smba, SMBIOSIZE);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
+
+ return 0;
}
-static struct pci_driver sch_driver = {
- .name = "isch_smbus",
- .id_table = sch_ids,
- .probe = sch_probe,
- .remove = __devexit_p(sch_remove),
+static struct platform_driver smbus_sch_driver = {
+ .driver = {
+ .name = "isch_smbus",
+ .owner = THIS_MODULE,
+ },
+ .probe = smbus_sch_probe,
+ .remove = __devexit_p(smbus_sch_remove),
};
static int __init i2c_sch_init(void)
{
- return pci_register_driver(&sch_driver);
+ return platform_driver_register(&smbus_sch_driver);
}
static void __exit i2c_sch_exit(void)
{
- pci_unregister_driver(&sch_driver);
+ platform_driver_unregister(&smbus_sch_driver);
}
MODULE_AUTHOR("Jacob Pan <[email protected]>");
@@ -337,3 +325,4 @@
module_init(i2c_sch_init);
module_exit(i2c_sch_exit);
+MODULE_ALIAS("platform:isch_smbus");
diff -Nru linux-2.6.33-rc7.orig/drivers/i2c/busses/Kconfig linux-2.6.33-rc7/drivers/i2c/busses/Kconfig
--- linux-2.6.33-rc7.orig/drivers/i2c/busses/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/i2c/busses/Kconfig 2010-02-10 15:23:12.000000000 +0200
@@ -104,7 +104,7 @@
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
- depends on PCI
+ select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
based systems.
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/Kconfig linux-2.6.33-rc7/drivers/gpio/Kconfig
--- linux-2.6.33-rc7.orig/drivers/gpio/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/Kconfig 2010-02-10 15:15:49.000000000 +0200
@@ -85,6 +85,22 @@
help
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+config GPIO_SCH
+ tristate "Intel SCH GPIO"
+ depends on GPIOLIB
+ select LPC_SCH
+ help
+ Say yes here to support GPIO interface on Intel Poulsbo SCH.
+ The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+ powered by the core power rail and are turned off during sleep
+ modes (S3 and higher). The remaining four GPIOs are powered by
+ the Intel SCH suspend power supply. These GPIOs remain
+ active during S3. The suspend powered GPIOs can be used to wake the
+ system from the Suspend-to-RAM state.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch-gpio.
+
comment "I2C GPIO expanders:"
config GPIO_MAX732X
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/Makefile linux-2.6.33-rc7/drivers/gpio/Makefile
--- linux-2.6.33-rc7.orig/drivers/gpio/Makefile 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/Makefile 2010-02-10 15:15:49.000000000 +0200
@@ -22,3 +22,4 @@
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
+obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/sch_gpio.c linux-2.6.33-rc7/drivers/gpio/sch_gpio.c
--- linux-2.6.33-rc7.orig/drivers/gpio/sch_gpio.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/sch_gpio.c 2010-02-10 15:44:20.000000000 +0200
@@ -0,0 +1,285 @@
+/*
+ * sch_gpio.c - GPIO interface for Intel Poulsbo SCH
+ *
+ * Copyright (c) 2010 CompuLab Ltd
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+#include <linux/gpio.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+#define CGEN (0x00)
+#define CGIO (0x04)
+#define CGLV (0x08)
+
+#define RGEN (0x20)
+#define RGIO (0x24)
+#define RGLV (0x28)
+
+static unsigned short gpio_ba;
+
+static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+
+ if (!(curr_dirs & (1 << bit)))
+ outb(curr_dirs | (1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ int res;
+ unsigned short offset, bit;
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ res = !!(inb(gpio_ba + offset) & (1 << bit));
+ return res;
+}
+
+static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_vals = inb(gpio_ba + offset);
+
+ if (val)
+ outb(curr_vals | (1 << bit), gpio_ba + offset);
+ else
+ outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_core_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ sch_gpio_core_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+ if (curr_dirs & (1 << bit))
+ outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_core = {
+ .label = "sch_gpio_core",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_core_direction_in,
+ .get = sch_gpio_core_get,
+ .direction_output = sch_gpio_core_direction_out,
+ .set = sch_gpio_core_set,
+};
+
+static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
+ unsigned gpio_num)
+{
+ u8 curr_dirs;
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+
+ if (!(curr_dirs & (1 << gpio_num)))
+ outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
+}
+
+static void sch_gpio_resume_set(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+
+ spin_lock(&gpio_lock);
+
+ curr_vals = inb(gpio_ba + RGLV);
+
+ if (val)
+ outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
+ else
+ outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
+
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+
+ sch_gpio_resume_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+ if (curr_dirs & (1 << gpio_num))
+ outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_resume = {
+ .label = "sch_gpio_resume",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_resume_direction_in,
+ .get = sch_gpio_resume_get,
+ .direction_output = sch_gpio_resume_direction_out,
+ .set = sch_gpio_resume_set,
+};
+
+static int __devinit sch_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
+
+ if (acpi_check_region(res->start, resource_size(res), pdev->name))
+ return -ENODEV;
+
+ if (!request_region(res->start, resource_size(res), pdev->name))
+ return -EBUSY;
+
+ gpio_ba = res->start;
+
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 10;
+ sch_gpio_core.dev = &pdev->dev;
+
+ sch_gpio_resume.base = 10;
+ sch_gpio_resume.ngpio = 4;
+ sch_gpio_resume.dev = &pdev->dev;
+
+ err = gpiochip_add(&sch_gpio_core);
+ if (err < 0)
+ goto err_sch_gpio_core;
+
+ err = gpiochip_add(&sch_gpio_resume);
+ if (err < 0)
+ goto err_sch_gpio_resume;
+
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ outb(0x3, gpio_ba + CGEN + 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ outb(0x8, gpio_ba + RGEN);
+
+ return 0;
+
+err_sch_gpio_resume:
+ gpiochip_remove(&sch_gpio_core);
+
+err_sch_gpio_core:
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+
+ return err;
+}
+
+static int __devexit sch_gpio_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ if (gpio_ba) {
+ gpiochip_remove(&sch_gpio_core);
+ gpiochip_remove(&sch_gpio_resume);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+ }
+
+ return 0;
+}
+
+static struct platform_driver sch_gpio_driver = {
+ .driver = {
+ .name = "sch_gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = sch_gpio_probe,
+ .remove = __devexit_p(sch_gpio_remove),
+};
+
+static int __init sch_gpio_init(void)
+{
+ return platform_driver_register(&sch_gpio_driver);
+}
+
+static void __exit sch_gpio_exit(void)
+{
+ platform_driver_unregister(&sch_gpio_driver);
+}
+
+module_init(sch_gpio_init);
+module_exit(sch_gpio_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sch_gpio");
Hi Denis,
On Thu, Feb 11, 2010 at 12:26:19PM +0200, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
> simultaneous use of SMBus and GPIO interfaces on the SCH.
That looks like an nice patch to me. Before merging it, I'd like to get
Jacob's view on it though. Jacob, does moving the SCH SMBus driver to a
platform one look fine to you ?
Cheers,
Samuel.
> Signed-off-by: Denis Turischev <[email protected]>
>
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
> --- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-10 15:15:40.000000000 +0200
> @@ -348,6 +348,14 @@
> read/write functions for the devices to get access to this chip.
> This chip embeds various other multimedia funtionalities as well.
>
> +config LPC_SCH
> + tristate "Intel SCH LPC"
> + default m
> + depends on PCI
> + help
> + LPC bridge function of the Intel SCH provides support for
> + System Management Bus and General Purpose I/O.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
> --- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
> @@ -0,0 +1,133 @@
> +/*
> + * 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
> + * Author: Denis Turischev <[email protected]>
> + *
> + * 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.
> + */
> +
> +#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>
> +
> +#define SMBASE 0x40
> +#define SMBUS_IO_SIZE 64
> +
> +#define GPIOBASE 0x44
> +#define GPIO_IO_SIZE 64
> +
> +static struct resource smbus_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +
> +static struct resource gpio_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +static struct mfd_cell lpc_sch_cells[] = {
> + {
> + .name = "isch_smbus",
> + .num_resources = 1,
> + .resources = &smbus_sch_resource,
> + },
> + {
> + .name = "sch_gpio",
> + .num_resources = 1,
> + .resources = &gpio_sch_resource,
> + },
> +};
> +
> +static struct pci_device_id lpc_sch_ids[] = {
> + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> + { 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
> +
> +static int __devinit lpc_sch_probe(struct pci_dev *dev,
> + const struct pci_device_id *id)
> +{
> + unsigned int base_addr_cfg;
> + unsigned short base_addr;
> +
> + pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
> + return -ENODEV;
> + }
> +
> + smbus_sch_resource.start = base_addr;
> + smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
> +
> + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
> + return -ENODEV;
> + }
> +
> + gpio_sch_resource.start = base_addr;
> + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
> +
> + return mfd_add_devices(&dev->dev, -1,
> + lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
> +}
> +
> +static void __devexit lpc_sch_remove(struct pci_dev *dev)
> +{
> + mfd_remove_devices(&dev->dev);
> +}
> +
> +static struct pci_driver lpc_sch_driver = {
> + .name = "lpc_sch",
> + .id_table = lpc_sch_ids,
> + .probe = lpc_sch_probe,
> + .remove = __devexit_p(lpc_sch_remove),
> +};
> +
> +static int __init lpc_sch_init(void)
> +{
> + return pci_register_driver(&lpc_sch_driver);
> +}
> +
> +static void __exit lpc_sch_exit(void)
> +{
> + pci_unregister_driver(&lpc_sch_driver);
> +}
> +
> +module_init(lpc_sch_init);
> +module_exit(lpc_sch_exit);
> +
> +MODULE_AUTHOR("Denis Turischev <[email protected]>");
> +MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
> +MODULE_LICENSE("GPL");
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
> --- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
> @@ -55,4 +55,5 @@
> obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
> obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
> obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
> -obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> \ No newline at end of file
> +obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> +obj-$(CONFIG_LPC_SCH) += lpc_sch.o
--
Intel Open Source Technology Centre
http://oss.intel.com/
>
>On Thu, Feb 11, 2010 at 12:26:19PM +0200, Denis Turischev wrote:
>> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
>> functions. Creating and MFD driver for the LPC bridge controller allows
>> simultaneous use of SMBus and GPIO interfaces on the SCH.
>That looks like an nice patch to me. Before merging it, I'd like to get
>Jacob's view on it though. Jacob, does moving the SCH SMBus driver to a
>platform one look fine to you ?
>
[[JPAN]] i agree with merging gpio and smbus into lpc driver. the only question
i had was whether impact to the user space tools has been considered. iirc,
there are sensors detect tools probe pci bus for smbus controllers, not sure it
does that for platform bus.
On Tue, 16 Feb 2010 05:59:34 -0800, Pan, Jacob jun wrote:
>
> >
> >On Thu, Feb 11, 2010 at 12:26:19PM +0200, Denis Turischev wrote:
> >> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> >> functions. Creating and MFD driver for the LPC bridge controller allows
> >> simultaneous use of SMBus and GPIO interfaces on the SCH.
> >That looks like an nice patch to me. Before merging it, I'd like to get
> >Jacob's view on it though. Jacob, does moving the SCH SMBus driver to a
> >platform one look fine to you ?
> >
> [[JPAN]] i agree with merging gpio and smbus into lpc driver. the only question
> i had was whether impact to the user space tools has been considered. iirc,
> there are sensors detect tools probe pci bus for smbus controllers, not sure it
> does that for platform bus.
That shouldn't be a problem. The PCI device is still present, so
sensors-detect will see it. Then it will load the required driver, and
that driver will instantiate i2c adapters. The script then probes all
i2c adapters regardless of who created them, so the exact driver
implementation doesn't matter.
--
Jean Delvare
>> >On Thu, Feb 11, 2010 at 12:26:19PM +0200, Denis Turischev wrote:
>> >> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
>> >> functions. Creating and MFD driver for the LPC bridge controller allows
>> >> simultaneous use of SMBus and GPIO interfaces on the SCH.
>> >That looks like an nice patch to me. Before merging it, I'd like to get
>> >Jacob's view on it though. Jacob, does moving the SCH SMBus driver to a
>> >platform one look fine to you ?
>> >
>> [[JPAN]] i agree with merging gpio and smbus into lpc driver. the only
>question
>> i had was whether impact to the user space tools has been considered. iirc,
>> there are sensors detect tools probe pci bus for smbus controllers, not sure
>it
>> does that for platform bus.
>
>That shouldn't be a problem. The PCI device is still present, so
>sensors-detect will see it. Then it will load the required driver, and
>that driver will instantiate i2c adapters. The script then probes all
>i2c adapters regardless of who created them, so the exact driver
>implementation doesn't matter.
>
[[JPAN]] thanks for explaining it, all made sense to me. looking at
sensors-detect, it will still load isch driver for the same pci id.
but how would it know it depends on the new lpc driver to set up resources?
i can't see shared symbols.
}, {
vendid => 0x8086,
devid => 0x8119,
procid => "Intel SCH",
driver => "i2c-isch",
},
On Tue, 16 Feb 2010 09:19:58 -0800, Pan, Jacob jun wrote:
> >That shouldn't be a problem. The PCI device is still present, so
> >sensors-detect will see it. Then it will load the required driver, and
> >that driver will instantiate i2c adapters. The script then probes all
> >i2c adapters regardless of who created them, so the exact driver
> >implementation doesn't matter.
> >
> [[JPAN]] thanks for explaining it, all made sense to me. looking at
> sensors-detect, it will still load isch driver for the same pci id.
> but how would it know it depends on the new lpc driver to set up resources?
> i can't see shared symbols.
>
> }, {
> vendid => 0x8086,
> devid => 0x8119,
> procid => "Intel SCH",
> driver => "i2c-isch",
> },
On systems with udev, I would expect the mfd driver to load
automatically through module aliases, so this should be OK. Actually,
even i2c-isch should load automatically if we setup proper aliases for
the platform devices it instantiates. For other systems, indeed, the
mfd driver won't load in the absence of a shared symbol. Might be worth
adding request_module() call in platform drivers? If this isn't
acceptable for whatever reason, I can certainly hack sensors-detect to
treat this uncommon case properly, but solving this problem at the
application level seems wrong.
--
Jean Delvare
On Thursday 11 February 2010, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
Spelling nit: "Creating an" (not and). Keyboard, brain, or edit fault. ;)
> simultaneous use of SMBus and GPIO interfaces on the SCH.
This looks like the right way to package such southbridge level
componentry. Maye not just these two interfaces, either.
But ... how does this play with ACPI? The last several Intel
systems I looked at seemed to expect ACPI to manage GPIOs and the
IRQs they may issue. (He wrote, staring at an ICH8-system where
ACPI uses GPIOs to manage several buttons and LEDs.)
It would seem error-prone to ignore that coupling on systems
with ACPI. Linux has enough trouble sorting out issues caused
by buggy AML (ACPI bytecode) without introducing conflicts in
who manages which hardware resource (ACPI vs. operating system).
Of course, if ACPI weren't being used to hide such board-specific
details from operating systems, such issues would not exist. But
such hiding is one of the basic goals of ACPI ... annoying.
On Tue, 16 Feb 2010 11:57:46 -0800, David Brownell wrote:
> On Thursday 11 February 2010, Denis Turischev wrote:
> > Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> > functions. Creating and MFD driver for the LPC bridge controller allows
>
> Spelling nit: "Creating an" (not and). Keyboard, brain, or edit fault. ;)
>
>
> > simultaneous use of SMBus and GPIO interfaces on the SCH.
>
> This looks like the right way to package such southbridge level
> componentry. Maye not just these two interfaces, either.
>
> But ... how does this play with ACPI? The last several Intel
> systems I looked at seemed to expect ACPI to manage GPIOs and the
> IRQs they may issue. (He wrote, staring at an ICH8-system where
> ACPI uses GPIOs to manage several buttons and LEDs.)
>
> It would seem error-prone to ignore that coupling on systems
> with ACPI. Linux has enough trouble sorting out issues caused
> by buggy AML (ACPI bytecode) without introducing conflicts in
> who manages which hardware resource (ACPI vs. operating system).
Might be a good idea to use acpi_check_resource_conflict() or similar
before instantiating the platform devices.
> Of course, if ACPI weren't being used to hide such board-specific
> details from operating systems, such issues would not exist. But
> such hiding is one of the basic goals of ACPI ... annoying.
Don't start me on this :(
--
Jean Delvare
Jean Delvare wrote:
> On Tue, 16 Feb 2010 11:57:46 -0800, David Brownell wrote:
>> On Thursday 11 February 2010, Denis Turischev wrote:
>>> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
>>> functions. Creating and MFD driver for the LPC bridge controller allows
>> Spelling nit: "Creating an" (not and). Keyboard, brain, or edit fault. ;)
>>
>>
>>> simultaneous use of SMBus and GPIO interfaces on the SCH.
>> This looks like the right way to package such southbridge level
>> componentry. Maye not just these two interfaces, either.
>>
>> But ... how does this play with ACPI? The last several Intel
>> systems I looked at seemed to expect ACPI to manage GPIOs and the
>> IRQs they may issue. (He wrote, staring at an ICH8-system where
>> ACPI uses GPIOs to manage several buttons and LEDs.)
>>
>> It would seem error-prone to ignore that coupling on systems
>> with ACPI. Linux has enough trouble sorting out issues caused
>> by buggy AML (ACPI bytecode) without introducing conflicts in
>> who manages which hardware resource (ACPI vs. operating system).
>
> Might be a good idea to use acpi_check_resource_conflict() or similar
> before instantiating the platform devices.
May be it worth to add such resource check directly to mfd_add_device function?
>
>> Of course, if ACPI weren't being used to hide such board-specific
>> details from operating systems, such issues would not exist. But
>> such hiding is one of the basic goals of ACPI ... annoying.
>
> Don't start me on this :(
>
On Wed, 17 Feb 2010 12:03:17 +0200, Denis Turischev wrote:
> Jean Delvare wrote:
> > Might be a good idea to use acpi_check_resource_conflict() or similar
> > before instantiating the platform devices.
>
> May be it worth to add such resource check directly to mfd_add_device function?
I'm not sure. I suspect that many MFD devices are never used on
ACPI-aware systems, so it might be considered overkill. OTOH the calls
resolve to empty stubs when ACPI is disabled so... I have no objection,
but I'll leave the decision to somebody else ;)
--
Jean Delvare
Samuel,
Jean Delvare wrote:
> On Wed, 17 Feb 2010 12:03:17 +0200, Denis Turischev wrote:
>> Jean Delvare wrote:
>>> Might be a good idea to use acpi_check_resource_conflict() or similar
>>> before instantiating the platform devices.
>> May be it worth to add such resource check directly to mfd_add_device function?
>
> I'm not sure. I suspect that many MFD devices are never used on
> ACPI-aware systems, so it might be considered overkill. OTOH the calls
> resolve to empty stubs when ACPI is disabled so... I have no objection,
> but I'll leave the decision to somebody else ;)
>
What do you think? Shall we add something like mfd_verify_resources that will call
acpi_check_region or something similar?
--
Sincerely yours,
Mike.
Hi Mike,
On Wed, Feb 17, 2010 at 02:35:30PM +0200, Mike Rapoport wrote:
> Samuel,
>
> Jean Delvare wrote:
> > On Wed, 17 Feb 2010 12:03:17 +0200, Denis Turischev wrote:
> >> Jean Delvare wrote:
> >>> Might be a good idea to use acpi_check_resource_conflict() or similar
> >>> before instantiating the platform devices.
> >> May be it worth to add such resource check directly to mfd_add_device function?
> >
> > I'm not sure. I suspect that many MFD devices are never used on
> > ACPI-aware systems, so it might be considered overkill. OTOH the calls
> > resolve to empty stubs when ACPI is disabled so... I have no objection,
> > but I'll leave the decision to somebody else ;)
> >
>
> What do you think? Shall we add something like mfd_verify_resources that will call
> acpi_check_region or something similar?
Yes, that sounds like a reasonable idea. We should probably call
acpi_check_resource_conflict() straight from mfd_add_device(). I'll do that,
no need for Denis to add that patch for its code to be merged.
Cheers,
Samuel.
>
> --
> Sincerely yours,
> Mike.
--
Intel Open Source Technology Centre
http://oss.intel.com/
acpi_check_region will be implemented in mfd-core, therefore v2 version avoids
this check
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/Kconfig linux-2.6.33-rc7/drivers/gpio/Kconfig
--- linux-2.6.33-rc7.orig/drivers/gpio/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/Kconfig 2010-02-10 15:15:49.000000000 +0200
@@ -85,6 +85,22 @@
help
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+config GPIO_SCH
+ tristate "Intel SCH GPIO"
+ depends on GPIOLIB
+ select LPC_SCH
+ help
+ Say yes here to support GPIO interface on Intel Poulsbo SCH.
+ The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+ powered by the core power rail and are turned off during sleep
+ modes (S3 and higher). The remaining four GPIOs are powered by
+ the Intel SCH suspend power supply. These GPIOs remain
+ active during S3. The suspend powered GPIOs can be used to wake the
+ system from the Suspend-to-RAM state.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch-gpio.
+
comment "I2C GPIO expanders:"
config GPIO_MAX732X
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/Makefile linux-2.6.33-rc7/drivers/gpio/Makefile
--- linux-2.6.33-rc7.orig/drivers/gpio/Makefile 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/Makefile 2010-02-10 15:15:49.000000000 +0200
@@ -22,3 +22,4 @@
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
+obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
diff -Nru linux-2.6.33-rc7.orig/drivers/gpio/sch_gpio.c linux-2.6.33-rc7/drivers/gpio/sch_gpio.c
--- linux-2.6.33-rc7.orig/drivers/gpio/sch_gpio.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.33-rc7/drivers/gpio/sch_gpio.c 2010-02-17 17:08:16.000000000 +0200
@@ -0,0 +1,282 @@
+/*
+ * sch_gpio.c - GPIO interface for Intel Poulsbo SCH
+ *
+ * Copyright (c) 2010 CompuLab Ltd
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+#include <linux/gpio.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+#define CGEN (0x00)
+#define CGIO (0x04)
+#define CGLV (0x08)
+
+#define RGEN (0x20)
+#define RGIO (0x24)
+#define RGLV (0x28)
+
+static unsigned short gpio_ba;
+
+static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+
+ if (!(curr_dirs & (1 << bit)))
+ outb(curr_dirs | (1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ int res;
+ unsigned short offset, bit;
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ res = !!(inb(gpio_ba + offset) & (1 << bit));
+ return res;
+}
+
+static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_vals = inb(gpio_ba + offset);
+
+ if (val)
+ outb(curr_vals | (1 << bit), gpio_ba + offset);
+ else
+ outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_core_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ sch_gpio_core_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+ if (curr_dirs & (1 << bit))
+ outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_core = {
+ .label = "sch_gpio_core",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_core_direction_in,
+ .get = sch_gpio_core_get,
+ .direction_output = sch_gpio_core_direction_out,
+ .set = sch_gpio_core_set,
+};
+
+static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
+ unsigned gpio_num)
+{
+ u8 curr_dirs;
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+
+ if (!(curr_dirs & (1 << gpio_num)))
+ outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
+}
+
+static void sch_gpio_resume_set(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+
+ spin_lock(&gpio_lock);
+
+ curr_vals = inb(gpio_ba + RGLV);
+
+ if (val)
+ outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
+ else
+ outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
+
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+
+ sch_gpio_resume_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+ if (curr_dirs & (1 << gpio_num))
+ outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_resume = {
+ .label = "sch_gpio_resume",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_resume_direction_in,
+ .get = sch_gpio_resume_get,
+ .direction_output = sch_gpio_resume_direction_out,
+ .set = sch_gpio_resume_set,
+};
+
+static int __devinit sch_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
+
+ if (!request_region(res->start, resource_size(res), pdev->name))
+ return -EBUSY;
+
+ gpio_ba = res->start;
+
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 10;
+ sch_gpio_core.dev = &pdev->dev;
+
+ sch_gpio_resume.base = 10;
+ sch_gpio_resume.ngpio = 4;
+ sch_gpio_resume.dev = &pdev->dev;
+
+ err = gpiochip_add(&sch_gpio_core);
+ if (err < 0)
+ goto err_sch_gpio_core;
+
+ err = gpiochip_add(&sch_gpio_resume);
+ if (err < 0)
+ goto err_sch_gpio_resume;
+
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ outb(0x3, gpio_ba + CGEN + 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ outb(0x8, gpio_ba + RGEN);
+
+ return 0;
+
+err_sch_gpio_resume:
+ gpiochip_remove(&sch_gpio_core);
+
+err_sch_gpio_core:
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+
+ return err;
+}
+
+static int __devexit sch_gpio_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ if (gpio_ba) {
+ gpiochip_remove(&sch_gpio_core);
+ gpiochip_remove(&sch_gpio_resume);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+ }
+
+ return 0;
+}
+
+static struct platform_driver sch_gpio_driver = {
+ .driver = {
+ .name = "sch_gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = sch_gpio_probe,
+ .remove = __devexit_p(sch_gpio_remove),
+};
+
+static int __init sch_gpio_init(void)
+{
+ return platform_driver_register(&sch_gpio_driver);
+}
+
+static void __exit sch_gpio_exit(void)
+{
+ platform_driver_unregister(&sch_gpio_driver);
+}
+
+module_init(sch_gpio_init);
+module_exit(sch_gpio_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sch_gpio");
acpi_check_region will be implemented in mfd-core, therefore v2 version avoids
this check
Signed-off-by: Denis Turischev <[email protected]>
--- linux-2.6.33-rc7.orig/drivers/i2c/busses/i2c-isch.c 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/i2c/busses/i2c-isch.c 2010-02-17 17:08:53.000000000 +0200
@@ -27,7 +27,7 @@
*/
#include <linux/module.h>
-#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
@@ -46,12 +46,6 @@
#define SMBHSTDAT1 (7 + sch_smba)
#define SMBBLKDAT (0x20 + sch_smba)
-/* count for request_region */
-#define SMBIOSIZE 64
-
-/* PCI Address Constants */
-#define SMBBA_SCH 0x40
-
/* Other settings */
#define MAX_TIMEOUT 500
@@ -63,7 +57,6 @@
#define SCH_BLOCK_DATA 0x05
static unsigned short sch_smba;
-static struct pci_driver sch_driver;
static struct i2c_adapter sch_adapter;
/*
@@ -256,37 +249,23 @@
.algo = &smbus_algorithm,
};
-static struct pci_device_id sch_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, sch_ids);
-
-static int __devinit sch_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int __devinit smbus_sch_probe(struct platform_device *dev)
{
+ struct resource *res;
int retval;
- unsigned int smba;
- pci_read_config_dword(dev, SMBBA_SCH, &smba);
- if (!(smba & (1 << 31))) {
- dev_err(&dev->dev, "SMBus I/O space disabled!\n");
- return -ENODEV;
- }
+ res = platform_get_resource(dev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
- sch_smba = (unsigned short)smba;
- if (sch_smba == 0) {
- dev_err(&dev->dev, "SMBus base address uninitialized!\n");
- return -ENODEV;
- }
- if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
- return -ENODEV;
- if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
+ if (!request_region(res->start, resource_size(res), dev->name)) {
dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
sch_smba);
return -EBUSY;
}
+
+ sch_smba = res->start;
+
dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
/* set up the sysfs linkage to our parent device */
@@ -298,37 +277,43 @@
retval = i2c_add_adapter(&sch_adapter);
if (retval) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
- release_region(sch_smba, SMBIOSIZE);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
return retval;
}
-static void __devexit sch_remove(struct pci_dev *dev)
+static int __devexit smbus_sch_remove(struct platform_device *pdev)
{
+ struct resource *res;
if (sch_smba) {
i2c_del_adapter(&sch_adapter);
- release_region(sch_smba, SMBIOSIZE);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
+
+ return 0;
}
-static struct pci_driver sch_driver = {
- .name = "isch_smbus",
- .id_table = sch_ids,
- .probe = sch_probe,
- .remove = __devexit_p(sch_remove),
+static struct platform_driver smbus_sch_driver = {
+ .driver = {
+ .name = "isch_smbus",
+ .owner = THIS_MODULE,
+ },
+ .probe = smbus_sch_probe,
+ .remove = __devexit_p(smbus_sch_remove),
};
static int __init i2c_sch_init(void)
{
- return pci_register_driver(&sch_driver);
+ return platform_driver_register(&smbus_sch_driver);
}
static void __exit i2c_sch_exit(void)
{
- pci_unregister_driver(&sch_driver);
+ platform_driver_unregister(&smbus_sch_driver);
}
MODULE_AUTHOR("Jacob Pan <[email protected]>");
@@ -337,3 +322,4 @@
module_init(i2c_sch_init);
module_exit(i2c_sch_exit);
+MODULE_ALIAS("platform:isch_smbus");
Intel Poulsbo (SCH) chipset LPC bridge controller contains several
functions. Creating and MFD driver for the LPC bridge controller allows
simultaneous use of SMBus and GPIO interfaces on the SCH.
v2 version contains "select MFD_CORE" line.
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
--- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18 19:32:01.000000000 +0200
@@ -348,6 +348,15 @@
read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well.
+config LPC_SCH
+ tristate "Intel SCH LPC"
+ default m
+ depends on PCI
+ select MFD_CORE
+ help
+ LPC bridge function of the Intel SCH provides support for
+ System Management Bus and General Purpose I/O.
+
endmenu
menu "Multimedia Capabilities Port drivers"
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
--- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * 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
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#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>
+
+#define SMBASE 0x40
+#define SMBUS_IO_SIZE 64
+
+#define GPIOBASE 0x44
+#define GPIO_IO_SIZE 64
+
+static struct resource smbus_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+
+static struct resource gpio_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell lpc_sch_cells[] = {
+ {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+ },
+ {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+ },
+};
+
+static struct pci_device_id lpc_sch_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
+
+static int __devinit lpc_sch_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ unsigned int base_addr_cfg;
+ unsigned short base_addr;
+
+ pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
+ return -ENODEV;
+ }
+
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+ return -ENODEV;
+ }
+
+ gpio_sch_resource.start = base_addr;
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+
+ return mfd_add_devices(&dev->dev, -1,
+ lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+}
+
+static void __devexit lpc_sch_remove(struct pci_dev *dev)
+{
+ mfd_remove_devices(&dev->dev);
+}
+
+static struct pci_driver lpc_sch_driver = {
+ .name = "lpc_sch",
+ .id_table = lpc_sch_ids,
+ .probe = lpc_sch_probe,
+ .remove = __devexit_p(lpc_sch_remove),
+};
+
+static int __init lpc_sch_init(void)
+{
+ return pci_register_driver(&lpc_sch_driver);
+}
+
+static void __exit lpc_sch_exit(void)
+{
+ pci_unregister_driver(&lpc_sch_driver);
+}
+
+module_init(lpc_sch_init);
+module_exit(lpc_sch_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
--- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
@@ -55,4 +55,5 @@
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
-obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
\ No newline at end of file
+obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+obj-$(CONFIG_LPC_SCH) += lpc_sch.o
On 02/18/10 09:42, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
> simultaneous use of SMBus and GPIO interfaces on the SCH.
>
> v2 version contains "select MFD_CORE" line.
>
> Signed-off-by: Denis Turischev <[email protected]>
>
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig
> linux-2.6.33-rc7/drivers/mfd/Kconfig
> --- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07
> 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18
> 19:32:01.000000000 +0200
> @@ -348,6 +348,15 @@
> read/write functions for the devices to get access to this chip.
> This chip embeds various other multimedia funtionalities as well.
>
> +config LPC_SCH
> + tristate "Intel SCH LPC"
> + default m
Please don't default random modules to be enabled/built.
That could be done via defconfig .. if at all.
> + depends on PCI
> + select MFD_CORE
> + help
> + LPC bridge function of the Intel SCH provides support for
> + System Management Bus and General Purpose I/O.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
--
~Randy
Intel Poulsbo (SCH) chipset LPC bridge controller contains several
functions. Creating and MFD driver for the LPC bridge controller allows
simultaneous use of SMBus and GPIO interfaces on the SCH.
v2: added "select MFD_CORE"
v3: removed "default m"
Signed-off-by: Denis Turischev <[email protected]>
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
--- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18 19:56:19.000000000 +0200
@@ -348,6 +348,14 @@
read/write functions for the devices to get access to this chip.
This chip embeds various other multimedia funtionalities as well.
+config LPC_SCH
+ tristate "Intel SCH LPC"
+ depends on PCI
+ select MFD_CORE
+ help
+ LPC bridge function of the Intel SCH provides support for
+ System Management Bus and General Purpose I/O.
+
endmenu
menu "Multimedia Capabilities Port drivers"
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
--- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+ * 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
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#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>
+
+#define SMBASE 0x40
+#define SMBUS_IO_SIZE 64
+
+#define GPIOBASE 0x44
+#define GPIO_IO_SIZE 64
+
+static struct resource smbus_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+
+static struct resource gpio_sch_resource = {
+ .flags = IORESOURCE_IO,
+};
+
+static struct mfd_cell lpc_sch_cells[] = {
+ {
+ .name = "isch_smbus",
+ .num_resources = 1,
+ .resources = &smbus_sch_resource,
+ },
+ {
+ .name = "sch_gpio",
+ .num_resources = 1,
+ .resources = &gpio_sch_resource,
+ },
+};
+
+static struct pci_device_id lpc_sch_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
+ { 0, }
+};
+MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
+
+static int __devinit lpc_sch_probe(struct pci_dev *dev,
+ const struct pci_device_id *id)
+{
+ unsigned int base_addr_cfg;
+ unsigned short base_addr;
+
+ pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
+ return -ENODEV;
+ }
+
+ smbus_sch_resource.start = base_addr;
+ smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
+
+ pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
+ if (!(base_addr_cfg & (1 << 31))) {
+ dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
+ return -ENODEV;
+ }
+ base_addr = (unsigned short)base_addr_cfg;
+ if (base_addr == 0) {
+ dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
+ return -ENODEV;
+ }
+
+ gpio_sch_resource.start = base_addr;
+ gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
+
+ return mfd_add_devices(&dev->dev, -1,
+ lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
+}
+
+static void __devexit lpc_sch_remove(struct pci_dev *dev)
+{
+ mfd_remove_devices(&dev->dev);
+}
+
+static struct pci_driver lpc_sch_driver = {
+ .name = "lpc_sch",
+ .id_table = lpc_sch_ids,
+ .probe = lpc_sch_probe,
+ .remove = __devexit_p(lpc_sch_remove),
+};
+
+static int __init lpc_sch_init(void)
+{
+ return pci_register_driver(&lpc_sch_driver);
+}
+
+static void __exit lpc_sch_exit(void)
+{
+ pci_unregister_driver(&lpc_sch_driver);
+}
+
+module_init(lpc_sch_init);
+module_exit(lpc_sch_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
--- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
+++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
@@ -55,4 +55,5 @@
obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
-obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
\ No newline at end of file
+obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
+obj-$(CONFIG_LPC_SCH) += lpc_sch.o
On 02/18/10 10:01, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
> simultaneous use of SMBus and GPIO interfaces on the SCH.
>
> v2: added "select MFD_CORE"
> v3: removed "default m"
Thanks.
> Signed-off-by: Denis Turischev <[email protected]>
>
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig
> linux-2.6.33-rc7/drivers/mfd/Kconfig
> --- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07
> 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18
> 19:56:19.000000000 +0200
> @@ -348,6 +348,14 @@
> read/write functions for the devices to get access to this chip.
> This chip embeds various other multimedia funtionalities as well.
>
> +config LPC_SCH
> + tristate "Intel SCH LPC"
> + depends on PCI
> + select MFD_CORE
> + help
> + LPC bridge function of the Intel SCH provides support for
> + System Management Bus and General Purpose I/O.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
--
~Randy
Hi Denis,
On Thu, Feb 18, 2010 at 08:01:33PM +0200, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
> simultaneous use of SMBus and GPIO interfaces on the SCH.
>
> v2: added "select MFD_CORE"
> v3: removed "default m"
>
> Signed-off-by: Denis Turischev <[email protected]>
patch applied, many thanks.
Cheers,
Samuel.
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
> --- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18 19:56:19.000000000 +0200
> @@ -348,6 +348,14 @@
> read/write functions for the devices to get access to this chip.
> This chip embeds various other multimedia funtionalities as well.
>
> +config LPC_SCH
> + tristate "Intel SCH LPC"
> + depends on PCI
> + select MFD_CORE
> + help
> + LPC bridge function of the Intel SCH provides support for
> + System Management Bus and General Purpose I/O.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
> --- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
> @@ -0,0 +1,133 @@
> +/*
> + * 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
> + * Author: Denis Turischev <[email protected]>
> + *
> + * 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.
> + */
> +
> +#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>
> +
> +#define SMBASE 0x40
> +#define SMBUS_IO_SIZE 64
> +
> +#define GPIOBASE 0x44
> +#define GPIO_IO_SIZE 64
> +
> +static struct resource smbus_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +
> +static struct resource gpio_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +static struct mfd_cell lpc_sch_cells[] = {
> + {
> + .name = "isch_smbus",
> + .num_resources = 1,
> + .resources = &smbus_sch_resource,
> + },
> + {
> + .name = "sch_gpio",
> + .num_resources = 1,
> + .resources = &gpio_sch_resource,
> + },
> +};
> +
> +static struct pci_device_id lpc_sch_ids[] = {
> + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> + { 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
> +
> +static int __devinit lpc_sch_probe(struct pci_dev *dev,
> + const struct pci_device_id *id)
> +{
> + unsigned int base_addr_cfg;
> + unsigned short base_addr;
> +
> + pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
> + return -ENODEV;
> + }
> +
> + smbus_sch_resource.start = base_addr;
> + smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
> +
> + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
> + return -ENODEV;
> + }
> +
> + gpio_sch_resource.start = base_addr;
> + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
> +
> + return mfd_add_devices(&dev->dev, -1,
> + lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
> +}
> +
> +static void __devexit lpc_sch_remove(struct pci_dev *dev)
> +{
> + mfd_remove_devices(&dev->dev);
> +}
> +
> +static struct pci_driver lpc_sch_driver = {
> + .name = "lpc_sch",
> + .id_table = lpc_sch_ids,
> + .probe = lpc_sch_probe,
> + .remove = __devexit_p(lpc_sch_remove),
> +};
> +
> +static int __init lpc_sch_init(void)
> +{
> + return pci_register_driver(&lpc_sch_driver);
> +}
> +
> +static void __exit lpc_sch_exit(void)
> +{
> + pci_unregister_driver(&lpc_sch_driver);
> +}
> +
> +module_init(lpc_sch_init);
> +module_exit(lpc_sch_exit);
> +
> +MODULE_AUTHOR("Denis Turischev <[email protected]>");
> +MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
> +MODULE_LICENSE("GPL");
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
> --- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
> @@ -55,4 +55,5 @@
> obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
> obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
> obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
> -obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> \ No newline at end of file
> +obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> +obj-$(CONFIG_LPC_SCH) += lpc_sch.o
--
Intel Open Source Technology Centre
http://oss.intel.com/
Hi Denis,
On Wed, Feb 17, 2010 at 05:42:21PM +0200, Denis Turischev wrote:
> acpi_check_region will be implemented in mfd-core, therefore v2 version avoids
> this check
>
> Signed-off-by: Denis Turischev <[email protected]>
This patch doesnt apply properly against neither my mfd tree nor Linus tree.
Could you refresh it against the latest Linus tree, please ?
Same applies to the GPIO patch, btw.
Cheers,
Samuel.
> --- linux-2.6.33-rc7.orig/drivers/i2c/busses/i2c-isch.c 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/i2c/busses/i2c-isch.c 2010-02-17 17:08:53.000000000 +0200
> @@ -27,7 +27,7 @@
> */
>
> #include <linux/module.h>
> -#include <linux/pci.h>
> +#include <linux/platform_device.h>
> #include <linux/kernel.h>
> #include <linux/delay.h>
> #include <linux/stddef.h>
> @@ -46,12 +46,6 @@
> #define SMBHSTDAT1 (7 + sch_smba)
> #define SMBBLKDAT (0x20 + sch_smba)
>
> -/* count for request_region */
> -#define SMBIOSIZE 64
> -
> -/* PCI Address Constants */
> -#define SMBBA_SCH 0x40
> -
> /* Other settings */
> #define MAX_TIMEOUT 500
>
> @@ -63,7 +57,6 @@
> #define SCH_BLOCK_DATA 0x05
>
> static unsigned short sch_smba;
> -static struct pci_driver sch_driver;
> static struct i2c_adapter sch_adapter;
>
> /*
> @@ -256,37 +249,23 @@
> .algo = &smbus_algorithm,
> };
>
> -static struct pci_device_id sch_ids[] = {
> - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> - { 0, }
> -};
> -
> -MODULE_DEVICE_TABLE(pci, sch_ids);
> -
> -static int __devinit sch_probe(struct pci_dev *dev,
> - const struct pci_device_id *id)
> +static int __devinit smbus_sch_probe(struct platform_device *dev)
> {
> + struct resource *res;
> int retval;
> - unsigned int smba;
>
> - pci_read_config_dword(dev, SMBBA_SCH, &smba);
> - if (!(smba & (1 << 31))) {
> - dev_err(&dev->dev, "SMBus I/O space disabled!\n");
> - return -ENODEV;
> - }
> + res = platform_get_resource(dev, IORESOURCE_IO, 0);
> + if (!res)
> + return -EBUSY;
>
> - sch_smba = (unsigned short)smba;
> - if (sch_smba == 0) {
> - dev_err(&dev->dev, "SMBus base address uninitialized!\n");
> - return -ENODEV;
> - }
> - if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
> - return -ENODEV;
> - if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
> + if (!request_region(res->start, resource_size(res), dev->name)) {
> dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
> sch_smba);
> return -EBUSY;
> }
> +
> + sch_smba = res->start;
> +
> dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
>
> /* set up the sysfs linkage to our parent device */
> @@ -298,37 +277,43 @@
> retval = i2c_add_adapter(&sch_adapter);
> if (retval) {
> dev_err(&dev->dev, "Couldn't register adapter!\n");
> - release_region(sch_smba, SMBIOSIZE);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
>
> return retval;
> }
>
> -static void __devexit sch_remove(struct pci_dev *dev)
> +static int __devexit smbus_sch_remove(struct platform_device *pdev)
> {
> + struct resource *res;
> if (sch_smba) {
> i2c_del_adapter(&sch_adapter);
> - release_region(sch_smba, SMBIOSIZE);
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
> +
> + return 0;
> }
>
> -static struct pci_driver sch_driver = {
> - .name = "isch_smbus",
> - .id_table = sch_ids,
> - .probe = sch_probe,
> - .remove = __devexit_p(sch_remove),
> +static struct platform_driver smbus_sch_driver = {
> + .driver = {
> + .name = "isch_smbus",
> + .owner = THIS_MODULE,
> + },
> + .probe = smbus_sch_probe,
> + .remove = __devexit_p(smbus_sch_remove),
> };
>
> static int __init i2c_sch_init(void)
> {
> - return pci_register_driver(&sch_driver);
> + return platform_driver_register(&smbus_sch_driver);
> }
>
> static void __exit i2c_sch_exit(void)
> {
> - pci_unregister_driver(&sch_driver);
> + platform_driver_unregister(&smbus_sch_driver);
> }
>
> MODULE_AUTHOR("Jacob Pan <[email protected]>");
> @@ -337,3 +322,4 @@
>
> module_init(i2c_sch_init);
> module_exit(i2c_sch_exit);
> +MODULE_ALIAS("platform:isch_smbus");
--
Intel Open Source Technology Centre
http://oss.intel.com/
v2: there is no acpi_check_region, it will be implemented in mfd-core
v3: patch refreshed against the latest Linus tree
Signed-off-by: Denis Turischev <[email protected]>
---
drivers/i2c/busses/Kconfig | 2 +-
drivers/i2c/busses/i2c-isch.c | 68 ++++++++++++++++------------------------
2 files changed, 28 insertions(+), 42 deletions(-)
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 5f318ce..d15b6d3 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -104,7 +104,7 @@ config I2C_I801
config I2C_ISCH
tristate "Intel SCH SMBus 1.0"
- depends on PCI
+ select LPC_SCH
help
Say Y here if you want to use SMBus controller on the Intel SCH
based systems.
diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
index dba6eb0..ddc258e 100644
--- a/drivers/i2c/busses/i2c-isch.c
+++ b/drivers/i2c/busses/i2c-isch.c
@@ -27,7 +27,7 @@
*/
#include <linux/module.h>
-#include <linux/pci.h>
+#include <linux/platform_device.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/stddef.h>
@@ -46,12 +46,6 @@
#define SMBHSTDAT1 (7 + sch_smba)
#define SMBBLKDAT (0x20 + sch_smba)
-/* count for request_region */
-#define SMBIOSIZE 64
-
-/* PCI Address Constants */
-#define SMBBA_SCH 0x40
-
/* Other settings */
#define MAX_TIMEOUT 500
@@ -63,7 +57,6 @@
#define SCH_BLOCK_DATA 0x05
static unsigned short sch_smba;
-static struct pci_driver sch_driver;
static struct i2c_adapter sch_adapter;
/*
@@ -256,37 +249,23 @@ static struct i2c_adapter sch_adapter = {
.algo = &smbus_algorithm,
};
-static struct pci_device_id sch_ids[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
- { 0, }
-};
-
-MODULE_DEVICE_TABLE(pci, sch_ids);
-
-static int __devinit sch_probe(struct pci_dev *dev,
- const struct pci_device_id *id)
+static int __devinit smbus_sch_probe(struct platform_device *dev)
{
+ struct resource *res;
int retval;
- unsigned int smba;
- pci_read_config_dword(dev, SMBBA_SCH, &smba);
- if (!(smba & (1 << 31))) {
- dev_err(&dev->dev, "SMBus I/O space disabled!\n");
- return -ENODEV;
- }
+ res = platform_get_resource(dev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
- sch_smba = (unsigned short)smba;
- if (sch_smba == 0) {
- dev_err(&dev->dev, "SMBus base address uninitialized!\n");
- return -ENODEV;
- }
- if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
- return -ENODEV;
- if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
+ if (!request_region(res->start, resource_size(res), dev->name)) {
dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
sch_smba);
return -EBUSY;
}
+
+ sch_smba = res->start;
+
dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
/* set up the sysfs linkage to our parent device */
@@ -298,37 +277,43 @@ static int __devinit sch_probe(struct pci_dev *dev,
retval = i2c_add_adapter(&sch_adapter);
if (retval) {
dev_err(&dev->dev, "Couldn't register adapter!\n");
- release_region(sch_smba, SMBIOSIZE);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
return retval;
}
-static void __devexit sch_remove(struct pci_dev *dev)
+static int __devexit smbus_sch_remove(struct platform_device *pdev)
{
+ struct resource *res;
if (sch_smba) {
i2c_del_adapter(&sch_adapter);
- release_region(sch_smba, SMBIOSIZE);
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ release_region(res->start, resource_size(res));
sch_smba = 0;
}
+
+ return 0;
}
-static struct pci_driver sch_driver = {
- .name = "isch_smbus",
- .id_table = sch_ids,
- .probe = sch_probe,
- .remove = __devexit_p(sch_remove),
+static struct platform_driver smbus_sch_driver = {
+ .driver = {
+ .name = "isch_smbus",
+ .owner = THIS_MODULE,
+ },
+ .probe = smbus_sch_probe,
+ .remove = __devexit_p(smbus_sch_remove),
};
static int __init i2c_sch_init(void)
{
- return pci_register_driver(&sch_driver);
+ return platform_driver_register(&smbus_sch_driver);
}
static void __exit i2c_sch_exit(void)
{
- pci_unregister_driver(&sch_driver);
+ platform_driver_unregister(&smbus_sch_driver);
}
MODULE_AUTHOR("Jacob Pan <[email protected]>");
@@ -337,3 +322,4 @@ MODULE_LICENSE("GPL");
module_init(i2c_sch_init);
module_exit(i2c_sch_exit);
+MODULE_ALIAS("platform:isch_smbus");
--
1.6.3.3
v2: there is no acpi_check_region, it will be implemented in mfd-core
v3: patch refreshed against the latest Linus tree
Signed-off-by: Denis Turischev <[email protected]>
---
drivers/gpio/Kconfig | 16 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/sch_gpio.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 299 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/sch_gpio.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 1f1d88a..1730068 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -85,6 +85,22 @@ config GPIO_VR41XX
help
Say yes here to support the NEC VR4100 series General-purpose I/O Uint
+config GPIO_SCH
+ tristate "Intel SCH GPIO"
+ depends on GPIOLIB
+ select LPC_SCH
+ help
+ Say yes here to support GPIO interface on Intel Poulsbo SCH.
+ The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
+ powered by the core power rail and are turned off during sleep
+ modes (S3 and higher). The remaining four GPIOs are powered by
+ the Intel SCH suspend power supply. These GPIOs remain
+ active during S3. The suspend powered GPIOs can be used to wake the
+ system from the Suspend-to-RAM state.
+
+ This driver can also be built as a module. If so, the module
+ will be called sch-gpio.
+
comment "I2C GPIO expanders:"
config GPIO_MAX732X
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 4868723..aa1d06e 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o
obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
+obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/sch_gpio.c
new file mode 100644
index 0000000..761071a
--- /dev/null
+++ b/drivers/gpio/sch_gpio.c
@@ -0,0 +1,282 @@
+/*
+ * sch_gpio.c - GPIO interface for Intel Poulsbo SCH
+ *
+ * Copyright (c) 2010 CompuLab Ltd
+ * Author: Denis Turischev <[email protected]>
+ *
+ * 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.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/io.h>
+#include <linux/errno.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+
+#include <linux/gpio.h>
+
+static DEFINE_SPINLOCK(gpio_lock);
+
+#define CGEN (0x00)
+#define CGIO (0x04)
+#define CGLV (0x08)
+
+#define RGEN (0x20)
+#define RGIO (0x24)
+#define RGLV (0x28)
+
+static unsigned short gpio_ba;
+
+static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+
+ if (!(curr_dirs & (1 << bit)))
+ outb(curr_dirs | (1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ int res;
+ unsigned short offset, bit;
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ res = !!(inb(gpio_ba + offset) & (1 << bit));
+ return res;
+}
+
+static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+ unsigned short offset, bit;
+
+ spin_lock(&gpio_lock);
+
+ offset = CGLV + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_vals = inb(gpio_ba + offset);
+
+ if (val)
+ outb(curr_vals | (1 << bit), gpio_ba + offset);
+ else
+ outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_core_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+ unsigned short offset, bit;
+
+ sch_gpio_core_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ offset = CGIO + gpio_num / 8;
+ bit = gpio_num % 8;
+
+ curr_dirs = inb(gpio_ba + offset);
+ if (curr_dirs & (1 << bit))
+ outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_core = {
+ .label = "sch_gpio_core",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_core_direction_in,
+ .get = sch_gpio_core_get,
+ .direction_output = sch_gpio_core_direction_out,
+ .set = sch_gpio_core_set,
+};
+
+static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
+ unsigned gpio_num)
+{
+ u8 curr_dirs;
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+
+ if (!(curr_dirs & (1 << gpio_num)))
+ outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
+{
+ return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
+}
+
+static void sch_gpio_resume_set(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_vals;
+
+ spin_lock(&gpio_lock);
+
+ curr_vals = inb(gpio_ba + RGLV);
+
+ if (val)
+ outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
+ else
+ outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
+
+ spin_unlock(&gpio_lock);
+}
+
+static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
+ unsigned gpio_num, int val)
+{
+ u8 curr_dirs;
+
+ sch_gpio_resume_set(gc, gpio_num, val);
+
+ spin_lock(&gpio_lock);
+
+ curr_dirs = inb(gpio_ba + RGIO);
+ if (curr_dirs & (1 << gpio_num))
+ outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
+
+ spin_unlock(&gpio_lock);
+ return 0;
+}
+
+static struct gpio_chip sch_gpio_resume = {
+ .label = "sch_gpio_resume",
+ .owner = THIS_MODULE,
+ .direction_input = sch_gpio_resume_direction_in,
+ .get = sch_gpio_resume_get,
+ .direction_output = sch_gpio_resume_direction_out,
+ .set = sch_gpio_resume_set,
+};
+
+static int __devinit sch_gpio_probe(struct platform_device *pdev)
+{
+ struct resource *res;
+ int err;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res)
+ return -EBUSY;
+
+ if (!request_region(res->start, resource_size(res), pdev->name))
+ return -EBUSY;
+
+ gpio_ba = res->start;
+
+ sch_gpio_core.base = 0;
+ sch_gpio_core.ngpio = 10;
+ sch_gpio_core.dev = &pdev->dev;
+
+ sch_gpio_resume.base = 10;
+ sch_gpio_resume.ngpio = 4;
+ sch_gpio_resume.dev = &pdev->dev;
+
+ err = gpiochip_add(&sch_gpio_core);
+ if (err < 0)
+ goto err_sch_gpio_core;
+
+ err = gpiochip_add(&sch_gpio_resume);
+ if (err < 0)
+ goto err_sch_gpio_resume;
+
+ /*
+ * GPIO[6:0] enabled by default
+ * GPIO7 is configured by the CMC as SLPIOVR
+ * Enable GPIO[9:8] core powered gpios explicitly
+ */
+ outb(0x3, gpio_ba + CGEN + 1);
+ /*
+ * SUS_GPIO[2:0] enabled by default
+ * Enable SUS_GPIO3 resume powered gpio explicitly
+ */
+ outb(0x8, gpio_ba + RGEN);
+
+ return 0;
+
+err_sch_gpio_resume:
+ gpiochip_remove(&sch_gpio_core);
+
+err_sch_gpio_core:
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+
+ return err;
+}
+
+static int __devexit sch_gpio_remove(struct platform_device *pdev)
+{
+ struct resource *res;
+ if (gpio_ba) {
+ gpiochip_remove(&sch_gpio_core);
+ gpiochip_remove(&sch_gpio_resume);
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+
+ release_region(res->start, resource_size(res));
+ gpio_ba = 0;
+ }
+
+ return 0;
+}
+
+static struct platform_driver sch_gpio_driver = {
+ .driver = {
+ .name = "sch_gpio",
+ .owner = THIS_MODULE,
+ },
+ .probe = sch_gpio_probe,
+ .remove = __devexit_p(sch_gpio_remove),
+};
+
+static int __init sch_gpio_init(void)
+{
+ return platform_driver_register(&sch_gpio_driver);
+}
+
+static void __exit sch_gpio_exit(void)
+{
+ platform_driver_unregister(&sch_gpio_driver);
+}
+
+module_init(sch_gpio_init);
+module_exit(sch_gpio_exit);
+
+MODULE_AUTHOR("Denis Turischev <[email protected]>");
+MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sch_gpio");
--
1.6.3.3
On Sunday 21 February 2010, Denis Turischev wrote:
> v2: there is no acpi_check_region, it will be implemented in mfd-core
> v3: patch refreshed against the latest Linus tree
Could such call really address the GPIO conflict issue I mentioned?
The AML bytecodes I looked at were writing directly to Southbridge
GPIO registers (or reading them), or relying on ACPI to mediate the
GPIO interrupts. ISTR that button drivers, and code to switch into
or out of low power states, were good sources of such bad examples.
Calls like that should clearly be able to handle cases where ACPI
has a "Real" Driver (tm) ... e.g. for SMBus hardware.
I'm not sure what a good solution for this would be, short of just
not using ACPI ... which may not be practical, given the limited
degree of x86 board/system support for Linux.
I mention this mostly because when I looked at the issue in the
context of an ICHx GPIO driver, I didn't see a good solution to
the problem then ... and nothing seems to have changed meanwhile.
- Dave
Hi Jean,
Denis Turischev wrote:
> v2: there is no acpi_check_region, it will be implemented in mfd-core
> v3: patch refreshed against the latest Linus tree
>
> Signed-off-by: Denis Turischev <[email protected]>
Any chance this can go to 2.6.34?
> ---
> drivers/i2c/busses/Kconfig | 2 +-
> drivers/i2c/busses/i2c-isch.c | 68
> ++++++++++++++++------------------------
> 2 files changed, 28 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 5f318ce..d15b6d3 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -104,7 +104,7 @@ config I2C_I801
>
> config I2C_ISCH
> tristate "Intel SCH SMBus 1.0"
> - depends on PCI
> + select LPC_SCH
> help
> Say Y here if you want to use SMBus controller on the Intel SCH
> based systems.
> diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
> index dba6eb0..ddc258e 100644
> --- a/drivers/i2c/busses/i2c-isch.c
> +++ b/drivers/i2c/busses/i2c-isch.c
> @@ -27,7 +27,7 @@
> */
>
> #include <linux/module.h>
> -#include <linux/pci.h>
> +#include <linux/platform_device.h>
> #include <linux/kernel.h>
> #include <linux/delay.h>
> #include <linux/stddef.h>
> @@ -46,12 +46,6 @@
> #define SMBHSTDAT1 (7 + sch_smba)
> #define SMBBLKDAT (0x20 + sch_smba)
>
> -/* count for request_region */
> -#define SMBIOSIZE 64
> -
> -/* PCI Address Constants */
> -#define SMBBA_SCH 0x40
> -
> /* Other settings */
> #define MAX_TIMEOUT 500
>
> @@ -63,7 +57,6 @@
> #define SCH_BLOCK_DATA 0x05
>
> static unsigned short sch_smba;
> -static struct pci_driver sch_driver;
> static struct i2c_adapter sch_adapter;
>
> /*
> @@ -256,37 +249,23 @@ static struct i2c_adapter sch_adapter = {
> .algo = &smbus_algorithm,
> };
>
> -static struct pci_device_id sch_ids[] = {
> - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> - { 0, }
> -};
> -
> -MODULE_DEVICE_TABLE(pci, sch_ids);
> -
> -static int __devinit sch_probe(struct pci_dev *dev,
> - const struct pci_device_id *id)
> +static int __devinit smbus_sch_probe(struct platform_device *dev)
> {
> + struct resource *res;
> int retval;
> - unsigned int smba;
>
> - pci_read_config_dword(dev, SMBBA_SCH, &smba);
> - if (!(smba & (1 << 31))) {
> - dev_err(&dev->dev, "SMBus I/O space disabled!\n");
> - return -ENODEV;
> - }
> + res = platform_get_resource(dev, IORESOURCE_IO, 0);
> + if (!res)
> + return -EBUSY;
>
> - sch_smba = (unsigned short)smba;
> - if (sch_smba == 0) {
> - dev_err(&dev->dev, "SMBus base address uninitialized!\n");
> - return -ENODEV;
> - }
> - if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
> - return -ENODEV;
> - if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
> + if (!request_region(res->start, resource_size(res), dev->name)) {
> dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
> sch_smba);
> return -EBUSY;
> }
> +
> + sch_smba = res->start;
> +
> dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
>
> /* set up the sysfs linkage to our parent device */
> @@ -298,37 +277,43 @@ static int __devinit sch_probe(struct pci_dev *dev,
> retval = i2c_add_adapter(&sch_adapter);
> if (retval) {
> dev_err(&dev->dev, "Couldn't register adapter!\n");
> - release_region(sch_smba, SMBIOSIZE);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
>
> return retval;
> }
>
> -static void __devexit sch_remove(struct pci_dev *dev)
> +static int __devexit smbus_sch_remove(struct platform_device *pdev)
> {
> + struct resource *res;
> if (sch_smba) {
> i2c_del_adapter(&sch_adapter);
> - release_region(sch_smba, SMBIOSIZE);
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
> +
> + return 0;
> }
>
> -static struct pci_driver sch_driver = {
> - .name = "isch_smbus",
> - .id_table = sch_ids,
> - .probe = sch_probe,
> - .remove = __devexit_p(sch_remove),
> +static struct platform_driver smbus_sch_driver = {
> + .driver = {
> + .name = "isch_smbus",
> + .owner = THIS_MODULE,
> + },
> + .probe = smbus_sch_probe,
> + .remove = __devexit_p(smbus_sch_remove),
> };
>
> static int __init i2c_sch_init(void)
> {
> - return pci_register_driver(&sch_driver);
> + return platform_driver_register(&smbus_sch_driver);
> }
>
> static void __exit i2c_sch_exit(void)
> {
> - pci_unregister_driver(&sch_driver);
> + platform_driver_unregister(&smbus_sch_driver);
> }
>
> MODULE_AUTHOR("Jacob Pan <[email protected]>");
> @@ -337,3 +322,4 @@ MODULE_LICENSE("GPL");
>
> module_init(i2c_sch_init);
> module_exit(i2c_sch_exit);
> +MODULE_ALIAS("platform:isch_smbus");
--
Sincerely yours,
Mike.
Hi Mike,
On Tue, 23 Feb 2010 09:00:28 +0200, Mike Rapoport wrote:
> Hi Jean,
>
> Denis Turischev wrote:
> > v2: there is no acpi_check_region, it will be implemented in mfd-core
> > v3: patch refreshed against the latest Linus tree
> >
> > Signed-off-by: Denis Turischev <[email protected]>
>
> Any chance this can go to 2.6.34?
I can add my
Acked-by: Jean Delvare <[email protected]>
but the patch itself would rather go through Samuel's mfd tree. The
different patches depend on each other so pushing them through
different trees would cause trouble.
--
Jean Delvare
Hi Jean,
On Tue, Feb 23, 2010 at 09:12:21AM +0100, Jean Delvare wrote:
> Hi Mike,
>
> On Tue, 23 Feb 2010 09:00:28 +0200, Mike Rapoport wrote:
> > Hi Jean,
> >
> > Denis Turischev wrote:
> > > v2: there is no acpi_check_region, it will be implemented in mfd-core
> > > v3: patch refreshed against the latest Linus tree
> > >
> > > Signed-off-by: Denis Turischev <[email protected]>
> >
> > Any chance this can go to 2.6.34?
>
> I can add my
>
> Acked-by: Jean Delvare <[email protected]>
>
> but the patch itself would rather go through Samuel's mfd tree. The
> different patches depend on each other so pushing them through
> different trees would cause trouble.
Exactly. I asked Denis to rebase them against Linus' latest because I was
planning to merge them through my tree.
I'll take patches 2 and 3 from this patchset.
Cheers,
Samuel.
> --
> Jean Delvare
--
Intel Open Source Technology Centre
http://oss.intel.com/
Hi Samuel,
On Tue, 23 Feb 2010 09:20:55 +0100, Samuel Ortiz wrote:
> Hi Jean,
>
> On Tue, Feb 23, 2010 at 09:12:21AM +0100, Jean Delvare wrote:
> > I can add my
> >
> > Acked-by: Jean Delvare <[email protected]>
> >
> > but the patch itself would rather go through Samuel's mfd tree. The
> > different patches depend on each other so pushing them through
> > different trees would cause trouble.
> Exactly. I asked Denis to rebase them against Linus' latest because I was
> planning to merge them through my tree.
> I'll take patches 2 and 3 from this patchset.
You mean patches 1 and 2?
--
Jean Delvare
On Thu, 18 Feb 2010 20:01:33 +0200, Denis Turischev wrote:
> Intel Poulsbo (SCH) chipset LPC bridge controller contains several
> functions. Creating and MFD driver for the LPC bridge controller allows
> simultaneous use of SMBus and GPIO interfaces on the SCH.
>
> v2: added "select MFD_CORE"
> v3: removed "default m"
>
> Signed-off-by: Denis Turischev <[email protected]>
>
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Kconfig linux-2.6.33-rc7/drivers/mfd/Kconfig
> --- linux-2.6.33-rc7.orig/drivers/mfd/Kconfig 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Kconfig 2010-02-18 19:56:19.000000000 +0200
> @@ -348,6 +348,14 @@
> read/write functions for the devices to get access to this chip.
> This chip embeds various other multimedia funtionalities as well.
>
> +config LPC_SCH
> + tristate "Intel SCH LPC"
> + depends on PCI
> + select MFD_CORE
> + help
> + LPC bridge function of the Intel SCH provides support for
> + System Management Bus and General Purpose I/O.
> +
> endmenu
>
> menu "Multimedia Capabilities Port drivers"
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c linux-2.6.33-rc7/drivers/mfd/lpc_sch.c
> --- linux-2.6.33-rc7.orig/drivers/mfd/lpc_sch.c 1970-01-01 02:00:00.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/lpc_sch.c 2010-02-11 10:31:54.000000000 +0200
> @@ -0,0 +1,133 @@
> +/*
> + * 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
> + * Author: Denis Turischev <[email protected]>
> + *
> + * 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.
> + */
> +
> +#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>
> +
> +#define SMBASE 0x40
> +#define SMBUS_IO_SIZE 64
> +
> +#define GPIOBASE 0x44
> +#define GPIO_IO_SIZE 64
> +
> +static struct resource smbus_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +
> +static struct resource gpio_sch_resource = {
> + .flags = IORESOURCE_IO,
> +};
> +
> +static struct mfd_cell lpc_sch_cells[] = {
> + {
> + .name = "isch_smbus",
> + .num_resources = 1,
> + .resources = &smbus_sch_resource,
> + },
> + {
> + .name = "sch_gpio",
> + .num_resources = 1,
> + .resources = &gpio_sch_resource,
> + },
> +};
These names are nicely inconsistent. What about "isch_gpio"?
> +
> +static struct pci_device_id lpc_sch_ids[] = {
> + { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> + { 0, }
> +};
> +MODULE_DEVICE_TABLE(pci, lpc_sch_ids);
> +
> +static int __devinit lpc_sch_probe(struct pci_dev *dev,
> + const struct pci_device_id *id)
> +{
> + unsigned int base_addr_cfg;
> + unsigned short base_addr;
> +
> + pci_read_config_dword(dev, SMBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the SMBus I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for SMBus uninitialized\n");
> + return -ENODEV;
> + }
> +
> + smbus_sch_resource.start = base_addr;
> + smbus_sch_resource.end = base_addr + SMBUS_IO_SIZE - 1;
> +
> + pci_read_config_dword(dev, GPIOBASE, &base_addr_cfg);
> + if (!(base_addr_cfg & (1 << 31))) {
> + dev_err(&dev->dev, "Decode of the GPIO I/O range disabled\n");
> + return -ENODEV;
> + }
> + base_addr = (unsigned short)base_addr_cfg;
> + if (base_addr == 0) {
> + dev_err(&dev->dev, "I/O space for GPIO uninitialized\n");
> + return -ENODEV;
> + }
> +
> + gpio_sch_resource.start = base_addr;
> + gpio_sch_resource.end = base_addr + GPIO_IO_SIZE - 1;
> +
> + return mfd_add_devices(&dev->dev, -1,
> + lpc_sch_cells, ARRAY_SIZE(lpc_sch_cells), NULL, 0);
> +}
> +
> +static void __devexit lpc_sch_remove(struct pci_dev *dev)
> +{
> + mfd_remove_devices(&dev->dev);
> +}
> +
> +static struct pci_driver lpc_sch_driver = {
> + .name = "lpc_sch",
> + .id_table = lpc_sch_ids,
> + .probe = lpc_sch_probe,
> + .remove = __devexit_p(lpc_sch_remove),
> +};
> +
> +static int __init lpc_sch_init(void)
> +{
> + return pci_register_driver(&lpc_sch_driver);
> +}
> +
> +static void __exit lpc_sch_exit(void)
> +{
> + pci_unregister_driver(&lpc_sch_driver);
> +}
> +
> +module_init(lpc_sch_init);
> +module_exit(lpc_sch_exit);
> +
> +MODULE_AUTHOR("Denis Turischev <[email protected]>");
> +MODULE_DESCRIPTION("LPC interface for Intel Poulsbo SCH");
> +MODULE_LICENSE("GPL");
> diff -Nru linux-2.6.33-rc7.orig/drivers/mfd/Makefile linux-2.6.33-rc7/drivers/mfd/Makefile
> --- linux-2.6.33-rc7.orig/drivers/mfd/Makefile 2010-02-07 00:17:12.000000000 +0200
> +++ linux-2.6.33-rc7/drivers/mfd/Makefile 2010-02-10 15:15:40.000000000 +0200
> @@ -55,4 +55,5 @@
> obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o
> obj-$(CONFIG_AB4500_CORE) += ab4500-core.o
> obj-$(CONFIG_MFD_88PM8607) += 88pm8607.o
> -obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> \ No newline at end of file
> +obj-$(CONFIG_PMIC_ADP5520) += adp5520.o
> +obj-$(CONFIG_LPC_SCH) += lpc_sch.o
I don't like this name either. There is another vendor (SMSC) shipping
LPC devices with "SCH" in their names, so there is room for confusion.
"isch" makes it clearer that we are talking about the Intel ones.
--
Jean Delvare
Hi Samuel,
Regarding renaming of sch* to isch* do you want incremental patch, or fresh version?
Denis
Jean Delvare wrote:
>> +static struct mfd_cell lpc_sch_cells[] = {
>> + {
>> + .name = "isch_smbus",
>> + .num_resources = 1,
>> + .resources = &smbus_sch_resource,
>> + },
>> + {
>> + .name = "sch_gpio",
>> + .num_resources = 1,
>> + .resources = &gpio_sch_resource,
>> + },
>> +};
>
> These names are nicely inconsistent. What about "isch_gpio"?
>
>> +obj-$(CONFIG_LPC_SCH) += lpc_sch.o
>
> I don't like this name either. There is another vendor (SMSC) shipping
> LPC devices with "SCH" in their names, so there is room for confusion.
> "isch" makes it clearer that we are talking about the Intel ones.
>
Hi Denis,
On Tue, Feb 23, 2010 at 11:25:59AM +0200, Denis Turischev wrote:
> Hi Samuel,
> Regarding renaming of sch* to isch* do you want incremental patch, or fresh version?
>
I'll fix that myself, no worries.
Cheers,
Samuel.
> Denis
>
> Jean Delvare wrote:
> >>+static struct mfd_cell lpc_sch_cells[] = {
> >>+ {
> >>+ .name = "isch_smbus",
> >>+ .num_resources = 1,
> >>+ .resources = &smbus_sch_resource,
> >>+ },
> >>+ {
> >>+ .name = "sch_gpio",
> >>+ .num_resources = 1,
> >>+ .resources = &gpio_sch_resource,
> >>+ },
> >>+};
> >
> >These names are nicely inconsistent. What about "isch_gpio"?
> >
>
> >>+obj-$(CONFIG_LPC_SCH) += lpc_sch.o
> >
> >I don't like this name either. There is another vendor (SMSC) shipping
> >LPC devices with "SCH" in their names, so there is room for confusion.
> >"isch" makes it clearer that we are talking about the Intel ones.
> >
>
--
Intel Open Source Technology Centre
http://oss.intel.com/
David Brownell wrote:
> On Sunday 21 February 2010, Denis Turischev wrote:
>> v2: there is no acpi_check_region, it will be implemented in mfd-core
>> v3: patch refreshed against the latest Linus tree
>
> Could such call really address the GPIO conflict issue I mentioned?
>
> The AML bytecodes I looked at were writing directly to Southbridge
> GPIO registers (or reading them), or relying on ACPI to mediate the
> GPIO interrupts. ISTR that button drivers, and code to switch into
> or out of low power states, were good sources of such bad examples.
I'm really not an ACPI expert, but as far as I understand possibility of
such conflicts largely depends on particular board/BIOS implementation.
On the hardware we have such conflict cannot happen, unless there are
bugs in ACPI we are not yet aware of. :)
> Calls like that should clearly be able to handle cases where ACPI
> has a "Real" Driver (tm) ... e.g. for SMBus hardware.
>
> I'm not sure what a good solution for this would be, short of just
> not using ACPI ... which may not be practical, given the limited
> degree of x86 board/system support for Linux.
>
> I mention this mostly because when I looked at the issue in the
> context of an ICHx GPIO driver, I didn't see a good solution to
> the problem then ... and nothing seems to have changed meanwhile.
I've looked at two x86 drivers in drivers/gpiolib (cs5535 and langwell)
and there's no treatment of ACPI in either of them. Since SCH is defined
by Intel as "embedded" product, having a GPIO driver for it seems
logical even despite problems you mention.
> - Dave
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
--
Sincerely yours,
Mike.
Hi Denis,
On Sun, Feb 21, 2010 at 02:50:41PM +0200, Denis Turischev wrote:
> v2: there is no acpi_check_region, it will be implemented in mfd-core
> v3: patch refreshed against the latest Linus tree
As with patch #2 of that serie, patch still doesnt apply against Linus' latest
tree:
patching file drivers/gpio/Kconfig
Hunk #1 FAILED at 85.
1 out of 1 hunk FAILED -- saving rejects to file drivers/gpio/Kconfig.rej
patching file drivers/gpio/Makefile
Hunk #1 FAILED at 22.
1 out of 1 hunk FAILED -- saving rejects to file drivers/gpio/Makefile.rej
patching file drivers/gpio/sch_gpio.c
Cheers,
Samuel.
> Signed-off-by: Denis Turischev <[email protected]>
> ---
> drivers/gpio/Kconfig | 16 +++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/sch_gpio.c | 282 +++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 299 insertions(+), 0 deletions(-)
> create mode 100644 drivers/gpio/sch_gpio.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 1f1d88a..1730068 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -85,6 +85,22 @@ config GPIO_VR41XX
> help
> Say yes here to support the NEC VR4100 series General-purpose I/O Uint
>
> +config GPIO_SCH
> + tristate "Intel SCH GPIO"
> + depends on GPIOLIB
> + select LPC_SCH
> + help
> + Say yes here to support GPIO interface on Intel Poulsbo SCH.
> + The Intel SCH contains a total of 14 GPIO pins. Ten GPIOs are
> + powered by the core power rail and are turned off during sleep
> + modes (S3 and higher). The remaining four GPIOs are powered by
> + the Intel SCH suspend power supply. These GPIOs remain
> + active during S3. The suspend powered GPIOs can be used to wake the
> + system from the Suspend-to-RAM state.
> +
> + This driver can also be built as a module. If so, the module
> + will be called sch-gpio.
> +
> comment "I2C GPIO expanders:"
>
> config GPIO_MAX732X
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 4868723..aa1d06e 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -22,3 +22,4 @@ obj-$(CONFIG_GPIO_CS5535) += cs5535-gpio.o
> obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o
> obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
> obj-$(CONFIG_GPIO_WM831X) += wm831x-gpio.o
> +obj-$(CONFIG_GPIO_SCH) += sch_gpio.o
> diff --git a/drivers/gpio/sch_gpio.c b/drivers/gpio/sch_gpio.c
> new file mode 100644
> index 0000000..761071a
> --- /dev/null
> +++ b/drivers/gpio/sch_gpio.c
> @@ -0,0 +1,282 @@
> +/*
> + * sch_gpio.c - GPIO interface for Intel Poulsbo SCH
> + *
> + * Copyright (c) 2010 CompuLab Ltd
> + * Author: Denis Turischev <[email protected]>
> + *
> + * 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.
> + */
> +
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/io.h>
> +#include <linux/errno.h>
> +#include <linux/acpi.h>
> +#include <linux/platform_device.h>
> +
> +#include <linux/gpio.h>
> +
> +static DEFINE_SPINLOCK(gpio_lock);
> +
> +#define CGEN (0x00)
> +#define CGIO (0x04)
> +#define CGLV (0x08)
> +
> +#define RGEN (0x20)
> +#define RGIO (0x24)
> +#define RGLV (0x28)
> +
> +static unsigned short gpio_ba;
> +
> +static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
> +{
> + u8 curr_dirs;
> + unsigned short offset, bit;
> +
> + spin_lock(&gpio_lock);
> +
> + offset = CGIO + gpio_num / 8;
> + bit = gpio_num % 8;
> +
> + curr_dirs = inb(gpio_ba + offset);
> +
> + if (!(curr_dirs & (1 << bit)))
> + outb(curr_dirs | (1 << bit), gpio_ba + offset);
> +
> + spin_unlock(&gpio_lock);
> + return 0;
> +}
> +
> +static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
> +{
> + int res;
> + unsigned short offset, bit;
> +
> + offset = CGLV + gpio_num / 8;
> + bit = gpio_num % 8;
> +
> + res = !!(inb(gpio_ba + offset) & (1 << bit));
> + return res;
> +}
> +
> +static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
> +{
> + u8 curr_vals;
> + unsigned short offset, bit;
> +
> + spin_lock(&gpio_lock);
> +
> + offset = CGLV + gpio_num / 8;
> + bit = gpio_num % 8;
> +
> + curr_vals = inb(gpio_ba + offset);
> +
> + if (val)
> + outb(curr_vals | (1 << bit), gpio_ba + offset);
> + else
> + outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
> + spin_unlock(&gpio_lock);
> +}
> +
> +static int sch_gpio_core_direction_out(struct gpio_chip *gc,
> + unsigned gpio_num, int val)
> +{
> + u8 curr_dirs;
> + unsigned short offset, bit;
> +
> + sch_gpio_core_set(gc, gpio_num, val);
> +
> + spin_lock(&gpio_lock);
> +
> + offset = CGIO + gpio_num / 8;
> + bit = gpio_num % 8;
> +
> + curr_dirs = inb(gpio_ba + offset);
> + if (curr_dirs & (1 << bit))
> + outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
> +
> + spin_unlock(&gpio_lock);
> + return 0;
> +}
> +
> +static struct gpio_chip sch_gpio_core = {
> + .label = "sch_gpio_core",
> + .owner = THIS_MODULE,
> + .direction_input = sch_gpio_core_direction_in,
> + .get = sch_gpio_core_get,
> + .direction_output = sch_gpio_core_direction_out,
> + .set = sch_gpio_core_set,
> +};
> +
> +static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
> + unsigned gpio_num)
> +{
> + u8 curr_dirs;
> +
> + spin_lock(&gpio_lock);
> +
> + curr_dirs = inb(gpio_ba + RGIO);
> +
> + if (!(curr_dirs & (1 << gpio_num)))
> + outb(curr_dirs | (1 << gpio_num) , gpio_ba + RGIO);
> +
> + spin_unlock(&gpio_lock);
> + return 0;
> +}
> +
> +static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
> +{
> + return !!(inb(gpio_ba + RGLV) & (1 << gpio_num));
> +}
> +
> +static void sch_gpio_resume_set(struct gpio_chip *gc,
> + unsigned gpio_num, int val)
> +{
> + u8 curr_vals;
> +
> + spin_lock(&gpio_lock);
> +
> + curr_vals = inb(gpio_ba + RGLV);
> +
> + if (val)
> + outb(curr_vals | (1 << gpio_num), gpio_ba + RGLV);
> + else
> + outb((curr_vals & ~(1 << gpio_num)), gpio_ba + RGLV);
> +
> + spin_unlock(&gpio_lock);
> +}
> +
> +static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
> + unsigned gpio_num, int val)
> +{
> + u8 curr_dirs;
> +
> + sch_gpio_resume_set(gc, gpio_num, val);
> +
> + spin_lock(&gpio_lock);
> +
> + curr_dirs = inb(gpio_ba + RGIO);
> + if (curr_dirs & (1 << gpio_num))
> + outb(curr_dirs & ~(1 << gpio_num), gpio_ba + RGIO);
> +
> + spin_unlock(&gpio_lock);
> + return 0;
> +}
> +
> +static struct gpio_chip sch_gpio_resume = {
> + .label = "sch_gpio_resume",
> + .owner = THIS_MODULE,
> + .direction_input = sch_gpio_resume_direction_in,
> + .get = sch_gpio_resume_get,
> + .direction_output = sch_gpio_resume_direction_out,
> + .set = sch_gpio_resume_set,
> +};
> +
> +static int __devinit sch_gpio_probe(struct platform_device *pdev)
> +{
> + struct resource *res;
> + int err;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + if (!res)
> + return -EBUSY;
> +
> + if (!request_region(res->start, resource_size(res), pdev->name))
> + return -EBUSY;
> +
> + gpio_ba = res->start;
> +
> + sch_gpio_core.base = 0;
> + sch_gpio_core.ngpio = 10;
> + sch_gpio_core.dev = &pdev->dev;
> +
> + sch_gpio_resume.base = 10;
> + sch_gpio_resume.ngpio = 4;
> + sch_gpio_resume.dev = &pdev->dev;
> +
> + err = gpiochip_add(&sch_gpio_core);
> + if (err < 0)
> + goto err_sch_gpio_core;
> +
> + err = gpiochip_add(&sch_gpio_resume);
> + if (err < 0)
> + goto err_sch_gpio_resume;
> +
> + /*
> + * GPIO[6:0] enabled by default
> + * GPIO7 is configured by the CMC as SLPIOVR
> + * Enable GPIO[9:8] core powered gpios explicitly
> + */
> + outb(0x3, gpio_ba + CGEN + 1);
> + /*
> + * SUS_GPIO[2:0] enabled by default
> + * Enable SUS_GPIO3 resume powered gpio explicitly
> + */
> + outb(0x8, gpio_ba + RGEN);
> +
> + return 0;
> +
> +err_sch_gpio_resume:
> + gpiochip_remove(&sch_gpio_core);
> +
> +err_sch_gpio_core:
> + release_region(res->start, resource_size(res));
> + gpio_ba = 0;
> +
> + return err;
> +}
> +
> +static int __devexit sch_gpio_remove(struct platform_device *pdev)
> +{
> + struct resource *res;
> + if (gpio_ba) {
> + gpiochip_remove(&sch_gpio_core);
> + gpiochip_remove(&sch_gpio_resume);
> +
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> +
> + release_region(res->start, resource_size(res));
> + gpio_ba = 0;
> + }
> +
> + return 0;
> +}
> +
> +static struct platform_driver sch_gpio_driver = {
> + .driver = {
> + .name = "sch_gpio",
> + .owner = THIS_MODULE,
> + },
> + .probe = sch_gpio_probe,
> + .remove = __devexit_p(sch_gpio_remove),
> +};
> +
> +static int __init sch_gpio_init(void)
> +{
> + return platform_driver_register(&sch_gpio_driver);
> +}
> +
> +static void __exit sch_gpio_exit(void)
> +{
> + platform_driver_unregister(&sch_gpio_driver);
> +}
> +
> +module_init(sch_gpio_init);
> +module_exit(sch_gpio_exit);
> +
> +MODULE_AUTHOR("Denis Turischev <[email protected]>");
> +MODULE_DESCRIPTION("GPIO interface for Intel Poulsbo SCH");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("platform:sch_gpio");
> --
> 1.6.3.3
>
--
Intel Open Source Technology Centre
http://oss.intel.com/
Hi Denis,
On Sun, Feb 21, 2010 at 02:46:58PM +0200, Denis Turischev wrote:
> v2: there is no acpi_check_region, it will be implemented in mfd-core
> v3: patch refreshed against the latest Linus tree
Still failing to apply against Linus' latest tree:
patching file drivers/i2c/busses/Kconfig
Hunk #1 FAILED at 104.
1 out of 1 hunk FAILED -- saving rejects to file
drivers/i2c/busses/Kconfig.rej
patching file drivers/i2c/busses/i2c-isch.c
Hunk #1 FAILED at 27.
Hunk #2 FAILED at 46.
Hunk #3 FAILED at 57.
Hunk #4 FAILED at 249.
Hunk #5 FAILED at 277.
Hunk #6 FAILED at 322.
6 out of 6 hunks FAILED -- saving rejects to file
drivers/i2c/busses/i2c-isch.c.rej
Cheers,
Samuel.
> Signed-off-by: Denis Turischev <[email protected]>
> ---
> drivers/i2c/busses/Kconfig | 2 +-
> drivers/i2c/busses/i2c-isch.c | 68 ++++++++++++++++------------------------
> 2 files changed, 28 insertions(+), 42 deletions(-)
>
> diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
> index 5f318ce..d15b6d3 100644
> --- a/drivers/i2c/busses/Kconfig
> +++ b/drivers/i2c/busses/Kconfig
> @@ -104,7 +104,7 @@ config I2C_I801
>
> config I2C_ISCH
> tristate "Intel SCH SMBus 1.0"
> - depends on PCI
> + select LPC_SCH
> help
> Say Y here if you want to use SMBus controller on the Intel SCH
> based systems.
> diff --git a/drivers/i2c/busses/i2c-isch.c b/drivers/i2c/busses/i2c-isch.c
> index dba6eb0..ddc258e 100644
> --- a/drivers/i2c/busses/i2c-isch.c
> +++ b/drivers/i2c/busses/i2c-isch.c
> @@ -27,7 +27,7 @@
> */
>
> #include <linux/module.h>
> -#include <linux/pci.h>
> +#include <linux/platform_device.h>
> #include <linux/kernel.h>
> #include <linux/delay.h>
> #include <linux/stddef.h>
> @@ -46,12 +46,6 @@
> #define SMBHSTDAT1 (7 + sch_smba)
> #define SMBBLKDAT (0x20 + sch_smba)
>
> -/* count for request_region */
> -#define SMBIOSIZE 64
> -
> -/* PCI Address Constants */
> -#define SMBBA_SCH 0x40
> -
> /* Other settings */
> #define MAX_TIMEOUT 500
>
> @@ -63,7 +57,6 @@
> #define SCH_BLOCK_DATA 0x05
>
> static unsigned short sch_smba;
> -static struct pci_driver sch_driver;
> static struct i2c_adapter sch_adapter;
>
> /*
> @@ -256,37 +249,23 @@ static struct i2c_adapter sch_adapter = {
> .algo = &smbus_algorithm,
> };
>
> -static struct pci_device_id sch_ids[] = {
> - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_SCH_LPC) },
> - { 0, }
> -};
> -
> -MODULE_DEVICE_TABLE(pci, sch_ids);
> -
> -static int __devinit sch_probe(struct pci_dev *dev,
> - const struct pci_device_id *id)
> +static int __devinit smbus_sch_probe(struct platform_device *dev)
> {
> + struct resource *res;
> int retval;
> - unsigned int smba;
>
> - pci_read_config_dword(dev, SMBBA_SCH, &smba);
> - if (!(smba & (1 << 31))) {
> - dev_err(&dev->dev, "SMBus I/O space disabled!\n");
> - return -ENODEV;
> - }
> + res = platform_get_resource(dev, IORESOURCE_IO, 0);
> + if (!res)
> + return -EBUSY;
>
> - sch_smba = (unsigned short)smba;
> - if (sch_smba == 0) {
> - dev_err(&dev->dev, "SMBus base address uninitialized!\n");
> - return -ENODEV;
> - }
> - if (acpi_check_region(sch_smba, SMBIOSIZE, sch_driver.name))
> - return -ENODEV;
> - if (!request_region(sch_smba, SMBIOSIZE, sch_driver.name)) {
> + if (!request_region(res->start, resource_size(res), dev->name)) {
> dev_err(&dev->dev, "SMBus region 0x%x already in use!\n",
> sch_smba);
> return -EBUSY;
> }
> +
> + sch_smba = res->start;
> +
> dev_dbg(&dev->dev, "SMBA = 0x%X\n", sch_smba);
>
> /* set up the sysfs linkage to our parent device */
> @@ -298,37 +277,43 @@ static int __devinit sch_probe(struct pci_dev *dev,
> retval = i2c_add_adapter(&sch_adapter);
> if (retval) {
> dev_err(&dev->dev, "Couldn't register adapter!\n");
> - release_region(sch_smba, SMBIOSIZE);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
>
> return retval;
> }
>
> -static void __devexit sch_remove(struct pci_dev *dev)
> +static int __devexit smbus_sch_remove(struct platform_device *pdev)
> {
> + struct resource *res;
> if (sch_smba) {
> i2c_del_adapter(&sch_adapter);
> - release_region(sch_smba, SMBIOSIZE);
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + release_region(res->start, resource_size(res));
> sch_smba = 0;
> }
> +
> + return 0;
> }
>
> -static struct pci_driver sch_driver = {
> - .name = "isch_smbus",
> - .id_table = sch_ids,
> - .probe = sch_probe,
> - .remove = __devexit_p(sch_remove),
> +static struct platform_driver smbus_sch_driver = {
> + .driver = {
> + .name = "isch_smbus",
> + .owner = THIS_MODULE,
> + },
> + .probe = smbus_sch_probe,
> + .remove = __devexit_p(smbus_sch_remove),
> };
>
> static int __init i2c_sch_init(void)
> {
> - return pci_register_driver(&sch_driver);
> + return platform_driver_register(&smbus_sch_driver);
> }
>
> static void __exit i2c_sch_exit(void)
> {
> - pci_unregister_driver(&sch_driver);
> + platform_driver_unregister(&smbus_sch_driver);
> }
>
> MODULE_AUTHOR("Jacob Pan <[email protected]>");
> @@ -337,3 +322,4 @@ MODULE_LICENSE("GPL");
>
> module_init(i2c_sch_init);
> module_exit(i2c_sch_exit);
> +MODULE_ALIAS("platform:isch_smbus");
> --
> 1.6.3.3
--
Intel Open Source Technology Centre
http://oss.intel.com/