Skip to content

Commit

Permalink
ARM: sa11x0: assabet: better reset handling
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Russell King committed Dec 12, 2013
1 parent 374b105 commit 7dde0c0
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 4 deletions.
135 changes: 133 additions & 2 deletions arch/arm/mach-sa1100/assabet.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);


/*
Expand Down
6 changes: 4 additions & 2 deletions arch/arm/mach-sa1100/include/mach/assabet.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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)

Expand Down

0 comments on commit 7dde0c0

Please sign in to comment.