Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7dde0c03 authored by Russell King's avatar Russell King
Browse files

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: default avatarRussell King <rmk+kernel@arm.linux.org.uk>
parent 374b1057
Loading
Loading
Loading
Loading
+133 −2
Original line number Diff line number Diff line
@@ -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);


/*
+4 −2
Original line number Diff line number Diff line
@@ -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)