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/