Re: is there any generic GPIO chip framework like IRQ chips?

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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");
 	}
-
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/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux