diff options
author | Albert ARIBAUD <albert.u.boot@aribaud.net> | 2013-05-30 14:45:06 +0200 |
---|---|---|
committer | Albert ARIBAUD <albert.u.boot@aribaud.net> | 2013-05-30 14:45:06 +0200 |
commit | a19b0dd62d7b8efc658fa1aa685ff5665878f3ee (patch) | |
tree | 1fadf0fb3da83203ba28f209ec99e1b33e03f4d5 /drivers | |
parent | 60985bba58e7695dac1fddae8cdbb62d8cfd1254 (diff) | |
parent | a71d45d706a5b51c348160163b6c159632273fed (diff) | |
download | u-boot-imx-a19b0dd62d7b8efc658fa1aa685ff5665878f3ee.zip u-boot-imx-a19b0dd62d7b8efc658fa1aa685ff5665878f3ee.tar.gz u-boot-imx-a19b0dd62d7b8efc658fa1aa685ff5665878f3ee.tar.bz2 |
Merge branch 'u-boot/master' into 'u-boot-arm/master'
Conflicts:
common/cmd_fpga.c
drivers/usb/host/ohci-at91.c
Diffstat (limited to 'drivers')
34 files changed, 1976 insertions, 1030 deletions
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 9df1e26..f77c1ec 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -47,6 +47,8 @@ COBJS-$(CONFIG_OMAP_GPIO) += omap_gpio.o COBJS-$(CONFIG_DB8500_GPIO) += db8500_gpio.o COBJS-$(CONFIG_BCM2835_GPIO) += bcm2835_gpio.o COBJS-$(CONFIG_S3C2440_GPIO) += s3c2440_gpio.o +COBJS-$(CONFIG_XILINX_GPIO) += xilinx_gpio.o +COBJS-$(CONFIG_ADI_GPIO2) += adi_gpio2.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/gpio/adi_gpio2.c b/drivers/gpio/adi_gpio2.c new file mode 100644 index 0000000..7a034eb --- /dev/null +++ b/drivers/gpio/adi_gpio2.c @@ -0,0 +1,440 @@ +/* + * ADI GPIO2 Abstraction Layer + * Support BF54x, BF60x and future processors. + * + * Copyright 2008-2013 Analog Devices Inc. + * + * Licensed under the GPL-2 or later + */ + +#include <common.h> +#include <asm/errno.h> +#include <asm/gpio.h> +#include <asm/portmux.h> + +static struct gpio_port_t * const gpio_array[] = { + (struct gpio_port_t *)PORTA_FER, + (struct gpio_port_t *)PORTB_FER, + (struct gpio_port_t *)PORTC_FER, + (struct gpio_port_t *)PORTD_FER, + (struct gpio_port_t *)PORTE_FER, + (struct gpio_port_t *)PORTF_FER, + (struct gpio_port_t *)PORTG_FER, +#if defined(CONFIG_BF54x) + (struct gpio_port_t *)PORTH_FER, + (struct gpio_port_t *)PORTI_FER, + (struct gpio_port_t *)PORTJ_FER, +#endif +}; + +#define RESOURCE_LABEL_SIZE 16 + +static struct str_ident { + char name[RESOURCE_LABEL_SIZE]; +} str_ident[MAX_RESOURCES]; + +static void gpio_error(unsigned gpio) +{ + printf("adi_gpio2: GPIO %d wasn't requested!\n", gpio); +} + +static void set_label(unsigned short ident, const char *label) +{ + if (label) { + strncpy(str_ident[ident].name, label, + RESOURCE_LABEL_SIZE); + str_ident[ident].name[RESOURCE_LABEL_SIZE - 1] = 0; + } +} + +static char *get_label(unsigned short ident) +{ + return *str_ident[ident].name ? str_ident[ident].name : "UNKNOWN"; +} + +static int cmp_label(unsigned short ident, const char *label) +{ + if (label == NULL) + printf("adi_gpio2: please provide none-null label\n"); + + if (label) + return strcmp(str_ident[ident].name, label); + else + return -EINVAL; +} + +#define map_entry(m, i) reserved_##m##_map[gpio_bank(i)] +#define is_reserved(m, i, e) (map_entry(m, i) & gpio_bit(i)) +#define reserve(m, i) (map_entry(m, i) |= gpio_bit(i)) +#define unreserve(m, i) (map_entry(m, i) &= ~gpio_bit(i)) +#define DECLARE_RESERVED_MAP(m, c) unsigned short reserved_##m##_map[c] + +static DECLARE_RESERVED_MAP(gpio, GPIO_BANK_NUM); +static DECLARE_RESERVED_MAP(peri, gpio_bank(MAX_RESOURCES)); + +inline int check_gpio(unsigned gpio) +{ +#if defined(CONFIG_BF54x) + if (gpio == GPIO_PB15 || gpio == GPIO_PC14 || gpio == GPIO_PC15 || + gpio == GPIO_PH14 || gpio == GPIO_PH15 || + gpio == GPIO_PJ14 || gpio == GPIO_PJ15) + return -EINVAL; +#endif + if (gpio >= MAX_GPIOS) + return -EINVAL; + return 0; +} + +static void port_setup(unsigned gpio, unsigned short usage) +{ +#if defined(CONFIG_BF54x) + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer &= ~gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer |= gpio_bit(gpio); +#else + if (usage == GPIO_USAGE) + gpio_array[gpio_bank(gpio)]->port_fer_clear = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->port_fer_set = gpio_bit(gpio); +#endif + SSYNC(); +} + +inline void portmux_setup(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + u16 function = P_FUNCT2MUX(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + pmux &= ~(0x3 << (2 * gpio_sub_n(ident))); + pmux |= (function & 0x3) << (2 * gpio_sub_n(ident)); + + gpio_array[gpio_bank(ident)]->port_mux = pmux; +} + +inline u16 get_portmux(unsigned short per) +{ + u32 pmux; + u16 ident = P_IDENT(per); + + pmux = gpio_array[gpio_bank(ident)]->port_mux; + + return pmux >> (2 * gpio_sub_n(ident)) & 0x3; +} + +unsigned short get_gpio_dir(unsigned gpio) +{ + return 0x01 & + (gpio_array[gpio_bank(gpio)]->dir_clear >> gpio_sub_n(gpio)); +} + +/*********************************************************** +* +* FUNCTIONS: Peripheral Resource Allocation +* and PortMux Setup +* +* INPUTS/OUTPUTS: +* per Peripheral Identifier +* label String +* +* DESCRIPTION: Peripheral Resource Allocation and Setup API +**************************************************************/ + +int peripheral_request(unsigned short per, const char *label) +{ + unsigned short ident = P_IDENT(per); + + /* + * Don't cares are pins with only one dedicated function + */ + + if (per & P_DONTCARE) + return 0; + + if (!(per & P_DEFINED)) + return -ENODEV; + + BUG_ON(ident >= MAX_RESOURCES); + + /* If a pin can be muxed as either GPIO or peripheral, make + * sure it is not already a GPIO pin when we request it. + */ + if (unlikely(!check_gpio(ident) && is_reserved(gpio, ident, 1))) { + printf("%s: Peripheral %d is already reserved as GPIO by %s!\n", + __func__, ident, get_label(ident)); + return -EBUSY; + } + + if (unlikely(is_reserved(peri, ident, 1))) { + /* + * Pin functions like AMC address strobes my + * be requested and used by several drivers + */ + + if (!((per & P_MAYSHARE) && + get_portmux(per) == P_FUNCT2MUX(per))) { + /* + * Allow that the identical pin function can + * be requested from the same driver twice + */ + + if (cmp_label(ident, label) == 0) + goto anyway; + + printf("%s: Peripheral %d function %d is already " + "reserved by %s!\n", __func__, ident, + P_FUNCT2MUX(per), get_label(ident)); + return -EBUSY; + } + } + + anyway: + reserve(peri, ident); + + portmux_setup(per); + port_setup(ident, PERIPHERAL_USAGE); + + set_label(ident, label); + + return 0; +} + +int peripheral_request_list(const unsigned short per[], const char *label) +{ + u16 cnt; + int ret; + + for (cnt = 0; per[cnt] != 0; cnt++) { + ret = peripheral_request(per[cnt], label); + + if (ret < 0) { + for (; cnt > 0; cnt--) + peripheral_free(per[cnt - 1]); + + return ret; + } + } + + return 0; +} + +void peripheral_free(unsigned short per) +{ + unsigned short ident = P_IDENT(per); + + if (per & P_DONTCARE) + return; + + if (!(per & P_DEFINED)) + return; + + if (unlikely(!is_reserved(peri, ident, 0))) + return; + + if (!(per & P_MAYSHARE)) + port_setup(ident, GPIO_USAGE); + + unreserve(peri, ident); + + set_label(ident, "free"); +} + +void peripheral_free_list(const unsigned short per[]) +{ + u16 cnt; + for (cnt = 0; per[cnt] != 0; cnt++) + peripheral_free(per[cnt]); +} + +/*********************************************************** +* +* FUNCTIONS: GPIO Driver +* +* INPUTS/OUTPUTS: +* gpio PIO Number between 0 and MAX_GPIOS +* label String +* +* DESCRIPTION: GPIO Driver API +**************************************************************/ + +int gpio_request(unsigned gpio, const char *label) +{ + if (check_gpio(gpio) < 0) + return -EINVAL; + + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + return -EBUSY; + } + + reserve(gpio, gpio); + set_label(gpio, label); + + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (check_gpio(gpio) < 0) + return -1; + + if (unlikely(!is_reserved(gpio, gpio, 0))) { + gpio_error(gpio); + return -1; + } + + unreserve(gpio, gpio); + + set_label(gpio, "free"); + + return 0; +} + +#ifdef ADI_SPECIAL_GPIO_BANKS +static DECLARE_RESERVED_MAP(special_gpio, gpio_bank(MAX_RESOURCES)); + +int special_gpio_request(unsigned gpio, const char *label) +{ + /* + * Allow that the identical GPIO can + * be requested from the same driver twice + * Do nothing and return - + */ + + if (cmp_label(gpio, label) == 0) + return 0; + + if (unlikely(is_reserved(special_gpio, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved by %s!\n", + gpio, get_label(gpio)); + return -EBUSY; + } + if (unlikely(is_reserved(peri, gpio, 1))) { + printf("adi_gpio2: GPIO %d is already reserved as Peripheral " + "by %s!\n", gpio, get_label(gpio)); + + return -EBUSY; + } + + reserve(special_gpio, gpio); + reserve(peri, gpio); + + set_label(gpio, label); + port_setup(gpio, GPIO_USAGE); + + return 0; +} + +void special_gpio_free(unsigned gpio) +{ + if (unlikely(!is_reserved(special_gpio, gpio, 0))) { + gpio_error(gpio); + return; + } + + reserve(special_gpio, gpio); + reserve(peri, gpio); + set_label(gpio, "free"); +} +#endif + +static inline void __gpio_direction_input(unsigned gpio) +{ + gpio_array[gpio_bank(gpio)]->dir_clear = gpio_bit(gpio); +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen |= gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_set = gpio_bit(gpio); +#endif +} + +int gpio_direction_input(unsigned gpio) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + __gpio_direction_input(gpio); + local_irq_restore(flags); + + return 0; +} + +int gpio_set_value(unsigned gpio, int arg) +{ + if (arg) + gpio_array[gpio_bank(gpio)]->data_set = gpio_bit(gpio); + else + gpio_array[gpio_bank(gpio)]->data_clear = gpio_bit(gpio); + + return 0; +} + +int gpio_direction_output(unsigned gpio, int value) +{ + unsigned long flags; + + if (!is_reserved(gpio, gpio, 0)) { + gpio_error(gpio); + return -EINVAL; + } + + local_irq_save(flags); + +#if defined(CONFIG_BF54x) + gpio_array[gpio_bank(gpio)]->inen &= ~gpio_bit(gpio); +#else + gpio_array[gpio_bank(gpio)]->inen_clear = gpio_bit(gpio); +#endif + gpio_set_value(gpio, value); + gpio_array[gpio_bank(gpio)]->dir_set = gpio_bit(gpio); + + local_irq_restore(flags); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + return 1 & (gpio_array[gpio_bank(gpio)]->data >> gpio_sub_n(gpio)); +} + +void gpio_labels(void) +{ + int c, gpio; + + for (c = 0; c < MAX_RESOURCES; c++) { + gpio = is_reserved(gpio, c, 1); + if (!check_gpio(c) && gpio) + printf("GPIO_%d:\t%s\tGPIO %s\n", c, get_label(c), + get_gpio_dir(c) ? "OUTPUT" : "INPUT"); + else if (is_reserved(peri, c, 1)) + printf("GPIO_%d:\t%s\tPeripheral\n", c, get_label(c)); + else + continue; + } +} diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c new file mode 100644 index 0000000..37fb0c5 --- /dev/null +++ b/drivers/gpio/xilinx_gpio.c @@ -0,0 +1,364 @@ +/* + * Copyright (c) 2013 Xilinx, Michal Simek + * + * See file CREDITS for list of people who contributed to this + * project. + * + * 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., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <errno.h> +#include <malloc.h> +#include <linux/list.h> +#include <asm/io.h> +#include <asm/gpio.h> + +static LIST_HEAD(gpio_list); + +enum gpio_direction { + GPIO_DIRECTION_OUT = 0, + GPIO_DIRECTION_IN = 1, +}; + +/* Gpio simple map */ +struct gpio_regs { + u32 gpiodata; + u32 gpiodir; +}; + +#define GPIO_NAME_SIZE 10 + +struct gpio_names { + char name[GPIO_NAME_SIZE]; +}; + +/* Initialized, rxbd_current, rx_first_buf must be 0 after init */ +struct xilinx_gpio_priv { + struct gpio_regs *regs; + u32 gpio_min; + u32 gpio_max; + u32 gpiodata_store; + char name[GPIO_NAME_SIZE]; + struct list_head list; + struct gpio_names *gpio_name; +}; + +/* Store number of allocated gpio pins */ +static u32 xilinx_gpio_max; + +/* Get associated gpio controller */ +static struct xilinx_gpio_priv *gpio_get_controller(unsigned gpio) +{ + struct list_head *entry; + struct xilinx_gpio_priv *priv = NULL; + + list_for_each(entry, &gpio_list) { + priv = list_entry(entry, struct xilinx_gpio_priv, list); + if (gpio >= priv->gpio_min && gpio <= priv->gpio_max) { + debug("%s: reg: %x, min-max: %d-%d\n", __func__, + (u32)priv->regs, priv->gpio_min, priv->gpio_max); + return priv; + } + } + puts("!!!Can't get gpio controller!!!\n"); + return NULL; +} + +/* Get gpio pin name if used/setup */ +static char *get_name(unsigned gpio) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + debug("%s\n", __func__); + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + + return *priv->gpio_name[gpio_priv].name ? + priv->gpio_name[gpio_priv].name : "UNKNOWN"; + } + return "UNKNOWN"; +} + +/* Get output value */ +static int gpio_get_output_value(unsigned gpio) +{ + u32 val, gpio_priv; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + gpio_priv = gpio - priv->gpio_min; + val = !!(priv->gpiodata_store & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + return -1; +} + +/* Get input value */ +static int gpio_get_input_value(unsigned gpio) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = readl(®s->gpiodata); + val = !!(val & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + return -1; +} + +/* Set gpio direction */ +static int gpio_set_direction(unsigned gpio, enum gpio_direction direction) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + val = readl(®s->gpiodir); + + gpio_priv = gpio - priv->gpio_min; + if (direction == GPIO_DIRECTION_OUT) + val &= ~(1 << gpio_priv); + else + val |= 1 << gpio_priv; + + writel(val, ®s->gpiodir); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return 0; + } + + return -1; +} + +/* Get gpio direction */ +static int gpio_get_direction(unsigned gpio) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = readl(®s->gpiodir); + val = !!(val & (1 << gpio_priv)); + debug("%s: reg: %x, gpio_no: %d, dir: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + + return val; + } + + return -1; +} + +/* + * Get input value + * for example gpio setup to output only can't get input value + * which is breaking gpio toggle command + */ +int gpio_get_value(unsigned gpio) +{ + u32 val; + + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + val = gpio_get_output_value(gpio); + else + val = gpio_get_input_value(gpio); + + return val; +} + +/* Set output value */ +static int gpio_set_output_value(unsigned gpio, int value) +{ + u32 val, gpio_priv; + struct gpio_regs *regs; + struct xilinx_gpio_priv *priv = gpio_get_controller(gpio); + + if (priv) { + regs = priv->regs; + gpio_priv = gpio - priv->gpio_min; + val = priv->gpiodata_store; + if (value) + val |= 1 << gpio_priv; + else + val &= ~(1 << gpio_priv); + + writel(val, ®s->gpiodata); + debug("%s: reg: %x, gpio_no: %d, output_val: %d\n", __func__, + (u32)priv->regs, gpio_priv, val); + priv->gpiodata_store = val; + + return 0; + } + + return -1; +} + +int gpio_set_value(unsigned gpio, int value) +{ + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + return gpio_set_output_value(gpio, value); + + return -1; +} + +/* Set GPIO as input */ +int gpio_direction_input(unsigned gpio) +{ + debug("%s\n", __func__); + return gpio_set_direction(gpio, GPIO_DIRECTION_IN); +} + +/* Setup GPIO as output and set output value */ +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_set_direction(gpio, GPIO_DIRECTION_OUT); + + debug("%s\n", __func__); + + if (ret < 0) + return ret; + + return gpio_set_output_value(gpio, value); +} + +/* Show gpio status */ +void gpio_info(void) +{ + unsigned gpio; + + struct list_head *entry; + struct xilinx_gpio_priv *priv = NULL; + + list_for_each(entry, &gpio_list) { + priv = list_entry(entry, struct xilinx_gpio_priv, list); + printf("\n%s: %s/%x (%d-%d)\n", __func__, priv->name, + (u32)priv->regs, priv->gpio_min, priv->gpio_max); + + for (gpio = priv->gpio_min; gpio <= priv->gpio_max; gpio++) { + printf("GPIO_%d:\t%s is an ", gpio, get_name(gpio)); + if (gpio_get_direction(gpio) == GPIO_DIRECTION_OUT) + printf("OUTPUT value = %d\n", + gpio_get_output_value(gpio)); + else + printf("INPUT value = %d\n", + gpio_get_input_value(gpio)); + } + } +} + +int gpio_request(unsigned gpio, const char *label) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + if (gpio >= xilinx_gpio_max) + return -EINVAL; + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + + if (label != NULL) { + strncpy(priv->gpio_name[gpio_priv].name, label, + GPIO_NAME_SIZE); + priv->gpio_name[gpio_priv].name[GPIO_NAME_SIZE - 1] = + '\0'; + } + return 0; + } + + return -1; +} + +int gpio_free(unsigned gpio) +{ + u32 gpio_priv; + struct xilinx_gpio_priv *priv; + + if (gpio >= xilinx_gpio_max) + return -EINVAL; + + priv = gpio_get_controller(gpio); + if (priv) { + gpio_priv = gpio - priv->gpio_min; + priv->gpio_name[gpio_priv].name[0] = '\0'; + + /* Do nothing here */ + return 0; + } + + return -1; +} + +int gpio_alloc(u32 baseaddr, const char *name, u32 gpio_no) +{ + struct xilinx_gpio_priv *priv; + + priv = calloc(1, sizeof(struct xilinx_gpio_priv)); + + /* Setup gpio name */ + if (name != NULL) { + strncpy(priv->name, name, GPIO_NAME_SIZE); + priv->name[GPIO_NAME_SIZE - 1] = '\0'; + } + priv->regs = (struct gpio_regs *)baseaddr; + + priv->gpio_min = xilinx_gpio_max; + xilinx_gpio_max = priv->gpio_min + gpio_no; + priv->gpio_max = xilinx_gpio_max - 1; + + priv->gpio_name = calloc(gpio_no, sizeof(struct gpio_names)); + + INIT_LIST_HEAD(&priv->list); + list_add_tail(&priv->list, &gpio_list); + + printf("%s: Add %s (%d-%d)\n", __func__, name, + priv->gpio_min, priv->gpio_max); + + /* Return the first gpio allocated for this device */ + return priv->gpio_min; +} + +/* Dual channel gpio is one IP with two independent channels */ +int gpio_alloc_dual(u32 baseaddr, const char *name, u32 gpio_no0, u32 gpio_no1) +{ + int ret; + + ret = gpio_alloc(baseaddr, name, gpio_no0); + gpio_alloc(baseaddr + 8, strcat((char *)name, "_1"), gpio_no1); + + /* Return the first gpio allocated for this device */ + return ret; +} diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index 7cd4281..24648a2 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -25,14 +25,11 @@ include $(TOPDIR)/config.mk LIB := $(obj)libmmc.o -ifdef CONFIG_SPL_BUILD -COBJS-$(CONFIG_SPL_MMC_SUPPORT) += spl_mmc.o -endif COBJS-$(CONFIG_BFIN_SDH) += bfin_sdh.o COBJS-$(CONFIG_DAVINCI_MMC) += davinci_mmc.o COBJS-$(CONFIG_FSL_ESDHC) += fsl_esdhc.o -COBJS-$(CONFIG_FTSDC010) += ftsdc010_esdhc.o +COBJS-$(CONFIG_FTSDC010) += ftsdc010_mci.o COBJS-$(CONFIG_GENERIC_MMC) += mmc.o COBJS-$(CONFIG_GENERIC_ATMEL_MCI) += gen_atmel_mci.o COBJS-$(CONFIG_MMC_SPI) += mmc_spi.o @@ -46,6 +43,7 @@ COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_BCM2835_SDHCI) += bcm2835_sdhci.o COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o +COBJS-$(CONFIG_SPEAR_SDHCI) += spear_sdhci.o COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o COBJS-$(CONFIG_DWMMC) += dw_mmc.o COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o diff --git a/drivers/mmc/davinci_mmc.c b/drivers/mmc/davinci_mmc.c index e2379e3..5aa2184 100644 --- a/drivers/mmc/davinci_mmc.c +++ b/drivers/mmc/davinci_mmc.c @@ -285,8 +285,11 @@ dmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) */ if (bytes_left > fifo_bytes) dmmc_wait_fifo_status(regs, 0x4a); - else if (bytes_left == fifo_bytes) + else if (bytes_left == fifo_bytes) { dmmc_wait_fifo_status(regs, 0x40); + if (cmd->cmdidx == MMC_CMD_SEND_EXT_CSD) + udelay(600); + } for (i = 0; bytes_left && (i < fifo_words); i++) { cmddata = get_val(®s->mmcdrr); diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index e945c0a..861f4b9 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -178,7 +178,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) int timeout; struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; -#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO uint wml_value; wml_value = data->blocksize/4; @@ -601,8 +601,7 @@ int fsl_esdhc_mmc_init(bd_t *bis) { struct fsl_esdhc_cfg *cfg; - cfg = malloc(sizeof(struct fsl_esdhc_cfg)); - memset(cfg, 0, sizeof(struct fsl_esdhc_cfg)); + cfg = calloc(sizeof(struct fsl_esdhc_cfg), 1); cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR; cfg->sdhc_clk = gd->arch.sdhc_clk; return fsl_esdhc_initialize(bis, cfg); diff --git a/drivers/mmc/ftsdc010_esdhc.c b/drivers/mmc/ftsdc010_esdhc.c deleted file mode 100644 index 42f0e0c..0000000 --- a/drivers/mmc/ftsdc010_esdhc.c +++ /dev/null @@ -1,687 +0,0 @@ -/* - * Copyright (C) 2011 Andes Technology Corporation - * Macpaul Lin, Andes Technology Corporation <macpaul@andestech.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ - -#include <config.h> -#include <common.h> -#include <mmc.h> - -#include <asm/io.h> -#include <faraday/ftsdc010.h> - -/* - * supported mmc hosts - * setting the number CONFIG_FTSDC010_NUMBER in your configuration file. - */ -static struct mmc ftsdc010_dev[CONFIG_FTSDC010_NUMBER]; -static struct mmc_host ftsdc010_host[CONFIG_FTSDC010_NUMBER]; - -static struct ftsdc010_mmc *ftsdc010_get_base_mmc(int dev_index) -{ - return (struct ftsdc010_mmc *)CONFIG_FTSDC010_BASE + dev_index; -} - -#ifdef DEBUG -static void ftsdc010_dump_reg(struct mmc_host *host) -{ - debug("cmd: %08x\n", readl(&host->reg->cmd)); - debug("argu: %08x\n", readl(&host->reg->argu)); - debug("rsp0: %08x\n", readl(&host->reg->rsp0)); - debug("rsp1: %08x\n", readl(&host->reg->rsp1)); - debug("rsp2: %08x\n", readl(&host->reg->rsp2)); - debug("rsp3: %08x\n", readl(&host->reg->rsp3)); - debug("rsp_cmd: %08x\n", readl(&host->reg->rsp_cmd)); - debug("dcr: %08x\n", readl(&host->reg->dcr)); - debug("dtr: %08x\n", readl(&host->reg->dtr)); - debug("dlr: %08x\n", readl(&host->reg->dlr)); - debug("status: %08x\n", readl(&host->reg->status)); - debug("clr: %08x\n", readl(&host->reg->clr)); - debug("int_mask: %08x\n", readl(&host->reg->int_mask)); - debug("pcr: %08x\n", readl(&host->reg->pcr)); - debug("ccr: %08x\n", readl(&host->reg->ccr)); - debug("bwr: %08x\n", readl(&host->reg->bwr)); - debug("dwr: %08x\n", readl(&host->reg->dwr)); - debug("feature: %08x\n", readl(&host->reg->feature)); - debug("rev: %08x\n", readl(&host->reg->rev)); -} -#endif - -static unsigned int enable_imask(struct ftsdc010_mmc *reg, unsigned int imask) -{ - unsigned int newmask; - - newmask = readl(®->int_mask); - newmask |= imask; - - writel(newmask, ®->int_mask); - - return newmask; -} - -static void ftsdc010_pio_read(struct mmc_host *host, char *buf, unsigned int size) -{ - unsigned int fifo; - unsigned int fifo_words; - unsigned int *ptr; - unsigned int status; - unsigned int retry = 0; - - /* get_data_buffer */ - ptr = (unsigned int *)buf; - - while (size) { - status = readl(&host->reg->status); - debug("%s: size: %08x\n", __func__, size); - - if (status & FTSDC010_STATUS_FIFO_ORUN) { - - debug("%s: FIFO OVERRUN: sta: %08x\n", - __func__, status); - - fifo = host->fifo_len > size ? - size : host->fifo_len; - - size -= fifo; - - fifo_words = fifo >> 2; - - while (fifo_words--) - *ptr++ = readl(&host->reg->dwr); - - /* - * for adding some delays for SD card to put - * data into FIFO again - */ - udelay(4*FTSDC010_DELAY_UNIT); - -#ifdef CONFIG_FTSDC010_SDIO - /* sdio allow non-power-of-2 blksz */ - if (fifo & 3) { - unsigned int n = fifo & 3; - unsigned int data = readl(&host->reg->dwr); - - unsigned char *p = (unsigned char *)ptr; - - while (n--) { - *p++ = data; - data >>= 8; - } - } -#endif - } else { - udelay(1); - if (++retry >= FTSDC010_PIO_RETRY) { - debug("%s: PIO_RETRY timeout\n", __func__); - return; - } - } - } -} - -static void ftsdc010_pio_write(struct mmc_host *host, const char *buf, - unsigned int size) -{ - unsigned int fifo; - unsigned int *ptr; - unsigned int status; - unsigned int retry = 0; - - /* get data buffer */ - ptr = (unsigned int *)buf; - - while (size) { - status = readl(&host->reg->status); - - if (status & FTSDC010_STATUS_FIFO_URUN) { - fifo = host->fifo_len > size ? - size : host->fifo_len; - - size -= fifo; - - fifo = (fifo + 3) >> 2; - - while (fifo--) { - writel(*ptr, &host->reg->dwr); - ptr++; - } - } else { - udelay(1); - if (++retry >= FTSDC010_PIO_RETRY) { - debug("%s: PIO_RETRY timeout\n", __func__); - return; - } - } - } -} - -static int ftsdc010_check_rsp(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc_host *host = mmc->priv; - unsigned int sta, clear; - - sta = readl(&host->reg->status); - debug("%s: sta: %08x cmd %d\n", __func__, sta, cmd->cmdidx); - - /* check RSP TIMEOUT or FAIL */ - if (sta & FTSDC010_STATUS_RSP_TIMEOUT) { - /* RSP TIMEOUT */ - debug("%s: RSP timeout: sta: %08x\n", __func__, sta); - - clear |= FTSDC010_CLR_RSP_TIMEOUT; - writel(clear, &host->reg->clr); - - return TIMEOUT; - } else if (sta & FTSDC010_STATUS_RSP_CRC_FAIL) { - /* clear response fail bit */ - debug("%s: RSP CRC FAIL: sta: %08x\n", __func__, sta); - - clear |= FTSDC010_CLR_RSP_CRC_FAIL; - writel(clear, &host->reg->clr); - - return COMM_ERR; - } else if (sta & FTSDC010_STATUS_RSP_CRC_OK) { - - /* clear response CRC OK bit */ - clear |= FTSDC010_CLR_RSP_CRC_OK; - } - - writel(clear, &host->reg->clr); - return 0; -} - -static int ftsdc010_check_data(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc_host *host = mmc->priv; - unsigned int sta, clear; - - sta = readl(&host->reg->status); - debug("%s: sta: %08x cmd %d\n", __func__, sta, cmd->cmdidx); - - /* check DATA TIMEOUT or FAIL */ - if (data) { - - /* Transfer Complete */ - if (sta & FTSDC010_STATUS_DATA_END) - clear |= FTSDC010_STATUS_DATA_END; - - /* Data CRC_OK */ - if (sta & FTSDC010_STATUS_DATA_CRC_OK) - clear |= FTSDC010_STATUS_DATA_CRC_OK; - - /* DATA TIMEOUT or DATA CRC FAIL */ - if (sta & FTSDC010_STATUS_DATA_TIMEOUT) { - /* DATA TIMEOUT */ - debug("%s: DATA TIMEOUT: sta: %08x\n", __func__, sta); - - clear |= FTSDC010_STATUS_DATA_TIMEOUT; - writel(clear, &host->reg->clr); - - return TIMEOUT; - } else if (sta & FTSDC010_STATUS_DATA_CRC_FAIL) { - /* DATA CRC FAIL */ - debug("%s: DATA CRC FAIL: sta: %08x\n", __func__, sta); - - clear |= FTSDC010_STATUS_DATA_CRC_FAIL; - writel(clear, &host->reg->clr); - - return COMM_ERR; - } - writel(clear, &host->reg->clr); - } - return 0; -} - -static int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc_host *host = mmc->priv; - -#ifdef CONFIG_FTSDC010_SDIO - unsigned int scon; -#endif - unsigned int ccon; - unsigned int mask, tmpmask; - unsigned int ret; - unsigned int sta, i; - - ret = 0; - - if (data) - mask = FTSDC010_INT_MASK_RSP_TIMEOUT; - else if (cmd->resp_type & MMC_RSP_PRESENT) - mask = FTSDC010_INT_MASK_RSP_TIMEOUT; - else - mask = FTSDC010_INT_MASK_CMD_SEND; - - /* write argu reg */ - debug("%s: argu: %08x\n", __func__, host->reg->argu); - writel(cmd->cmdarg, &host->reg->argu); - - /* setup commnad */ - ccon = FTSDC010_CMD_IDX(cmd->cmdidx); - - /* setup command flags */ - ccon |= FTSDC010_CMD_CMD_EN; - - /* - * This hardware didn't support specific commands for mapping - * MMC_RSP_BUSY and MMC_RSP_OPCODE. Hence we don't deal with it. - */ - if (cmd->resp_type & MMC_RSP_PRESENT) { - ccon |= FTSDC010_CMD_NEED_RSP; - mask |= FTSDC010_INT_MASK_RSP_CRC_OK | - FTSDC010_INT_MASK_RSP_CRC_FAIL; - } - - if (cmd->resp_type & MMC_RSP_136) - ccon |= FTSDC010_CMD_LONG_RSP; - - /* In Linux driver, MMC_CMD_APP_CMD is checked in last_opcode */ - if (host->last_opcode == MMC_CMD_APP_CMD) - ccon |= FTSDC010_CMD_APP_CMD; - -#ifdef CONFIG_FTSDC010_SDIO - scon = readl(&host->reg->sdio_ctrl1); - if (host->card_type == MMC_TYPE_SDIO) - scon |= FTSDC010_SDIO_CTRL1_SDIO_ENABLE; - else - scon &= ~FTSDC010_SDIO_CTRL1_SDIO_ENABLE; - writel(scon, &host->reg->sdio_ctrl1); -#endif - - /* record last opcode for specifing the command type to hardware */ - host->last_opcode = cmd->cmdidx; - - /* write int_mask reg */ - tmpmask = readl(&host->reg->int_mask); - tmpmask |= mask; - writel(tmpmask, &host->reg->int_mask); - - /* write cmd reg */ - debug("%s: ccon: %08x\n", __func__, ccon); - writel(ccon, &host->reg->cmd); - - /* check CMD_SEND */ - for (i = 0; i < FTSDC010_CMD_RETRY; i++) { - /* - * If we read status register too fast - * will lead hardware error and the RSP_TIMEOUT - * flag will be raised incorrectly. - */ - udelay(16*FTSDC010_DELAY_UNIT); - sta = readl(&host->reg->status); - - /* Command Complete */ - /* - * Note: - * Do not clear FTSDC010_CLR_CMD_SEND flag. - * (by writing FTSDC010_CLR_CMD_SEND bit to clear register) - * It will make the driver becomes very slow. - * If the operation hasn't been finished, hardware will - * clear this bit automatically. - * In origin, the driver will clear this flag if there is - * no data need to be read. - */ - if (sta & FTSDC010_STATUS_CMD_SEND) - break; - } - - if (i > FTSDC010_CMD_RETRY) { - printf("%s: send command timeout\n", __func__); - return TIMEOUT; - } - - /* check rsp status */ - ret = ftsdc010_check_rsp(mmc, cmd, data); - if (ret) - return ret; - - /* read response if we have RSP_OK */ - if (ccon & FTSDC010_CMD_LONG_RSP) { - cmd->response[0] = readl(&host->reg->rsp3); - cmd->response[1] = readl(&host->reg->rsp2); - cmd->response[2] = readl(&host->reg->rsp1); - cmd->response[3] = readl(&host->reg->rsp0); - } else { - cmd->response[0] = readl(&host->reg->rsp0); - } - - /* read/write data */ - if (data && (data->flags & MMC_DATA_READ)) { - ftsdc010_pio_read(host, data->dest, - data->blocksize * data->blocks); - } else if (data && (data->flags & MMC_DATA_WRITE)) { - ftsdc010_pio_write(host, data->src, - data->blocksize * data->blocks); - } - - /* check data status */ - if (data) { - ret = ftsdc010_check_data(mmc, cmd, data); - if (ret) - return ret; - } - - udelay(FTSDC010_DELAY_UNIT); - return ret; -} - -static unsigned int cal_blksz(unsigned int blksz) -{ - unsigned int blksztwo = 0; - - while (blksz >>= 1) - blksztwo++; - - return blksztwo; -} - -static int ftsdc010_setup_data(struct mmc *mmc, struct mmc_data *data) -{ - struct mmc_host *host = mmc->priv; - unsigned int dcon, newmask; - - /* configure data transfer paramter */ - if (!data) - return 0; - - if (((data->blocksize - 1) & data->blocksize) != 0) { - printf("%s: can't do non-power-of 2 sized block transfers" - " (blksz %d)\n", __func__, data->blocksize); - return -1; - } - - /* - * We cannot deal with unaligned blocks with more than - * one block being transfered. - */ - if ((data->blocksize <= 2) && (data->blocks > 1)) { - printf("%s: can't do non-word sized block transfers" - " (blksz %d)\n", __func__, data->blocksize); - return -1; - } - - /* data length */ - dcon = data->blocksize * data->blocks; - writel(dcon, &host->reg->dlr); - - /* write data control */ - dcon = cal_blksz(data->blocksize); - - /* add to IMASK register */ - newmask = (FTSDC010_STATUS_RSP_CRC_FAIL | FTSDC010_STATUS_DATA_TIMEOUT); - - /* - * enable UNDERRUN will trigger interrupt immediatedly - * So setup it when rsp is received successfully - */ - if (data->flags & MMC_DATA_WRITE) { - dcon |= FTSDC010_DCR_DATA_WRITE; - } else { - dcon &= ~FTSDC010_DCR_DATA_WRITE; - newmask |= FTSDC010_STATUS_FIFO_ORUN; - } - enable_imask(host->reg, newmask); - -#ifdef CONFIG_FTSDC010_SDIO - /* always reset fifo since last transfer may fail */ - dcon |= FTSDC010_DCR_FIFO_RST; - - if (data->blocks > 1) - dcon |= FTSDC010_SDIO_CTRL1_SDIO_BLK_MODE; -#endif - - /* enable data transfer which will be pended until cmd is send */ - dcon |= FTSDC010_DCR_DATA_EN; - writel(dcon, &host->reg->dcr); - - return 0; -} - -static int ftsdc010_send_request(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - int ret; - - if (data) { - ret = ftsdc010_setup_data(mmc, data); - - if (ret) { - printf("%s: setup data error\n", __func__); - return -1; - } - - if ((data->flags & MMC_DATA_BOTH_DIR) == MMC_DATA_BOTH_DIR) { - printf("%s: data is both direction\n", __func__); - return -1; - } - } - - /* Send command */ - ret = ftsdc010_send_cmd(mmc, cmd, data); - return ret; -} - -static int ftsdc010_card_detect(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned int sta; - - sta = readl(&host->reg->status); - debug("%s: card status: %08x\n", __func__, sta); - - return (sta & FTSDC010_STATUS_CARD_DETECT) ? 0 : 1; -} - -static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - int ret; - - if (ftsdc010_card_detect(mmc) == 0) { - printf("%s: no medium present\n", __func__); - return -1; - } else { - ret = ftsdc010_send_request(mmc, cmd, data); - return ret; - } -} - -static void ftsdc010_set_clk(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned char clk_div; - unsigned int real_rate; - unsigned int clock; - - debug("%s: mmc_set_clock: %x\n", __func__, mmc->clock); - clock = readl(&host->reg->ccr); - - if (mmc->clock == 0) { - real_rate = 0; - clock |= FTSDC010_CCR_CLK_DIS; - } else { - debug("%s, mmc->clock: %08x, origin clock: %08x\n", - __func__, mmc->clock, clock); - - for (clk_div = 0; clk_div <= 127; clk_div++) { - real_rate = (CONFIG_SYS_CLK_FREQ / 2) / - (2 * (clk_div + 1)); - - if (real_rate <= mmc->clock) - break; - } - - debug("%s: computed real_rate: %x, clk_div: %x\n", - __func__, real_rate, clk_div); - - if (clk_div > 127) - debug("%s: no match clock rate, %x\n", - __func__, mmc->clock); - - clock = (clock & ~FTSDC010_CCR_CLK_DIV(0x7f)) | - FTSDC010_CCR_CLK_DIV(clk_div); - - clock &= ~FTSDC010_CCR_CLK_DIS; - } - - debug("%s, set clock: %08x\n", __func__, clock); - writel(clock, &host->reg->ccr); -} - -static void ftsdc010_set_ios(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned int power; - unsigned long val; - unsigned int bus_width; - - debug("%s: bus_width: %x, clock: %d\n", - __func__, mmc->bus_width, mmc->clock); - - /* set pcr: power on */ - power = readl(&host->reg->pcr); - power |= FTSDC010_PCR_POWER_ON; - writel(power, &host->reg->pcr); - - if (mmc->clock) - ftsdc010_set_clk(mmc); - - /* set bwr: bus width reg */ - bus_width = readl(&host->reg->bwr); - bus_width &= ~(FTSDC010_BWR_WIDE_8_BUS | FTSDC010_BWR_WIDE_4_BUS | - FTSDC010_BWR_SINGLE_BUS); - - if (mmc->bus_width == 8) - bus_width |= FTSDC010_BWR_WIDE_8_BUS; - else if (mmc->bus_width == 4) - bus_width |= FTSDC010_BWR_WIDE_4_BUS; - else - bus_width |= FTSDC010_BWR_SINGLE_BUS; - - writel(bus_width, &host->reg->bwr); - - /* set fifo depth */ - val = readl(&host->reg->feature); - host->fifo_len = FTSDC010_FEATURE_FIFO_DEPTH(val) * 4; /* 4 bytes */ - - /* set data timeout register */ - val = -1; - writel(val, &host->reg->dtr); -} - -static void ftsdc010_reset(struct mmc_host *host) -{ - unsigned int timeout; - unsigned int sta; - - /* Do SDC_RST: Software reset for all register */ - writel(FTSDC010_CMD_SDC_RST, &host->reg->cmd); - - host->clock = 0; - - /* this hardware has no reset finish flag to read */ - /* wait 100ms maximum */ - timeout = 100; - - /* hw clears the bit when it's done */ - while (readl(&host->reg->dtr) != 0) { - if (timeout == 0) { - printf("%s: reset timeout error\n", __func__); - return; - } - timeout--; - udelay(10*FTSDC010_DELAY_UNIT); - } - - sta = readl(&host->reg->status); - if (sta & FTSDC010_STATUS_CARD_CHANGE) - writel(FTSDC010_CLR_CARD_CHANGE, &host->reg->clr); -} - -static int ftsdc010_core_init(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned int mask; - unsigned int major, minor, revision; - - /* get hardware version */ - host->version = readl(&host->reg->rev); - - major = FTSDC010_REV_MAJOR(host->version); - minor = FTSDC010_REV_MINOR(host->version); - revision = FTSDC010_REV_REVISION(host->version); - - printf("ftsdc010 hardware ver: %d_%d_r%d\n", major, minor, revision); - - /* Interrupt MASK register init - mask all */ - writel(0x0, &host->reg->int_mask); - - mask = FTSDC010_INT_MASK_CMD_SEND | - FTSDC010_INT_MASK_DATA_END | - FTSDC010_INT_MASK_CARD_CHANGE; -#ifdef CONFIG_FTSDC010_SDIO - mask |= FTSDC010_INT_MASK_CP_READY | - FTSDC010_INT_MASK_CP_BUF_READY | - FTSDC010_INT_MASK_PLAIN_TEXT_READY | - FTSDC010_INT_MASK_SDIO_IRPT; -#endif - - writel(mask, &host->reg->int_mask); - - return 0; -} - -int ftsdc010_mmc_init(int dev_index) -{ - struct mmc *mmc; - struct mmc_host *host; - - mmc = &ftsdc010_dev[dev_index]; - - sprintf(mmc->name, "FTSDC010 SD/MMC"); - mmc->priv = &ftsdc010_host[dev_index]; - mmc->send_cmd = ftsdc010_request; - mmc->set_ios = ftsdc010_set_ios; - mmc->init = ftsdc010_core_init; - mmc->getcd = NULL; - mmc->getwp = NULL; - - mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; - - mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; - - mmc->f_min = CONFIG_SYS_CLK_FREQ / 2 / (2*128); - mmc->f_max = CONFIG_SYS_CLK_FREQ / 2 / 2; - - ftsdc010_host[dev_index].clock = 0; - ftsdc010_host[dev_index].reg = ftsdc010_get_base_mmc(dev_index); - mmc_register(mmc); - - /* reset mmc */ - host = (struct mmc_host *)mmc->priv; - ftsdc010_reset(host); - - return 0; -} diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c new file mode 100644 index 0000000..562b14a --- /dev/null +++ b/drivers/mmc/ftsdc010_mci.c @@ -0,0 +1,377 @@ +/* + * Faraday MMC/SD Host Controller + * + * (C) Copyright 2010 Faraday Technology + * Dante Su <dantesu@faraday-tech.com> + * + * This file is released under the terms of GPL v2 and any later version. + * See the file COPYING in the root directory of the source tree for details. + */ + +#include <common.h> +#include <malloc.h> +#include <part.h> +#include <mmc.h> + +#include <asm/io.h> +#include <asm/errno.h> +#include <asm/byteorder.h> +#include <faraday/ftsdc010.h> + +#define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */ +#define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */ + +struct ftsdc010_chip { + void __iomem *regs; + uint32_t wprot; /* write protected (locked) */ + uint32_t rate; /* actual SD clock in Hz */ + uint32_t sclk; /* FTSDC010 source clock in Hz */ + uint32_t fifo; /* fifo depth in bytes */ + uint32_t acmd; +}; + +static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + int ret = TIMEOUT; + uint32_t ts, st; + uint32_t cmd = FTSDC010_CMD_IDX(mmc_cmd->cmdidx); + uint32_t arg = mmc_cmd->cmdarg; + uint32_t flags = mmc_cmd->resp_type; + + cmd |= FTSDC010_CMD_CMD_EN; + + if (chip->acmd) { + cmd |= FTSDC010_CMD_APP_CMD; + chip->acmd = 0; + } + + if (flags & MMC_RSP_PRESENT) + cmd |= FTSDC010_CMD_NEED_RSP; + + if (flags & MMC_RSP_136) + cmd |= FTSDC010_CMD_LONG_RSP; + + writel(FTSDC010_STATUS_RSP_MASK | FTSDC010_STATUS_CMD_SEND, + ®s->clr); + writel(arg, ®s->argu); + writel(cmd, ®s->cmd); + + if (!(flags & (MMC_RSP_PRESENT | MMC_RSP_136))) { + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + if (readl(®s->status) & FTSDC010_STATUS_CMD_SEND) { + writel(FTSDC010_STATUS_CMD_SEND, ®s->clr); + ret = 0; + break; + } + } + } else { + st = 0; + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + st = readl(®s->status); + writel(st & FTSDC010_STATUS_RSP_MASK, ®s->clr); + if (st & FTSDC010_STATUS_RSP_MASK) + break; + } + if (st & FTSDC010_STATUS_RSP_CRC_OK) { + if (flags & MMC_RSP_136) { + mmc_cmd->response[0] = readl(®s->rsp3); + mmc_cmd->response[1] = readl(®s->rsp2); + mmc_cmd->response[2] = readl(®s->rsp1); + mmc_cmd->response[3] = readl(®s->rsp0); + } else { + mmc_cmd->response[0] = readl(®s->rsp0); + } + ret = 0; + } else { + debug("ftsdc010: rsp err (cmd=%d, st=0x%x)\n", + mmc_cmd->cmdidx, st); + } + } + + if (ret) { + debug("ftsdc010: cmd timeout (op code=%d)\n", + mmc_cmd->cmdidx); + } else if (mmc_cmd->cmdidx == MMC_CMD_APP_CMD) { + chip->acmd = 1; + } + + return ret; +} + +static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + uint32_t div; + + for (div = 0; div < 0x7f; ++div) { + if (rate >= chip->sclk / (2 * (div + 1))) + break; + } + chip->rate = chip->sclk / (2 * (div + 1)); + + writel(FTSDC010_CCR_CLK_DIV(div), ®s->ccr); + + if (IS_SD(mmc)) { + setbits_le32(®s->ccr, FTSDC010_CCR_CLK_SD); + + if (chip->rate > 25000000) + setbits_le32(®s->ccr, FTSDC010_CCR_CLK_HISPD); + else + clrbits_le32(®s->ccr, FTSDC010_CCR_CLK_HISPD); + } +} + +static inline int ftsdc010_is_ro(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + const uint8_t *csd = (const uint8_t *)mmc->csd; + + return chip->wprot || (csd[1] & 0x30); +} + +static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) +{ + int ret = TIMEOUT; + uint32_t st, ts; + + for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + st = readl(®s->status); + if (!(st & mask)) + continue; + writel(st & mask, ®s->clr); + ret = 0; + break; + } + + if (ret) + debug("ftsdc010: wait st(0x%x) timeout\n", mask); + + return ret; +} + +/* + * u-boot mmc api + */ + +static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + int ret = UNUSABLE_ERR; + uint32_t len = 0; + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + + if (data && (data->flags & MMC_DATA_WRITE) && chip->wprot) { + printf("ftsdc010: the card is write protected!\n"); + return ret; + } + + if (data) { + uint32_t dcr; + + len = data->blocksize * data->blocks; + + /* 1. data disable + fifo reset */ + writel(FTSDC010_DCR_FIFO_RST, ®s->dcr); + + /* 2. clear status register */ + writel(FTSDC010_STATUS_DATA_MASK | FTSDC010_STATUS_FIFO_URUN + | FTSDC010_STATUS_FIFO_ORUN, ®s->clr); + + /* 3. data timeout (1 sec) */ + writel(chip->rate, ®s->dtr); + + /* 4. data length (bytes) */ + writel(len, ®s->dlr); + + /* 5. data enable */ + dcr = (ffs(data->blocksize) - 1) | FTSDC010_DCR_DATA_EN; + if (data->flags & MMC_DATA_WRITE) + dcr |= FTSDC010_DCR_DATA_WRITE; + writel(dcr, ®s->dcr); + } + + ret = ftsdc010_send_cmd(mmc, cmd); + if (ret) { + printf("ftsdc010: CMD%d failed\n", cmd->cmdidx); + return ret; + } + + if (!data) + return ret; + + if (data->flags & MMC_DATA_WRITE) { + const uint8_t *buf = (const uint8_t *)data->src; + + while (len > 0) { + int wlen; + + /* wait for tx ready */ + ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_URUN); + if (ret) + break; + + /* write bytes to ftsdc010 */ + for (wlen = 0; wlen < len && wlen < chip->fifo; ) { + writel(*(uint32_t *)buf, ®s->dwr); + buf += 4; + wlen += 4; + } + + len -= wlen; + } + + } else { + uint8_t *buf = (uint8_t *)data->dest; + + while (len > 0) { + int rlen; + + /* wait for rx ready */ + ret = ftsdc010_wait(regs, FTSDC010_STATUS_FIFO_ORUN); + if (ret) + break; + + /* fetch bytes from ftsdc010 */ + for (rlen = 0; rlen < len && rlen < chip->fifo; ) { + *(uint32_t *)buf = readl(®s->dwr); + buf += 4; + rlen += 4; + } + + len -= rlen; + } + + } + + if (!ret) { + ret = ftsdc010_wait(regs, + FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR); + } + + return ret; +} + +static void ftsdc010_set_ios(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + + ftsdc010_clkset(mmc, mmc->clock); + + clrbits_le32(®s->bwr, FTSDC010_BWR_MODE_MASK); + switch (mmc->bus_width) { + case 4: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_4BIT); + break; + case 8: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_8BIT); + break; + default: + setbits_le32(®s->bwr, FTSDC010_BWR_MODE_1BIT); + break; + } +} + +static int ftsdc010_init(struct mmc *mmc) +{ + struct ftsdc010_chip *chip = mmc->priv; + struct ftsdc010_mmc __iomem *regs = chip->regs; + uint32_t ts; + + if (readl(®s->status) & FTSDC010_STATUS_CARD_DETECT) + return NO_CARD_ERR; + + if (readl(®s->status) & FTSDC010_STATUS_WRITE_PROT) { + printf("ftsdc010: write protected\n"); + chip->wprot = 1; + } + + chip->fifo = (readl(®s->feature) & 0xff) << 2; + + /* 1. chip reset */ + writel(FTSDC010_CMD_SDC_RST, ®s->cmd); + for (ts = get_timer(0); get_timer(ts) < CFG_RST_TIMEOUT; ) { + if (readl(®s->cmd) & FTSDC010_CMD_SDC_RST) + continue; + break; + } + if (readl(®s->cmd) & FTSDC010_CMD_SDC_RST) { + printf("ftsdc010: reset failed\n"); + return UNUSABLE_ERR; + } + + /* 2. enter low speed mode (400k card detection) */ + ftsdc010_clkset(mmc, 400000); + + /* 3. interrupt disabled */ + writel(0, ®s->int_mask); + + return 0; +} + +int ftsdc010_mmc_init(int devid) +{ + struct mmc *mmc; + struct ftsdc010_chip *chip; + struct ftsdc010_mmc __iomem *regs; +#ifdef CONFIG_FTSDC010_BASE_LIST + uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST; + + if (devid < 0 || devid >= ARRAY_SIZE(base_list)) + return -1; + regs = (void __iomem *)base_list[devid]; +#else + regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20)); +#endif + + mmc = malloc(sizeof(struct mmc)); + if (!mmc) + return -ENOMEM; + memset(mmc, 0, sizeof(struct mmc)); + + chip = malloc(sizeof(struct ftsdc010_chip)); + if (!chip) { + free(mmc); + return -ENOMEM; + } + memset(chip, 0, sizeof(struct ftsdc010_chip)); + + chip->regs = regs; + mmc->priv = chip; + + sprintf(mmc->name, "ftsdc010"); + mmc->send_cmd = ftsdc010_request; + mmc->set_ios = ftsdc010_set_ios; + mmc->init = ftsdc010_init; + + mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz; + switch (readl(®s->bwr) & FTSDC010_BWR_CAPS_MASK) { + case FTSDC010_BWR_CAPS_4BIT: + mmc->host_caps |= MMC_MODE_4BIT; + break; + case FTSDC010_BWR_CAPS_8BIT: + mmc->host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; + break; + default: + break; + } + +#ifdef CONFIG_SYS_CLK_FREQ + chip->sclk = CONFIG_SYS_CLK_FREQ; +#else + chip->sclk = clk_get_rate("SDC"); +#endif + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->f_max = chip->sclk / 2; + mmc->f_min = chip->sclk / 0x100; + mmc->block_dev.part_type = PART_TYPE_DOS; + + mmc_register(mmc); + + return 0; +} diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 2590f1b..0a2f535 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -524,48 +524,70 @@ static int sd_send_op_cond(struct mmc *mmc) return 0; } -static int mmc_send_op_cond(struct mmc *mmc) +/* We pass in the cmd since otherwise the init seems to fail */ +static int mmc_send_op_cond_iter(struct mmc *mmc, struct mmc_cmd *cmd, + int use_arg) { - int timeout = 10000; - struct mmc_cmd cmd; int err; + cmd->cmdidx = MMC_CMD_SEND_OP_COND; + cmd->resp_type = MMC_RSP_R3; + cmd->cmdarg = 0; + if (use_arg && !mmc_host_is_spi(mmc)) { + cmd->cmdarg = + (mmc->voltages & + (mmc->op_cond_response & OCR_VOLTAGE_MASK)) | + (mmc->op_cond_response & OCR_ACCESS_MODE); + + if (mmc->host_caps & MMC_MODE_HC) + cmd->cmdarg |= OCR_HCS; + } + err = mmc_send_cmd(mmc, cmd, NULL); + if (err) + return err; + mmc->op_cond_response = cmd->response[0]; + return 0; +} + +int mmc_send_op_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int err, i; + /* Some cards seem to need this */ mmc_go_idle(mmc); /* Asking to the card its capabilities */ - cmd.cmdidx = MMC_CMD_SEND_OP_COND; - cmd.resp_type = MMC_RSP_R3; - cmd.cmdarg = 0; - - err = mmc_send_cmd(mmc, &cmd, NULL); + mmc->op_cond_pending = 1; + for (i = 0; i < 2; i++) { + err = mmc_send_op_cond_iter(mmc, &cmd, i != 0); + if (err) + return err; - if (err) - return err; + /* exit if not busy (flag seems to be inverted) */ + if (mmc->op_cond_response & OCR_BUSY) + return 0; + } + return IN_PROGRESS; +} - udelay(1000); +int mmc_complete_op_cond(struct mmc *mmc) +{ + struct mmc_cmd cmd; + int timeout = 1000; + uint start; + int err; + mmc->op_cond_pending = 0; + start = get_timer(0); do { - cmd.cmdidx = MMC_CMD_SEND_OP_COND; - cmd.resp_type = MMC_RSP_R3; - cmd.cmdarg = (mmc_host_is_spi(mmc) ? 0 : - (mmc->voltages & - (cmd.response[0] & OCR_VOLTAGE_MASK)) | - (cmd.response[0] & OCR_ACCESS_MODE)); - - if (mmc->host_caps & MMC_MODE_HC) - cmd.cmdarg |= OCR_HCS; - - err = mmc_send_cmd(mmc, &cmd, NULL); - + err = mmc_send_op_cond_iter(mmc, &cmd, 1); if (err) return err; - - udelay(1000); - } while (!(cmd.response[0] & OCR_BUSY) && timeout--); - - if (timeout <= 0) - return UNUSABLE_ERR; + if (get_timer(start) > timeout) + return UNUSABLE_ERR; + udelay(100); + } while (!(mmc->op_cond_response & OCR_BUSY)); if (mmc_host_is_spi(mmc)) { /* read OCR for spi */ cmd.cmdidx = MMC_CMD_SPI_READ_OCR; @@ -1274,7 +1296,7 @@ block_dev_desc_t *mmc_get_dev(int dev) } #endif -int mmc_init(struct mmc *mmc) +int mmc_start_init(struct mmc *mmc) { int err; @@ -1314,17 +1336,48 @@ int mmc_init(struct mmc *mmc) if (err == TIMEOUT) { err = mmc_send_op_cond(mmc); - if (err) { + if (err && err != IN_PROGRESS) { printf("Card did not respond to voltage select!\n"); return UNUSABLE_ERR; } } - err = mmc_startup(mmc); + if (err == IN_PROGRESS) + mmc->init_in_progress = 1; + + return err; +} + +static int mmc_complete_init(struct mmc *mmc) +{ + int err = 0; + + if (mmc->op_cond_pending) + err = mmc_complete_op_cond(mmc); + + if (!err) + err = mmc_startup(mmc); if (err) mmc->has_init = 0; else mmc->has_init = 1; + mmc->init_in_progress = 0; + return err; +} + +int mmc_init(struct mmc *mmc) +{ + int err = IN_PROGRESS; + unsigned start = get_timer(0); + + if (mmc->has_init) + return 0; + if (!mmc->init_in_progress) + err = mmc_start_init(mmc); + + if (!err || err == IN_PROGRESS) + err = mmc_complete_init(mmc); + debug("%s: %d, time %lu\n", __func__, err, get_timer(start)); return err; } @@ -1362,6 +1415,25 @@ int get_mmc_num(void) return cur_dev_num; } +void mmc_set_preinit(struct mmc *mmc, int preinit) +{ + mmc->preinit = preinit; +} + +static void do_preinit(void) +{ + struct mmc *m; + struct list_head *entry; + + list_for_each(entry, &mmc_devices) { + m = list_entry(entry, struct mmc, link); + + if (m->preinit) + mmc_start_init(m); + } +} + + int mmc_initialize(bd_t *bis) { INIT_LIST_HEAD (&mmc_devices); @@ -1372,5 +1444,6 @@ int mmc_initialize(bd_t *bis) print_mmc_devices(','); + do_preinit(); return 0; } diff --git a/drivers/mmc/mv_sdhci.c b/drivers/mmc/mv_sdhci.c index 2fe34b6..63e1f90 100644 --- a/drivers/mmc/mv_sdhci.c +++ b/drivers/mmc/mv_sdhci.c @@ -51,6 +51,5 @@ int mv_sdh_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; else host->version = sdhci_readw(host, SDHCI_HOST_VERSION); - add_sdhci(host, max_clk, min_clk); - return 0; + return add_sdhci(host, max_clk, min_clk); } diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index dc49d37..e50ff92 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -94,6 +94,5 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->host_caps = MMC_MODE_HC; - add_sdhci(host, 52000000, 400000); - return 0; + return add_sdhci(host, 52000000, 400000); } diff --git a/drivers/mmc/spear_sdhci.c b/drivers/mmc/spear_sdhci.c new file mode 100644 index 0000000..23f1f4b --- /dev/null +++ b/drivers/mmc/spear_sdhci.c @@ -0,0 +1,44 @@ +/* + * (C) Copyright 2012 + * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <common.h> +#include <malloc.h> +#include <sdhci.h> + +int spear_sdhci_init(u32 regbase, u32 max_clk, u32 min_clk, u32 quirks) +{ + struct sdhci_host *host = NULL; + host = (struct sdhci_host *)malloc(sizeof(struct sdhci_host)); + if (!host) { + printf("sdhci host malloc fail!\n"); + return 1; + } + + host->name = "sdhci"; + host->ioaddr = (void *)regbase; + host->quirks = quirks; + + if (quirks & SDHCI_QUIRK_REG32_RW) + host->version = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; + else + host->version = sdhci_readw(host, SDHCI_HOST_VERSION); + + add_sdhci(host, max_clk, min_clk); + return 0; +} diff --git a/drivers/mmc/spl_mmc.c b/drivers/mmc/spl_mmc.c deleted file mode 100644 index 7efdcb8..0000000 --- a/drivers/mmc/spl_mmc.c +++ /dev/null @@ -1,137 +0,0 @@ -/* - * (C) Copyright 2010 - * Texas Instruments, <www.ti.com> - * - * Aneesh V <aneesh@ti.com> - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA - */ -#include <common.h> -#include <spl.h> -#include <asm/u-boot.h> -#include <asm/utils.h> -#include <mmc.h> -#include <fat.h> -#include <version.h> - -DECLARE_GLOBAL_DATA_PTR; - -static void mmc_load_image_raw(struct mmc *mmc) -{ - unsigned long err; - u32 image_size_sectors; - struct image_header *header; - - header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - - sizeof(struct image_header)); - - /* read image header to find the image size & load address */ - err = mmc->block_dev.block_read(0, - CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR, 1, - header); - - if (err == 0) - goto end; - - spl_parse_image_header(header); - - /* convert size to sectors - round up */ - image_size_sectors = (spl_image.size + mmc->read_bl_len - 1) / - mmc->read_bl_len; - - /* Read the header too to avoid extra memcpy */ - err = mmc->block_dev.block_read(0, - CONFIG_SYS_MMCSD_RAW_MODE_U_BOOT_SECTOR, - image_size_sectors, (void *)spl_image.load_addr); - -end: - if (err == 0) { - printf("spl: mmc blk read err - %lu\n", err); - hang(); - } -} - -#ifdef CONFIG_SPL_FAT_SUPPORT -static void mmc_load_image_fat(struct mmc *mmc) -{ - int err; - struct image_header *header; - - header = (struct image_header *)(CONFIG_SYS_TEXT_BASE - - sizeof(struct image_header)); - - err = fat_register_device(&mmc->block_dev, - CONFIG_SYS_MMC_SD_FAT_BOOT_PARTITION); - if (err) { - printf("spl: fat register err - %d\n", err); - hang(); - } - - err = file_fat_read(CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME, - header, sizeof(struct image_header)); - if (err <= 0) - goto end; - - spl_parse_image_header(header); - - err = file_fat_read(CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME, - (u8 *)spl_image.load_addr, 0); - -end: - if (err <= 0) { - printf("spl: error reading image %s, err - %d\n", - CONFIG_SPL_FAT_LOAD_PAYLOAD_NAME, err); - hang(); - } -} -#endif - -void spl_mmc_load_image(void) -{ - struct mmc *mmc; - int err; - u32 boot_mode; - - mmc_initialize(gd->bd); - /* We register only one device. So, the dev id is always 0 */ - mmc = find_mmc_device(0); - if (!mmc) { - puts("spl: mmc device not found!!\n"); - hang(); - } - - err = mmc_init(mmc); - if (err) { - printf("spl: mmc init failed: err - %d\n", err); - hang(); - } - boot_mode = spl_boot_mode(); - if (boot_mode == MMCSD_MODE_RAW) { - debug("boot mode - RAW\n"); - mmc_load_image_raw(mmc); -#ifdef CONFIG_SPL_FAT_SUPPORT - } else if (boot_mode == MMCSD_MODE_FAT) { - debug("boot mode - FAT\n"); - mmc_load_image_fat(mmc); -#endif - } else { - puts("spl: wrong MMC boot mode\n"); - hang(); - } -} diff --git a/drivers/mtd/cfi_flash.c b/drivers/mtd/cfi_flash.c index 22d8440..25f8752 100644 --- a/drivers/mtd/cfi_flash.c +++ b/drivers/mtd/cfi_flash.c @@ -38,6 +38,7 @@ #include <asm/processor.h> #include <asm/io.h> #include <asm/byteorder.h> +#include <asm/unaligned.h> #include <environment.h> #include <mtd/cfi_flash.h> #include <watchdog.h> @@ -183,16 +184,16 @@ u64 flash_read64(void *addr)__attribute__((weak, alias("__flash_read64"))); flash_info_t *flash_get_info(ulong base) { int i; - flash_info_t *info = NULL; + flash_info_t *info; for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { - info = & flash_info[i]; + info = &flash_info[i]; if (info->size && info->start[0] <= base && base <= info->start[0] + info->size - 1) - break; + return info; } - return info; + return NULL; } #endif @@ -1640,9 +1641,10 @@ static void cfi_reverse_geometry(struct cfi_qry *qry) u32 tmp; for (i = 0, j = qry->num_erase_regions - 1; i < j; i++, j--) { - tmp = qry->erase_region_info[i]; - qry->erase_region_info[i] = qry->erase_region_info[j]; - qry->erase_region_info[j] = tmp; + tmp = get_unaligned(&(qry->erase_region_info[i])); + put_unaligned(get_unaligned(&(qry->erase_region_info[j])), + &(qry->erase_region_info[i])); + put_unaligned(tmp, &(qry->erase_region_info[j])); } } @@ -2073,8 +2075,8 @@ ulong flash_get_size (phys_addr_t base, int banknum) info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); if (flash_detect_cfi (info, &qry)) { - info->vendor = le16_to_cpu(qry.p_id); - info->ext_addr = le16_to_cpu(qry.p_adr); + info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id))); + info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr))); num_erase_regions = qry.num_erase_regions; if (info->ext_addr) { @@ -2163,7 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum) break; } - tmp = le32_to_cpu(qry.erase_region_info[i]); + tmp = le32_to_cpu(get_unaligned( + &(qry.erase_region_info[i]))); debug("erase region %u: 0x%08lx\n", i, tmp); erase_region_count = (tmp & 0xffff) + 1; diff --git a/drivers/mtd/spi/spansion.c b/drivers/mtd/spi/spansion.c index bc558c4..dad30b5 100644 --- a/drivers/mtd/spi/spansion.c +++ b/drivers/mtd/spi/spansion.c @@ -94,7 +94,7 @@ static const struct spansion_spi_flash_params spansion_spi_flash_table[] = { .idcode2 = 0x4d01, .pages_per_sector = 256, .nr_sectors = 256, - .name = "S25FL129P_64K", + .name = "S25FL129P_64K/S25FL128S", }, { .idcode1 = 0x0219, diff --git a/drivers/net/bfin_mac.c b/drivers/net/bfin_mac.c index c63398e..0ffd59d 100644 --- a/drivers/net/bfin_mac.c +++ b/drivers/net/bfin_mac.c @@ -122,8 +122,6 @@ static int bfin_EMAC_send(struct eth_device *dev, void *packet, int length) { int i; int result = 0; - unsigned int *buf; - buf = (unsigned int *)packet; if (length <= 0) { printf("Ethernet: bad packet size: %d\n", length); diff --git a/drivers/net/fm/Makefile b/drivers/net/fm/Makefile index f191c79..9aaa828 100644 --- a/drivers/net/fm/Makefile +++ b/drivers/net/fm/Makefile @@ -46,6 +46,7 @@ COBJS-$(CONFIG_PPC_P4080) += p4080.o COBJS-$(CONFIG_PPC_P5020) += p5020.o COBJS-$(CONFIG_PPC_P5040) += p5040.o COBJS-$(CONFIG_PPC_T4240) += t4240.o +COBJS-$(CONFIG_PPC_T4160) += t4240.o COBJS-$(CONFIG_PPC_B4420) += b4860.o COBJS-$(CONFIG_PPC_B4860) += b4860.o endif diff --git a/drivers/net/fm/b4860.c b/drivers/net/fm/b4860.c index 8cde7af..3b5defe 100644 --- a/drivers/net/fm/b4860.c +++ b/drivers/net/fm/b4860.c @@ -55,8 +55,10 @@ phy_interface_t fman_port_enet_if(enum fm_port port) if (is_device_disabled(port)) return PHY_INTERFACE_MODE_NONE; - if ((port == FM1_10GEC1 || port == FM1_10GEC2) - && (is_serdes_configured(XAUI_FM1))) + /*B4860 has two 10Gig Mac*/ + if ((port == FM1_10GEC1 || port == FM1_10GEC2) && + ((is_serdes_configured(XAUI_FM1_MAC9)) || + (is_serdes_configured(XAUI_FM1_MAC10)))) return PHY_INTERFACE_MODE_XGMII; /* Fix me need to handle RGMII here first */ diff --git a/drivers/net/fm/eth.c b/drivers/net/fm/eth.c index 54b142f..9b139ee 100644 --- a/drivers/net/fm/eth.c +++ b/drivers/net/fm/eth.c @@ -568,6 +568,8 @@ static int fm_eth_init_mac(struct fm_eth *fm_eth, struct ccsr_fman *reg) num = fm_eth->num; #ifdef CONFIG_SYS_FMAN_V3 + if (fm_eth->type == FM_ETH_10G_E) + num += 8; base = ®->memac[num].fm_memac; phyregs = ®->memac[num].fm_memac_mdio; #else diff --git a/drivers/net/fm/fm.h b/drivers/net/fm/fm.h index 228df33..ba581e9 100644 --- a/drivers/net/fm/fm.h +++ b/drivers/net/fm/fm.h @@ -152,4 +152,6 @@ struct fm_eth { #define MAX_RXBUF_LOG2 11 #define MAX_RXBUF_LEN (1 << MAX_RXBUF_LOG2) +#define PORT_IS_ENABLED(port) fm_info[fm_port_to_index(port)].enabled + #endif /* __FM_H__ */ diff --git a/drivers/net/fm/init.c b/drivers/net/fm/init.c index ae389b8..5908c32 100644 --- a/drivers/net/fm/init.c +++ b/drivers/net/fm/init.c @@ -74,9 +74,15 @@ struct fm_eth_info fm_info[] = { #if (CONFIG_SYS_NUM_FM1_10GEC >= 1) FM_TGEC_INFO_INITIALIZER(1, 1), #endif +#if (CONFIG_SYS_NUM_FM1_10GEC >= 2) + FM_TGEC_INFO_INITIALIZER(1, 2), +#endif #if (CONFIG_SYS_NUM_FM2_10GEC >= 1) FM_TGEC_INFO_INITIALIZER(2, 1), #endif +#if (CONFIG_SYS_NUM_FM2_10GEC >= 2) + FM_TGEC_INFO_INITIALIZER(2, 2), +#endif }; int fm_standard_init(bd_t *bis) @@ -232,6 +238,26 @@ static void ft_fixup_port(void *blob, struct fm_eth_info *info, char *prop) return ; } +#ifdef CONFIG_SYS_FMAN_V3 + /* + * Physically FM1_DTSEC9 and FM1_10GEC1 use the same dual-role MAC, when + * FM1_10GEC1 is enabled and FM1_DTSEC9 is disabled, ensure that the + * dual-role MAC is not disabled, ditto for other dual-role MACs. + */ + if (((info->port == FM1_DTSEC9) && (PORT_IS_ENABLED(FM1_10GEC1))) || + ((info->port == FM1_DTSEC10) && (PORT_IS_ENABLED(FM1_10GEC2))) || + ((info->port == FM1_10GEC1) && (PORT_IS_ENABLED(FM1_DTSEC9))) || + ((info->port == FM1_10GEC2) && (PORT_IS_ENABLED(FM1_DTSEC10))) +#if (CONFIG_SYS_NUM_FMAN == 2) + || + ((info->port == FM2_DTSEC9) && (PORT_IS_ENABLED(FM2_10GEC1))) || + ((info->port == FM2_DTSEC10) && (PORT_IS_ENABLED(FM2_10GEC2))) || + ((info->port == FM2_10GEC1) && (PORT_IS_ENABLED(FM2_DTSEC9))) || + ((info->port == FM2_10GEC2) && (PORT_IS_ENABLED(FM2_DTSEC10))) +#endif + ) + return; +#endif /* board code might have caused offset to change */ off = fdt_node_offset_by_compat_reg(blob, prop, paddr); @@ -249,10 +275,15 @@ void fdt_fixup_fman_ethernet(void *blob) { int i; +#ifdef CONFIG_SYS_FMAN_V3 + for (i = 0; i < ARRAY_SIZE(fm_info); i++) + ft_fixup_port(blob, &fm_info[i], "fsl,fman-memac"); +#else for (i = 0; i < ARRAY_SIZE(fm_info); i++) { if (fm_info[i].type == FM_ETH_1G_E) ft_fixup_port(blob, &fm_info[i], "fsl,fman-1g-mac"); else ft_fixup_port(blob, &fm_info[i], "fsl,fman-10g-mac"); } +#endif } diff --git a/drivers/net/fm/t4240.c b/drivers/net/fm/t4240.c index 48c530c..275395f 100644 --- a/drivers/net/fm/t4240.c +++ b/drivers/net/fm/t4240.c @@ -70,12 +70,18 @@ phy_interface_t fman_port_enet_if(enum fm_port port) if (is_device_disabled(port)) return PHY_INTERFACE_MODE_NONE; - if ((port == FM1_10GEC1 || port == FM1_10GEC2) - && (is_serdes_configured(XAUI_FM1))) + if ((port == FM1_10GEC1 || port == FM1_10GEC2) && + ((is_serdes_configured(XAUI_FM1_MAC9)) || + (is_serdes_configured(XAUI_FM1_MAC10)) || + (is_serdes_configured(XFI_FM1_MAC9)) || + (is_serdes_configured(XFI_FM1_MAC10)))) return PHY_INTERFACE_MODE_XGMII; - if ((port == FM2_10GEC1 || port == FM2_10GEC2) - && (is_serdes_configured(XAUI_FM2))) + if ((port == FM2_10GEC1 || port == FM2_10GEC2) && + ((is_serdes_configured(XAUI_FM2_MAC9)) || + (is_serdes_configured(XAUI_FM2_MAC10)) || + (is_serdes_configured(XFI_FM2_MAC9)) || + (is_serdes_configured(XFI_FM2_MAC10)))) return PHY_INTERFACE_MODE_XGMII; #define FSL_CORENET_RCWSR13_EC1 0x60000000 /* bits 417..418 */ diff --git a/drivers/net/phy/teranetics.c b/drivers/net/phy/teranetics.c index 78447b7..84ce736 100644 --- a/drivers/net/phy/teranetics.c +++ b/drivers/net/phy/teranetics.c @@ -34,9 +34,21 @@ int tn2020_config(struct phy_device *phydev) unsigned short restart_an = (MDIO_AN_CTRL1_RESTART | MDIO_AN_CTRL1_ENABLE | MDIO_AN_CTRL1_XNP); + u8 phy_hwversion; - phy_write(phydev, 30, 93, 2); - phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an); + /* + * bit 15:12 of register 30.32 indicates PHY hardware + * version. It can be used to distinguish TN80xx from + * TN2020. TN2020 needs write 0x2 to 30.93, but TN80xx + * needs 0x1. + */ + phy_hwversion = (phy_read(phydev, 30, 32) >> 12) & 0xf; + if (phy_hwversion <= 3) { + phy_write(phydev, 30, 93, 2); + phy_write(phydev, MDIO_MMD_AN, MDIO_CTRL1, restart_an); + } else { + phy_write(phydev, 30, 93, 1); + } } return 0; diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c index 6c5cb99..c283d82 100644 --- a/drivers/net/phy/vitesse.c +++ b/drivers/net/phy/vitesse.c @@ -48,6 +48,19 @@ #define MIIM_VSC8601_SKEW_CTRL 0x1c #define PHY_EXT_PAGE_ACCESS 0x1f +#define PHY_EXT_PAGE_ACCESS_GENERAL 0x10 +#define PHY_EXT_PAGE_ACCESS_EXTENDED3 0x3 + +/* Vitesse VSC8574 control register */ +#define MIIM_VSC8574_MAC_SERDES_CON 0x10 +#define MIIM_VSC8574_MAC_SERDES_ANEG 0x80 +#define MIIM_VSC8574_GENERAL18 0x12 +#define MIIM_VSC8574_GENERAL19 0x13 + +/* Vitesse VSC8574 gerenal purpose register 18 */ +#define MIIM_VSC8574_18G_SGMII 0x80f0 +#define MIIM_VSC8574_18G_QSGMII 0x80e0 +#define MIIM_VSC8574_18G_CMDSTAT 0x8000 /* CIS8201 */ static int vitesse_config(struct phy_device *phydev) @@ -145,6 +158,49 @@ static int vsc8601_config(struct phy_device *phydev) return 0; } +static int vsc8574_config(struct phy_device *phydev) +{ + u32 val; + /* configure regiser 19G for MAC */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_GENERAL); + + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19); + if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { + /* set bit 15:14 to '01' for QSGMII mode */ + val = (val & 0x3fff) | (1 << 14); + phy_write(phydev, MDIO_DEVAD_NONE, + MIIM_VSC8574_GENERAL19, val); + /* Enable 4 ports MAC QSGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18, + MIIM_VSC8574_18G_QSGMII); + } else { + /* set bit 15:14 to '00' for SGMII mode */ + val = val & 0x3fff; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL19, val); + /* Enable 4 ports MAC SGMII */ + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18, + MIIM_VSC8574_18G_SGMII); + } + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18); + /* When bit 15 is cleared the command has completed */ + while (val & MIIM_VSC8574_18G_CMDSTAT) + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_GENERAL18); + + /* Enable Serdes Auto-negotiation */ + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, + PHY_EXT_PAGE_ACCESS_EXTENDED3); + val = phy_read(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON); + val = val | MIIM_VSC8574_MAC_SERDES_ANEG; + phy_write(phydev, MDIO_DEVAD_NONE, MIIM_VSC8574_MAC_SERDES_CON, val); + + phy_write(phydev, MDIO_DEVAD_NONE, PHY_EXT_PAGE_ACCESS, 0); + + genphy_config_aneg(phydev); + + return 0; +} + static struct phy_driver VSC8211_driver = { .name = "Vitesse VSC8211", .uid = 0xfc4b0, @@ -185,6 +241,16 @@ static struct phy_driver VSC8234_driver = { .shutdown = &genphy_shutdown, }; +static struct phy_driver VSC8574_driver = { + .name = "Vitesse VSC8574", + .uid = 0x704a0, + .mask = 0xffff0, + .features = PHY_GBIT_FEATURES, + .config = &vsc8574_config, + .startup = &vitesse_startup, + .shutdown = &genphy_shutdown, +}; + static struct phy_driver VSC8601_driver = { .name = "Vitesse VSC8601", .uid = 0x70420, @@ -244,6 +310,7 @@ int phy_vitesse_init(void) phy_register(&VSC8244_driver); phy_register(&VSC8211_driver); phy_register(&VSC8221_driver); + phy_register(&VSC8574_driver); phy_register(&VSC8662_driver); phy_register(&cis8201_driver); phy_register(&cis8204_driver); diff --git a/drivers/net/smc911x.h b/drivers/net/smc911x.h index a290073..f63a069 100644 --- a/drivers/net/smc911x.h +++ b/drivers/net/smc911x.h @@ -484,7 +484,7 @@ static void smc911x_reset(struct eth_device *dev) while (timeout-- && !(smc911x_reg_read(dev, PMT_CTRL) & PMT_CTRL_READY)) udelay(10); - if (!timeout) { + if (timeout < 0) { printf(DRIVERNAME ": timeout waiting for PM restore\n"); return; @@ -500,7 +500,7 @@ static void smc911x_reset(struct eth_device *dev) while (timeout-- && smc911x_reg_read(dev, E2P_CMD) & E2P_CMD_EPC_BUSY) udelay(10); - if (!timeout) { + if (timeout < 0) { printf(DRIVERNAME ": reset timeout\n"); return; } diff --git a/drivers/serial/Makefile b/drivers/serial/Makefile index fbc4e97..442b7ea 100644 --- a/drivers/serial/Makefile +++ b/drivers/serial/Makefile @@ -52,6 +52,7 @@ COBJS-$(CONFIG_XILINX_UARTLITE) += serial_xuartlite.o COBJS-$(CONFIG_SANDBOX_SERIAL) += sandbox.o COBJS-$(CONFIG_SCIF_CONSOLE) += serial_sh.o COBJS-$(CONFIG_ZYNQ_SERIAL) += serial_zynq.o +COBJS-$(CONFIG_BFIN_SERIAL) += serial_bfin.o ifndef CONFIG_SPL_BUILD COBJS-$(CONFIG_USB_TTY) += usbtty.o diff --git a/drivers/serial/serial.c b/drivers/serial/serial.c index 9f04643..daa8003 100644 --- a/drivers/serial/serial.c +++ b/drivers/serial/serial.c @@ -143,7 +143,6 @@ serial_initfunc(au1x00_serial_initialize); serial_initfunc(asc_serial_initialize); serial_initfunc(jz_serial_initialize); serial_initfunc(mpc5xx_serial_initialize); -serial_initfunc(mpc8220_serial_initialize); serial_initfunc(mpc8260_scc_serial_initialize); serial_initfunc(mpc8260_smc_serial_initialize); serial_initfunc(mpc85xx_serial_initialize); @@ -236,7 +235,6 @@ void serial_initialize(void) asc_serial_initialize(); jz_serial_initialize(); mpc5xx_serial_initialize(); - mpc8220_serial_initialize(); mpc8260_scc_serial_initialize(); mpc8260_smc_serial_initialize(); mpc85xx_serial_initialize(); diff --git a/drivers/serial/serial_bfin.c b/drivers/serial/serial_bfin.c new file mode 100644 index 0000000..0443b84 --- /dev/null +++ b/drivers/serial/serial_bfin.c @@ -0,0 +1,411 @@ +/* + * U-boot - serial.c Blackfin Serial Driver + * + * Copyright (c) 2005-2008 Analog Devices Inc. + * + * Copyright (c) 2003 Bas Vermeulen <bas@buyways.nl>, + * BuyWays B.V. (www.buyways.nl) + * + * Based heavily on: + * blkfinserial.c: Serial driver for BlackFin DSP internal USRTs. + * Copyright(c) 2003 Metrowerks <mwaddel@metrowerks.com> + * Copyright(c) 2001 Tony Z. Kou <tonyko@arcturusnetworks.com> + * Copyright(c) 2001-2002 Arcturus Networks Inc. <www.arcturusnetworks.com> + * + * Based on code from 68328 version serial driver imlpementation which was: + * Copyright (C) 1995 David S. Miller <davem@caip.rutgers.edu> + * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> + * Copyright (C) 1998, 1999 D. Jeff Dionne <jeff@uclinux.org> + * Copyright (C) 1999 Vladimir Gurevich <vgurevic@cisco.com> + * + * (C) Copyright 2000-2004 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * Licensed under the GPL-2 or later. + */ + +/* Anomaly notes: + * 05000086 - we don't support autobaud + * 05000099 - we only use DR bit, so losing others is not a problem + * 05000100 - we don't use the UART_IIR register + * 05000215 - we poll the uart (no dma/interrupts) + * 05000225 - no workaround possible, but this shouldnt cause errors ... + * 05000230 - we tweak the baud rate calculation slightly + * 05000231 - we always use 1 stop bit + * 05000309 - we always enable the uart before we modify it in anyway + * 05000350 - we always enable the uart regardless of boot mode + * 05000363 - we don't support break signals, so don't generate one + */ + +#include <common.h> +#include <post.h> +#include <watchdog.h> +#include <serial.h> +#include <linux/compiler.h> +#include <asm/blackfin.h> +#include <asm/serial.h> + +DECLARE_GLOBAL_DATA_PTR; + +#ifdef CONFIG_UART_CONSOLE + +#ifdef CONFIG_DEBUG_SERIAL +static uart_lsr_t cached_lsr[256]; +static uart_lsr_t cached_rbr[256]; +static size_t cache_count; + +/* The LSR is read-to-clear on some parts, so we have to make sure status + * bits aren't inadvertently lost when doing various tests. This also + * works around anomaly 05000099 at the same time by keeping a cumulative + * tally of all the status bits. + */ +static uart_lsr_t uart_lsr_save; +static uart_lsr_t uart_lsr_read(uint32_t uart_base) +{ + uart_lsr_t lsr = _lsr_read(pUART); + uart_lsr_save |= (lsr & (OE|PE|FE|BI)); + return lsr | uart_lsr_save; +} +/* Just do the clear for everyone since it can't hurt. */ +static void uart_lsr_clear(uint32_t uart_base) +{ + uart_lsr_save = 0; + _lsr_write(pUART, -1); +} +#else +/* When debugging is disabled, we only care about the DR bit, so if other + * bits get set/cleared, we don't really care since we don't read them + * anyways (and thus anomaly 05000099 is irrelevant). + */ +static inline uart_lsr_t uart_lsr_read(uint32_t uart_base) +{ + return _lsr_read(pUART); +} +static void uart_lsr_clear(uint32_t uart_base) +{ + _lsr_write(pUART, -1); +} +#endif + +static void uart_putc(uint32_t uart_base, const char c) +{ + /* send a \r for compatibility */ + if (c == '\n') + serial_putc('\r'); + + WATCHDOG_RESET(); + + /* wait for the hardware fifo to clear up */ + while (!(uart_lsr_read(uart_base) & THRE)) + continue; + + /* queue the character for transmission */ + bfin_write(&pUART->thr, c); + SSYNC(); + + WATCHDOG_RESET(); +} + +static int uart_tstc(uint32_t uart_base) +{ + WATCHDOG_RESET(); + return (uart_lsr_read(uart_base) & DR) ? 1 : 0; +} + +static int uart_getc(uint32_t uart_base) +{ + uint16_t uart_rbr_val; + + /* wait for data ! */ + while (!uart_tstc(uart_base)) + continue; + + /* grab the new byte */ + uart_rbr_val = bfin_read(&pUART->rbr); + +#ifdef CONFIG_DEBUG_SERIAL + /* grab & clear the LSR */ + uart_lsr_t uart_lsr_val = uart_lsr_read(uart_base); + + cached_lsr[cache_count] = uart_lsr_val; + cached_rbr[cache_count] = uart_rbr_val; + cache_count = (cache_count + 1) % ARRAY_SIZE(cached_lsr); + + if (uart_lsr_val & (OE|PE|FE|BI)) { + printf("\n[SERIAL ERROR]\n"); + do { + --cache_count; + printf("\t%3zu: RBR=0x%02x LSR=0x%02x\n", cache_count, + cached_rbr[cache_count], cached_lsr[cache_count]); + } while (cache_count > 0); + return -1; + } +#endif + uart_lsr_clear(uart_base); + + return uart_rbr_val; +} + +#if CONFIG_POST & CONFIG_SYS_POST_UART +# define LOOP(x) x +#else +# define LOOP(x) +#endif + +#if BFIN_UART_HW_VER < 4 + +LOOP( +static void uart_loop(uint32_t uart_base, int state) +{ + u16 mcr; + + /* Drain the TX fifo first so bytes don't come back */ + while (!(uart_lsr_read(uart_base) & TEMT)) + continue; + + mcr = bfin_read(&pUART->mcr); + if (state) + mcr |= LOOP_ENA | MRTS; + else + mcr &= ~(LOOP_ENA | MRTS); + bfin_write(&pUART->mcr, mcr); +} +) + +#else + +LOOP( +static void uart_loop(uint32_t uart_base, int state) +{ + u32 control; + + /* Drain the TX fifo first so bytes don't come back */ + while (!(uart_lsr_read(uart_base) & TEMT)) + continue; + + control = bfin_read(&pUART->control); + if (state) + control |= LOOP_ENA | MRTS; + else + control &= ~(LOOP_ENA | MRTS); + bfin_write(&pUART->control, control); +} +) + +#endif + +static inline void __serial_set_baud(uint32_t uart_base, uint32_t baud) +{ +#ifdef CONFIG_DEBUG_EARLY_SERIAL + serial_early_set_baud(uart_base, baud); +#else + uint16_t divisor = (get_uart_clk() + (baud * 8)) / (baud * 16) + - ANOMALY_05000230; + + /* Program the divisor to get the baud rate we want */ + serial_set_divisor(uart_base, divisor); +#endif +} + +static void uart_puts(uint32_t uart_base, const char *s) +{ + while (*s) + uart_putc(uart_base, *s++); +} + +#define DECL_BFIN_UART(n) \ +static int uart##n##_init(void) \ +{ \ + const unsigned short pins[] = { _P_UART(n, RX), _P_UART(n, TX), 0, }; \ + peripheral_request_list(pins, "bfin-uart"); \ + uart_init(MMR_UART(n)); \ + __serial_set_baud(MMR_UART(n), gd->baudrate); \ + uart_lsr_clear(MMR_UART(n)); \ + return 0; \ +} \ +\ +static int uart##n##_uninit(void) \ +{ \ + return serial_early_uninit(MMR_UART(n)); \ +} \ +\ +static void uart##n##_setbrg(void) \ +{ \ + __serial_set_baud(MMR_UART(n), gd->baudrate); \ +} \ +\ +static int uart##n##_getc(void) \ +{ \ + return uart_getc(MMR_UART(n)); \ +} \ +\ +static int uart##n##_tstc(void) \ +{ \ + return uart_tstc(MMR_UART(n)); \ +} \ +\ +static void uart##n##_putc(const char c) \ +{ \ + uart_putc(MMR_UART(n), c); \ +} \ +\ +static void uart##n##_puts(const char *s) \ +{ \ + uart_puts(MMR_UART(n), s); \ +} \ +\ +LOOP( \ +static void uart##n##_loop(int state) \ +{ \ + uart_loop(MMR_UART(n), state); \ +} \ +) \ +\ +struct serial_device bfin_serial##n##_device = { \ + .name = "bfin_uart"#n, \ + .start = uart##n##_init, \ + .stop = uart##n##_uninit, \ + .setbrg = uart##n##_setbrg, \ + .getc = uart##n##_getc, \ + .tstc = uart##n##_tstc, \ + .putc = uart##n##_putc, \ + .puts = uart##n##_puts, \ + LOOP(.loop = uart##n##_loop) \ +}; + +#ifdef UART0_RBR +DECL_BFIN_UART(0) +#endif +#ifdef UART1_RBR +DECL_BFIN_UART(1) +#endif +#ifdef UART2_RBR +DECL_BFIN_UART(2) +#endif +#ifdef UART3_RBR +DECL_BFIN_UART(3) +#endif + +__weak struct serial_device *default_serial_console(void) +{ +#if CONFIG_UART_CONSOLE == 0 + return &bfin_serial0_device; +#elif CONFIG_UART_CONSOLE == 1 + return &bfin_serial1_device; +#elif CONFIG_UART_CONSOLE == 2 + return &bfin_serial2_device; +#elif CONFIG_UART_CONSOLE == 3 + return &bfin_serial3_device; +#endif +} + +void bfin_serial_initialize(void) +{ +#ifdef UART0_RBR + serial_register(&bfin_serial0_device); +#endif +#ifdef UART1_RBR + serial_register(&bfin_serial1_device); +#endif +#ifdef UART2_RBR + serial_register(&bfin_serial2_device); +#endif +#ifdef UART3_RBR + serial_register(&bfin_serial3_device); +#endif +} + +#ifdef CONFIG_DEBUG_EARLY_SERIAL +inline void uart_early_putc(uint32_t uart_base, const char c) +{ + /* send a \r for compatibility */ + if (c == '\n') + uart_early_putc(uart_base, '\r'); + + /* wait for the hardware fifo to clear up */ + while (!(_lsr_read(pUART) & THRE)) + continue; + + /* queue the character for transmission */ + bfin_write(&pUART->thr, c); + SSYNC(); +} + +void uart_early_puts(const char *s) +{ + while (*s) + uart_early_putc(UART_BASE, *s++); +} + +/* Symbol for our assembly to call. */ +void _serial_early_set_baud(uint32_t baud) +{ + serial_early_set_baud(UART_BASE, baud); +} + +/* Symbol for our assembly to call. */ +void _serial_early_init(void) +{ + serial_early_init(UART_BASE); +} +#endif + +#elif defined(CONFIG_UART_MEM) + +char serial_logbuf[CONFIG_UART_MEM]; +char *serial_logbuf_head = serial_logbuf; + +int serial_mem_init(void) +{ + serial_logbuf_head = serial_logbuf; + return 0; +} + +void serial_mem_setbrg(void) +{ +} + +int serial_mem_tstc(void) +{ + return 0; +} + +int serial_mem_getc(void) +{ + return 0; +} + +void serial_mem_putc(const char c) +{ + *serial_logbuf_head = c; + if (++serial_logbuf_head == serial_logbuf + CONFIG_UART_MEM) + serial_logbuf_head = serial_logbuf; +} + +void serial_mem_puts(const char *s) +{ + while (*s) + serial_putc(*s++); +} + +struct serial_device bfin_serial_mem_device = { + .name = "bfin_uart_mem", + .start = serial_mem_init, + .setbrg = serial_mem_setbrg, + .getc = serial_mem_getc, + .tstc = serial_mem_tstc, + .putc = serial_mem_putc, + .puts = serial_mem_puts, +}; + + +__weak struct serial_device *default_serial_console(void) +{ + return &bfin_serial_mem_device; +} + +void bfin_serial_initialize(void) +{ + serial_register(&bfin_serial_mem_device); +} +#endif /* CONFIG_UART_MEM */ diff --git a/drivers/spi/bfin_spi.c b/drivers/spi/bfin_spi.c index ab2e8b9..a9a4d92 100644 --- a/drivers/spi/bfin_spi.c +++ b/drivers/spi/bfin_spi.c @@ -13,7 +13,6 @@ #include <spi.h> #include <asm/blackfin.h> -#include <asm/dma.h> #include <asm/gpio.h> #include <asm/portmux.h> #include <asm/mach-common/bits/spi.h> @@ -242,109 +241,15 @@ void spi_release_bus(struct spi_slave *slave) SSYNC(); } -#ifdef __ADSPBF54x__ -# define SPI_DMA_BASE DMA4_NEXT_DESC_PTR -#elif defined(__ADSPBF533__) || defined(__ADSPBF532__) || defined(__ADSPBF531__) || \ - defined(__ADSPBF538__) || defined(__ADSPBF539__) -# define SPI_DMA_BASE DMA5_NEXT_DESC_PTR -#elif defined(__ADSPBF561__) -# define SPI_DMA_BASE DMA2_4_NEXT_DESC_PTR -#elif defined(__ADSPBF537__) || defined(__ADSPBF536__) || defined(__ADSPBF534__) || \ - defined(__ADSPBF52x__) || defined(__ADSPBF51x__) -# define SPI_DMA_BASE DMA7_NEXT_DESC_PTR -# elif defined(__ADSPBF50x__) -# define SPI_DMA_BASE DMA6_NEXT_DESC_PTR -#else -# error "Please provide SPI DMA channel defines" -#endif -static volatile struct dma_register *dma = (void *)SPI_DMA_BASE; - #ifndef CONFIG_BFIN_SPI_IDLE_VAL # define CONFIG_BFIN_SPI_IDLE_VAL 0xff #endif -#ifdef CONFIG_BFIN_SPI_NO_DMA -# define SPI_DMA 0 -#else -# define SPI_DMA 1 -#endif - -static int spi_dma_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, - uint bytes) -{ - int ret = -1; - u16 ndsize, spi_config, dma_config; - struct dmasg dmasg[2]; - const u8 *buf; - - if (tx) { - debug("%s: doing half duplex TX\n", __func__); - buf = tx; - spi_config = TDBR_DMA; - dma_config = 0; - } else { - debug("%s: doing half duplex RX\n", __func__); - buf = rx; - spi_config = RDBR_DMA; - dma_config = WNR; - } - - dmasg[0].start_addr = (unsigned long)buf; - dmasg[0].x_modify = 1; - dma_config |= WDSIZE_8 | DMAEN; - if (bytes <= 65536) { - blackfin_dcache_flush_invalidate_range(buf, buf + bytes); - ndsize = NDSIZE_5; - dmasg[0].cfg = NDSIZE_0 | dma_config | FLOW_STOP | DI_EN; - dmasg[0].x_count = bytes; - } else { - blackfin_dcache_flush_invalidate_range(buf, buf + 65536 - 1); - ndsize = NDSIZE_7; - dmasg[0].cfg = NDSIZE_5 | dma_config | FLOW_ARRAY | DMA2D; - dmasg[0].x_count = 0; /* 2^16 */ - dmasg[0].y_count = bytes >> 16; /* count / 2^16 */ - dmasg[0].y_modify = 1; - dmasg[1].start_addr = (unsigned long)(buf + (bytes & ~0xFFFF)); - dmasg[1].cfg = NDSIZE_0 | dma_config | FLOW_STOP | DI_EN; - dmasg[1].x_count = bytes & 0xFFFF; /* count % 2^16 */ - dmasg[1].x_modify = 1; - } - - dma->cfg = 0; - dma->irq_status = DMA_DONE | DMA_ERR; - dma->curr_desc_ptr = dmasg; - write_SPI_CTL(bss, (bss->ctl & ~TDBR_CORE)); - write_SPI_STAT(bss, -1); - SSYNC(); - - write_SPI_TDBR(bss, CONFIG_BFIN_SPI_IDLE_VAL); - dma->cfg = ndsize | FLOW_ARRAY | DMAEN; - write_SPI_CTL(bss, (bss->ctl & ~TDBR_CORE) | spi_config); - SSYNC(); - - /* - * We already invalidated the first 64k, - * now while we just wait invalidate the remaining part. - * Its not likely that the DMA is going to overtake - */ - if (bytes > 65536) - blackfin_dcache_flush_invalidate_range(buf + 65536, buf + bytes); - - while (!(dma->irq_status & DMA_DONE)) - if (ctrlc()) - goto done; - - dma->cfg = 0; - - ret = 0; - done: - write_SPI_CTL(bss, bss->ctl); - return ret; -} - static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, uint bytes) { + /* discard invalid data and clear RXS */ + read_SPI_RDBR(bss); /* todo: take advantage of hardware fifos */ while (bytes--) { u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); @@ -393,11 +298,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, if (flags & SPI_XFER_BEGIN) spi_cs_activate(slave); - /* TX DMA doesn't work quite right */ - if (SPI_DMA && bytes > 6 && (!tx /*|| !rx*/)) - ret = spi_dma_xfer(bss, tx, rx, bytes); - else - ret = spi_pio_xfer(bss, tx, rx, bytes); + ret = spi_pio_xfer(bss, tx, rx, bytes); done: if (flags & SPI_XFER_END) diff --git a/drivers/usb/eth/smsc95xx.c b/drivers/usb/eth/smsc95xx.c index fd8f8a7..685702e 100644 --- a/drivers/usb/eth/smsc95xx.c +++ b/drivers/usb/eth/smsc95xx.c @@ -798,6 +798,7 @@ struct smsc95xx_dongle { static const struct smsc95xx_dongle smsc95xx_dongles[] = { { 0x0424, 0xec00 }, /* LAN9512/LAN9514 Ethernet */ { 0x0424, 0x9500 }, /* LAN9500 Ethernet */ + { 0x0424, 0x9730 }, /* LAN9730 Ethernet (HSIC) */ { 0x0000, 0x0000 } /* END - Do not remove */ }; diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index c816878..e0f3e4b 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -122,6 +122,31 @@ static struct descriptor { #define ehci_is_TDI() (0) #endif +int __ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) +{ + return PORTSC_PSPD(reg); +} + +int ehci_get_port_speed(struct ehci_hcor *hcor, uint32_t reg) + __attribute__((weak, alias("__ehci_get_port_speed"))); + +void __ehci_set_usbmode(int index) +{ + uint32_t tmp; + uint32_t *reg_ptr; + + reg_ptr = (uint32_t *)((u8 *)&ehcic[index].hcor->or_usbcmd + USBMODE); + tmp = ehci_readl(reg_ptr); + tmp |= USBMODE_CM_HC; +#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) + tmp |= USBMODE_BE; +#endif + ehci_writel(reg_ptr, tmp); +} + +void ehci_set_usbmode(int index) + __attribute__((weak, alias("__ehci_set_usbmode"))); + void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) { mdelay(50); @@ -149,8 +174,6 @@ static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec) static int ehci_reset(int index) { uint32_t cmd; - uint32_t tmp; - uint32_t *reg_ptr; int ret = 0; cmd = ehci_readl(&ehcic[index].hcor->or_usbcmd); @@ -163,15 +186,8 @@ static int ehci_reset(int index) goto out; } - if (ehci_is_TDI()) { - reg_ptr = (uint32_t *)((u8 *)ehcic[index].hcor + USBMODE); - tmp = ehci_readl(reg_ptr); - tmp |= USBMODE_CM_HC; -#if defined(CONFIG_EHCI_MMIO_BIG_ENDIAN) - tmp |= USBMODE_BE; -#endif - ehci_writel(reg_ptr, tmp); - } + if (ehci_is_TDI()) + ehci_set_usbmode(index); #ifdef CONFIG_USB_EHCI_TXFIFO_THRESH cmd = ehci_readl(&ehcic[index].hcor->or_txfilltuning); @@ -587,16 +603,6 @@ fail: return -1; } -static inline int min3(int a, int b, int c) -{ - - if (b < a) - a = b; - if (c < a) - a = c; - return a; -} - int ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int length, struct devrequest *req) @@ -607,15 +613,14 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, int len, srclen; uint32_t reg; uint32_t *status_reg; + int port = le16_to_cpu(req->index) & 0xff; struct ehci_ctrl *ctrl = dev->controller; - if (le16_to_cpu(req->index) > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { - printf("The request port(%d) is not configured\n", - le16_to_cpu(req->index) - 1); + if (port > CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS) { + printf("The request port(%d) is not configured\n", port - 1); return -1; } - status_reg = (uint32_t *)&ctrl->hcor->or_portsc[ - le16_to_cpu(req->index) - 1]; + status_reg = (uint32_t *)&ctrl->hcor->or_portsc[port - 1]; srclen = 0; debug("req=%u (%#x), type=%u (%#x), value=%u, index=%u\n", @@ -711,7 +716,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[1] |= USB_PORT_STAT_POWER >> 8; if (ehci_is_TDI()) { - switch (PORTSC_PSPD(reg)) { + switch (ehci_get_port_speed(ctrl->hcor, reg)) { case PORTSC_PSPD_FS: break; case PORTSC_PSPD_LS: @@ -732,7 +737,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, tmpbuf[2] |= USB_PORT_STAT_C_ENABLE; if (reg & EHCI_PS_OCC) tmpbuf[2] |= USB_PORT_STAT_C_OVERCURRENT; - if (ctrl->portreset & (1 << le16_to_cpu(req->index))) + if (ctrl->portreset & (1 << port)) tmpbuf[2] |= USB_PORT_STAT_C_RESET; srcptr = tmpbuf; @@ -758,7 +763,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, EHCI_PS_IS_LOWSPEED(reg)) { /* Low speed device, give up ownership. */ debug("port %d low speed --> companion\n", - req->index - 1); + port - 1); reg |= EHCI_PS_PO; ehci_writel(status_reg, reg); break; @@ -784,13 +789,17 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, ret = handshake(status_reg, EHCI_PS_PR, 0, 2 * 1000); if (!ret) - ctrl->portreset |= - 1 << le16_to_cpu(req->index); + ctrl->portreset |= 1 << port; else printf("port(%d) reset error\n", - le16_to_cpu(req->index) - 1); + port - 1); } break; + case USB_PORT_FEAT_TEST: + reg &= ~(0xf << 16); + reg |= ((le16_to_cpu(req->index) >> 8) & 0xf) << 16; + ehci_writel(status_reg, reg); + break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); goto unknown; @@ -817,7 +826,7 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer, reg = (reg & ~EHCI_PS_CLEAR) | EHCI_PS_OCC; break; case USB_PORT_FEAT_C_RESET: - ctrl->portreset &= ~(1 << le16_to_cpu(req->index)); + ctrl->portreset &= ~(1 << port); break; default: debug("unknown feature %x\n", le16_to_cpu(req->value)); diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index 086cd0f..2060a3e 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -60,7 +60,7 @@ int usb_cpu_init(void) writel(1 << ATMEL_ID_UHP, &pmc->pcer); #endif -#ifdef CONFIG_AT91SAM9261 +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scer); #else writel(ATMEL_PMC_UHP, &pmc->scer); @@ -80,7 +80,7 @@ int usb_cpu_stop(void) writel(1 << ATMEL_ID_UHP, &pmc->pcdr); #endif -#ifdef CONFIG_AT91SAM9261 +#if defined(CONFIG_AT91SAM9261) || defined(CONFIG_AT91SAM9G10) writel(ATMEL_PMC_UHP | AT91_PMC_HCK0, &pmc->scdr); #else writel(ATMEL_PMC_UHP, &pmc->scdr); diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index 13e7c37..d57578d 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -33,6 +33,7 @@ endif COBJS-$(CONFIG_TNETV107X_WATCHDOG) += tnetv107x_wdt.o COBJS-$(CONFIG_S5P) += s5p_wdt.o COBJS-$(CONFIG_XILINX_TB_WATCHDOG) += xilinx_tb_wdt.o +COBJS-$(CONFIG_BFIN_WATCHDOG) += bfin_wdt.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/watchdog/bfin_wdt.c b/drivers/watchdog/bfin_wdt.c new file mode 100644 index 0000000..7a6756b --- /dev/null +++ b/drivers/watchdog/bfin_wdt.c @@ -0,0 +1,26 @@ +/* + * watchdog.c - driver for Blackfin on-chip watchdog + * + * Copyright (c) 2007-2009 Analog Devices Inc. + * + * Licensed under the GPL-2 or later. + */ + +#include <common.h> +#include <watchdog.h> +#include <asm/blackfin.h> +#include <asm/mach-common/bits/watchdog.h> + +void hw_watchdog_reset(void) +{ + bfin_write_WDOG_STAT(0); +} + +void hw_watchdog_init(void) +{ + bfin_write_WDOG_CTL(WDDIS); + SSYNC(); + bfin_write_WDOG_CNT(CONFIG_WATCHDOG_TIMEOUT_MSECS / 1000 * get_sclk()); + hw_watchdog_reset(); + bfin_write_WDOG_CTL(WDEN); +} |