From e05d8c97db483f50e999702be6b3a627e3b7499e Mon Sep 17 00:00:00 2001 From: Angelo Arrifano 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 --- 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 + * Copyright (C) 2009 Angelo Arrifano + * + * 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 +#include + +#include +#include + +#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