From: Eric Miao <eric.y.miao-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
To: i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org,
linux-arm-kernel
<linux-arm-kernel-xIg/pKzrS19vn6HldHNs0ANdhmdF6hFW@public.gmane.org>
Cc: David Brownell <david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>,
Jack Ren <jack.ren-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Subject: [PATCH] gpio: max732x: add support for MAX7319, MAX7320-7327 I2C Port Expanders
Date: Thu, 10 Jul 2008 14:13:39 +0800 [thread overview]
Message-ID: <4875A893.3090402@gmail.com> (raw)
Signed-off-by: Jack Ren <jack.ren-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Eric Miao <eric.miao-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
---
drivers/gpio/Kconfig | 22 +++
drivers/gpio/Makefile | 1 +
drivers/gpio/max732x.c | 342 +++++++++++++++++++++++++++++++++++++++++++
include/linux/i2c/max732x.h | 19 +++
4 files changed, 384 insertions(+), 0 deletions(-)
create mode 100644 drivers/gpio/max732x.c
create mode 100644 include/linux/i2c/max732x.h
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index bbd2834..9f8d030 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -61,6 +61,28 @@ config GPIO_PCF857X
This driver provides an in-kernel interface to those GPIOs using
platform-neutral GPIO calls.
+config GPIO_MAX732X
+ tristate "MAX7319, MAX7320-7327 8/16-bit I2C Port Expanders"
+ depends on I2C
+ help
+ Say yes here to support the MAX7319, MAX7320-7327 series of I2C
+ Port Expanders. Each IO port on these chips has a fixed role of
+ Input (designated by 'I'), Push-Pull Output ('O'), or Open-Drain
+ Input and Output (designed by 'P'). The combinations are listed
+ below:
+
+ MAX7319 (8I), MAX7320 (8O), MAX7321 (8P), MAX7322 (4I4O),
+ MAX7323 (4P4O), MAX7324 (8I8O), MAX7325 (8P8O)
+ MAX7326 (4I12O), MAX7327 (4P12O)
+
+ The board code has to specify the model to use, and the start
+ number for these GPIOs. Model specific information is calculated
+ automatically. Due to the fixed role of each port, configuration
+ of the port direction will be ignored and a warning be issued if
+ the corresponding port isn't applicable. And gpio_get_value() to
+ those output only ports will return the configured output status
+ instead of a real input.
+
comment "SPI GPIO expanders:"
config GPIO_MCP23S08
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index fdde992..814698c 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -7,3 +7,4 @@ obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o
obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o
obj-$(CONFIG_GPIO_PCA953X) += pca953x.o
obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o
+obj-$(CONFIG_GPIO_MAX732X) += max732x.o
diff --git a/drivers/gpio/max732x.c b/drivers/gpio/max732x.c
new file mode 100644
index 0000000..b1b9b62
--- /dev/null
+++ b/drivers/gpio/max732x.c
@@ -0,0 +1,342 @@
+/*
+ * max732x.c - I2C Port Expander with 8/16 I/O
+ *
+ * Copyright (C) 2007 Marvell International Ltd.
+ * Copyright (C) 2008 Jack Ren <jack.ren-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ * Copyright (C) 2008 Eric Miao <eric.miao-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>
+ *
+ * Derived from drivers/gpio/pca953x.c
+ *
+ * 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; version 2 of the License.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/max732x.h>
+
+#include <asm/gpio.h>
+
+/* Each port of MAX732x (including MAX7319) falls into one of the
+ * following three types:
+ *
+ * - Push Pull Output
+ * - Input
+ * - Open Drain I/O
+ *
+ * designated by 'O', 'I' and 'P' individually according to MAXIM's
+ * datasheets.
+ *
+ * There are two groups of I/O ports, each group usually includes
+ * up to 8 I/O ports, and is accessed by different I2C address:
+ *
+ * - Group A : by I2C address 0b'110xxxx
+ * - Group B : by I2C address 0b'101xxxx
+ *
+ * where 'xxxx' is decided by the connections of pin AD2/AD0.
+ *
+ * Within each group of ports, there are five known combinations of
+ * I/O ports: 4I4O, 4P4O, 8I, 8P, 8O, see the definitions below for
+ * the detailed organization of these ports.
+ *
+ * GPIO numbers start from 'gpio_base + 0' to 'gpio_base + 8/16',
+ * and GPIOs from GROUP_A are numbered before those from GROUP_B
+ * (if there are two groups).
+ *
+ * NOTE: MAX7328/MAX7329, however, resembles much closer to PCF8574,
+ * they are not supported by this driver.
+ */
+
+#define PORT_NONE 0x0 /* '/' No Port */
+#define PORT_OUTPUT 0x1 /* 'O' Push-Pull, Output Only */
+#define PORT_INPUT 0x2 /* 'I' Input Only */
+#define PORT_OPENDRAIN 0x3 /* 'P' Open-Drain, I/O */
+
+#define IO_4I4O 0x5AA5 /* O7 O6 I5 I4 I3 I2 O1 O0 */
+#define IO_4P4O 0x5FF5 /* O7 O6 P5 P4 P3 P2 O1 O0 */
+#define IO_8I 0xAAAA /* I7 I6 I5 I4 I3 I2 I1 I0 */
+#define IO_8P 0xFFFF /* P7 P6 P5 P4 P3 P2 P1 P0 */
+#define IO_8O 0x5555 /* O7 O6 O5 O4 O3 O2 O1 O0 */
+
+#define GROUP_A(x) ((x) & 0xffff) /* I2C Addr: 0b'110xxxx */
+#define GROUP_B(x) ((x) << 16) /* I2C Addr: 0b'101xxxx */
+
+static const struct i2c_device_id max732x_id[] = {
+ { "max7319", GROUP_A(IO_8I) },
+ { "max7320", GROUP_B(IO_8O) },
+ { "max7321", GROUP_A(IO_8P) },
+ { "max7322", GROUP_A(IO_4I4O) },
+ { "max7323", GROUP_A(IO_4P4O) },
+ { "max7324", GROUP_A(IO_8I) | GROUP_B(IO_8O) },
+ { "max7325", GROUP_A(IO_8P) | GROUP_B(IO_8O) },
+ { "max7326", GROUP_A(IO_4I4O) | GROUP_B(IO_8O) },
+ { "max7327", GROUP_A(IO_4P4O) | GROUP_B(IO_8O) },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, max732x_id);
+
+struct max732x_chip {
+ struct i2c_client *client;
+ struct gpio_chip gpio_chip;
+
+ unsigned gpio_start;
+ unsigned short addr_group_a;
+ unsigned short addr_group_b;
+
+ unsigned int mask_group_a;
+ unsigned int dir_input;
+ unsigned int dir_output;
+ uint8_t reg_out[2];
+};
+
+/* NOTE: we can't currently rely on fault codes to come from SMBus
+ * calls, so we map all errors to EIO here and return zero otherwise.
+ */
+static int max732x_write(struct max732x_chip *chip, int group_a, uint8_t val)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ client->addr = group_a ? chip->addr_group_a : chip->addr_group_b;
+
+ ret = i2c_smbus_write_byte(client, val);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed writing\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int max732x_read(struct max732x_chip *chip, int group_a, uint8_t *val)
+{
+ struct i2c_client *client = chip->client;
+ int ret;
+
+ client->addr = group_a ? chip->addr_group_a : chip->addr_group_b;
+
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed reading\n");
+ return -EIO;
+ }
+
+ *val = (uint8_t)ret;
+ return 0;
+}
+
+static inline int is_group_a(struct max732x_chip *chip, unsigned off)
+{
+ return (1u << off) & chip->mask_group_a;
+}
+
+static int max732x_gpio_get_value(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip;
+ uint8_t reg_val;
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ ret = max732x_read(chip, is_group_a(chip, off), ®_val);
+ if (ret < 0)
+ return 0;
+
+ return (reg_val & (1u << (off & 0x7))) ? 1 : 0;
+}
+
+static void max732x_gpio_set_value(struct gpio_chip *gc, unsigned off, int val)
+{
+ struct max732x_chip *chip;
+ uint8_t reg_out, mask = 1u << (off & 0x7);
+ int ret;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ reg_out = (off > 7) ? chip->reg_out[1] : chip->reg_out[0];
+ reg_out = (val) ? reg_out | mask : reg_out & ~mask;
+
+ ret = max732x_write(chip, is_group_a(chip, off), reg_out);
+ if (ret < 0)
+ return;
+
+ /* update the shadow register then */
+ if (off > 7)
+ chip->reg_out[1] = reg_out;
+ else
+ chip->reg_out[0] = reg_out;
+}
+
+static int max732x_gpio_direction_input(struct gpio_chip *gc, unsigned off)
+{
+ struct max732x_chip *chip;
+ unsigned int mask = 1u << off;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ if ((mask & chip->dir_input) == 0)
+ pr_warning("%s: port %d is output only\n", __func__, off);
+
+ return 0;
+}
+
+static int max732x_gpio_direction_output(struct gpio_chip *gc,
+ unsigned off, int val)
+{
+ struct max732x_chip *chip;
+ unsigned int mask = 1u << off;
+
+ chip = container_of(gc, struct max732x_chip, gpio_chip);
+
+ if ((mask & chip->dir_output) == 0) {
+ pr_warning("%s: port %d is input only\n", __func__, off);
+ return 0;
+ }
+
+ max732x_gpio_set_value(gc, off, val);
+ return 0;
+}
+
+static void max732x_setup_gpio(struct max732x_chip *chip, uint32_t id_data)
+{
+ struct gpio_chip *gc = &chip->gpio_chip;
+ int i, port = 0, nr_port;
+
+ for (i = 0; i < 16; i++, id_data >>= 2) {
+ unsigned int mask = 1 << port;
+
+ switch (id_data & 0x3) {
+ case PORT_OUTPUT:
+ chip->dir_output |= mask;
+ break;
+ case PORT_INPUT:
+ chip->dir_input |= mask;
+ break;
+ case PORT_OPENDRAIN:
+ chip->dir_output |= mask;
+ chip->dir_input |= mask;
+ break;
+ default:
+ continue;
+ }
+
+ if (i < 8)
+ chip->mask_group_a |= mask;
+ port++;
+ }
+
+ nr_port = port;
+
+ if (nr_port > 7) {
+ max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]);
+ max732x_read(chip, is_group_a(chip, 8), &chip->reg_out[1]);
+ } else
+ max732x_read(chip, is_group_a(chip, 0), &chip->reg_out[0]);
+
+ gc->direction_input = max732x_gpio_direction_input;
+ gc->direction_output = max732x_gpio_direction_output;
+ gc->get = max732x_gpio_get_value;
+ gc->set = max732x_gpio_set_value;
+ gc->can_sleep = 1;
+
+ gc->base = chip->gpio_start;
+ gc->ngpio = port;
+ gc->label = chip->client->name;
+ gc->owner = THIS_MODULE;
+}
+
+static int __devinit max732x_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max732x_platform_data *pdata;
+ struct max732x_chip *chip;
+ int ret;
+
+ pdata = client->dev.platform_data;
+ if (pdata == NULL)
+ return -ENODEV;
+
+ chip = kzalloc(sizeof(struct max732x_chip), GFP_KERNEL);
+ if (chip == NULL)
+ return -ENOMEM;
+
+ chip->client = client;
+
+ chip->gpio_start = pdata->gpio_base;
+ chip->addr_group_a = (client->addr & 0x0f) | 0x60;
+ chip->addr_group_b = (client->addr & 0x0f) | 0x50;
+
+ max732x_setup_gpio(chip, id->driver_data);
+
+ ret = gpiochip_add(&chip->gpio_chip);
+ if (ret)
+ goto out_failed;
+
+ if (pdata->setup) {
+ ret = pdata->setup(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0)
+ dev_warn(&client->dev, "setup failed, %d\n", ret);
+ }
+
+ i2c_set_clientdata(client, chip);
+ return 0;
+
+out_failed:
+ kfree(chip);
+ return ret;
+}
+
+static int max732x_remove(struct i2c_client *client)
+{
+ struct max732x_platform_data *pdata = client->dev.platform_data;
+ struct max732x_chip *chip = i2c_get_clientdata(client);
+ int ret = 0;
+
+ if (pdata->teardown) {
+ ret = pdata->teardown(client, chip->gpio_chip.base,
+ chip->gpio_chip.ngpio, pdata->context);
+ if (ret < 0) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "teardown", ret);
+ return ret;
+ }
+ }
+
+ ret = gpiochip_remove(&chip->gpio_chip);
+ if (ret) {
+ dev_err(&client->dev, "%s failed, %d\n",
+ "gpiochip_remove()", ret);
+ return ret;
+ }
+
+ kfree(chip);
+ return 0;
+}
+
+static struct i2c_driver max732x_driver = {
+ .driver = {
+ .name = "max732x",
+ },
+ .probe = max732x_probe,
+ .remove = max732x_remove,
+ .id_table = max732x_id,
+};
+
+static int __init max732x_init(void)
+{
+ return i2c_add_driver(&max732x_driver);
+}
+module_init(max732x_init);
+
+static void __exit max732x_exit(void)
+{
+ i2c_del_driver(&max732x_driver);
+}
+module_exit(max732x_exit);
+
+MODULE_AUTHOR("Eric Miao <eric.miao-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org>");
+MODULE_DESCRIPTION("GPIO expander driver for MAX732X");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/i2c/max732x.h b/include/linux/i2c/max732x.h
new file mode 100644
index 0000000..e103366
--- /dev/null
+++ b/include/linux/i2c/max732x.h
@@ -0,0 +1,19 @@
+#ifndef __LINUX_I2C_MAX732X_H
+#define __LINUX_I2C_MAX732X_H
+
+/* platform data for the MAX732x 8/16-bit I/O expander driver */
+
+struct max732x_platform_data {
+ /* number of the first GPIO */
+ unsigned gpio_base;
+
+ void *context; /* param to setup/teardown */
+
+ int (*setup)(struct i2c_client *client,
+ unsigned gpio, unsigned ngpio,
+ void *context);
+ int (*teardown)(struct i2c_client *client,
+ unsigned gpio, unsigned ngpio,
+ void *context);
+};
+#endif /* __LINUX_I2C_MAX732X_H */
--
1.5.4.3
_______________________________________________
i2c mailing list
i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org
http://lists.lm-sensors.org/mailman/listinfo/i2c
next reply other threads:[~2008-07-10 6:13 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2008-07-10 6:13 Eric Miao [this message]
[not found] ` <4875A893.3090402-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2008-07-11 8:29 ` [PATCH] gpio: max732x: add support for MAX7319, MAX7320-7327 I2C Port Expanders Jean Delvare
[not found] ` <20080711102952.31d2d943-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-11 8:57 ` eric miao
[not found] ` <f17812d70807110157p5e421222sc9ef420ceb80970c-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-11 9:31 ` Jean Delvare
[not found] ` <20080711113114.79d80212-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-11 9:39 ` eric miao
[not found] ` <f17812d70807110239q6c175f5cn70db966681ae387d-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-11 9:48 ` eric miao
[not found] ` <f17812d70807110248y7fab3328q59ea41084c491df-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-11 11:15 ` Jean Delvare
2008-07-11 21:25 ` David Brownell
[not found] ` <200807111425.00961.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-07-12 7:16 ` Jean Delvare
[not found] ` <20080712091610.4ec242c3-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-12 7:46 ` David Brownell
[not found] ` <200807120046.29389.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-07-12 7:53 ` Jean Delvare
[not found] ` <20080712095300.1ba4b3a7-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-12 21:42 ` David Brownell
2008-07-13 6:55 ` Jean Delvare
2008-07-13 6:04 ` eric miao
[not found] ` <f17812d70807122304o533d8af9k1384db653b264912-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-13 7:20 ` Jean Delvare
[not found] ` <20080713092050.6dffd8d3-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-13 8:53 ` eric miao
[not found] ` <f17812d70807130153g290e17ecq10ab12529effc8d4-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-13 9:13 ` Jean Delvare
[not found] ` <20080713111306.791bbc41-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-13 13:45 ` eric miao
[not found] ` <f17812d70807130645i755c1c62la30d5c515c2d2080-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-07-13 19:18 ` David Brownell
[not found] ` <200807131218.29575.david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org>
2008-07-14 1:35 ` Eric Miao
2008-07-13 9:12 ` David Brownell
[not found] ` <20080713091236.015E920ABDD-ZcXrCSuhvln6VZ3dlLfH/g4gEjPzgfUyLrfjE7I9kuVHxeISYlDBzl6hYfS7NtTn@public.gmane.org>
2008-07-13 9:18 ` Jean Delvare
2008-07-13 14:37 ` Jean Delvare
2008-07-11 9:54 ` Jean Delvare
[not found] ` <20080711115436.22134ce7-ig7AzVSIIG7kN2dkZ6Wm7A@public.gmane.org>
2008-07-11 10:04 ` eric miao
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4875A893.3090402@gmail.com \
--to=eric.y.miao-re5jqeeqqe8avxtiumwx3w@public.gmane.org \
--cc=david-b-yBeKhBN/0LDR7s880joybQ@public.gmane.org \
--cc=i2c-GZX6beZjE8VD60Wz+7aTrA@public.gmane.org \
--cc=jack.ren-eYqpPyKDWXRBDgjK7y7TUQ@public.gmane.org \
--cc=linux-arm-kernel-xIg/pKzrS19vn6HldHNs0ANdhmdF6hFW@public.gmane.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.