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>
This commit is contained in:
Родитель
374b105797
Коммит
7dde0c0328
|
@ -75,12 +75,143 @@ 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);
|
||||
|
||||
|
||||
/*
|
||||
* Assabet flash support code.
|
||||
|
|
|
@ -39,8 +39,8 @@ extern unsigned long SCR_value;
|
|||
|
||||
#define ASSABET_BCR_CF_PWR (1<<0) /* Compact Flash Power (1 = 3.3v, 0 = off) */
|
||||
#define ASSABET_BCR_CF_RST (1<<1) /* Compact Flash Reset (1 = power up reset) */
|
||||
#define ASSABET_BCR_GFX_RST (1<<1) /* Graphics Accelerator Reset (0 = hold reset) */
|
||||
#define ASSABET_BCR_CODEC_RST (1<<2) /* 0 = Holds UCB1300, ADI7171, and UDA1341 in reset */
|
||||
#define ASSABET_BCR_NGFX_RST (1<<1) /* Graphics Accelerator Reset (0 = hold reset) */
|
||||
#define ASSABET_BCR_NCODEC_RST (1<<2) /* 0 = Holds UCB1300, ADI7171, and UDA1341 in reset */
|
||||
#define ASSABET_BCR_IRDA_FSEL (1<<3) /* IRDA Frequency select (0 = SIR, 1 = MIR/ FIR) */
|
||||
#define ASSABET_BCR_IRDA_MD0 (1<<4) /* Range/Power select */
|
||||
#define ASSABET_BCR_IRDA_MD1 (1<<5) /* Range/Power select */
|
||||
|
@ -69,6 +69,8 @@ extern void ASSABET_BCR_frob(unsigned int mask, unsigned int set);
|
|||
#define ASSABET_BCR_frob(x,y) do { } while (0)
|
||||
#endif
|
||||
|
||||
extern void assabet_uda1341_reset(int set);
|
||||
|
||||
#define ASSABET_BCR_set(x) ASSABET_BCR_frob((x), (x))
|
||||
#define ASSABET_BCR_clear(x) ASSABET_BCR_frob((x), 0)
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче