From e05d8c97db483f50e999702be6b3a627e3b7499e Mon Sep 17 00:00:00 2001
From: Angelo Arrifano <miknix@gmail.com>
Date: Thu, 3 Sep 2009 00:31:38 +0100
Subject: [PATCH] HTCWIZ/HER: Add htcpld-dbg driver.

This driver allows blinking certain LEDs at boot for knowing
if the execution stopped in critical points.

* The driver is independent from the GPIO framework,
it writes directly to the GPIO bank registers. This allows
us to use the driver early enough for debugging.
* Uses a dirty udelay() function that doesn't rely on system timers
to allow the use of this driver early enough when the clocks are not
properly set up up yet.

Status: Working.

Signed-off-by: Angelo Arrifano <miknix@gmail.com>
---
 arch/arm/mach-omap1/Kconfig                  |    7 +
 arch/arm/mach-omap1/Makefile                 |    1 +
 arch/arm/mach-omap1/htcpld-dbg.c             |  338 ++++++++++++++++++++++++++
 arch/arm/plat-omap/include/mach/htcpld-dbg.h |    6 +
 4 files changed, 352 insertions(+), 0 deletions(-)
 create mode 100644 arch/arm/mach-omap1/htcpld-dbg.c
 create mode 100644 arch/arm/plat-omap/include/mach/htcpld-dbg.h

diff --git a/arch/arm/mach-omap1/Kconfig b/arch/arm/mach-omap1/Kconfig
index 55ecc01..50403aa 100644
--- a/arch/arm/mach-omap1/Kconfig
+++ b/arch/arm/mach-omap1/Kconfig
@@ -220,3 +220,10 @@ config OMAP_ARM_30MHZ
 	help
           Enable 30MHz clock for OMAP CPU. If unsure, say N.
 
+config HTCPLD_DEBUG_HELPER
+	bool "CPLD early debug helper routines."
+	depends on (MACH_OMAP_HTCWIZARD || MACH_HERALD)
+	help
+	  Enable this to use the htcpld_dbg_set() function to light up
+	  leds very early at kernel boot. This will help you debug when
+	  a output console is not available.
diff --git a/arch/arm/mach-omap1/Makefile b/arch/arm/mach-omap1/Makefile
index 37fee59..8e67fce 100644
--- a/arch/arm/mach-omap1/Makefile
+++ b/arch/arm/mach-omap1/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_MACH_NOKIA770)		+= board-nokia770.o
 obj-$(CONFIG_MACH_AMS_DELTA)		+= board-ams-delta.o
 obj-$(CONFIG_MACH_SX1)			+= board-sx1.o board-sx1-mmc.o
 obj-$(CONFIG_MACH_OMAP_HTCWIZARD)	+= board-htcwizard.o
+obj-$(CONFIG_HTCPLD_DEBUG_HELPER)	+= htcpld-dbg.o
 
 ifeq ($(CONFIG_ARCH_OMAP15XX),y)
 # Innovator-1510 FPGA
diff --git a/arch/arm/mach-omap1/htcpld-dbg.c b/arch/arm/mach-omap1/htcpld-dbg.c
new file mode 100644
index 0000000..dd88648
--- /dev/null
+++ b/arch/arm/mach-omap1/htcpld-dbg.c
@@ -0,0 +1,338 @@
+/*
+ *  htcpld-dbg.c - Early debug helper routines for HTC Wizard and Herald
+ *  based on drivers/i2c/algos/i2c-algo-bit.c
+ *  based on arch/arm/plat-omap/gpio.c
+ *
+ *
+ *
+ *  Copyright (C) 2009 Cory Maccarrone <darkstar6262@gmail.com>
+ *  Copyright (C) 2009 Angelo Arrifano <miknix@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+
+#include <mach/common.h>
+#include <mach/htcpld-dbg.h>
+
+#define BB_SDA   69
+#define BB_SCL   70
+#define BB_DELAY 2
+#define NAK_OK   0
+#define RETRIES  3
+#define TIMEOUT HZ / 10
+
+static unsigned char __initdata htcpld_dbg_cache = 0;
+
+/*
+ * bank = &gpio_bank[1 + (gpio >> 5)]
+ * For GPIOS 69 and 70, the bank is 3
+ *   - BASE -          ,  - IRQ -           ,  - VSTART -       , - METHOD -
+ * { OMAP850_GPIO3_BASE,  INT_850_GPIO_BANK3,  IH_GPIO_BASE + 64,	METHOD_GPIO_850 }
+ */
+
+#define OMAP850_GPIO3_BASE		(void __iomem *)0xfffbd000
+
+#define OMAP850_GPIO_DATA_INPUT	0x00
+#define OMAP850_GPIO_DIR_CONTROL	0x08
+#define OMAP850_GPIO_DATA_OUTPUT	0x04
+
+static inline int _get_gpio_index(int gpio)
+{
+	return gpio & 0x1f;
+}
+
+static void _gpio_set_direction(int gpio, int is_input)
+{
+	void __iomem *reg = OMAP850_GPIO3_BASE + OMAP850_GPIO_DIR_CONTROL;
+	u32 l;
+	
+	gpio = _get_gpio_index(gpio);
+
+	l = omap_readl(reg);
+	if (is_input)
+		l |= 1 << gpio;
+	else
+		l &= ~(1 << gpio);
+	omap_writel(l, reg);
+}
+
+static void _gpio_set_value(int gpio, int enable)
+{
+	void __iomem *reg = OMAP850_GPIO3_BASE + OMAP850_GPIO_DATA_OUTPUT;
+	u32 l;
+	
+	gpio = _get_gpio_index(gpio);
+
+	l = omap_readl(reg);
+	if (enable)
+		l |= 1 << gpio;
+	else
+		l &= ~(1 << gpio);
+	omap_writel(l, reg);
+}
+
+static int _gpio_get_value(int gpio)
+{
+	void __iomem *reg = OMAP850_GPIO3_BASE + OMAP850_GPIO_DATA_INPUT;
+	
+	return (omap_readl(reg) & (1 << _get_gpio_index(gpio))) != 0;
+}
+
+static inline void _gpio_direction_input(int gpio)
+{
+	_gpio_set_direction(gpio, 1);
+}
+
+static inline void _gpio_direction_output(int gpio, int value)
+{
+	_gpio_set_direction(gpio, 0);
+	_gpio_set_value(gpio, value);
+}
+
+static void _udelay(unsigned int us)
+{
+// clock tick (ns)
+#define CLK_TICK 3
+	unsigned int i = 0;
+	while (i < us*1000) i += CLK_TICK;
+}
+
+static inline void setsda(int state)
+{
+	if (state)
+		_gpio_direction_input(BB_SDA);
+	else
+		_gpio_direction_output(BB_SDA, 0);
+}
+
+static inline void setscl(int state)
+{
+	if (state)
+		_gpio_direction_input(BB_SCL);
+	else
+		_gpio_direction_output(BB_SCL, 0);
+}
+
+static inline int getsda(void)
+{
+	return _gpio_get_value(BB_SDA);
+}
+
+static inline int getscl(void)
+{
+	return _gpio_get_value(BB_SCL);
+}
+
+static inline void sdalo(void)
+{
+	setsda(0);
+	_udelay((BB_DELAY + 1) / 2);
+}
+
+static inline void sdahi(void)
+{
+	setsda(1);
+	_udelay((BB_DELAY + 1) / 2);
+}
+
+static inline void scllo(void)
+{
+	setscl(0);
+	_udelay(BB_DELAY / 2);
+}
+
+static int sclhi(void)
+{
+	unsigned long start;
+
+	setscl(1);
+
+	/* Not all adapters have scl sense line... */
+	/*if (!omap_get_gpio_datain(BB_SCL))
+		goto done;*/
+
+	start = jiffies;
+	while (!getscl()) {
+		/* This hw knows how to read the clock line, so we wait
+		 * until it actually gets high.  This is safer as some
+		 * chips may hold it low ("clock stretching") while they
+		 * are processing data internally.
+		 */
+		if (time_after_eq(jiffies, start + TIMEOUT)) {
+			/*printk("htcpld-dbg: sclhi timeout\n");*/
+			return -ETIMEDOUT;
+		}
+		cond_resched();
+	}
+
+	_udelay(BB_DELAY);
+	return 0;
+}
+
+int htcpld_bb_outb(unsigned char c)
+{
+	int i;
+	int sb;
+	int ack;
+
+	/* assert: scl is low */
+	for (i = 7; i >= 0; i--) {
+		sb = (c >> i) & 1;
+		setsda(sb);
+		_udelay((BB_DELAY + 1) / 2);
+		if (sclhi() < 0) { /* timed out */
+			/*printk("i2c_outb: 0x%02x, timeout at bit #%d\n", (int)c, i);*/
+			return -ETIMEDOUT;
+		}
+		
+		scllo();
+	}
+	sdahi();
+	if (sclhi() < 0) { /* timeout */
+		/*printk("i2c_outb: 0x%02x, timeout at ack\n", (int)c);*/
+		return -ETIMEDOUT;
+	}
+
+	/* read ack: SDA should be pulled down by slave, or it may
+	 * NAK (usually to report problems with the data we wrote).
+	 */
+	ack = !getsda(); /* ack: sda is pulled low -> success */
+	/*printk("i2c_outb: 0x%02x %s\n", (int)c, ack ? "A" : "NA");*/
+
+	scllo();
+	return ack;
+	/* assert: scl is low (sda undef) */
+}
+
+static void htcpld_bb_start(void)
+{
+	/* assert: scl, sda are high */
+	setsda(0);
+	_udelay(BB_DELAY);
+	scllo();
+}
+
+static void htcpld_bb_stop(void)
+{
+	/* assert: scl is low */
+	sdalo();
+	sclhi();
+	setsda(1);
+	_udelay(BB_DELAY);
+}
+
+static int try_address(unsigned char addr, int retries)
+{
+	int i, ret = -1;
+
+	for (i = 0; i <= retries; i++) {
+		ret = htcpld_bb_outb(addr);
+		if (ret == 1 || i == retries)
+			break;
+		/*printk("emitting stop condition\n");*/
+		htcpld_bb_stop();
+		_udelay(BB_DELAY);
+		yield();
+		/*printk("emitting start condition\n");*/
+		htcpld_bb_start();
+	}
+	/*
+	if (i && ret)
+		printk("Used %d tries\n", i + 1);*/
+	
+	return ret;
+}
+
+static int bit_doAddress(void)
+{
+	unsigned short nak_ok = NAK_OK;
+	unsigned char addr;
+	int ret, retries;
+
+	retries = nak_ok ? 0 : RETRIES;
+
+	/* normal 7bit address	*/
+	addr = 0x05 << 1;
+	ret = try_address(addr, retries);
+	if ((ret != 1) && !nak_ok) {
+		/*printk("htcpld-dbg: Remote IO2 failure\n");*/
+		return -1;
+	}
+
+	return 0;
+}
+
+static int sendbytes(void)
+{
+	unsigned short nak_ok = NAK_OK;
+	int retval;
+
+	retval = htcpld_bb_outb(htcpld_dbg_cache);
+
+	/* OK/ACK; or ignored NAK */
+	if ((retval > 0) || (nak_ok && (retval == 0))) {
+		return 1;
+	} else if (retval == 0) {
+		/*printk("htcpld-dbg: sendbytes: NAK bailout.\n");*/
+	} else {
+		/*printk("htcpld-dbg: sendbytes: error %d\n", retval);*/
+		return retval;
+	}
+
+	return 0;
+}
+
+void __init htcpld_dbg_set(unsigned char val)
+{
+	unsigned short nak_ok = NAK_OK;
+	int ret;
+
+	/* Set mux */
+	omap_writel(omap_readl(OMAP850_IO_CONF_5) |  0x000000CC, OMAP850_IO_CONF_5);
+   omap_writel(omap_readl(OMAP850_IO_CONF_5) & ~0x00000033, OMAP850_IO_CONF_5);
+
+	/* set gpio pins */
+	_gpio_direction_input(BB_SDA);
+	_gpio_direction_input(BB_SCL);
+
+	htcpld_dbg_cache |= val;
+
+/*	printk("htcpld-dbg: emitting start condition\n");*/
+	htcpld_bb_start();
+	ret = bit_doAddress();
+	if ((ret != 0) && !nak_ok) {
+/*		printk("htcpld-dbg: NAK from device addr\n");*/
+		goto bailout;
+	}
+
+	/* write bytes from buffer */
+	ret = sendbytes();
+/*	if (ret >= 1)
+		printk("htcpld-dbg: wrote %d byte%s\n", ret, ret == 1 ? "" : "s");*/
+	if (ret < 1) {
+	/*	if (ret >= 0)
+			printk("htcpld-dbg: IO failure\n");*/
+		goto bailout;
+	}
+	
+bailout:
+	/*printk("htcpld-dbg: emitting stop condition\n");*/
+	htcpld_bb_stop();
+
+	return;
+}
diff --git a/arch/arm/plat-omap/include/mach/htcpld-dbg.h b/arch/arm/plat-omap/include/mach/htcpld-dbg.h
new file mode 100644
index 0000000..2a930d2
--- /dev/null
+++ b/arch/arm/plat-omap/include/mach/htcpld-dbg.h
@@ -0,0 +1,6 @@
+#ifndef _HTCPLD_DBG_H_
+#define _HTCPLD_DBG_H_
+
+void htcpld_dbg_set(unsigned char val);
+
+#endif /* _HTCPLD_DBG_H_*/
-- 
1.6.3.3

