>From 0aed2aee358992d46ebfc2783569a8b3c350bd63 Mon Sep 17 00:00:00 2001 From: Alejandro del Rio Date: Mon, 16 Jan 2012 00:33:05 -0800 Subject: [PATCH 2/2] [temporal change] [temporal change] Signed-off-by: Alejandro del Rio --- drivers/gpio/Kconfig | 12 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-w83627hf.c | 492 ++++++++++++++++++++++++++++++++++++++++++ drivers/hwmon/Kconfig | 1 + 4 files changed, 506 insertions(+), 0 deletions(-) create mode 100644 drivers/gpio/gpio-w83627hf.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 37c4bd1..1ada7ad 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -189,6 +189,18 @@ config GPIO_VX855 additional drivers must be enabled in order to use the functionality of the device. +config GPIO_W83627HF + tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" + select MFD_CORE + select MFD_W83627HF + help + If you say yes here you get support for the Winbond W836X7 series + of sensor chips: the W83627HF, W83627THF, W83637HF, W83687THF and + W83697HF. + + This driver can also be built as a module. If so, the module + will be called w83627hf. + comment "I2C GPIO expanders:" config GPIO_MAX7300 diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fa10df6..48979bb 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -57,6 +57,7 @@ obj-$(CONFIG_GPIO_TWL4030) += gpio-twl4030.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o +obj-$(CONFIG_GPIO_W83627HF) += gpio-w83627hf.o obj-$(CONFIG_GPIO_WM831X) += gpio-wm831x.o obj-$(CONFIG_GPIO_WM8350) += gpio-wm8350.o obj-$(CONFIG_GPIO_WM8994) += gpio-wm8994.o diff --git a/drivers/gpio/gpio-w83627hf.c b/drivers/gpio/gpio-w83627hf.c new file mode 100644 index 0000000..934333e --- /dev/null +++ b/drivers/gpio/gpio-w83627hf.c @@ -0,0 +1,492 @@ +/* + * gpio-w83627hf.c - GPIO support for w83627hf chip + * + * Copyright (c) 2010-2011 Rodolfo Giometti + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Constants specified below */ + +#define W83627HF_LD_GPIO3_MUX 0x29 +#define W83627HF_LD_GPIO1_MUX 0x2a +#define W83627HF_LD_GPIO2_MUX 0x2b + +static const unsigned int ngpio[] = { + /* port 1 - port 2 - port 3 */ + 8 + 8 + 6, + 0, + 0, + 0, + 0, +}; + +struct w83627hf_data { + struct gpio_chip chip; + struct bitmap { + unsigned long status:1; + } bit[3 * 8]; +}; + +/* + * Local functions + */ + +static void gpio_mode(struct w83627hf_sio_data *sio, + unsigned int bit, unsigned int mode) +{ + u8 val; + + val = superio_inb(sio, 0xf0); + if (mode == 0) + val &= ~(1 << bit); + else + val |= (1 << bit); + superio_outb(sio, 0xf0, val); +} + +static int gpio_get(struct w83627hf_sio_data *sio, unsigned int bit) +{ + u8 val; + + val = superio_inb(sio, 0xf1); + val &= (1 << bit); + + return val; +} + +static void gpio_set(struct w83627hf_sio_data *sio, + unsigned int bit, unsigned int status) +{ + u8 val; + + val = superio_inb(sio, 0xf1); + if (status) + val |= (1 << bit); + else + val &= ~(1 << bit); + superio_outb(sio, 0xf1, val); +} + +static void gpio1_enable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Select GPIO1 into pins multiplxer */ + val = superio_inb(sio, W83627HF_LD_GPIO1_MUX); + superio_outb(sio, W83627HF_LD_GPIO1_MUX, val | 0xfc); + + /* Enable GPIO1 */ + superio_select(sio, W83627HF_LD_GPIO1); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val | 0x01); + + superio_exit(sio); +} + +static void gpio1_disable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Disable GPIO1 */ + superio_select(sio, W83627HF_LD_GPIO1); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val & ~0x01); + + superio_exit(sio); +} + +static void gpio2_enable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Select GPIO2 into pins multiplxer */ + val = superio_inb(sio, W83627HF_LD_GPIO1_MUX); + superio_outb(sio, W83627HF_LD_GPIO1_MUX, val | 0x01); + val = superio_inb(sio, W83627HF_LD_GPIO2_MUX); + superio_outb(sio, W83627HF_LD_GPIO2_MUX, val | 0xff); + + /* Enable GPIO2 */ + superio_select(sio, W83627HF_LD_GPIO2); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val | 0x01); + + superio_exit(sio); +} + +static void gpio2_disable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Disable GPIO2 */ + superio_select(sio, W83627HF_LD_GPIO2); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val & ~0x01); + + superio_exit(sio); +} + +static void gpio3_enable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Select GPIO3 into pins multiplxer */ + val = superio_inb(sio, W83627HF_LD_GPIO3_MUX); + superio_outb(sio, W83627HF_LD_GPIO3_MUX, val | 0xfc); + + /* Enable GPIO3 */ + superio_select(sio, W83627HF_LD_GPIO3); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val | 0x01); + + superio_exit(sio); +} + +static void gpio3_disable(struct w83627hf_sio_data *sio) +{ + u8 val; + + superio_enter(sio); + + /* Disable GPIO3 */ + superio_select(sio, W83627HF_LD_GPIO3); + val = superio_inb(sio, 0x30); + superio_outb(sio, 0x30, val & ~0x01); + + superio_exit(sio); +} + +/* + * GPIOs methods + */ + +static int w83627hf_request(struct gpio_chip *chip, unsigned offset) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio_data = dev->parent->platform_data; + struct w83627hf_data *data = container_of(chip, + struct w83627hf_data, chip); + struct bitmap *bit = data->bit; + + switch (offset) { + case 0 ... 7: + gpio1_enable(sio_data); + break; + + case 8 ... 15: + /* Port2 is currently not supported */ + gpio2_enable(sio_data); + break; + + case 16 ... 21: + gpio3_enable(sio_data); + break; + + default: + BUG(); + return -EINVAL; + } + + bit[offset].status = 1; + + return 0; +} + +static void w83627hf_free(struct gpio_chip *chip, unsigned offset) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio_data = dev->parent->platform_data; + struct w83627hf_data *data = container_of(chip, + struct w83627hf_data, chip); + struct bitmap *bit = data->bit; + int i; + + switch (offset) { + case 0 ... 7: + bit[offset].status = 0; + for (i = 0; i < 8; i++) + if (bit[i].status) + break; + if (i == 8) + gpio1_disable(sio_data); + break; + + case 8 ... 15: + bit[offset].status = 0; + for (i = 8; i < 16; i++) + if (bit[i].status) + break; + if (i == 16) + gpio2_disable(sio_data); + break; + + case 16 ... 21: + bit[offset].status = 0; + for (i = 16; i < 22; i++) + if (bit[i].status) + break; + if (i == 22) + gpio3_disable(sio_data); + break; + + default: + BUG(); + } +} + +static int w83627hf_direction_in(struct gpio_chip *chip, unsigned offset) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio = dev->parent->platform_data; + + superio_enter(sio); + + switch (offset) { + case 0 ... 7: + superio_select(sio, W83627HF_LD_GPIO1); + gpio_mode(sio, offset, 1); + break; + + case 8 ... 15: + superio_select(sio, W83627HF_LD_GPIO2); + gpio_mode(sio, offset - 8, 1); + break; + + case 16 ... 21: + superio_select(sio, W83627HF_LD_GPIO3); + gpio_mode(sio, offset - 16, 1); + break; + + default: + BUG(); + } + + superio_exit(sio); + + return 0; +} + +static int w83627hf_get(struct gpio_chip *chip, unsigned offset) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio = dev->parent->platform_data; + u8 val; + + superio_enter(sio); + + switch (offset) { + case 0 ... 7: + superio_select(sio, W83627HF_LD_GPIO1); + val = gpio_get(sio, offset); + break; + + case 8 ... 15: + superio_select(sio, W83627HF_LD_GPIO2); + val = gpio_get(sio, offset - 8); + break; + + case 16 ... 21: + superio_select(sio, W83627HF_LD_GPIO3); + val = gpio_get(sio, offset - 16); + break; + + default: + BUG(); + val = -EINVAL; + } + + superio_exit(sio); + + return val; +} + +static int w83627hf_direction_out(struct gpio_chip *chip, + unsigned offset, int value) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio = dev->parent->platform_data; + int ret = 0; + + superio_enter(sio); + + switch (offset) { + case 0 ... 7: + superio_select(sio, W83627HF_LD_GPIO1); + gpio_mode(sio, offset, 0); + break; + + case 8 ... 15: + superio_select(sio, W83627HF_LD_GPIO2); + gpio_mode(sio, offset - 8, value); + break; + + case 16 ... 21: + superio_select(sio, W83627HF_LD_GPIO3); + gpio_mode(sio, offset - 16, value); + break; + + default: + BUG(); + } + + superio_exit(sio); + + return ret; +} + +static void w83627hf_set(struct gpio_chip *chip, unsigned offset, int value) +{ + struct device *dev = chip->dev; + struct w83627hf_sio_data *sio = dev->parent->platform_data; + + superio_enter(sio); + + switch (offset) { + case 0 ... 7: + superio_select(sio, W83627HF_LD_GPIO1); + gpio_set(sio, offset, value); + break; + + case 8 ... 15: + superio_select(sio, W83627HF_LD_GPIO2); + gpio_set(sio, offset - 8, value); + break; + + case 16 ... 21: + superio_select(sio, W83627HF_LD_GPIO3); + gpio_set(sio, offset - 16, value); + break; + + default: + BUG(); + } + + superio_exit(sio); +} + +static int __devinit w83627hf_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct w83627hf_sio_data *sio_data = dev->parent->platform_data; + struct w83627hf_data *data; + int err; + + if (ngpio[sio_data->type] == 0) { + dev_err(&pdev->dev, "gpio support not available " + "for this chip type\n"); + return -ENODEV; + } + + data = kzalloc(sizeof(struct w83627hf_data), GFP_KERNEL); + if (!data) + return -ENOMEM; + platform_set_drvdata(pdev, data); + + data->chip.base = -1; + data->chip.ngpio = ngpio[sio_data->type]; + data->chip.label = sio_data->name; + data->chip.dev = dev; + + data->chip.owner = THIS_MODULE; + data->chip.request = w83627hf_request; + data->chip.free = w83627hf_free; + data->chip.direction_input = w83627hf_direction_in; + data->chip.get = w83627hf_get; + data->chip.direction_output = w83627hf_direction_out; + data->chip.set = w83627hf_set; + data->chip.can_sleep = 1; + + err = gpiochip_add(&data->chip); + if (err < 0) { + dev_err(&pdev->dev, "could not register gpiochip, %d\n", err); + goto free_platform_data; + } + + return 0; + +free_platform_data: + kfree(data); + platform_set_drvdata(pdev, NULL); + + return err; +} + +static int __devexit w83627hf_remove(struct platform_device *pdev) +{ + struct w83627hf_data *data = platform_get_drvdata(pdev); + int err; + + err = gpiochip_remove(&data->chip); + if (err < 0) { + dev_err(&pdev->dev, "could not deregister gpiochip, %d\n", err); + return err; + } + + kfree(data); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver w83627hf_driver = { + .driver = { + .owner = THIS_MODULE, + .name = DRVNAME "_gpio", + }, + .probe = w83627hf_probe, + .remove = __devexit_p(w83627hf_remove), +}; + +/* + * Module stuff + */ + +static int __init gpio_w83627hf_init(void) +{ + return platform_driver_register(&w83627hf_driver); +} + +static void __exit gpio_w83627hf_exit(void) +{ + platform_driver_unregister(&w83627hf_driver); +} + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("W83627HF gpio driver"); +MODULE_LICENSE("GPL"); + +module_init(gpio_w83627hf_init); +module_exit(gpio_w83627hf_exit); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index cb351d3..029b8b1 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -1272,6 +1272,7 @@ config SENSORS_W83L786NG config SENSORS_W83627HF tristate "Winbond W83627HF, W83627THF, W83637HF, W83687THF, W83697HF" depends on !PPC + select MFD_W83627HF select HWMON_VID help If you say yes here you get support for the Winbond W836X7 series -- 1.7.4.1