diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-07-09 11:27:12 +0200 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-12-12 23:59:15 +0100 |
commit | 7dde0c032869ea92206398ad5c538d01931e3947 (patch) | |
tree | cc85223cd5c247a1ef6cdc41aaf55d25a4293508 /arch/arm/mach-sa1100/assabet.c | |
parent | Linux 3.13-rc3 (diff) | |
download | linux-7dde0c032869ea92206398ad5c538d01931e3947.tar.xz linux-7dde0c032869ea92206398ad5c538d01931e3947.zip |
ARM: sa11x0: assabet: better reset handling
The codec reset pin is connected to several peripherals. When the
reset is released, unfortunately the ADV7171 powers itself up rather
than remaining in power-down mode. As we don't have a driver for
this device, we end up needlessly consuming an additional 330mW.
Not only that but we should have a way to arbitrate the reset signal.
This patch provides that facility: we program the ADV7171 to sleep
mode whenever the reset is released, and we release the reset when
any one of the three peripherals requests the reset to be released.
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Diffstat (limited to 'arch/arm/mach-sa1100/assabet.c')
-rw-r--r-- | arch/arm/mach-sa1100/assabet.c | 135 |
1 files changed, 133 insertions, 2 deletions
diff --git a/arch/arm/mach-sa1100/assabet.c b/arch/arm/mach-sa1100/assabet.c index c9808c684152..6a15354d43b8 100644 --- a/arch/arm/mach-sa1100/assabet.c +++ b/arch/arm/mach-sa1100/assabet.c @@ -75,11 +75,142 @@ void ASSABET_BCR_frob(unsigned int mask, unsigned int val) EXPORT_SYMBOL(ASSABET_BCR_frob); +/* + * The codec reset goes to three devices, so we need to release + * the rest when any one of these requests it. However, that + * causes the ADV7171 to consume around 100mA - more than half + * the LCD-blanked power. + * + * With the ADV7171, LCD and backlight enabled, we go over + * budget on the MAX846 Li-Ion charger, and if no Li-Ion battery + * is connected, the Assabet crashes. + */ +#define RST_UCB1X00 (1 << 0) +#define RST_UDA1341 (1 << 1) +#define RST_ADV7171 (1 << 2) + +#define SDA GPIO_GPIO(15) +#define SCK GPIO_GPIO(18) +#define MOD GPIO_GPIO(17) + +static void adv7171_start(void) +{ + GPSR = SCK; + udelay(1); + GPSR = SDA; + udelay(2); + GPCR = SDA; +} + +static void adv7171_stop(void) +{ + GPSR = SCK; + udelay(2); + GPSR = SDA; + udelay(1); +} + +static void adv7171_send(unsigned byte) +{ + unsigned i; + + for (i = 0; i < 8; i++, byte <<= 1) { + GPCR = SCK; + udelay(1); + if (byte & 0x80) + GPSR = SDA; + else + GPCR = SDA; + udelay(1); + GPSR = SCK; + udelay(1); + } + GPCR = SCK; + udelay(1); + GPSR = SDA; + udelay(1); + GPDR &= ~SDA; + GPSR = SCK; + udelay(1); + if (GPLR & SDA) + printk(KERN_WARNING "No ACK from ADV7171\n"); + udelay(1); + GPCR = SCK | SDA; + udelay(1); + GPDR |= SDA; + udelay(1); +} + +static void adv7171_write(unsigned reg, unsigned val) +{ + unsigned gpdr = GPDR; + unsigned gplr = GPLR; + + ASSABET_BCR = BCR_value | ASSABET_BCR_AUDIO_ON; + udelay(100); + + GPCR = SDA | SCK | MOD; /* clear L3 mode to ensure UDA1341 doesn't respond */ + GPDR = (GPDR | SCK | MOD) & ~SDA; + udelay(10); + if (!(GPLR & SDA)) + printk(KERN_WARNING "Something dragging SDA down?\n"); + GPDR |= SDA; + + adv7171_start(); + adv7171_send(0x54); + adv7171_send(reg); + adv7171_send(val); + adv7171_stop(); + + /* Restore GPIO state for L3 bus */ + GPSR = gplr & (SDA | SCK | MOD); + GPCR = (~gplr) & (SDA | SCK | MOD); + GPDR = gpdr; +} + +static void adv7171_sleep(void) +{ + /* Put the ADV7171 into sleep mode */ + adv7171_write(0x04, 0x40); +} + +static unsigned codec_nreset; + +static void assabet_codec_reset(unsigned mask, int set) +{ + unsigned long flags; + bool old; + + local_irq_save(flags); + old = !codec_nreset; + if (set) + codec_nreset &= ~mask; + else + codec_nreset |= mask; + + if (old != !codec_nreset) { + if (codec_nreset) { + ASSABET_BCR_set(ASSABET_BCR_NCODEC_RST); + adv7171_sleep(); + } else { + ASSABET_BCR_clear(ASSABET_BCR_NCODEC_RST); + } + } + local_irq_restore(flags); +} + static void assabet_ucb1x00_reset(enum ucb1x00_reset state) { - if (state == UCB_RST_PROBE) - ASSABET_BCR_set(ASSABET_BCR_CODEC_RST); + int set = state == UCB_RST_REMOVE || state == UCB_RST_SUSPEND || + state == UCB_RST_PROBE_FAIL; + assabet_codec_reset(RST_UCB1X00, set); +} + +void assabet_uda1341_reset(int set) +{ + assabet_codec_reset(RST_UDA1341, set); } +EXPORT_SYMBOL(assabet_uda1341_reset); /* |