diff options
Diffstat (limited to 'drivers')
30 files changed, 2157 insertions, 191 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig index 883b0e7bc9..70797c182c 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -12,11 +12,13 @@ source "drivers/video/Kconfig" source "drivers/mci/Kconfig" source "drivers/clk/Kconfig" source "drivers/mfd/Kconfig" +source "drivers/misc/Kconfig" source "drivers/led/Kconfig" source "drivers/eeprom/Kconfig" source "drivers/input/Kconfig" source "drivers/watchdog/Kconfig" source "drivers/pwm/Kconfig" source "drivers/dma/Kconfig" +source "drivers/gpio/Kconfig" endmenu diff --git a/drivers/Makefile b/drivers/Makefile index ea3263f615..28a5cb8f35 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -15,5 +15,7 @@ obj-$(CONFIG_LED) += led/ obj-y += eeprom/ obj-$(CONFIG_PWM) += pwm/ obj-y += input/ +obj-y += misc/ obj-y += dma/ obj-y += watchdog/ +obj-y += gpio/ diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig new file mode 100644 index 0000000000..022a30988e --- /dev/null +++ b/drivers/gpio/Kconfig @@ -0,0 +1,13 @@ +config GPIOLIB + bool + +if GPIOLIB + +menu "GPIO " + +config GPIO_STMPE + depends on I2C_STMPE + bool "STMPE GPIO Expander" +endmenu + +endif diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile new file mode 100644 index 0000000000..945122b083 --- /dev/null +++ b/drivers/gpio/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_GPIOLIB) += gpio.o +obj-$(CONFIG_GPIO_STMPE) += gpio-stmpe.o diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c new file mode 100644 index 0000000000..fa3b041534 --- /dev/null +++ b/drivers/gpio/gpio-stmpe.c @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2012 Pengutronix + * Steffen Trumtrar <s.trumtrar@pengutronix.de> + * + * 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. + * + */ + +#include <common.h> +#include <errno.h> +#include <io.h> +#include <gpio.h> +#include <init.h> +#include <mfd/stmpe-i2c.h> + +#define GPIO_BASE 0x80 +#define GPIO_SET (GPIO_BASE + 0x02) +#define GPIO_CLR (GPIO_BASE + 0x04) +#define GPIO_MP (GPIO_BASE + 0x06) +#define GPIO_SET_DIR (GPIO_BASE + 0x08) +#define GPIO_ED (GPIO_BASE + 0x0a) +#define GPIO_RE (GPIO_BASE + 0x0c) +#define GPIO_FE (GPIO_BASE + 0x0e) +#define GPIO_PULL_UP (GPIO_BASE + 0x10) +#define GPIO_AF (GPIO_BASE + 0x12) +#define GPIO_LT (GPIO_BASE + 0x16) + +#define OFFSET(gpio) (0xff & (1 << (gpio)) ? 1 : 0) + +struct stmpe_gpio_chip { + struct gpio_chip chip; + struct stmpe_client_info *ci; +}; + +static void stmpe_gpio_set_value(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct stmpe_gpio_chip *stmpegpio = container_of(chip, struct stmpe_gpio_chip, chip); + struct stmpe_client_info *ci = (struct stmpe_client_info *)stmpegpio->ci; + int ret; + u8 val; + + ci->read_reg(ci->stmpe, GPIO_MP + OFFSET(gpio), &val); + + val |= 1 << (gpio % 8); + + if (value) + ret = ci->write_reg(ci->stmpe, GPIO_SET + OFFSET(gpio), val); + else + ret = ci->write_reg(ci->stmpe, GPIO_CLR + OFFSET(gpio), val); + + if (ret) + dev_err(chip->dev, "write failed!\n"); +} + +static int stmpe_gpio_direction_input(struct gpio_chip *chip, unsigned gpio) +{ + struct stmpe_gpio_chip *stmpegpio = container_of(chip, struct stmpe_gpio_chip, chip); + struct stmpe_client_info *ci = (struct stmpe_client_info *)stmpegpio->ci; + int ret; + u8 val; + + ci->read_reg(ci->stmpe, GPIO_SET_DIR + OFFSET(gpio), &val); + val &= ~(1 << (gpio % 8)); + ret = ci->write_reg(ci->stmpe, GPIO_SET_DIR + OFFSET(gpio), val); + + if (ret) + dev_err(chip->dev, "couldn't change direction. Write failed!\n"); + + return ret; +} + +static int stmpe_gpio_direction_output(struct gpio_chip *chip, unsigned gpio, int value) +{ + struct stmpe_gpio_chip *stmpegpio = container_of(chip, struct stmpe_gpio_chip, chip); + struct stmpe_client_info *ci = (struct stmpe_client_info *)stmpegpio->ci; + int ret; + u8 val; + + ci->read_reg(ci->stmpe, GPIO_SET_DIR + OFFSET(gpio), &val); + val |= 1 << (gpio % 8); + ret = ci->write_reg(ci->stmpe, GPIO_SET_DIR + OFFSET(gpio), val); + + stmpe_gpio_set_value(chip, gpio, value); + + if (ret) + dev_err(chip->dev, "couldn't change direction. Write failed!\n"); + + return ret; +} + +static int stmpe_gpio_get_value(struct gpio_chip *chip, unsigned gpio) +{ + struct stmpe_gpio_chip *stmpegpio = container_of(chip, struct stmpe_gpio_chip, chip); + struct stmpe_client_info *ci = (struct stmpe_client_info *)stmpegpio->ci; + u8 val; + int ret; + + ret = ci->read_reg(ci->stmpe, GPIO_MP + OFFSET(gpio), &val); + + if (ret) + dev_err(chip->dev, "read failed\n"); + + return val & (1 << (gpio % 8)) ? 1 : 0; +} + +static struct gpio_ops stmpe_gpio_ops = { + .direction_input = stmpe_gpio_direction_input, + .direction_output = stmpe_gpio_direction_output, + .get = stmpe_gpio_get_value, + .set = stmpe_gpio_set_value, +}; + +static int stmpe_gpio_probe(struct device_d *dev) +{ + struct stmpe_gpio_chip *stmpegpio; + struct stmpe_client_info *ci; + int ret; + + stmpegpio = xzalloc(sizeof(*stmpegpio)); + + stmpegpio->chip.ops = &stmpe_gpio_ops; + stmpegpio->ci = dev->platform_data; + + ci = (struct stmpe_client_info *)stmpegpio->ci; + + if (ci->stmpe->pdata->gpio_base) + stmpegpio->chip.base = ci->stmpe->pdata->gpio_base; + else + stmpegpio->chip.base = -1; + stmpegpio->chip.ngpio = 16; + stmpegpio->chip.dev = dev; + + ret = gpiochip_add(&stmpegpio->chip); + + if (ret) { + dev_err(dev, "couldn't add gpiochip\n"); + return ret; + } + + dev_info(dev, "probed stmpe gpiochip%d with base %d\n", dev->id, stmpegpio->chip.base); + return 0; +} + +static struct driver_d stmpe_gpio_driver = { + .name = "stmpe-gpio", + .probe = stmpe_gpio_probe, +}; + +static int stmpe_gpio_add(void) +{ + return register_driver(&stmpe_gpio_driver); +} +coredevice_initcall(stmpe_gpio_add); diff --git a/drivers/gpio/gpio.c b/drivers/gpio/gpio.c new file mode 100644 index 0000000000..6ad8d274e7 --- /dev/null +++ b/drivers/gpio/gpio.c @@ -0,0 +1,134 @@ +#include <common.h> +#include <gpio.h> +#include <errno.h> + +static LIST_HEAD(chip_list); + +#define ARCH_NR_GPIOS 256 + +static struct gpio_chip *gpio_desc[ARCH_NR_GPIOS]; + +static int gpio_is_valid(unsigned gpio) +{ + if (gpio < ARCH_NR_GPIOS) + return 1; + return 0; +} + +void gpio_set_value(unsigned gpio, int value) +{ + struct gpio_chip *chip = gpio_desc[gpio]; + + if (!gpio_is_valid(gpio)) + return; + if (!chip) + return; + if (!chip->ops->set) + return; + chip->ops->set(chip, gpio - chip->base, value); +} +EXPORT_SYMBOL(gpio_set_value); + +int gpio_get_value(unsigned gpio) +{ + struct gpio_chip *chip = gpio_desc[gpio]; + + if (!gpio_is_valid(gpio)) + return -EINVAL; + if (!chip) + return -ENODEV; + if (!chip->ops->get) + return -ENOSYS; + return chip->ops->get(chip, gpio - chip->base); +} +EXPORT_SYMBOL(gpio_get_value); + +int gpio_direction_output(unsigned gpio, int value) +{ + struct gpio_chip *chip = gpio_desc[gpio]; + + if (!gpio_is_valid(gpio)) + return -EINVAL; + if (!chip) + return -ENODEV; + if (!chip->ops->direction_output) + return -ENOSYS; + return chip->ops->direction_output(chip, gpio - chip->base, value); +} +EXPORT_SYMBOL(gpio_direction_output); + +int gpio_direction_input(unsigned gpio) +{ + struct gpio_chip *chip = gpio_desc[gpio]; + + if (!gpio_is_valid(gpio)) + return -EINVAL; + if (!chip) + return -ENODEV; + if (!chip->ops->direction_input) + return -ENOSYS; + return chip->ops->direction_input(chip, gpio - chip->base); +} +EXPORT_SYMBOL(gpio_direction_input); + +static int gpiochip_find_base(int start, int ngpio) +{ + int i; + int spare = 0; + int base = -ENOSPC; + + if (start < 0) + start = 0; + + for (i = start; i < ARCH_NR_GPIOS; i++) { + struct gpio_chip *chip = gpio_desc[i]; + + if (!chip) { + spare++; + if (spare == ngpio) { + base = i + 1 - ngpio; + break; + } + } else { + spare = 0; + i += chip->ngpio - 1; + } + } + + if (gpio_is_valid(base)) + debug("%s: found new base at %d\n", __func__, base); + return base; +} + +int gpiochip_add(struct gpio_chip *chip) +{ + int base, i; + + base = gpiochip_find_base(chip->base, chip->ngpio); + if (base < 0) + return base; + + if (chip->base >= 0 && chip->base != base) + return -EBUSY; + + chip->base = base; + + list_add_tail(&chip->list, &chip_list); + + for (i = chip->base; i < chip->base + chip->ngpio; i++) + gpio_desc[i] = chip; + + return 0; +} + +int gpio_get_num(struct device_d *dev, int gpio) +{ + struct gpio_chip *chip; + + list_for_each_entry(chip, &chip_list, list) { + if (chip->dev == dev) + return chip->base + gpio; + } + + return -ENODEV; +} diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index 1ce5c00276..3f998eafa5 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -5,8 +5,8 @@ menu "I2C Hardware Bus support" config I2C_IMX - bool "i.MX I2C Master driver" - depends on ARCH_IMX && !ARCH_IMX1 + bool "MPC85xx/i.MX I2C Master driver" + depends on (ARCH_IMX && !ARCH_IMX1) || ARCH_MPC85XX config I2C_OMAP bool "OMAP I2C Master driver" diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index da6218f43c..60182042db 100644 --- a/drivers/i2c/busses/i2c-imx.c +++ b/drivers/i2c/busses/i2c-imx.c @@ -1,4 +1,5 @@ /* + * Copyright 2012 GE Intelligent Platforms, Inc * Copyright (C) 2002 Motorola GSG-China * 2009 Marc Kleine-Budde, Pengutronix * @@ -22,7 +23,8 @@ * * Desc.: * Implementation of I2C Adapter/Algorithm Driver - * for I2C Bus integrated in Freescale i.MX/MXC processors + * for I2C Bus integrated in Freescale i.MX/MXC processors and + * 85xx processors. * * Derived from Motorola GSG China I2C example driver * @@ -36,7 +38,6 @@ #include <clock.h> #include <common.h> #include <driver.h> -#include <gpio.h> #include <init.h> #include <malloc.h> #include <types.h> @@ -46,23 +47,23 @@ #include <io.h> #include <i2c/i2c.h> -#include <mach/generic.h> #include <mach/clock.h> /* This will be the driver name */ -#define DRIVER_NAME "i2c-imx" +#define DRIVER_NAME "i2c-fsl" /* Default value */ -#define IMX_I2C_BIT_RATE 100000 /* 100kHz */ +#define FSL_I2C_BIT_RATE 100000 /* 100kHz */ -/* IMX I2C registers */ -#define IMX_I2C_IADR 0x00 /* i2c slave address */ -#define IMX_I2C_IFDR 0x04 /* i2c frequency divider */ -#define IMX_I2C_I2CR 0x08 /* i2c control */ -#define IMX_I2C_I2SR 0x0C /* i2c status */ -#define IMX_I2C_I2DR 0x10 /* i2c transfer data */ +/* FSL I2C registers */ +#define FSL_I2C_IADR 0x00 /* i2c slave address */ +#define FSL_I2C_IFDR 0x04 /* i2c frequency divider */ +#define FSL_I2C_I2CR 0x08 /* i2c control */ +#define FSL_I2C_I2SR 0x0C /* i2c status */ +#define FSL_I2C_I2DR 0x10 /* i2c transfer data */ +#define FSL_I2C_DFSRR 0x14 /* i2c digital filter sampling rate */ -/* Bits of IMX I2C registers */ +/* Bits of FSL I2C registers */ #define I2SR_RXAK 0x01 #define I2SR_IIF 0x02 #define I2SR_SRW 0x04 @@ -85,6 +86,7 @@ * * Duplicated divider values removed from list */ +#ifndef CONFIG_PPC static u16 i2c_clk_div[50][2] = { { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, @@ -100,24 +102,26 @@ static u16 i2c_clk_div[50][2] = { { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, { 3072, 0x1E }, { 3840, 0x1F } }; +#endif -struct imx_i2c_struct { +struct fsl_i2c_struct { void __iomem *base; struct i2c_adapter adapter; unsigned int disable_delay; int stopped; - unsigned int ifdr; /* IMX_I2C_IFDR */ + unsigned int ifdr; /* FSL_I2C_IFDR */ + unsigned int dfsrr; /* FSL_I2C_DFSRR */ }; -#define to_imx_i2c_struct(a) container_of(a, struct imx_i2c_struct, adapter) +#define to_fsl_i2c_struct(a) container_of(a, struct fsl_i2c_struct, adapter) #ifdef CONFIG_I2C_DEBUG -static void i2c_imx_dump_reg(struct i2c_adapter *adapter) +static void i2c_fsl_dump_reg(struct i2c_adapter *adapter) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); u32 reg_cr, reg_sr; - reg_cr = readb(i2c_imx->base + IMX_I2C_I2CR); - reg_sr = readb(i2c_imx->base + IMX_I2C_I2SR); + reg_cr = readb(i2c_fsl->base + FSL_I2C_I2CR); + reg_sr = readb(i2c_fsl->base + FSL_I2C_I2SR); dev_dbg(adapter->dev, "CONTROL:\t" "IEN =%d, IIEN=%d, MSTA=%d, MTX =%d, TXAK=%d, RSTA=%d\n", @@ -133,22 +137,22 @@ static void i2c_imx_dump_reg(struct i2c_adapter *adapter) (reg_sr & I2SR_RXAK ? 1 : 0)); } #else -static inline void i2c_imx_dump_reg(struct i2c_adapter *adapter) +static inline void i2c_fsl_dump_reg(struct i2c_adapter *adapter) { return; } #endif -static int i2c_imx_bus_busy(struct i2c_adapter *adapter, int for_busy) +static int i2c_fsl_bus_busy(struct i2c_adapter *adapter, int for_busy) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; uint64_t start; unsigned int temp; start = get_time_ns(); while (1) { - temp = readb(base + IMX_I2C_I2SR); + temp = readb(base + FSL_I2C_I2SR); if (for_busy && (temp & I2SR_IBB)) break; if (!for_busy && !(temp & I2SR_IBB)) @@ -164,15 +168,15 @@ static int i2c_imx_bus_busy(struct i2c_adapter *adapter, int for_busy) return 0; } -static int i2c_imx_trx_complete(struct i2c_adapter *adapter) +static int i2c_fsl_trx_complete(struct i2c_adapter *adapter) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; uint64_t start; start = get_time_ns(); while (1) { - unsigned int reg = readb(base + IMX_I2C_I2SR); + unsigned int reg = readb(base + FSL_I2C_I2SR); if (reg & I2SR_IIF) break; @@ -181,20 +185,20 @@ static int i2c_imx_trx_complete(struct i2c_adapter *adapter) return -EIO; } } - writeb(0, base + IMX_I2C_I2SR); + writeb(0, base + FSL_I2C_I2SR); return 0; } -static int i2c_imx_acked(struct i2c_adapter *adapter) +static int i2c_fsl_acked(struct i2c_adapter *adapter) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; uint64_t start; start = get_time_ns(); while (1) { - unsigned int reg = readb(base + IMX_I2C_I2SR); + unsigned int reg = readb(base + FSL_I2C_I2SR); if (!(reg & I2SR_RXAK)) break; @@ -207,71 +211,137 @@ static int i2c_imx_acked(struct i2c_adapter *adapter) return 0; } -static int i2c_imx_start(struct i2c_adapter *adapter) +static int i2c_fsl_start(struct i2c_adapter *adapter) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; unsigned int temp = 0; int result; - writeb(i2c_imx->ifdr, base + IMX_I2C_IFDR); + writeb(i2c_fsl->ifdr, base + FSL_I2C_IFDR); + if (i2c_fsl->dfsrr != -1) + writeb(i2c_fsl->dfsrr, base + FSL_I2C_DFSRR); + /* Enable I2C controller */ - writeb(0, base + IMX_I2C_I2SR); - writeb(I2CR_IEN, base + IMX_I2C_I2CR); + writeb(0, base + FSL_I2C_I2SR); + writeb(I2CR_IEN, base + FSL_I2C_I2CR); /* Wait controller to be stable */ udelay(100); /* Start I2C transaction */ - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp |= I2CR_MSTA; - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); - result = i2c_imx_bus_busy(adapter, 1); + result = i2c_fsl_bus_busy(adapter, 1); if (result) return result; - i2c_imx->stopped = 0; + i2c_fsl->stopped = 0; temp |= I2CR_MTX | I2CR_TXAK; - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); return result; } -static void i2c_imx_stop(struct i2c_adapter *adapter) +static void i2c_fsl_stop(struct i2c_adapter *adapter) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; unsigned int temp = 0; - if (!i2c_imx->stopped) { + if (!i2c_fsl->stopped) { /* Stop I2C transaction */ - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); /* wait for the stop condition to be send, otherwise the i2c * controller is disabled before the STOP is sent completely */ - i2c_imx->stopped = i2c_imx_bus_busy(adapter, 0) ? 0 : 1; - } - if (cpu_is_mx1()) { - /* - * This delay caused by an i.MXL hardware bug. - * If no (or too short) delay, no "STOP" bit will be generated. - */ - udelay(i2c_imx->disable_delay); + i2c_fsl->stopped = i2c_fsl_bus_busy(adapter, 0) ? 0 : 1; } - if (!i2c_imx->stopped) { - i2c_imx_bus_busy(adapter, 0); - i2c_imx->stopped = 1; + if (!i2c_fsl->stopped) { + i2c_fsl_bus_busy(adapter, 0); + i2c_fsl->stopped = 1; } /* Disable I2C controller, and force our state to stopped */ - writeb(0, base + IMX_I2C_I2CR); + writeb(0, base + FSL_I2C_I2CR); } -static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, +#ifdef CONFIG_PPC +static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, + unsigned int rate) +{ + void __iomem *base; + unsigned int i2c_clk; + unsigned short divider; + /* + * We want to choose an FDR/DFSR that generates an I2C bus speed that + * is equal to or lower than the requested speed. That means that we + * want the first divider that is equal to or greater than the + * calculated divider. + */ + u8 dfsr, fdr; + /* a, b and dfsr matches identifiers A,B and C respectively in AN2919 */ + unsigned short a, b, ga, gb; + unsigned long c_div, est_div; + + fdr = 0x31; /* Default if no FDR found */ + base = i2c_fsl->base; + i2c_clk = fsl_get_i2c_freq(); + divider = min((unsigned short)(i2c_clk / rate), (unsigned short) -1); + + /* + * Condition 1: dfsr <= 50ns/T (T=period of I2C source clock in ns). + * or (dfsr * T) <= 50ns. + * Translate to dfsr = 5 * Frequency / 100,000,000 + */ + dfsr = (5 * (i2c_clk / 1000)) / 100000; + dev_dbg(i2c_fsl->adapter.dev, + "<%s> requested speed:%d, i2c_clk:%d\n", __func__, + rate, i2c_clk); + if (!dfsr) + dfsr = 1; + + est_div = ~0; + for (ga = 0x4, a = 10; a <= 30; ga++, a += 2) { + for (gb = 0; gb < 8; gb++) { + b = 16 << gb; + c_div = b * (a + ((3*dfsr)/b)*2); + if ((c_div > divider) && (c_div < est_div)) { + unsigned short bin_gb, bin_ga; + + est_div = c_div; + bin_gb = gb << 2; + bin_ga = (ga & 0x3) | ((ga & 0x4) << 3); + fdr = bin_gb | bin_ga; + rate = i2c_clk / est_div; + dev_dbg(i2c_fsl->adapter.dev, + "FDR:0x%.2x, div:%ld, ga:0x%x, gb:0x%x," + " a:%d, b:%d, speed:%d\n", fdr, est_div, + ga, gb, a, b, rate); + /* Condition 2 not accounted for */ + dev_dbg(i2c_fsl->adapter.dev, + "Tr <= %d ns\n", (b - 3 * dfsr) * + 1000000 / (i2c_clk / 1000)); + } + } + if (a == 20) + a += 2; + if (a == 24) + a += 4; + } + dev_dbg(i2c_fsl->adapter.dev, + "divider:%d, est_div:%ld, DFSR:%d\n", divider, est_div, dfsr); + dev_dbg(i2c_fsl->adapter.dev, "FDR:0x%.2x, speed:%d\n", fdr, rate); + i2c_fsl->ifdr = fdr; + i2c_fsl->dfsrr = dfsr; +} +#else +static void i2c_fsl_set_clk(struct fsl_i2c_struct *i2c_fsl, unsigned int rate) { unsigned int i2c_clk_rate; @@ -279,7 +349,7 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, int i; /* Divider value calculation */ - i2c_clk_rate = imx_get_i2cclk(); + i2c_clk_rate = fsl_get_i2cclk(); div = (i2c_clk_rate + rate - 1) / rate; if (div < i2c_clk_div[0][0]) i = 0; @@ -290,7 +360,7 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, ; /* Store divider value */ - i2c_imx->ifdr = i2c_clk_div[i][1]; + i2c_fsl->ifdr = i2c_clk_div[i][1]; /* * There dummy delay is calculated. @@ -298,20 +368,21 @@ static void i2c_imx_set_clk(struct imx_i2c_struct *i2c_imx, * This delay is used in I2C bus disable function * to fix chip hardware bug. */ - i2c_imx->disable_delay = + i2c_fsl->disable_delay = (500000U * i2c_clk_div[i][0] + (i2c_clk_rate / 2) - 1) / (i2c_clk_rate / 2); - dev_dbg(i2c_imx->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", + dev_dbg(i2c_fsl->adapter.dev, "<%s> I2C_CLK=%d, REQ DIV=%d\n", __func__, i2c_clk_rate, div); - dev_dbg(i2c_imx->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", + dev_dbg(i2c_fsl->adapter.dev, "<%s> IFDR[IC]=0x%x, REAL DIV=%d\n", __func__, i2c_clk_div[i][1], i2c_clk_div[i][0]); } +#endif -static int i2c_imx_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) +static int i2c_fsl_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; int i, result; if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { @@ -320,12 +391,12 @@ static int i2c_imx_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) __func__, msgs->addr << 1); /* write slave address */ - writeb(msgs->addr << 1, base + IMX_I2C_I2DR); + writeb(msgs->addr << 1, base + FSL_I2C_I2DR); - result = i2c_imx_trx_complete(adapter); + result = i2c_fsl_trx_complete(adapter); if (result) return result; - result = i2c_imx_acked(adapter); + result = i2c_fsl_acked(adapter); if (result) return result; } @@ -335,27 +406,27 @@ static int i2c_imx_write(struct i2c_adapter *adapter, struct i2c_msg *msgs) dev_dbg(adapter->dev, "<%s> write byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); - writeb(msgs->buf[i], base + IMX_I2C_I2DR); + writeb(msgs->buf[i], base + FSL_I2C_I2DR); - result = i2c_imx_trx_complete(adapter); + result = i2c_fsl_trx_complete(adapter); if (result) return result; - result = i2c_imx_acked(adapter); + result = i2c_fsl_acked(adapter); if (result) return result; } return 0; } -static int i2c_imx_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) +static int i2c_fsl_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; int i, result; unsigned int temp; /* clear IIF */ - writeb(0x0, base + IMX_I2C_I2SR); + writeb(0x0, base + FSL_I2C_I2SR); if ( !(msgs->flags & I2C_M_DATA_ONLY) ) { dev_dbg(adapter->dev, @@ -363,28 +434,28 @@ static int i2c_imx_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) __func__, (msgs->addr << 1) | 0x01); /* write slave address */ - writeb((msgs->addr << 1) | 0x01, base + IMX_I2C_I2DR); + writeb((msgs->addr << 1) | 0x01, base + FSL_I2C_I2DR); - result = i2c_imx_trx_complete(adapter); + result = i2c_fsl_trx_complete(adapter); if (result) return result; - result = i2c_imx_acked(adapter); + result = i2c_fsl_acked(adapter); if (result) return result; } /* setup bus to read data */ - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp &= ~I2CR_MTX; if (msgs->len - 1) temp &= ~I2CR_TXAK; - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); - readb(base + IMX_I2C_I2DR); /* dummy read */ + readb(base + FSL_I2C_I2DR); /* dummy read */ /* read data */ for (i = 0; i < msgs->len; i++) { - result = i2c_imx_trx_complete(adapter); + result = i2c_fsl_trx_complete(adapter); if (result) return result; @@ -393,23 +464,23 @@ static int i2c_imx_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) * It must generate STOP before read I2DR to prevent * controller from generating another clock cycle */ - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp &= ~(I2CR_MSTA | I2CR_MTX); - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); /* * adding this delay helps on low bitrates */ - udelay(i2c_imx->disable_delay); + udelay(i2c_fsl->disable_delay); - i2c_imx_bus_busy(adapter, 0); - i2c_imx->stopped = 1; + i2c_fsl_bus_busy(adapter, 0); + i2c_fsl->stopped = 1; } else if (i == (msgs->len - 2)) { - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp |= I2CR_TXAK; - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); } - msgs->buf[i] = readb(base + IMX_I2C_I2DR); + msgs->buf[i] = readb(base + FSL_I2C_I2DR); dev_dbg(adapter->dev, "<%s> read byte: B%d=0x%02X\n", __func__, i, msgs->buf[i]); @@ -417,76 +488,77 @@ static int i2c_imx_read(struct i2c_adapter *adapter, struct i2c_msg *msgs) return 0; } -static int i2c_imx_xfer(struct i2c_adapter *adapter, +static int i2c_fsl_xfer(struct i2c_adapter *adapter, struct i2c_msg *msgs, int num) { - struct imx_i2c_struct *i2c_imx = to_imx_i2c_struct(adapter); - void __iomem *base = i2c_imx->base; + struct fsl_i2c_struct *i2c_fsl = to_fsl_i2c_struct(adapter); + void __iomem *base = i2c_fsl->base; unsigned int i, temp; int result; /* Start I2C transfer */ - result = i2c_imx_start(adapter); + result = i2c_fsl_start(adapter); if (result) goto fail0; /* read/write data */ for (i = 0; i < num; i++) { if (i && !(msgs[i].flags & I2C_M_DATA_ONLY)) { - temp = readb(base + IMX_I2C_I2CR); + temp = readb(base + FSL_I2C_I2CR); temp |= I2CR_RSTA; - writeb(temp, base + IMX_I2C_I2CR); + writeb(temp, base + FSL_I2C_I2CR); - result = i2c_imx_bus_busy(adapter, 1); + result = i2c_fsl_bus_busy(adapter, 1); if (result) goto fail0; } - i2c_imx_dump_reg(adapter); + i2c_fsl_dump_reg(adapter); /* write/read data */ if (msgs[i].flags & I2C_M_RD) - result = i2c_imx_read(adapter, &msgs[i]); + result = i2c_fsl_read(adapter, &msgs[i]); else - result = i2c_imx_write(adapter, &msgs[i]); + result = i2c_fsl_write(adapter, &msgs[i]); if (result) goto fail0; } fail0: /* Stop I2C transfer */ - i2c_imx_stop(adapter); + i2c_fsl_stop(adapter); return (result < 0) ? result : num; } -static int __init i2c_imx_probe(struct device_d *pdev) +static int __init i2c_fsl_probe(struct device_d *pdev) { - struct imx_i2c_struct *i2c_imx; + struct fsl_i2c_struct *i2c_fsl; struct i2c_platform_data *pdata; int ret; pdata = pdev->platform_data; - i2c_imx = kzalloc(sizeof(struct imx_i2c_struct), GFP_KERNEL); + i2c_fsl = kzalloc(sizeof(struct fsl_i2c_struct), GFP_KERNEL); - /* Setup i2c_imx driver structure */ - i2c_imx->adapter.master_xfer = i2c_imx_xfer; - i2c_imx->adapter.nr = pdev->id; - i2c_imx->adapter.dev = pdev; - i2c_imx->base = dev_request_mem_region(pdev, 0); + /* Setup i2c_fsl driver structure */ + i2c_fsl->adapter.master_xfer = i2c_fsl_xfer; + i2c_fsl->adapter.nr = pdev->id; + i2c_fsl->adapter.dev = pdev; + i2c_fsl->base = dev_request_mem_region(pdev, 0); + i2c_fsl->dfsrr = -1; /* Set up clock divider */ if (pdata && pdata->bitrate) - i2c_imx_set_clk(i2c_imx, pdata->bitrate); + i2c_fsl_set_clk(i2c_fsl, pdata->bitrate); else - i2c_imx_set_clk(i2c_imx, IMX_I2C_BIT_RATE); + i2c_fsl_set_clk(i2c_fsl, FSL_I2C_BIT_RATE); /* Set up chip registers to defaults */ - writeb(0, i2c_imx->base + IMX_I2C_I2CR); - writeb(0, i2c_imx->base + IMX_I2C_I2SR); + writeb(0, i2c_fsl->base + FSL_I2C_I2CR); + writeb(0, i2c_fsl->base + FSL_I2C_I2SR); /* Add I2C adapter */ - ret = i2c_add_numbered_adapter(&i2c_imx->adapter); + ret = i2c_add_numbered_adapter(&i2c_fsl->adapter); if (ret < 0) { dev_err(pdev, "registration failed\n"); goto fail; @@ -495,17 +567,17 @@ static int __init i2c_imx_probe(struct device_d *pdev) return 0; fail: - kfree(i2c_imx); + kfree(i2c_fsl); return ret; } -static struct driver_d i2c_imx_driver = { - .probe = i2c_imx_probe, +static struct driver_d i2c_fsl_driver = { + .probe = i2c_fsl_probe, .name = DRIVER_NAME, }; -static int __init i2c_adap_imx_init(void) +static int __init i2c_adap_fsl_init(void) { - return register_driver(&i2c_imx_driver); + return register_driver(&i2c_fsl_driver); } -device_initcall(i2c_adap_imx_init); +device_initcall(i2c_adap_fsl_init); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index af67935092..20eef86e39 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -33,4 +33,8 @@ config I2C_TWL6030 select I2C_TWLCORE bool "TWL6030 driver" +config I2C_STMPE + depends on I2C + bool "STMPE-i2c driver" + endmenu diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e11223b1f9..792ae2da8f 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -6,3 +6,4 @@ obj-$(CONFIG_I2C_LP3972) += lp3972.o obj-$(CONFIG_I2C_TWLCORE) += twl-core.o obj-$(CONFIG_I2C_TWL4030) += twl4030.o obj-$(CONFIG_I2C_TWL6030) += twl6030.o +obj-$(CONFIG_I2C_STMPE) += stmpe-i2c.o diff --git a/drivers/mfd/mc13xxx.c b/drivers/mfd/mc13xxx.c index 8bb0d00f8f..704446d019 100644 --- a/drivers/mfd/mc13xxx.c +++ b/drivers/mfd/mc13xxx.c @@ -32,6 +32,21 @@ #define DRIVERNAME "mc13xxx" +enum mc13xxx_mode { + MC13XXX_MODE_I2C, + MC13XXX_MODE_SPI, +}; + +struct mc13xxx { + struct cdev cdev; + union { + struct i2c_client *client; + struct spi_device *spi; + }; + enum mc13xxx_mode mode; + int revision; +}; + #define to_mc13xxx(a) container_of(a, struct mc13xxx, cdev) static struct mc13xxx *mc_dev; @@ -42,6 +57,12 @@ struct mc13xxx *mc13xxx_get(void) } EXPORT_SYMBOL(mc13xxx_get); +int mc13xxx_revision(struct mc13xxx *mc13xxx) +{ + return mc13xxx->revision; +} +EXPORT_SYMBOL(mc13xxx_revision); + #ifdef CONFIG_SPI static int spi_rw(struct spi_device *spi, void * buf, size_t len) { @@ -231,7 +252,7 @@ static struct mc13892_rev mc13892_revisions[] = { static int mc13xxx_query_revision(struct mc13xxx *mc13xxx) { unsigned int rev_id; - char *chipname, *revstr; + char *chipname, revstr[5]; int rev, i; mc13xxx_reg_read(mc13xxx, MC13XXX_REG_IDENTIFICATION, &rev_id); @@ -244,9 +265,9 @@ static int mc13xxx_query_revision(struct mc13xxx *mc13xxx) /* Ver 0.2 is actually 3.2a. Report as 3.2 */ if (rev == 0x02) { rev = 0x32; - revstr = "3.2a"; + strcpy(revstr, "3.2a"); } else - revstr = asprintf("%d.%d", rev / 0x10, rev % 10); + sprintf(revstr, "%d.%d", rev / 0x10, rev % 10); break; case 7: chipname = "MC13892"; @@ -258,12 +279,12 @@ static int mc13xxx_query_revision(struct mc13xxx *mc13xxx) return -EINVAL; rev = mc13892_revisions[i].rev; - revstr = mc13892_revisions[i].revstr; + strcpy(revstr, mc13892_revisions[i].revstr); if (rev == MC13892_REVISION_2_0) { if ((rev_id >> 9) & 0x3) { rev = MC13892_REVISION_2_0a; - revstr = "2.0a"; + strcpy(revstr, "2.0a"); } } break; diff --git a/drivers/mfd/stmpe-i2c.c b/drivers/mfd/stmpe-i2c.c new file mode 100644 index 0000000000..4af8b7b88c --- /dev/null +++ b/drivers/mfd/stmpe-i2c.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2012 Pengutronix + * Steffen Trumtrar <s.trumtrar@pengutronix.de> + * + * 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. + * + */ + +#include <common.h> +#include <init.h> +#include <driver.h> +#include <xfuncs.h> +#include <errno.h> + +#include <i2c/i2c.h> +#include <mfd/stmpe-i2c.h> + +#define DRIVERNAME "stmpe-i2c" + +#define to_stmpe(a) container_of(a, struct stmpe, cdev) + +int stmpe_reg_read(struct stmpe *stmpe, u32 reg, u8 *val) +{ + int ret; + + ret = i2c_read_reg(stmpe->client, reg, val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(stmpe_reg_read) + +int stmpe_reg_write(struct stmpe *stmpe, u32 reg, u8 val) +{ + int ret; + + ret = i2c_write_reg(stmpe->client, reg, &val, 1); + + return ret == 1 ? 0 : ret; +} +EXPORT_SYMBOL(stmpe_reg_write) + +int stmpe_set_bits(struct stmpe *stmpe, u32 reg, u8 mask, u8 val) +{ + u8 tmp; + int err; + + err = stmpe_reg_read(stmpe, reg, &tmp); + tmp = (tmp & ~mask) | val; + + if (!err) + err = stmpe_reg_write(stmpe, reg, tmp); + + return err; +} +EXPORT_SYMBOL(stmpe_set_bits); + +static ssize_t stmpe_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) +{ + struct stmpe *stmpe = to_stmpe(cdev); + u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = stmpe_reg_read(stmpe, offset, buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static ssize_t stmpe_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags) +{ + struct stmpe *stmpe = to_stmpe(cdev); + const u8 *buf = _buf; + size_t i = count; + int err; + + while (i) { + err = stmpe_reg_write(stmpe, offset, *buf); + if (err) + return (ssize_t)err; + buf++; + i--; + offset++; + } + + return count; +} + +static struct file_operations stmpe_fops = { + .lseek = dev_lseek_default, + .read = stmpe_read, + .write = stmpe_write, +}; + +static int stmpe_probe(struct device_d *dev) +{ + struct stmpe_platform_data *pdata = dev->platform_data; + struct stmpe *stmpe_dev; + struct stmpe_client_info *i2c_ci; + + if (!pdata) { + dev_dbg(dev, "no platform data\n"); + return -ENODEV; + } + + stmpe_dev = xzalloc(sizeof(struct stmpe)); + stmpe_dev->cdev.name = DRIVERNAME; + stmpe_dev->client = to_i2c_client(dev); + stmpe_dev->cdev.size = 191; /* 191 known registers */ + stmpe_dev->cdev.dev = dev; + stmpe_dev->cdev.ops = &stmpe_fops; + stmpe_dev->pdata = pdata; + dev->priv = stmpe_dev; + + i2c_ci = xzalloc(sizeof(struct stmpe_client_info)); + i2c_ci->stmpe = stmpe_dev; + i2c_ci->read_reg = stmpe_reg_read; + i2c_ci->write_reg = stmpe_reg_write; + + if (pdata->blocks &= STMPE_BLOCK_GPIO) + add_generic_device("stmpe-gpio", DEVICE_ID_DYNAMIC, NULL, 0, 0, IORESOURCE_MEM, i2c_ci); + + devfs_create(&stmpe_dev->cdev); + + return 0; +} + +static struct driver_d stmpe_driver = { + .name = DRIVERNAME, + .probe = stmpe_probe, +}; + +static int stmpe_init(void) +{ + register_driver(&stmpe_driver); + return 0; +} + +device_initcall(stmpe_init); diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig new file mode 100644 index 0000000000..c3d31ec9a2 --- /dev/null +++ b/drivers/misc/Kconfig @@ -0,0 +1,17 @@ +# +# Misc strange devices +# + +menuconfig MISC_DEVICES + bool "Misc devices " + help + Add support for misc strange devices + +if MISC_DEVICES + +config JTAG + tristate "JTAG Bitbang driver" + help + Controls JTAG chains connected to I/O pins + +endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile new file mode 100644 index 0000000000..b0855777a9 --- /dev/null +++ b/drivers/misc/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for misc devices that really don't fit anywhere else. +# + +obj-$(CONFIG_JTAG) += jtag.o diff --git a/drivers/misc/jtag.c b/drivers/misc/jtag.c new file mode 100644 index 0000000000..99fd081b31 --- /dev/null +++ b/drivers/misc/jtag.c @@ -0,0 +1,392 @@ +/* + * drivers/misc/jtag.c - More infos in include/jtag.h + * + * Written Aug 2009 by Davide Rizzo <elpa.rizzo@gmail.com> + * + * Ported to barebox Jul 2012 by + * Wjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.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 <fs.h> +#include <errno.h> +//#include <linux/list.h> +#include <jtag.h> +#include <gpio.h> +#include <driver.h> +#include <malloc.h> +#include <common.h> +#include <init.h> +#include <ioctl.h> + +#define VERSION_MAJ 1 +#define VERSION_MIN 0 + +/* Max devices in the jtag chain */ +#define MAX_DEVICES 16 + +struct jtag_info { + struct jtag_platdata *pdata; + struct cdev cdev; + unsigned int devices; /* Number of devices found in the jtag chain */ + /* Instruction register length of every device in the chain */ + unsigned int ir_len[MAX_DEVICES]; /* [devices] */ +}; + +static const unsigned long bypass = 0xFFFFFFFF; + +static void pulse_tms0(const struct jtag_platdata *pdata) +{ + gpio_set_value(pdata->gpio_tms, 0); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); +} + +static void pulse_tms1(const struct jtag_platdata *pdata) +{ + gpio_set_value(pdata->gpio_tms, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); +} + +static void jtag_reset(const struct jtag_platdata *pdata) +{ + gpio_set_value(pdata->gpio_tms, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); +} + +static void jtag_output(const struct jtag_platdata *pdata, + const unsigned long *data, unsigned int bitlen, int notlast) +{ + unsigned int a; + unsigned long mask; + gpio_set_value(pdata->gpio_tms, 0); + while (bitlen > 0) { + for (a = *data++, mask = 0x00000001; mask != 0 && bitlen > 0; + mask <<= 1, bitlen--) { + gpio_set_value(pdata->gpio_tdo, (a & mask) ? 1 : 0); + if ((bitlen == 1) && !notlast) + gpio_set_value(pdata->gpio_tms, 1); + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + } + } +} + +static int jtag_ioctl(struct cdev *inode, int cmd, void *arg) +{ + int ret = 0; + struct jtag_info *info = (struct jtag_info *)inode->priv; + int devices = info->devices; + struct jtag_cmd *jcmd = (struct jtag_cmd *)arg; + struct jtag_platdata *pdata = info->pdata; + + if (_IOC_TYPE(cmd) != JTAG_IOC_MAGIC) return -ENOTTY; + if (_IOC_NR(cmd) > JTAG_IOC_MAXNR) return -ENOTTY; + + switch (cmd) { + + case JTAG_GET_DEVICES: + /* Returns how many devices found in the chain */ + ret = info->devices; + break; + + case JTAG_GET_ID: + /* Returns ID register of selected device */ + if ((((struct jtag_rd_id *)arg)->device < 0) || + (((struct jtag_rd_id *)arg)->device >= devices)) { + ret = -EINVAL; + break; + } + jtag_reset(pdata); + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + while (devices-- > 0) { + unsigned long id = 0; + pulse_tms0(pdata); + if (gpio_get_value(pdata->gpio_tdi)) { + unsigned long mask; + for (id = 1, mask = 0x00000002; (mask != 0); + mask <<= 1) { + pulse_tms0(pdata); + if (gpio_get_value(pdata->gpio_tdi)) + id |= mask; + } + } + if (devices == ((struct jtag_rd_id *)arg)->device) { + ((struct jtag_rd_id *)arg)->id = id; + ret = 0; + break; + } + } + jtag_reset(pdata); + break; + + case JTAG_SET_IR_LENGTH: + /* Sets up IR length of one device */ + if ((jcmd->device >= 0) && (jcmd->device < devices)) + info->ir_len[jcmd->device] = jcmd->bitlen; + else + ret = -EINVAL; + break; + + case JTAG_RESET: + /* Resets all JTAG states */ + jtag_reset(pdata); + break; + + case JTAG_IR_WR: + /* + * Writes Instruction Register + * If device == -1 writes same Instruction Register in + * all devices. + * If device >= 0 writes Instruction Register in selected + * device and loads BYPASS instruction in all others. + */ + if ((jcmd->device < -1) || (jcmd->device >= devices)) { + ret = -EINVAL; + break; + } + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + while (devices-- > 0) { + if ((jcmd->device == -1) || (jcmd->device == devices)) + /* Loads desired instruction */ + jtag_output(pdata, jcmd->data, + info->ir_len[devices], devices); + else + /* Loads BYPASS instruction */ + jtag_output(pdata, &bypass, + info->ir_len[devices], devices); + } + pulse_tms1(pdata); + pulse_tms0(pdata); + break; + + case JTAG_DR_WR: + /* + * Writes Data Register of all devices + * If device == -1 writes same Data Register in all devices. + * If device >= 0 writes Data Register in selected device + * and loads BYPASS instruction in all others. + */ + if ((jcmd->device < -1) || (jcmd->device >= devices)) { + ret = -EINVAL; + break; + } + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + while (devices-- > 0) { + if ((jcmd->device == -1) || (devices == jcmd->device)) + /* Loads desired data */ + jtag_output(pdata, jcmd->data, jcmd->bitlen, + devices); + else + /* Loads 1 dummy bit in BYPASS data register */ + jtag_output(pdata, &bypass, 1, devices); + } + pulse_tms1(pdata); + pulse_tms0(pdata); + break; + + case JTAG_DR_RD: + /* Reads data register of selected device */ + if ((jcmd->device < 0) || (jcmd->device >= devices)) + ret = -EINVAL; + else { + unsigned long mask; + int bitlen = jcmd->bitlen; + unsigned long *data = jcmd->data; + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + devices -= (jcmd->device + 1); + while (devices-- > 0) + pulse_tms0(pdata); + while (bitlen > 0) { + for (*data = 0, mask = 0x00000001; + (mask != 0) && (bitlen > 0); + mask <<= 1, bitlen--) { + if (bitlen == 1) + pulse_tms1(pdata); + else + pulse_tms0(pdata); + if (gpio_get_value(pdata->gpio_tdi)) + *data |= mask; + } + data++; + } + pulse_tms1(pdata); + pulse_tms0(pdata); + } + break; + + case JTAG_CLK: + /* Generates arg clock pulses */ + gpio_set_value(pdata->gpio_tms, 0); + while ((*(unsigned int *) arg)--) { + gpio_set_value(pdata->gpio_tclk, 0); + gpio_set_value(pdata->gpio_tclk, 1); + } + break; + + default: + ret = -EFAULT; + } + + return ret; +} + +static struct file_operations jtag_operations = { + .ioctl = jtag_ioctl, +}; + +static int jtag_probe(struct device_d *pdev) +{ + int i, ret; + struct jtag_info *info; + struct jtag_platdata *pdata = pdev->platform_data; + + /* Setup gpio pins */ + gpio_direction_output(pdata->gpio_tms, 0); + gpio_direction_output(pdata->gpio_tclk, 1); + gpio_direction_output(pdata->gpio_tdo, 0); + gpio_direction_input(pdata->gpio_tdi); + if (pdata->use_gpio_trst) { + /* + * Keep fixed at 1 because some devices in the chain could + * not use it, to reset chain use jtag_reset() + */ + gpio_direction_output(pdata->gpio_trst, 1); + } + + /* Find how many devices in chain */ + jtag_reset(pdata); + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + gpio_set_value(pdata->gpio_tdo, 1); + /* Fills all IR with bypass instruction */ + for (i = 0; i < 32 * MAX_DEVICES; i++) + pulse_tms0(pdata); + pulse_tms1(pdata); + pulse_tms1(pdata); + pulse_tms1(pdata); + pulse_tms0(pdata); + pulse_tms0(pdata); + gpio_set_value(pdata->gpio_tdo, 0); + /* Fills all 1-bit bypass register with 0 */ + for (i = 0; i < MAX_DEVICES + 2; i++) + pulse_tms0(pdata); + gpio_set_value(pdata->gpio_tdo, 1); + /* Counts chain's bit length */ + for (i = 0; i < MAX_DEVICES + 1; i++) { + pulse_tms0(pdata); + if (gpio_get_value(pdata->gpio_tdi)) + break; + } + dev_notice(pdev, "%d devices found in chain\n", i); + + /* Allocate structure with chain specific infos */ + info = xzalloc(sizeof(struct jtag_info) + sizeof(info->ir_len[0]) * i); + + info->devices = i; + info->pdata = pdata; + pdev->priv = info; + + info->cdev.name = JTAG_NAME; + info->cdev.dev = pdev; + info->cdev.ops = &jtag_operations; + info->cdev.priv = info; + ret = devfs_create(&info->cdev); + + if (ret) + goto fail_devfs_create; + + return 0; + +fail_devfs_create: + pdev->priv = NULL; + free(info); + return ret; +} + +static void jtag_info(struct device_d *pdev) +{ + int dn, ret; + struct jtag_rd_id jid; + struct jtag_info *info = pdev->priv; + + printf(" JTAG:\n"); + printf(" Devices found: %d\n", info->devices); + for (dn = 0; dn < info->devices; dn++) { + jid.device = dn; + ret = jtag_ioctl(&info->cdev, JTAG_GET_ID, &jid); + printf(" Device number: %d\n", dn); + if (ret == -1) + printf(" JTAG_GET_ID failed: %s\n", strerror(errno)); + else + printf(" ID: 0x%lX\n", jid.id); + } +} + +static void jtag_remove(struct device_d *pdev) +{ + struct jtag_info *info = (struct jtag_info *) pdev->priv; + + devfs_remove(&info->cdev); + pdev->priv = NULL; + free(info); + dev_notice(pdev, "Device removed\n"); +} + +static struct driver_d jtag_driver = { + .name = JTAG_NAME, + .probe = jtag_probe, + .remove = jtag_remove, + .info = jtag_info, +}; + +static int jtag_module_init(void) +{ + return register_driver(&jtag_driver); +} + +device_initcall(jtag_module_init); + +MODULE_AUTHOR("Davide Rizzo <elpa.rizzo@gmail.com>"); +MODULE_AUTHOR("Wjatscheslaw Stoljarski <wjatscheslaw.stoljarski@kiwigrid.com>"); +MODULE_DESCRIPTION("JTAG bitbang driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/core.c b/drivers/mtd/core.c index 5510439a87..a6132e8ac5 100644 --- a/drivers/mtd/core.c +++ b/drivers/mtd/core.c @@ -79,7 +79,7 @@ static ssize_t mtd_write(struct cdev* cdev, const void *buf, size_t _count, return -EINVAL; } - dev_dbg(cdev->dev, "write: 0x%08lx 0x%08x\n", offset, count); + dev_dbg(cdev->dev, "write: 0x%08lx 0x%08lx\n", offset, count); while (count) { now = count > mtd->writesize ? mtd->writesize : count; @@ -100,7 +100,7 @@ static ssize_t mtd_write(struct cdev* cdev, const void *buf, size_t _count, ret = mtd->write(mtd, offset, now, &retlen, buf); dev_dbg(cdev->dev, - "offset: 0x%08lx now: 0x%08x retlen: 0x%08x\n", + "offset: 0x%08lx now: 0x%08lx retlen: 0x%08lx\n", offset, now, retlen); } if (ret) @@ -174,7 +174,7 @@ int mtd_ioctl(struct cdev *cdev, int request, void *buf) } #ifdef CONFIG_MTD_WRITE -static ssize_t mtd_erase(struct cdev *cdev, size_t count, loff_t offset) +static int mtd_erase(struct cdev *cdev, size_t count, loff_t offset) { struct mtd_info *mtd = cdev->priv; struct erase_info erase; @@ -248,7 +248,7 @@ int add_mtd_device(struct mtd_info *mtd, char *devname) list_for_each_entry(hook, &mtd_register_hooks, hook) if (hook->add_mtd_device) - hook->add_mtd_device(mtd, devname); + hook->add_mtd_device(mtd, devname, &hook->priv); return 0; } @@ -259,7 +259,9 @@ int del_mtd_device (struct mtd_info *mtd) list_for_each_entry(hook, &mtd_register_hooks, hook) if (hook->del_mtd_device) - hook->del_mtd_device(mtd); + hook->del_mtd_device(mtd, &hook->priv); + + devfs_remove(&mtd->cdev); unregister_device(&mtd->class_dev); free(mtd->param_size.value); free(mtd->cdev.name); diff --git a/drivers/mtd/mtd.h b/drivers/mtd/mtd.h index c8af6e3c9d..414bd6cdb5 100644 --- a/drivers/mtd/mtd.h +++ b/drivers/mtd/mtd.h @@ -25,8 +25,9 @@ */ struct mtddev_hook { struct list_head hook; - int (*add_mtd_device)(struct mtd_info *mtd, char *devname); - int (*del_mtd_device)(struct mtd_info *mtd); + int (*add_mtd_device)(struct mtd_info *mtd, char *devname, void **priv); + int (*del_mtd_device)(struct mtd_info *mtd, void **priv); + void *priv; }; struct cdev; diff --git a/drivers/mtd/mtdoob.c b/drivers/mtd/mtdoob.c index e4dd1a00c8..c7bf40cef8 100644 --- a/drivers/mtd/mtdoob.c +++ b/drivers/mtd/mtdoob.c @@ -69,7 +69,7 @@ static struct file_operations mtd_ops_oob = { .lseek = dev_lseek_default, }; -static int add_mtdoob_device(struct mtd_info *mtd, char *devname) +static int add_mtdoob_device(struct mtd_info *mtd, char *devname, void **priv) { struct mtdoob *mtdoob; @@ -80,13 +80,26 @@ static int add_mtdoob_device(struct mtd_info *mtd, char *devname) mtdoob->cdev.priv = mtdoob; mtdoob->cdev.dev = &mtd->class_dev; mtdoob->mtd = mtd; + *priv = mtdoob; devfs_create(&mtdoob->cdev); return 0; } +static int del_mtdoob_device(struct mtd_info *mtd, void **priv) +{ + struct mtdoob *mtdoob; + + mtdoob = *priv; + devfs_remove(&mtdoob->cdev); + free(mtdoob); + + return 0; +} + static struct mtddev_hook mtdoob_hook = { .add_mtd_device = add_mtdoob_device, + .del_mtd_device = del_mtdoob_device, }; static int __init register_mtdoob(void) diff --git a/drivers/mtd/mtdraw.c b/drivers/mtd/mtdraw.c index 24f7358098..16157e9f48 100644 --- a/drivers/mtd/mtdraw.c +++ b/drivers/mtd/mtdraw.c @@ -137,7 +137,7 @@ static ssize_t mtdraw_read(struct cdev *cdev, void *buf, size_t count, retlen += ret; } if (ret < 0) - printf("err %d\n", ret); + printf("err %lu\n", ret); else ret = retlen; return ret; @@ -222,7 +222,7 @@ static ssize_t mtdraw_write(struct cdev *cdev, const void *buf, size_t count, } } -static ssize_t mtdraw_erase(struct cdev *cdev, size_t count, loff_t _offset) +static int mtdraw_erase(struct cdev *cdev, size_t count, loff_t _offset) { struct mtd_info *mtd = to_mtd(cdev); struct erase_info erase; @@ -275,7 +275,7 @@ static const struct file_operations mtd_raw_fops = { .lseek = dev_lseek_default, }; -static int add_mtdraw_device(struct mtd_info *mtd, char *devname) +static int add_mtdraw_device(struct mtd_info *mtd, char *devname, void **priv) { struct mtdraw *mtdraw; @@ -290,13 +290,26 @@ static int add_mtdraw_device(struct mtd_info *mtd, char *devname) mtdraw->cdev.priv = mtdraw; mtdraw->cdev.dev = &mtd->class_dev; mtdraw->cdev.mtd = mtd; + *priv = mtdraw; devfs_create(&mtdraw->cdev); return 0; } +static int del_mtdraw_device(struct mtd_info *mtd, void **priv) +{ + struct mtdraw *mtdraw; + + mtdraw = *priv; + devfs_remove(&mtdraw->cdev); + free(mtdraw); + + return 0; +} + static struct mtddev_hook mtdraw_hook = { .add_mtd_device = add_mtdraw_device, + .del_mtd_device = del_mtdraw_device, }; static int __init register_mtdraw(void) diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 8c08c9f82c..d52c272c5c 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -14,4 +14,5 @@ obj-$(CONFIG_NAND_IMX) += nand_imx.o obj-$(CONFIG_NAND_OMAP_GPMC) += nand_omap_gpmc.o nand_omap_bch_decoder.o obj-$(CONFIG_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o +pbl-$(CONFIG_NAND_S3C24XX) += nand_s3c24xx.o obj-$(CONFIG_NAND_MXS) += nand_mxs.o diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index a5bf757e47..37e57b32ce 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1000,7 +1000,7 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) if (!chip->select_chip) chip->select_chip = nand_select_chip; - if (!chip->read_byte) + if (!chip->read_byte || chip->read_byte == nand_read_byte) chip->read_byte = busw ? nand_read_byte16 : nand_read_byte; if (!chip->read_word) chip->read_word = nand_read_word; @@ -1009,12 +1009,12 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) #ifdef CONFIG_MTD_WRITE if (!chip->block_markbad) chip->block_markbad = nand_default_block_markbad; - if (!chip->write_buf) + if (!chip->write_buf || chip->write_buf == nand_write_buf) chip->write_buf = busw ? nand_write_buf16 : nand_write_buf; #endif - if (!chip->read_buf) + if (!chip->read_buf || chip->read_buf == nand_read_buf) chip->read_buf = busw ? nand_read_buf16 : nand_read_buf; - if (!chip->verify_buf) + if (!chip->verify_buf || chip->verify_buf == nand_verify_buf) chip->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf; #ifdef CONFIG_NAND_BBT if (!chip->scan_bbt) @@ -1258,6 +1258,13 @@ ident_done: break; } + if (chip->options & NAND_BUSWIDTH_AUTO) { + chip->options |= busw; + nand_set_defaults(chip, busw); + if (chip->set_buswidth) + chip->set_buswidth(mtd, chip, busw); + } + /* * Check, if buswidth is correct. Hardware drivers should set * chip correct ! @@ -1326,6 +1333,11 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) struct nand_chip *chip = mtd->priv; struct nand_flash_dev *type; + if (chip->options & NAND_BUSWIDTH_AUTO && !chip->set_buswidth) { + printk(KERN_ERR "buswidth detection but no buswidth callback\n"); + return -EINVAL; + } + /* Get buswidth to select the correct functions */ busw = chip->options & NAND_BUSWIDTH_16; /* Set the default functions */ diff --git a/drivers/mtd/nand/nand_omap_gpmc.c b/drivers/mtd/nand/nand_omap_gpmc.c index 86d45748ed..fa6074f856 100644 --- a/drivers/mtd/nand/nand_omap_gpmc.c +++ b/drivers/mtd/nand/nand_omap_gpmc.c @@ -22,11 +22,7 @@ * static struct gpmc_nand_platform_data nand_plat = { * .cs = give the chip select of the device * .device_width = what is the width of the device 8 or 16? - * .max_timeout = delay desired for operation * .wait_mon_pin = do you use wait monitoring? if so wait pin - * .plat_options = platform options. - * NAND_HWECC_ENABLE/DISABLE - hw ecc enable/disable - * NAND_WAITPOL_LOW/HIGH - wait pin polarity * .oob = if you would like to replace oob with a custom OOB. * .nand_setup = if you would like a special setup function to be called * .priv = any params you'd like to save(e.g. like nand_setup to use) @@ -112,12 +108,11 @@ struct gpmc_nand_info { void *gpmc_address; void *gpmc_data; void __iomem *gpmc_base; - unsigned char wait_mon_mask; - uint64_t timeout; + u32 wait_mon_mask; unsigned inuse:1; - unsigned wait_pol:1; unsigned char ecc_parity_pairs; enum gpmc_ecc_mode ecc_mode; + void *cs_base; }; /* Typical BOOTROM oob layouts-requires hwecc **/ @@ -191,25 +186,11 @@ static int omap_dev_ready(struct mtd_info *mtd) { struct nand_chip *nand = (struct nand_chip *)(mtd->priv); struct gpmc_nand_info *oinfo = (struct gpmc_nand_info *)(nand->priv); - uint64_t start = get_time_ns(); - unsigned long comp; - /* What do we mean by assert and de-assert? */ - comp = (oinfo->wait_pol == NAND_WAITPOL_HIGH) ? - oinfo->wait_mon_mask : 0x0; - while (1) { - /* Breakout condition */ - if (is_timeout(start, oinfo->timeout)) { - debug("%s timedout\n", __func__); - return -ETIMEDOUT; - } - /* if the wait is released, we are good to go */ - if (comp == - (readl(oinfo->gpmc_base + GPMC_STATUS) && - oinfo->wait_mon_mask)) - break; - } - return 0; + if (readl(oinfo->gpmc_base + GPMC_STATUS) & oinfo->wait_mon_mask) + return 1; + else + return 0; } /** @@ -603,6 +584,101 @@ static int omap_gpmc_read_buf_manual(struct mtd_info *mtd, struct nand_chip *chi return bytes; } +/** + * omap_read_buf_pref - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) +{ + struct gpmc_nand_info *info = container_of(mtd, + struct gpmc_nand_info, minfo); + u32 r_count = 0; + u32 *p = (u32 *)buf; + + /* take care of subpage reads */ + if (len % 4) { + if (info->nand.options & NAND_BUSWIDTH_16) + readsw(info->cs_base, buf, (len % 4) / 2); + else + readsb(info->cs_base, buf, len % 4); + p = (u32 *) (buf + len % 4); + len -= len % 4; + } + + /* configure and start prefetch transfer */ + gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x0); + + do { + r_count = readl(info->gpmc_base + GPMC_PREFETCH_STATUS); + r_count = GPMC_PREFETCH_STATUS_FIFO_CNT(r_count); + r_count = r_count >> 2; + readsl(info->cs_base, p, r_count); + p += r_count; + len -= r_count << 2; + } while (len); + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(info->gpmc_cs); +} + +/** + * omap_write_buf_pref - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf_pref(struct mtd_info *mtd, + const u_char *buf, int len) +{ + struct gpmc_nand_info *info = container_of(mtd, + struct gpmc_nand_info, minfo); + u32 w_count = 0; + u_char *buf1 = (u_char *)buf; + u32 *p32 = (u32 *)buf; + uint64_t start; + + /* take care of subpage writes */ + while (len % 4 != 0) { + writeb(*buf, info->nand.IO_ADDR_W); + buf1++; + p32 = (u32 *)buf1; + len--; + } + + /* configure and start prefetch transfer */ + gpmc_prefetch_enable(info->gpmc_cs, + PREFETCH_FIFOTHRESHOLD_MAX, 0x0, len, 0x1); + + while (len >= 0) { + w_count = readl(info->gpmc_base + GPMC_PREFETCH_STATUS); + w_count = GPMC_PREFETCH_STATUS_FIFO_CNT(w_count); + w_count = w_count >> 2; + writesl(info->cs_base, p32, w_count); + p32 += w_count; + len -= w_count << 2; + } + + /* wait for data to flushed-out before reset the prefetch */ + start = get_time_ns(); + while (1) { + u32 regval, status; + regval = readl(info->gpmc_base + GPMC_PREFETCH_STATUS); + status = GPMC_PREFETCH_STATUS_COUNT(regval); + if (!status) + break; + if (is_timeout(start, 100 * MSECOND)) { + dev_err(mtd->dev, "prefetch flush timed out\n"); + break; + } + } + + /* disable and stop the PFPW engine */ + gpmc_prefetch_reset(info->gpmc_cs); +} + /* * read a page with the ecc layout used by the OMAP4 romcode. The * romcode expects an unusual ecc layout (f = free, e = ecc): @@ -803,6 +879,20 @@ static int omap_gpmc_eccmode_set(struct device_d *dev, struct param_d *param, co return omap_gpmc_eccmode(oinfo, i); } +static int gpmc_set_buswidth(struct mtd_info *mtd, struct nand_chip *chip, int buswidth) +{ + struct gpmc_nand_info *oinfo = chip->priv; + + if (buswidth == NAND_BUSWIDTH_16) + oinfo->pdata->nand_cfg->cfg[0] |= 0x00001000; + else + oinfo->pdata->nand_cfg->cfg[0] &= ~0x00001000; + + gpmc_cs_config(oinfo->pdata->cs, oinfo->pdata->nand_cfg); + + return 0; +} + /** * @brief nand device probe. * @@ -853,15 +943,23 @@ static int gpmc_nand_probe(struct device_d *pdev) oinfo->gpmc_command = (void *)(cs_base + GPMC_CS_NAND_COMMAND); oinfo->gpmc_address = (void *)(cs_base + GPMC_CS_NAND_ADDRESS); oinfo->gpmc_data = (void *)(cs_base + GPMC_CS_NAND_DATA); - oinfo->timeout = pdata->max_timeout; + oinfo->cs_base = (void *)pdata->nand_cfg->base; dev_dbg(pdev, "GPMC base=0x%p cmd=0x%p address=0x%p data=0x%p cs_base=0x%p\n", oinfo->gpmc_base, oinfo->gpmc_command, oinfo->gpmc_address, oinfo->gpmc_data, cs_base); - /* If we are 16 bit dev, our gpmc config tells us that */ - if ((readl(cs_base) & 0x3000) == 0x1000) { - dev_dbg(pdev, "16 bit dev\n"); + switch (pdata->device_width) { + case 0: + printk("probe buswidth\n"); + nand->options |= NAND_BUSWIDTH_AUTO; + break; + case 8: + break; + case 16: nand->options |= NAND_BUSWIDTH_16; + break; + default: + return -EINVAL; } /* Same data register for in and out */ @@ -879,11 +977,11 @@ static int gpmc_nand_probe(struct device_d *pdev) err = -EINVAL; goto out_release_mem; } + if (pdata->wait_mon_pin) { /* Set up the wait monitoring mask * This is GPMC_STATUS reg relevant */ oinfo->wait_mon_mask = (0x1 << (pdata->wait_mon_pin - 1)) << 8; - oinfo->wait_pol = (pdata->plat_options & NAND_WAITPOL_MASK); nand->dev_ready = omap_dev_ready; nand->chip_delay = 0; } else { @@ -901,6 +999,8 @@ static int gpmc_nand_probe(struct device_d *pdev) nand->options |= NAND_OWN_BUFFERS; nand->buffers = xzalloc(sizeof(*nand->buffers)); + nand->set_buswidth = gpmc_set_buswidth; + /* State my controller */ nand->controller = &oinfo->controller; @@ -928,18 +1028,12 @@ static int gpmc_nand_probe(struct device_d *pdev) goto out_release_mem; } - switch (pdata->device_width) { - case 8: - lsp = &ecc_sp_x8; - llp = &ecc_lp_x8; - break; - case 16: + if (nand->options & NAND_BUSWIDTH_16) { lsp = &ecc_sp_x16; llp = &ecc_lp_x16; - break; - default: - err = -EINVAL; - goto out_release_mem; + } else { + lsp = &ecc_sp_x8; + llp = &ecc_lp_x8; } switch (minfo->writesize) { @@ -954,6 +1048,9 @@ static int gpmc_nand_probe(struct device_d *pdev) goto out_release_mem; } + nand->read_buf = omap_read_buf_pref; + nand->write_buf = omap_write_buf_pref; + nand->options |= NAND_SKIP_BBTSCAN; dev_add_param(pdev, "eccmode", omap_gpmc_eccmode_set, NULL, 0); diff --git a/drivers/mtd/nand/nand_s3c24xx.c b/drivers/mtd/nand/nand_s3c24xx.c index c6297011a3..3d5732e02a 100644 --- a/drivers/mtd/nand/nand_s3c24xx.c +++ b/drivers/mtd/nand/nand_s3c24xx.c @@ -603,6 +603,16 @@ void __nand_boot_init s3c24x0_nand_load_image(void *dest, int size, int page) disable_nand_controller(host); } +#include <asm-generic/sections.h> + +void __nand_boot_init nand_boot(void) +{ + void *dest = _text; + int size = barebox_image_size; + int page = 0; + + s3c24x0_nand_load_image(dest, size, page); +} #ifdef CONFIG_NAND_S3C_BOOT_DEBUG #include <command.h> diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 340839d5d9..3c5f729db3 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -118,6 +118,11 @@ config DRIVER_NET_DESIGNWARE_ALTDESCRIPTOR depends on DRIVER_NET_DESIGNWARE default n +config DRIVER_NET_GIANFAR + bool "Gianfar Ethernet" + depends on ARCH_MPC85XX + select MIIDEV + source "drivers/net/usb/Kconfig" endmenu diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 29727b7d5b..4d960e856f 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -14,3 +14,4 @@ obj-$(CONFIG_NET_USB) += usb/ obj-$(CONFIG_DRIVER_NET_TSE) += altera_tse.o obj-$(CONFIG_DRIVER_NET_KS8851_MLL) += ks8851_mll.o obj-$(CONFIG_DRIVER_NET_DESIGNWARE) += designware.o +obj-$(CONFIG_DRIVER_NET_GIANFAR) += gianfar.o diff --git a/drivers/net/gianfar.c b/drivers/net/gianfar.c new file mode 100644 index 0000000000..19544de261 --- /dev/null +++ b/drivers/net/gianfar.c @@ -0,0 +1,548 @@ +/* + * Freescale Three Speed Ethernet Controller driver + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * Copyright 2012 GE Intelligent Platforms, Inc. + * Copyright 2004-2010 Freescale Semiconductor, Inc. + * (C) Copyright 2003, Motorola, Inc. + * based on work by Andy Fleming + */ + +#include <config.h> +#include <common.h> +#include <malloc.h> +#include <net.h> +#include <init.h> +#include <driver.h> +#include <miidev.h> +#include <command.h> +#include <errno.h> +#include <asm/io.h> +#include "gianfar.h" + +/* 2 seems to be the minimum number of TX descriptors to make it work. */ +#define TX_BUF_CNT 2 +#define RX_BUF_CNT PKTBUFSRX +#define BUF_ALIGN 8 + +/* + * Initialize required registers to appropriate values, zeroing + * those we don't care about (unless zero is bad, in which case, + * choose a more appropriate value) + */ +static void gfar_init_registers(void __iomem *regs) +{ + out_be32(regs + GFAR_IEVENT_OFFSET, GFAR_IEVENT_INIT_CLEAR); + + out_be32(regs + GFAR_IMASK_OFFSET, GFAR_IMASK_INIT_CLEAR); + + out_be32(regs + GFAR_IADDR(0), 0); + out_be32(regs + GFAR_IADDR(1), 0); + out_be32(regs + GFAR_IADDR(2), 0); + out_be32(regs + GFAR_IADDR(3), 0); + out_be32(regs + GFAR_IADDR(4), 0); + out_be32(regs + GFAR_IADDR(5), 0); + out_be32(regs + GFAR_IADDR(6), 0); + out_be32(regs + GFAR_IADDR(7), 0); + + out_be32(regs + GFAR_GADDR(0), 0); + out_be32(regs + GFAR_GADDR(1), 0); + out_be32(regs + GFAR_GADDR(2), 0); + out_be32(regs + GFAR_GADDR(3), 0); + out_be32(regs + GFAR_GADDR(4), 0); + out_be32(regs + GFAR_GADDR(5), 0); + out_be32(regs + GFAR_GADDR(6), 0); + out_be32(regs + GFAR_GADDR(7), 0); + + out_be32(regs + GFAR_RCTRL_OFFSET, 0x00000000); + + memset((void *)(regs + GFAR_TR64_OFFSET), 0, + GFAR_CAM2_OFFSET - GFAR_TR64_OFFSET); + + out_be32(regs + GFAR_CAM1_OFFSET, 0xffffffff); + out_be32(regs + GFAR_CAM2_OFFSET, 0xffffffff); + + out_be32(regs + GFAR_MRBLR_OFFSET, MRBLR_INIT_SETTINGS); + + out_be32(regs + GFAR_MINFLR_OFFSET, MINFLR_INIT_SETTINGS); + + out_be32(regs + GFAR_ATTR_OFFSET, ATTR_INIT_SETTINGS); + out_be32(regs + GFAR_ATTRELI_OFFSET, ATTRELI_INIT_SETTINGS); +} + +/* + * Configure maccfg2 based on negotiated speed and duplex + * reported by PHY handling code + */ +static void gfar_adjust_link(struct eth_device *edev) +{ + struct gfar_private *priv = edev->priv; + struct device_d *mdev = priv->miidev.parent; + void __iomem *regs = priv->regs; + u32 ecntrl, maccfg2; + uint32_t status; + + status = miidev_get_status(&priv->miidev); + + priv->link = status & MIIDEV_STATUS_IS_UP; + if (status & MIIDEV_STATUS_IS_FULL_DUPLEX) + priv->duplexity = 1; + else + priv->duplexity = 0; + + if (status & MIIDEV_STATUS_IS_1000MBIT) + priv->speed = 1000; + else if (status & MIIDEV_STATUS_IS_100MBIT) + priv->speed = 100; + else + priv->speed = 10; + + if (priv->link) { + /* clear all bits relative with interface mode */ + ecntrl = in_be32(regs + GFAR_ECNTRL_OFFSET); + ecntrl &= ~GFAR_ECNTRL_R100; + + maccfg2 = in_be32(regs + GFAR_MACCFG2_OFFSET); + maccfg2 &= ~(GFAR_MACCFG2_IF | GFAR_MACCFG2_FULL_DUPLEX); + + if (priv->duplexity != 0) + maccfg2 |= GFAR_MACCFG2_FULL_DUPLEX; + else + maccfg2 &= ~(GFAR_MACCFG2_FULL_DUPLEX); + + switch (priv->speed) { + case 1000: + maccfg2 |= GFAR_MACCFG2_GMII; + break; + case 100: + case 10: + maccfg2 |= GFAR_MACCFG2_MII; + /* + * Set R100 bit in all modes although + * it is only used in RGMII mode + */ + if (priv->speed == 100) + ecntrl |= GFAR_ECNTRL_R100; + break; + default: + dev_info(mdev, "Speed is unknown\n"); + break; + } + + out_be32(regs + GFAR_ECNTRL_OFFSET, ecntrl); + out_be32(regs + GFAR_MACCFG2_OFFSET, maccfg2); + + dev_info(mdev, "Speed: %d, %s duplex\n", priv->speed, + (priv->duplexity) ? "full" : "half"); + + } else { + dev_info(mdev, "No link.\n"); + } +} + +/* Stop the interface */ +static void gfar_halt(struct eth_device *edev) +{ + struct gfar_private *priv = edev->priv; + void __iomem *regs = priv->regs; + int value; + + clrbits_be32(regs + GFAR_DMACTRL_OFFSET, GFAR_DMACTRL_GRS | + GFAR_DMACTRL_GTS); + setbits_be32(regs + GFAR_DMACTRL_OFFSET, GFAR_DMACTRL_GRS | + GFAR_DMACTRL_GTS); + + value = in_be32(regs + GFAR_IEVENT_OFFSET); + value &= (GFAR_IEVENT_GRSC | GFAR_IEVENT_GTSC); + + while (value != (GFAR_IEVENT_GRSC | GFAR_IEVENT_GTSC)) { + value = in_be32(regs + GFAR_IEVENT_OFFSET); + value &= (GFAR_IEVENT_GRSC | GFAR_IEVENT_GTSC); + } + + clrbits_be32(regs + GFAR_MACCFG1_OFFSET, + GFAR_MACCFG1_TX_EN | GFAR_MACCFG1_RX_EN); +} + +/* Initializes registers for the controller. */ +static int gfar_init(struct eth_device *edev) +{ + struct gfar_private *priv = edev->priv; + void __iomem *regs = priv->regs; + + gfar_halt(edev); + + /* Init MACCFG2. Default to GMII */ + out_be32(regs + GFAR_MACCFG2_OFFSET, MACCFG2_INIT_SETTINGS); + out_be32(regs + GFAR_ECNTRL_OFFSET, ECNTRL_INIT_SETTINGS); + + priv->rxidx = 0; + priv->txidx = 0; + + gfar_init_registers(regs); + + miidev_restart_aneg(&priv->miidev); + + return 0; +} + +static int gfar_open(struct eth_device *edev) +{ + int ix; + struct gfar_private *priv = edev->priv; + void __iomem *regs = priv->regs; + + /* Point to the buffer descriptors */ + out_be32(regs + GFAR_TBASE0_OFFSET, (unsigned int)priv->txbd); + out_be32(regs + GFAR_RBASE0_OFFSET, (unsigned int)priv->rxbd); + + /* Initialize the Rx Buffer descriptors */ + for (ix = 0; ix < RX_BUF_CNT; ix++) { + priv->rxbd[ix].status = RXBD_EMPTY; + priv->rxbd[ix].length = 0; + priv->rxbd[ix].bufPtr = (uint) NetRxPackets[ix]; + } + priv->rxbd[RX_BUF_CNT - 1].status |= RXBD_WRAP; + + /* Initialize the TX Buffer Descriptors */ + for (ix = 0; ix < TX_BUF_CNT; ix++) { + priv->txbd[ix].status = 0; + priv->txbd[ix].length = 0; + priv->txbd[ix].bufPtr = 0; + } + priv->txbd[TX_BUF_CNT - 1].status |= TXBD_WRAP; + + miidev_wait_aneg(&priv->miidev); + gfar_adjust_link(edev); + + /* Enable Transmit and Receive */ + setbits_be32(regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_RX_EN | + GFAR_MACCFG1_TX_EN); + + /* Tell the DMA it is clear to go */ + setbits_be32(regs + GFAR_DMACTRL_OFFSET, DMACTRL_INIT_SETTINGS); + out_be32(regs + GFAR_TSTAT_OFFSET, GFAR_TSTAT_CLEAR_THALT); + out_be32(regs + GFAR_RSTAT_OFFSET, GFAR_RSTAT_CLEAR_RHALT); + clrbits_be32(regs + GFAR_DMACTRL_OFFSET, GFAR_DMACTRL_GRS | + GFAR_DMACTRL_GTS); + + return 0; +} + +static int gfar_get_ethaddr(struct eth_device *edev, unsigned char *mac) +{ + return -ENODEV; +} + +static int gfar_set_ethaddr(struct eth_device *edev, unsigned char *mac) +{ + struct gfar_private *priv = edev->priv; + void __iomem *regs = priv->regs; + char tmpbuf[MAC_ADDR_LEN]; + uint tempval; + int ix; + + for (ix = 0; ix < MAC_ADDR_LEN; ix++) + tmpbuf[MAC_ADDR_LEN - 1 - ix] = mac[ix]; + + tempval = (tmpbuf[0] << 24) | (tmpbuf[1] << 16) | (tmpbuf[2] << 8) | + tmpbuf[3]; + + out_be32(regs + GFAR_MACSTRADDR1_OFFSET, tempval); + + tempval = *((uint *)(tmpbuf + 4)); + + out_be32(regs + GFAR_MACSTRADDR2_OFFSET, tempval); + + return 0; +} + +/* Writes the given phy's reg with value, using the specified MDIO regs */ +static int gfar_local_mdio_write(void __iomem *phyregs, uint addr, uint reg, + uint value) +{ + uint64_t start; + + out_be32(phyregs + GFAR_MIIMADD_OFFSET, (addr << 8) | (reg & 0x1f)); + out_be32(phyregs + GFAR_MIIMCON_OFFSET, value); + + start = get_time_ns(); + while (!is_timeout(start, 10 * MSECOND)) { + if (!(in_be32(phyregs + GFAR_MIIMMIND_OFFSET) & + GFAR_MIIMIND_BUSY)) + return 0; + } + + return -EIO; +} + +/* + * Reads register regnum on the device's PHY through the + * specified registers. It lowers and raises the read + * command, and waits for the data to become valid (miimind + * notvalid bit cleared), and the bus to cease activity (miimind + * busy bit cleared), and then returns the value + */ +static uint gfar_local_mdio_read(void __iomem *phyregs, uint phyid, uint regnum) +{ + uint64_t start; + + /* Put the address of the phy, and the register number into MIIMADD */ + out_be32(phyregs + GFAR_MIIMADD_OFFSET, (phyid << 8) | (regnum & 0x1f)); + + /* Clear the command register, and wait */ + out_be32(phyregs + GFAR_MIIMCOM_OFFSET, 0); + + /* Initiate a read command, and wait */ + out_be32(phyregs + GFAR_MIIMCOM_OFFSET, GFAR_MIIM_READ_COMMAND); + + start = get_time_ns(); + while (!is_timeout(start, 10 * MSECOND)) { + if (!(in_be32(phyregs + GFAR_MIIMMIND_OFFSET) & + (GFAR_MIIMIND_NOTVALID | GFAR_MIIMIND_BUSY))) + return in_be32(phyregs + GFAR_MIIMSTAT_OFFSET); + } + + return -EIO; +} + +static void gfar_configure_serdes(struct gfar_private *priv) +{ + gfar_local_mdio_write(priv->phyregs_sgmii, + in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_ANA, + priv->tbiana); + gfar_local_mdio_write(priv->phyregs_sgmii, + in_be32(priv->regs + GFAR_TBIPA_OFFSET), + GFAR_TBI_TBICON, GFAR_TBICON_CLK_SELECT); + gfar_local_mdio_write(priv->phyregs_sgmii, + in_be32(priv->regs + GFAR_TBIPA_OFFSET), GFAR_TBI_CR, + priv->tbicr); +} + +/* Reset the internal and external PHYs. */ +static void gfar_init_phy(struct eth_device *dev) +{ + struct gfar_private *priv = dev->priv; + void __iomem *regs = priv->regs; + uint64_t start; + + /* Assign a Physical address to the TBI */ + out_be32(regs + GFAR_TBIPA_OFFSET, GFAR_TBIPA_VALUE); + + /* Reset MII (due to new addresses) */ + out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_RESET); + out_be32(priv->phyregs + GFAR_MIIMCFG_OFFSET, GFAR_MIIMCFG_INIT_VALUE); + + start = get_time_ns(); + while (!is_timeout(start, 10 * MSECOND)) { + if (!(in_be32(priv->phyregs + GFAR_MIIMMIND_OFFSET) & + GFAR_MIIMIND_BUSY)) + break; + } + + gfar_local_mdio_write(priv->phyregs, priv->phyaddr, GFAR_MIIM_CR, + GFAR_MIIM_CR_RST); + + start = get_time_ns(); + while (!is_timeout(start, 10 * MSECOND)) { + if (!(gfar_local_mdio_read(priv->phyregs, priv->phyaddr, + GFAR_MIIM_CR) & GFAR_MIIM_CR_RST)) + break; + } + + if (in_be32(regs + GFAR_ECNTRL_OFFSET) & GFAR_ECNTRL_SGMII_MODE) + gfar_configure_serdes(priv); +} + +static int gfar_send(struct eth_device *edev, void *packet, int length) +{ + struct gfar_private *priv = edev->priv; + void __iomem *regs = priv->regs; + struct device_d *dev = edev->parent; + uint64_t start; + uint tidx; + + tidx = priv->txidx; + priv->txbd[tidx].bufPtr = (uint) packet; + priv->txbd[tidx].length = length; + priv->txbd[tidx].status |= (TXBD_READY | TXBD_LAST | + TXBD_CRC | TXBD_INTERRUPT); + + /* Tell the DMA to go */ + out_be32(regs + GFAR_TSTAT_OFFSET, GFAR_TSTAT_CLEAR_THALT); + + /* Wait for buffer to be transmitted */ + start = get_time_ns(); + while (priv->txbd[tidx].status & TXBD_READY) { + if (is_timeout(start, 5 * MSECOND)) { + break; + } + } + + if (priv->txbd[tidx].status & TXBD_READY) { + dev_err(dev, "tx timeout: 0x%x\n", priv->txbd[tidx].status); + return -EBUSY; + } + else if (priv->txbd[tidx].status & TXBD_STATS) { + dev_err(dev, "TX error: 0x%x\n", priv->txbd[tidx].status); + return -EIO; + } + + priv->txidx = (priv->txidx + 1) % TX_BUF_CNT; + + return 0; +} + +static int gfar_recv(struct eth_device *edev) +{ + struct gfar_private *priv = edev->priv; + struct device_d *dev = edev->parent; + void __iomem *regs = priv->regs; + int length; + + if (priv->rxbd[priv->rxidx].status & RXBD_EMPTY) { + return 0; /* no data */ + } + + length = priv->rxbd[priv->rxidx].length; + + /* Send the packet up if there were no errors */ + if (!(priv->rxbd[priv->rxidx].status & RXBD_STATS)) { + net_receive(NetRxPackets[priv->rxidx], length - 4); + } else { + dev_err(dev, "Got error %x\n", + (priv->rxbd[priv->rxidx].status & RXBD_STATS)); + } + + priv->rxbd[priv->rxidx].length = 0; + + /* Set the wrap bit if this is the last element in the list */ + if ((priv->rxidx + 1) == RX_BUF_CNT) + priv->rxbd[priv->rxidx].status = RXBD_WRAP; + else + priv->rxbd[priv->rxidx].status = 0; + + priv->rxbd[priv->rxidx].status |= RXBD_EMPTY; + priv->rxidx = (priv->rxidx + 1) % RX_BUF_CNT; + + if (in_be32(regs + GFAR_IEVENT_OFFSET) & GFAR_IEVENT_BSY) { + out_be32(regs + GFAR_IEVENT_OFFSET, GFAR_IEVENT_BSY); + out_be32(regs + GFAR_RSTAT_OFFSET, GFAR_RSTAT_CLEAR_RHALT); + } + + return 0; +} + +/* Read a MII PHY register. */ +static int gfar_miiphy_read(struct mii_device *mdev, int addr, int reg) +{ + struct eth_device *edev = mdev->edev; + struct device_d *dev = edev->parent; + struct gfar_private *priv = edev->priv; + int ret; + + ret = gfar_local_mdio_read(priv->phyregs, addr, reg); + if (ret == -EIO) + dev_err(dev, "Can't read PHY at address %d\n", addr); + + return ret; +} + +/* Write a MII PHY register. */ +static int gfar_miiphy_write(struct mii_device *mdev, int addr, int reg, + int value) +{ + struct eth_device *edev = mdev->edev; + struct device_d *dev = edev->parent; + struct gfar_private *priv = edev->priv; + unsigned short val = value; + int ret; + + ret = gfar_local_mdio_write(priv->phyregs, addr, reg, val); + + if (ret) + dev_err(dev, "Can't write PHY at address %d\n", addr); + + return 0; +} + +/* + * Initialize device structure. Returns success if + * initialization succeeded. + */ +static int gfar_probe(struct device_d *dev) +{ + struct gfar_info_struct *gfar_info = dev->platform_data; + struct eth_device *edev; + struct gfar_private *priv; + size_t size; + char *p; + + priv = xzalloc(sizeof(struct gfar_private)); + + if (NULL == priv) + return -ENODEV; + + edev = &priv->edev; + + priv->regs = dev_request_mem_region(dev, 0); + priv->phyregs = dev_request_mem_region(dev, 1); + priv->phyregs_sgmii = dev_request_mem_region(dev, 2); + + priv->phyaddr = gfar_info->phyaddr; + priv->tbicr = gfar_info->tbicr; + priv->tbiana = gfar_info->tbiana; + + /* + * Allocate descriptors 64-bit aligned. Descriptors + * are 8 bytes in size. + */ + size = ((TX_BUF_CNT * sizeof(struct txbd8)) + + (RX_BUF_CNT * sizeof(struct rxbd8))) + BUF_ALIGN; + p = (char *)xmemalign(BUF_ALIGN, size); + priv->txbd = (struct txbd8 *)p; + priv->rxbd = (struct rxbd8 *)(p + (TX_BUF_CNT * sizeof(struct txbd8))); + + edev->priv = priv; + edev->init = gfar_init; + edev->open = gfar_open; + edev->halt = gfar_halt; + edev->send = gfar_send; + edev->recv = gfar_recv; + edev->get_ethaddr = gfar_get_ethaddr; + edev->set_ethaddr = gfar_set_ethaddr; + edev->parent = dev; + + setbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET); + udelay(2); + clrbits_be32(priv->regs + GFAR_MACCFG1_OFFSET, GFAR_MACCFG1_SOFT_RESET); + + priv->miidev.read = gfar_miiphy_read; + priv->miidev.write = gfar_miiphy_write; + priv->miidev.address = priv->phyaddr; + priv->miidev.flags = 0; + priv->miidev.edev = edev; + priv->miidev.parent = dev; + + gfar_init_phy(edev); + + mii_register(&priv->miidev); + + return eth_register(edev); +} + +static struct driver_d gfar_eth_driver = { + .name = "gfar", + .probe = gfar_probe, +}; + +static int gfar_eth_init(void) +{ + register_driver(&gfar_eth_driver); + return 0; +} + +device_initcall(gfar_eth_init); diff --git a/drivers/net/gianfar.h b/drivers/net/gianfar.h new file mode 100644 index 0000000000..a4ad99e3b7 --- /dev/null +++ b/drivers/net/gianfar.h @@ -0,0 +1,284 @@ +/* + * gianfar.h + * + * Driver for the Motorola Triple Speed Ethernet Controller + * + * This software may be used and distributed according to the + * terms of the GNU Public License, Version 2, incorporated + * herein by reference. + * + * Copyright 2012 GE Intelligent Platforms, Inc. + * Copyright 2004, 2007, 2009 Freescale Semiconductor, Inc. + * (C) Copyright 2003, Motorola, Inc. + * based on tsec.h by Xianghua Xiao and Andy Fleming 2003-2009 + */ + +#ifndef __GIANFAR_H +#define __GIANFAR_H + +#include <net.h> +#include <config.h> +#include <mach/gianfar.h> + +#define MAC_ADDR_LEN 6 + +/* TBI register addresses */ +#define GFAR_TBI_CR 0x00 +#define GFAR_TBI_SR 0x01 +#define GFAR_TBI_ANA 0x04 +#define GFAR_TBI_ANLPBPA 0x05 +#define GFAR_TBI_ANEX 0x06 +#define GFAR_TBI_TBICON 0x11 + +/* TBI MDIO register bit fields*/ +#define GFAR_TBICON_CLK_SELECT 0x0020 +#define GFAR_TBIANA_ASYMMETRIC_PAUSE 0x0100 +#define GFAR_TBIANA_SYMMETRIC_PAUSE 0x0080 +#define GFAR_TBIANA_HALF_DUPLEX 0x0040 +#define GFAR_TBIANA_FULL_DUPLEX 0x0020 +/* The two reserved bits below are used in AN3869 to enable SGMII. */ +#define GFAR_TBIANA_RESERVED1 0x4000 +#define GFAR_TBIANA_RESERVED15 0x0001 +#define GFAR_TBICR_PHY_RESET 0x8000 +#define GFAR_TBICR_ANEG_ENABLE 0x1000 +#define GFAR_TBICR_RESTART_ANEG 0x0200 +#define GFAR_TBICR_FULL_DUPLEX 0x0100 +#define GFAR_TBICR_SPEED1_SET 0x0040 + +/* MAC register bits */ +#define GFAR_MACCFG1_SOFT_RESET 0x80000000 +#define GFAR_MACCFG1_RESET_RX_MC 0x00080000 +#define GFAR_MACCFG1_RESET_TX_MC 0x00040000 +#define GFAR_MACCFG1_RESET_RX_FUN 0x00020000 +#define TESC_MACCFG1_RESET_TX_FUN 0x00010000 +#define GFAR_MACCFG1_LOOPBACK 0x00000100 +#define GFAR_MACCFG1_RX_FLOW 0x00000020 +#define GFAR_MACCFG1_TX_FLOW 0x00000010 +#define GFAR_MACCFG1_SYNCD_RX_EN 0x00000008 +#define GFAR_MACCFG1_RX_EN 0x00000004 +#define GFAR_MACCFG1_SYNCD_TX_EN 0x00000002 +#define GFAR_MACCFG1_TX_EN 0x00000001 + +#define MACCFG2_INIT_SETTINGS 0x00007205 +#define GFAR_MACCFG2_FULL_DUPLEX 0x00000001 +#define GFAR_MACCFG2_IF 0x00000300 +#define GFAR_MACCFG2_GMII 0x00000200 +#define GFAR_MACCFG2_MII 0x00000100 + +#define ECNTRL_INIT_SETTINGS 0x00001000 +#define GFAR_ECNTRL_TBI_MODE 0x00000020 +#define GFAR_ECNTRL_R100 0x00000008 +#define GFAR_ECNTRL_SGMII_MODE 0x00000002 + +#ifndef GFAR_TBIPA_VALUE + #define GFAR_TBIPA_VALUE 0x1f +#endif +#define GFAR_MIIMCFG_INIT_VALUE 0x00000003 +#define GFAR_MIIMCFG_RESET 0x80000000 + +#define GFAR_MIIMIND_BUSY 0x00000001 +#define GFAR_MIIMIND_NOTVALID 0x00000004 + +#define GFAR_MIIM_CONTROL 0x00000000 +#define GFAR_MIIM_CONTROL_RESET 0x00009140 +#define GFAR_MIIM_CONTROL_INIT 0x00001140 +#define GFAR_MIIM_CONTROL_RESTART 0x00001340 +#define GFAR_MIIM_ANEN 0x00001000 + +#define GFAR_MIIM_CR 0x00000000 +#define GFAR_MIIM_CR_RST 0x00008000 +#define GFAR_MIIM_CR_INIT 0x00001000 + +#define GFAR_MIIM_STATUS 0x1 +#define GFAR_MIIM_STATUS_AN_DONE 0x00000020 +#define GFAR_MIIM_STATUS_LINK 0x0004 + +#define GFAR_MIIM_PHYIR1 0x2 +#define GFAR_MIIM_PHYIR2 0x3 + +#define GFAR_MIIM_ANAR 0x4 +#define GFAR_MIIM_ANAR_INIT 0x1e1 + +#define GFAR_MIIM_TBI_ANLPBPA 0x5 +#define GFAR_MIIM_TBI_ANLPBPA_HALF 0x00000040 +#define GFAR_MIIM_TBI_ANLPBPA_FULL 0x00000020 + +#define GFAR_MIIM_TBI_ANEX 0x6 +#define GFAR_MIIM_TBI_ANEX_NP 0x00000004 +#define GFAR_MIIM_TBI_ANEX_PRX 0x00000002 + +#define GFAR_MIIM_GBIT_CONTROL 0x9 +#define GFAR_MIIM_GBIT_CONTROL_INIT 0xe00 + +#define GFAR_MIIM_EXT_PAGE_ACCESS 0x1f + +#define GFAR_MIIM_GBIT_CON 0x09 +#define GFAR_MIIM_GBIT_CON_ADVERT 0x0e00 + +#define GFAR_MIIM_READ_COMMAND 0x00000001 + +#define MRBLR_INIT_SETTINGS 1536 + +#define MINFLR_INIT_SETTINGS 0x00000040 + +#define DMACTRL_INIT_SETTINGS 0x000000c3 +#define GFAR_DMACTRL_GRS 0x00000010 +#define GFAR_DMACTRL_GTS 0x00000008 + +#define GFAR_TSTAT_CLEAR_THALT 0x80000000 +#define GFAR_RSTAT_CLEAR_RHALT 0x00800000 + +#define GFAR_IEVENT_INIT_CLEAR 0xffffffff +#define GFAR_IEVENT_BABR 0x80000000 +#define GFAR_IEVENT_RXC 0x40000000 +#define GFAR_IEVENT_BSY 0x20000000 +#define GFAR_IEVENT_EBERR 0x10000000 +#define GFAR_IEVENT_MSRO 0x04000000 +#define GFAR_IEVENT_GTSC 0x02000000 +#define GFAR_IEVENT_BABT 0x01000000 +#define GFAR_IEVENT_TXC 0x00800000 +#define GFAR_IEVENT_TXE 0x00400000 +#define GFAR_IEVENT_TXB 0x00200000 +#define GFAR_IEVENT_TXF 0x00100000 +#define GFAR_IEVENT_IE 0x00080000 +#define GFAR_IEVENT_LC 0x00040000 +#define GFAR_IEVENT_CRL 0x00020000 +#define GFAR_IEVENT_XFUN 0x00010000 +#define GFAR_IEVENT_RXB0 0x00008000 +#define GFAR_IEVENT_GRSC 0x00000100 +#define GFAR_IEVENT_RXF0 0x00000080 + +#define GFAR_IMASK_INIT_CLEAR 0x00000000 + +/* Default Attribute fields */ +#define ATTR_INIT_SETTINGS 0x000000c0 +#define ATTRELI_INIT_SETTINGS 0x00000000 + +/* TxBD status field bits */ +#define TXBD_READY 0x8000 +#define TXBD_PADCRC 0x4000 +#define TXBD_WRAP 0x2000 +#define TXBD_INTERRUPT 0x1000 +#define TXBD_LAST 0x0800 +#define TXBD_CRC 0x0400 +#define TXBD_DEF 0x0200 +#define TXBD_HUGEFRAME 0x0080 +#define TXBD_LATECOLLISION 0x0080 +#define TXBD_RETRYLIMIT 0x0040 +#define TXBD_RETRYCOUNTMASK 0x003c +#define TXBD_UNDERRUN 0x0002 +#define TXBD_STATS 0x03ff + +/* RxBD status field bits */ +#define RXBD_EMPTY 0x8000 +#define RXBD_RO1 0x4000 +#define RXBD_WRAP 0x2000 +#define RXBD_INTERRUPT 0x1000 +#define RXBD_LAST 0x0800 +#define RXBD_FIRST 0x0400 +#define RXBD_MISS 0x0100 +#define RXBD_BROADCAST 0x0080 +#define RXBD_MULTICAST 0x0040 +#define RXBD_LARGE 0x0020 +#define RXBD_NONOCTET 0x0010 +#define RXBD_SHORT 0x0008 +#define RXBD_CRCERR 0x0004 +#define RXBD_OVERRUN 0x0002 +#define RXBD_TRUNCATED 0x0001 +#define RXBD_STATS 0x003f + +struct txbd8 { + ushort status; /* Status Fields */ + ushort length; /* Buffer length */ + uint bufPtr; /* Buffer Pointer */ +}; + +struct rxbd8 { + ushort status; /* Status Fields */ + ushort length; /* Buffer Length */ + uint bufPtr; /* Buffer Pointer */ +}; + +/* eTSEC general control and status registers */ +#define GFAR_IEVENT_OFFSET 0x010 /* Interrupt Event */ +#define GFAR_IMASK_OFFSET 0x014 /* Interrupt Mask */ +#define GFAR_ECNTRL_OFFSET 0x020 /* Ethernet Control */ +#define GFAR_MINFLR_OFFSET 0x024 /* Minimum Frame Length */ +#define GFAR_DMACTRL_OFFSET 0x02c /* DMA Control */ +#define GFAR_TBIPA_OFFSET 0x030 /* TBI PHY address */ + +/* eTSEC transmit control and status register */ +#define GFAR_TSTAT_OFFSET 0x104 /* transmit status register */ +#define GFAR_TBASE0_OFFSET 0x204 /* TxBD Base Address */ + +/* eTSEC receive control and status register */ +#define GFAR_RCTRL_OFFSET 0x300 /* Receive Control */ +#define GFAR_RSTAT_OFFSET 0x304 /* transmit status register */ +#define GFAR_MRBLR_OFFSET 0x340 /* Maximum Receive Buffer Length */ +#define GFAR_RBASE0_OFFSET 0x404 /* RxBD Base Address */ + +/* eTSEC MAC registers */ +#define GFAR_MACCFG1_OFFSET 0x500 /* MAC Configuration #1 */ +#define GFAR_MACCFG2_OFFSET 0x504 /* MAC Configuration #2 */ +#define GFAR_MIIMCFG_OFFSET 0x520 /* MII management configuration */ +#define GFAR_MIIMCOM_OFFSET 0x524 /* MII management command */ +#define GFAR_MIIMADD_OFFSET 0x528 /* MII management address */ +#define GFAR_MIIMCON_OFFSET 0x52c /* MII management control */ +#define GFAR_MIIMSTAT_OFFSET 0x530 /* MII management status */ +#define GFAR_MIIMMIND_OFFSET 0x534 /* MII management indicator */ +#define GFAR_MACSTRADDR1_OFFSET 0x540 /* MAC station address #1 */ +#define GFAR_MACSTRADDR2_OFFSET 0x544 /* MAC station address #2 */ + +/* eTSEC transmit and receive counters registers. */ +#define GFAR_TR64_OFFSET 0x680 +/* eTSEC counter control and TOE statistics registers */ +#define GFAR_CAM1_OFFSET 0x738 +#define GFAR_CAM2_OFFSET 0x73c + +/* Individual/group address registers */ +#define GFAR_IADDR0_OFFSET 0x800 +#define GFAR_IADDR1_OFFSET 0x804 +#define GFAR_IADDR2_OFFSET 0x808 +#define GFAR_IADDR3_OFFSET 0x80c +#define GFAR_IADDR4_OFFSET 0x810 +#define GFAR_IADDR5_OFFSET 0x814 +#define GFAR_IADDR6_OFFSET 0x818 +#define GFAR_IADDR7_OFFSET 0x81c + +#define GFAR_IADDR(REGNUM) (GFAR_IADDR##REGNUM##_OFFSET) + +/* Group address registers */ +#define GFAR_GADDR0_OFFSET 0x880 +#define GFAR_GADDR1_OFFSET 0x884 +#define GFAR_GADDR2_OFFSET 0x888 +#define GFAR_GADDR3_OFFSET 0x88c +#define GFAR_GADDR4_OFFSET 0x890 +#define GFAR_GADDR5_OFFSET 0x894 +#define GFAR_GADDR6_OFFSET 0x898 +#define GFAR_GADDR7_OFFSET 0x89c + +#define GFAR_GADDR(REGNUM) (GFAR_GADDR##REGNUM##_OFFSET) + +/* eTSEC DMA attributes registers */ +#define GFAR_ATTR_OFFSET 0xbf8 /* Default Attribute Register */ +#define GFAR_ATTRELI_OFFSET 0xbfc /* Default Attribute Extract Len/Idx */ + +struct gfar_private { + struct eth_device edev; + void __iomem *regs; + void __iomem *phyregs; + void __iomem *phyregs_sgmii; + struct phy_info *phyinfo; + struct mii_device miidev; + volatile struct txbd8 *txbd; + volatile struct rxbd8 *rxbd; + uint txidx; + uint rxidx; + uint phyaddr; + uint tbicr; + uint tbiana; + uint link; + uint duplexity; + uint speed; +}; +#endif /* __GIANFAR_H */ diff --git a/drivers/net/miidev.c b/drivers/net/miidev.c index b49944bbfe..75b53e3c5c 100644 --- a/drivers/net/miidev.c +++ b/drivers/net/miidev.c @@ -116,7 +116,7 @@ int miidev_wait_aneg(struct mii_device *mdev) return -ETIMEDOUT; } - } while (!(status & BMSR_LSTATUS)); + } while (!(status & BMSR_ANEGCOMPLETE)); return 0; } diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index a249b81f51..b5c55a4afc 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -16,7 +16,7 @@ config DRIVER_SPI_IMX_0_0 config DRIVER_SPI_IMX_0_7 bool - depends on ARCH_IMX25 + depends on ARCH_IMX25 || ARCH_IMX35 default y config DRIVER_SPI_IMX_2_3 diff --git a/drivers/spi/imx_spi.c b/drivers/spi/imx_spi.c index 2e59b3c8dd..80a14504f0 100644 --- a/drivers/spi/imx_spi.c +++ b/drivers/spi/imx_spi.c @@ -517,7 +517,7 @@ static int imx_spi_probe(struct device_d *dev) version = SPI_IMX_VER_0_0; #endif #ifdef CONFIG_DRIVER_SPI_IMX_0_7 - if (cpu_is_mx25()) + if (cpu_is_mx25() || cpu_is_mx35()) version = SPI_IMX_VER_0_7; #endif #ifdef CONFIG_DRIVER_SPI_IMX_2_3 |