From: David Brownell <david-b@pacbell.net>
To: Paul Sokolovsky <pmiscml@gmail.com>
Cc: linux-arm-kernel@lists.arm.linux.org.uk, linux-kernel@vger.kernel.org
Subject: Re: is there any generic GPIO chip framework like IRQ chips?
Date: Sun, 15 Apr 2007 13:03:27 -0700 [thread overview]
Message-ID: <200704151303.27867.david-b@pacbell.net> (raw)
In-Reply-To: <1686019041.20070412165702@gmail.com>
To merge, this would be split apart ... what's interesting here is
the way the new leds-gpio driver can be made to talk to leds that
sit across an I2C link.
========== CUT HERE
This presumes various patches updating:
- LED framework to support generic GPIOs (in MM and LED queue)
- LED framework's generic GPIO support to understand that some GPIOs
can't be directly accessed in timer callbacks (patch submitted)
- I2C to support "new style" drivers (driver model conformance, in MM
and i2c queues);
- Kernel.org to match current OMAP trees (in the works, huge)
- OMAP to use the "struct gpio_chip" infrastructure for its GPIOs (a
previous patch in this series)
- TPS65010 to be such a "new style" I2C driver (blocked waiting for OMAP
and I2C patches to merge);
Given those prerequisites, this patch:
* Exposes tps65010 GPIOs and LEDs through the GPIO framework (gaining an
activity light for CF access)
* Converts the OSK board to use that framework (not quite everywhere; at
least the ads7846 driver and serial line wakeup still need updating)
* Removes "old style" OSK led support, except with the Mistral board
whose red-or-green LED isn't really supported by the new framework
Note that this is the first example of the "I2C GPIO expander" case,
where the GPIOs can't be accessed in non-sleeping contexts.
---
arch/arm/mach-omap1/board-osk.c | 66 +++++++++++++++++-------
arch/arm/mach-omap1/leds-osk.c | 92 ----------------------------------
arch/arm/mach-omap1/leds.c | 15 ++---
drivers/i2c/chips/tps65010.c | 59 +++++++++++++++++++++
include/asm-arm/arch-omap/board-osk.h | 11 ++++
include/asm-arm/arch-omap/tps65010.h | 17 ++++++
6 files changed, 141 insertions(+), 119 deletions(-)
--- osk2.orig/include/asm-arm/arch-omap/tps65010.h 2007-04-15 11:09:12.000000000 -0700
+++ osk2/include/asm-arm/arch-omap/tps65010.h 2007-04-15 11:09:15.000000000 -0700
@@ -152,5 +152,22 @@ extern int tps65010_config_vregs1(unsign
*/
extern int tps65013_set_low_pwr(unsigned mode);
+
+/**
+ * struct tps65010_board - packages GPIO and LED lines
+ * @base: the GPIO number to assign to GPIO-1
+ * @outmask: bit (N-1) is set to allow GPIO-N to be used
+ * as an (open drain) output
+ *
+ * Board data may be used to package the GPIO (and LED) lines
+ * for use in by the generic GPIO and LED frameworks. The first
+ * four GPIOs starting at gpio_base are GPIO1..GPIO4; the next
+ * two are LED1..LED2.
+ */
+struct tps65010_board {
+ int base;
+ unsigned outmask;
+};
+
#endif /* __ASM_ARCH_TPS65010_H */
--- osk2.orig/drivers/i2c/chips/tps65010.c 2007-04-15 11:09:12.000000000 -0700
+++ osk2/drivers/i2c/chips/tps65010.c 2007-04-15 11:09:15.000000000 -0700
@@ -34,9 +34,9 @@
#include <linux/mutex.h>
#include <asm/irq.h>
+#include <asm/gpio.h>
#include <asm/mach-types.h>
-#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include <asm/arch/tps65010.h>
@@ -91,7 +91,8 @@ struct tps65010 {
u8 chgstatus, regstatus, chgconf;
u8 nmask1, nmask2;
- /* not currently tracking GPIO state */
+ u8 outmask;
+ struct gpio_chip chip;
};
#define POWER_POLL_DELAY msecs_to_jiffies(5000)
@@ -454,6 +455,38 @@ static irqreturn_t tps65010_irq(int irq,
/*-------------------------------------------------------------------------*/
+/* offsets 0..3 == GPIO1..GPIO4
+ * offsets 4..5 == LED1..LED2 (we set one of the non-BLINK modes)
+ */
+static void
+tps65010_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
+{
+ if (offset < 4)
+ tps65010_set_gpio_out_value(offset + 1, value);
+ else
+ tps65010_set_led(offset - 3, value ? ON : OFF);
+}
+
+static int
+tps65010_output(struct gpio_chip *chip, unsigned offset, int value)
+{
+ /* GPIOs may be input-only */
+ if (offset < 4) {
+ struct tps65010 *tps;
+
+ tps = container_of(chip, struct tps65010, chip);
+ if (!(tps->outmask & (1 << offset)))
+ return -EINVAL;
+ tps65010_set_gpio_out_value(offset + 1, value);
+ } else
+ tps65010_set_led(offset - 3, value ? ON : OFF);
+
+ return 0;
+}
+
+
+/*-------------------------------------------------------------------------*/
+
static struct tps65010 *the_tps;
static int __exit tps65010_remove(struct i2c_client *client)
@@ -475,6 +508,7 @@ static int tps65010_probe(struct i2c_cli
struct tps65010 *tps;
int status;
unsigned long irqflags;
+ struct tps65010_board *board = client->dev.platform_data;
if (the_tps) {
dev_dbg(&client->dev, "only one %s for now\n", DRIVER_NAME);
@@ -585,6 +619,27 @@ static int tps65010_probe(struct i2c_cli
tps->file = debugfs_create_file(DRIVER_NAME, S_IRUGO, NULL,
tps, DEBUG_FOPS);
+
+ /* optionally register GPIOs (and leds) */
+ if (board && board->base > 0) {
+ tps->outmask = board->outmask;
+
+ /* tps->chip.get = tps65010_gpio_get; */
+ tps->chip.set = tps65010_gpio_set;
+ /* tps->chip.direction_input = tps65010_input; */
+ tps->chip.direction_output = tps65010_output;
+ tps->chip.base = board->base;
+ tps->chip.name = client->name;
+ /* we don't handle GPIO input irqs (yet?) ... */
+ tps->chip.ngpio = 6;
+ tps->chip.can_sleep = 1;
+
+ status = gpio_chip_declare(&tps->chip);
+ if (status < 0)
+ dev_dbg(&client->dev, "can't declare GPIOs, err %d\n",
+ status);
+ }
+
return 0;
fail1:
kfree(tps);
--- osk2.orig/include/asm-arm/arch-omap/board-osk.h 2007-04-15 11:09:12.000000000 -0700
+++ osk2/include/asm-arm/arch-omap/board-osk.h 2007-04-15 11:09:15.000000000 -0700
@@ -32,5 +32,16 @@
/* At OMAP5912 OSK the Ethernet is directly connected to CS1 */
#define OMAP_OSK_ETHR_START 0x04800300
+/* TPS65010 has four GPIOs. nPG and LED2 can be treated like GPIOs with
+ * alternate pin configurations for hardware-controlled blinking.
+ */
+#define OSK_TPS_GPIO_BASE (OMAP_MAX_GPIO_LINES + 16 /* MPUIO */)
+# define OSK_TPS_GPIO_USB_PWR_EN (OSK_TPS_GPIO_BASE + 0)
+# define OSK_TPS_GPIO_LED_D3 (OSK_TPS_GPIO_BASE + 1)
+# define OSK_TPS_GPIO_LAN_RESET (OSK_TPS_GPIO_BASE + 2)
+# define OSK_TPS_GPIO_DSP_PWR_EN (OSK_TPS_GPIO_BASE + 3)
+# define OSK_TPS_GPIO_LED_D9 (OSK_TPS_GPIO_BASE + 4)
+# define OSK_TPS_GPIO_LED_D2 (OSK_TPS_GPIO_BASE + 5)
+
#endif /* __ASM_ARCH_OMAP_OSK_H */
--- osk2.orig/arch/arm/mach-omap1/board-osk.c 2007-04-15 11:09:12.000000000 -0700
+++ osk2/arch/arm/mach-omap1/board-osk.c 2007-04-15 11:09:15.000000000 -0700
@@ -33,6 +33,7 @@
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
+#include <linux/leds.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
@@ -182,11 +183,17 @@ static struct platform_device *osk5912_d
&osk5912_mcbsp1_device,
};
+static struct tps65010_board tps_board = {
+ .base = OSK_TPS_GPIO_BASE,
+ .outmask = 0x0f,
+};
+
static struct i2c_board_info __initdata osk_i2c_board_info[] = {
{
I2C_BOARD_INFO("tps65010", 0x48),
.type = "tps65010",
.irq = OMAP_GPIO_IRQ(OMAP_MPUIO(1)),
+ .platform_data = &tps_board,
},
#ifdef CONFIG_OMAP_OSK_MISTRAL
{
@@ -202,7 +209,7 @@ static struct i2c_board_info __initdata
static void __init osk_init_smc91x(void)
{
- if ((omap_request_gpio(0)) < 0) {
+ if ((gpio_request(0, "smc_irq")) < 0) {
printk("Error requesting gpio 0 for smc91x irq\n");
return;
}
@@ -214,7 +221,7 @@ static void __init osk_init_smc91x(void)
static void __init osk_init_cf(void)
{
omap_cfg_reg(M7_1610_GPIO62);
- if ((omap_request_gpio(62)) < 0) {
+ if ((gpio_request(62, "cf_irq")) < 0) {
printk("Error requesting gpio 62 for CF irq\n");
return;
}
@@ -338,7 +345,7 @@ static struct platform_device *mistral_d
static int mistral_get_pendown_state(void)
{
- return !omap_get_gpio_datain(4);
+ return !gpio_get_value(4);
}
static const struct ads7846_platform_data mistral_ts_info = {
@@ -400,10 +407,9 @@ static void __init osk_mistral_init(void
omap_cfg_reg(W14_1610_CCP_DATAP);
/* CAM_PWDN */
- if (omap_request_gpio(11) == 0) {
+ if (gpio_request(11, "cam_pwdn") == 0) {
omap_cfg_reg(N20_1610_GPIO11);
- omap_set_gpio_direction(11, 0 /* out */);
- omap_set_gpio_dataout(11, 0 /* off */);
+ gpio_direction_output(11, 0 /* off */);
} else
pr_debug("OSK+Mistral: CAM_PWDN is awol\n");
@@ -416,9 +422,9 @@ static void __init osk_mistral_init(void
/* the sideways button (SW1) is for use as a "wakeup" button */
omap_cfg_reg(N15_1610_MPUIO2);
- if (omap_request_gpio(OMAP_MPUIO(2)) == 0) {
+ if (gpio_request(OMAP_MPUIO(2), "wakeup") == 0) {
int ret = 0;
- omap_set_gpio_direction(OMAP_MPUIO(2), 1);
+ gpio_direction_input(OMAP_MPUIO(2));
set_irq_type(OMAP_GPIO_IRQ(OMAP_MPUIO(2)), IRQT_RISING);
#ifdef CONFIG_PM
/* share the IRQ in case someone wants to use the
@@ -429,7 +435,7 @@ static void __init osk_mistral_init(void
IRQF_SHARED, "mistral_wakeup",
&osk_mistral_wake_interrupt);
if (ret != 0) {
- omap_free_gpio(OMAP_MPUIO(2));
+ gpio_free(OMAP_MPUIO(2));
printk(KERN_ERR "OSK+Mistral: no wakeup irq, %d?\n",
ret);
} else
@@ -442,10 +448,8 @@ static void __init osk_mistral_init(void
* board, like the touchscreen, EEPROM, and wakeup (!) switch.
*/
omap_cfg_reg(PWL);
- if (omap_request_gpio(2) == 0) {
- omap_set_gpio_direction(2, 0 /* out */);
- omap_set_gpio_dataout(2, 1 /* on */);
- }
+ if (gpio_request(2, "lcd_pwdn") == 0)
+ gpio_direction_output(2, 1 /* on */);
platform_add_devices(mistral_devices, ARRAY_SIZE(mistral_devices));
}
@@ -474,8 +478,8 @@ static void __init osk_init(void)
/* irq for tps65010 chip */
// omap_cfg_reg(U19_1610_MPUIO1);
- omap_request_gpio(OMAP_MPUIO(1));
- omap_set_gpio_direction(OMAP_MPUIO(1), 1);
+ if (gpio_request(OMAP_MPUIO(1), "tps65010"))
+ gpio_direction_input(OMAP_MPUIO(1));
i2c_register_board_info(1, osk_i2c_board_info,
ARRAY_SIZE(osk_i2c_board_info));
@@ -490,6 +494,26 @@ static void __init osk_map_io(void)
}
#ifdef CONFIG_TPS65010
+
+static struct gpio_led tps_leds[] = {
+ { .gpio = OSK_TPS_GPIO_LED_D9, .name = "d9:green",
+ .default_trigger = "ide-disk", /* CF */ },
+ { .gpio = OSK_TPS_GPIO_LED_D2, .name = "d2:green", },
+ { .gpio = OSK_TPS_GPIO_LED_D3, .name = "d3:green", .active_low = 1,
+ .default_trigger = "heartbeat", },
+};
+
+static struct gpio_led_platform_data tps_led_data = {
+ .num_leds = ARRAY_SIZE(tps_leds),
+ .leds = tps_leds,
+};
+
+static struct platform_device tps_led_dev = {
+ .name = "leds-gpio",
+ .id = -1,
+ .dev.platform_data = &tps_led_data,
+};
+
static int __init osk_tps_init(void)
{
if (!machine_is_omap_osk())
@@ -504,16 +528,20 @@ static int __init osk_tps_init(void)
/* Set GPIO 1 HIGH to disable VBUS power supply;
* OHCI driver powers it up/down as needed.
*/
- tps65010_set_gpio_out_value(GPIO1, HIGH);
+ if (gpio_request(OSK_TPS_GPIO_USB_PWR_EN, "vbus_dis") == 0)
+ gpio_direction_output(OSK_TPS_GPIO_USB_PWR_EN, 1);
/* Set GPIO 2 low to turn on LED D3 */
tps65010_set_gpio_out_value(GPIO2, HIGH);
/* Set GPIO 3 low to take ethernet out of reset */
- tps65010_set_gpio_out_value(GPIO3, LOW);
+ if (gpio_request(OSK_TPS_GPIO_LAN_RESET, "lan_reset") == 0)
+ gpio_direction_output(OSK_TPS_GPIO_LAN_RESET, 0);
/* gpio4 for VDD_DSP */
- // FIXME power DSP iff it's configured
+ /* FIXME power DSP iff it's configured */
+ if (gpio_request(OSK_TPS_GPIO_DSP_PWR_EN, "dsp_pwr_en") == 0)
+ gpio_direction_output(OSK_TPS_GPIO_DSP_PWR_EN, 1);
/* Enable LOW_PWR */
tps65010_set_low_pwr(ON);
@@ -522,7 +550,7 @@ static int __init osk_tps_init(void)
tps65010_config_vregs1(TPS_LDO2_ENABLE | TPS_VLDO2_3_0V
| TPS_LDO1_ENABLE);
- return 0;
+ return platform_device_register(&tps_led_dev);
}
fs_initcall(osk_tps_init);
#endif
--- osk2.orig/arch/arm/mach-omap1/leds-osk.c 2007-04-15 11:09:12.000000000 -0700
+++ osk2/arch/arm/mach-omap1/leds-osk.c 2007-04-15 11:09:15.000000000 -0700
@@ -1,7 +1,7 @@
/*
* linux/arch/arm/mach-omap1/leds-osk.c
*
- * LED driver for OSK, and optionally Mistral QVGA, boards
+ * LED driver for OSK's optional Mistral QVGA board
*/
#include <linux/init.h>
#include <linux/workqueue.h>
@@ -11,7 +11,6 @@
#include <asm/system.h>
#include <asm/arch/gpio.h>
-#include <asm/arch/tps65010.h>
#include "leds.h"
@@ -20,51 +19,11 @@
#define LED_STATE_CLAIMED (1 << 1)
static u8 led_state;
-#define GREEN_LED (1 << 0) /* TPS65010 LED1 */
-#define AMBER_LED (1 << 1) /* TPS65010 LED2 */
-#define RED_LED (1 << 2) /* TPS65010 GPIO2 */
#define TIMER_LED (1 << 3) /* Mistral board */
#define IDLE_LED (1 << 4) /* Mistral board */
static u8 hw_led_state;
-/* TPS65010 leds are changed using i2c -- from a task context.
- * Using one of these for the "idle" LED would be impractical...
- */
-#define TPS_LEDS (GREEN_LED | RED_LED | AMBER_LED)
-
-static u8 tps_leds_change;
-
-static void tps_work(struct work_struct *unused)
-{
- for (;;) {
- u8 leds;
-
- local_irq_disable();
- leds = tps_leds_change;
- tps_leds_change = 0;
- local_irq_enable();
-
- if (!leds)
- break;
-
- /* careful: the set_led() value is on/off/blink */
- if (leds & GREEN_LED)
- tps65010_set_led(LED1, !!(hw_led_state & GREEN_LED));
- if (leds & AMBER_LED)
- tps65010_set_led(LED2, !!(hw_led_state & AMBER_LED));
-
- /* the gpio led doesn't have that issue */
- if (leds & RED_LED)
- tps65010_set_gpio_out_value(GPIO2,
- !(hw_led_state & RED_LED));
- }
-}
-
-static DECLARE_WORK(work, tps_work);
-
-#ifdef CONFIG_OMAP_OSK_MISTRAL
-
/* For now, all system indicators require the Mistral board, since that
* LED can be manipulated without a task context. This LED is either red,
* or green, but not both; it can't give the full "disco led" effect.
@@ -88,24 +47,19 @@ static void mistral_setled(void)
omap_set_gpio_dataout(GPIO_LED_RED, red);
}
-#endif
-
void osk_leds_event(led_event_t evt)
{
unsigned long flags;
- u16 leds;
local_irq_save(flags);
if (!(led_state & LED_STATE_ENABLED) && evt != led_start)
goto done;
- leds = hw_led_state;
switch (evt) {
case led_start:
led_state |= LED_STATE_ENABLED;
hw_led_state = 0;
- leds = ~0;
break;
case led_halted:
@@ -118,7 +72,6 @@ void osk_leds_event(led_event_t evt)
case led_claim:
led_state |= LED_STATE_CLAIMED;
hw_led_state = 0;
- leds = ~0;
break;
case led_release:
@@ -126,8 +79,6 @@ void osk_leds_event(led_event_t evt)
hw_led_state = 0;
break;
-#ifdef CONFIG_OMAP_OSK_MISTRAL
-
case led_timer:
hw_led_state ^= TIMER_LED;
mistral_setled();
@@ -143,51 +94,10 @@ void osk_leds_event(led_event_t evt)
mistral_setled();
break;
-#endif /* CONFIG_OMAP_OSK_MISTRAL */
-
- /* "green" == tps LED1 (leftmost, normally power-good)
- * works only with DC adapter, not on battery power!
- */
- case led_green_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= GREEN_LED;
- break;
- case led_green_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~GREEN_LED;
- break;
-
- /* "amber" == tps LED2 (middle) */
- case led_amber_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= AMBER_LED;
- break;
- case led_amber_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~AMBER_LED;
- break;
-
- /* "red" == LED on tps gpio3 (rightmost) */
- case led_red_on:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state |= RED_LED;
- break;
- case led_red_off:
- if (led_state & LED_STATE_CLAIMED)
- hw_led_state &= ~RED_LED;
- break;
-
default:
break;
}
- leds ^= hw_led_state;
- leds &= TPS_LEDS;
- if (leds && (led_state & LED_STATE_CLAIMED)) {
- tps_leds_change |= leds;
- schedule_work(&work);
- }
-
done:
local_irq_restore(flags);
}
--- osk2.orig/arch/arm/mach-omap1/leds.c 2007-04-15 11:09:12.000000000 -0700
+++ osk2/arch/arm/mach-omap1/leds.c 2007-04-15 11:09:15.000000000 -0700
@@ -5,11 +5,12 @@
*/
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <asm/leds.h>
+#include <asm/gpio.h>
#include <asm/mach-types.h>
-#include <asm/arch/gpio.h>
#include <asm/arch/mux.h>
#include "leds.h"
@@ -25,17 +26,17 @@ omap_leds_init(void)
|| machine_is_omap_perseus2())
leds_event = h2p2_dbg_leds_event;
+#ifdef CONFIG_OMAP_OSK_MISTRAL
else if (machine_is_omap_osk())
leds_event = osk_leds_event;
+#endif
else
return -1;
if (machine_is_omap_h2()
|| machine_is_omap_h3()
-#ifdef CONFIG_OMAP_OSK_MISTRAL
|| machine_is_omap_osk()
-#endif
) {
/* LED1/LED2 pins can be used as GPIO (as done here), or by
@@ -47,14 +48,14 @@ omap_leds_init(void)
* that's a different kind of LED (just one color at a time).
*/
omap_cfg_reg(P18_1610_GPIO3);
- if (omap_request_gpio(3) == 0)
- omap_set_gpio_direction(3, 0);
+ if (gpio_request(3, "timer_led") == 0)
+ gpio_direction_output(3, 0);
else
printk(KERN_WARNING "LED: can't get GPIO3/red?\n");
omap_cfg_reg(MPUIO4);
- if (omap_request_gpio(OMAP_MPUIO(4)) == 0)
- omap_set_gpio_direction(OMAP_MPUIO(4), 0);
+ if (gpio_request(OMAP_MPUIO(4), "idle_led") == 0)
+ gpio_direction_output(OMAP_MPUIO(4), 0);
else
printk(KERN_WARNING "LED: can't get MPUIO4/green?\n");
}
prev parent reply other threads:[~2007-04-15 20:55 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <f17812d70704090309l8dd764cxc06fbd7b765c55d8@mail.gmail.com>
[not found] ` <200704090822.03338.david-b@pacbell.net>
[not found] ` <664892915.20070409201803@gmail.com>
[not found] ` <200704091322.26360.david-b@pacbell.net>
2007-04-10 23:11 ` is there any generic GPIO chip framework like IRQ chips? Paul Sokolovsky
2007-04-11 4:52 ` David Brownell
2007-04-12 13:57 ` Paul Sokolovsky
2007-04-15 19:47 ` David Brownell
2007-04-17 16:05 ` Paul Sokolovsky
2007-04-19 2:22 ` David Brownell
2007-04-23 8:45 ` Paul Sokolovsky
2007-04-24 4:52 ` David Brownell
2007-04-15 19:53 ` David Brownell
2007-04-15 19:58 ` David Brownell
2007-04-15 20:03 ` David Brownell [this message]
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=200704151303.27867.david-b@pacbell.net \
--to=david-b@pacbell.net \
--cc=linux-arm-kernel@lists.arm.linux.org.uk \
--cc=linux-kernel@vger.kernel.org \
--cc=pmiscml@gmail.com \
/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.