From 078078cfa91f72331421e6f7a46938a58a9b21a7 Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Tue, 15 May 2012 14:32:40 -0700 Subject: spi: Tegra2: Seaboard: fix UART corruption during SPI transactions Simon Glass's proposal to fix this on Seaboard was NAK'd, so I removed his NS16550 references and added a small delay before SPI/UART muxing. Tested on my Seaboard with large SPI reads/writes and saw no corruption (crc's matched) and no spurious comm chars. Signed-off-by: Tom Warren Acked-by: Simon Glass Tested-by: Jimmy Zhang --- drivers/spi/tegra2_spi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/tegra2_spi.c b/drivers/spi/tegra2_spi.c index 56cb229..fe7b405 100644 --- a/drivers/spi/tegra2_spi.c +++ b/drivers/spi/tegra2_spi.c @@ -28,13 +28,18 @@ #include #include #include -#include #include #include #include #include #include +#if defined(CONFIG_SPI_CORRUPTS_UART) + #define corrupt_delay() udelay(CONFIG_SPI_CORRUPTS_UART_DLY); +#else + #define corrupt_delay() +#endif + struct tegra_spi_slave { struct spi_slave slave; struct spi_tegra *regs; @@ -161,14 +166,20 @@ void spi_cs_activate(struct spi_slave *slave) /* CS is negated on Tegra, so drive a 1 to get a 0 */ setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); + + corrupt_delay(); /* Let UART settle */ } void spi_cs_deactivate(struct spi_slave *slave) { struct tegra_spi_slave *spi = to_tegra_spi(slave); + pinmux_select_uart(); + /* CS is negated on Tegra, so drive a 0 to get a 1 */ clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); + + corrupt_delay(); /* Let SPI settle */ } int spi_xfer(struct spi_slave *slave, unsigned int bitlen, -- cgit v1.1 From 1edaf094dc1a89c24017da4fd30723bd1c64ac46 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 24 May 2012 11:38:34 +0000 Subject: sf: winbond: Add support for the Winbond W25Q80BL This chip is present on the Compulab TrimSlice. Signed-off-by: Stephen Warren Signed-off-by: Tom Warren --- drivers/mtd/spi/winbond.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/spi/winbond.c b/drivers/mtd/spi/winbond.c index a6a6653..c20faa2 100644 --- a/drivers/mtd/spi/winbond.c +++ b/drivers/mtd/spi/winbond.c @@ -68,6 +68,14 @@ static const struct winbond_spi_flash_params winbond_spi_flash_table[] = { .name = "W25X64", }, { + .id = 0x4014, + .l2_page_size = 8, + .pages_per_sector = 16, + .sectors_per_block = 16, + .nr_blocks = 16, + .name = "W25Q80BL", + }, + { .id = 0x4015, .l2_page_size = 8, .pages_per_sector = 16, -- cgit v1.1 From edffa63d3d6e76991998789f9fcbaa483731ca65 Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Tue, 22 May 2012 07:33:47 +0000 Subject: spi: tegra2: rename tegra2_spi.* to tegra_spi.* In anticipation of Tegra3 support, start removing/renaming Tegra2-specific files. No functional changes (yet). Also updated copyright to 2012. Signed-off-by: Tom Warren --- drivers/spi/Makefile | 2 +- drivers/spi/tegra2_spi.c | 290 ----------------------------------------------- drivers/spi/tegra_spi.c | 290 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 291 insertions(+), 291 deletions(-) delete mode 100644 drivers/spi/tegra2_spi.c create mode 100644 drivers/spi/tegra_spi.c (limited to 'drivers') diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile index c967d87..c20f1f2 100644 --- a/drivers/spi/Makefile +++ b/drivers/spi/Makefile @@ -43,7 +43,7 @@ COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o COBJS-$(CONFIG_SH_SPI) += sh_spi.o COBJS-$(CONFIG_FSL_ESPI) += fsl_espi.o -COBJS-$(CONFIG_TEGRA2_SPI) += tegra2_spi.o +COBJS-$(CONFIG_TEGRA_SPI) += tegra_spi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/spi/tegra2_spi.c b/drivers/spi/tegra2_spi.c deleted file mode 100644 index fe7b405..0000000 --- a/drivers/spi/tegra2_spi.c +++ /dev/null @@ -1,290 +0,0 @@ -/* - * Copyright (c) 2010-2011 NVIDIA Corporation - * With help from the mpc8xxx SPI driver - * With more help from omap3_spi SPI driver - * - * 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 - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if defined(CONFIG_SPI_CORRUPTS_UART) - #define corrupt_delay() udelay(CONFIG_SPI_CORRUPTS_UART_DLY); -#else - #define corrupt_delay() -#endif - -struct tegra_spi_slave { - struct spi_slave slave; - struct spi_tegra *regs; - unsigned int freq; - unsigned int mode; -}; - -static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) -{ - return container_of(slave, struct tegra_spi_slave, slave); -} - -int spi_cs_is_valid(unsigned int bus, unsigned int cs) -{ - /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ - if (bus != 0 || cs != 0) - return 0; - else - return 1; -} - -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) -{ - struct tegra_spi_slave *spi; - - if (!spi_cs_is_valid(bus, cs)) { - printf("SPI error: unsupported bus %d / chip select %d\n", - bus, cs); - return NULL; - } - - if (max_hz > TEGRA2_SPI_MAX_FREQ) { - printf("SPI error: unsupported frequency %d Hz. Max frequency" - " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ); - return NULL; - } - - spi = malloc(sizeof(struct tegra_spi_slave)); - if (!spi) { - printf("SPI error: malloc of SPI structure failed\n"); - return NULL; - } - spi->slave.bus = bus; - spi->slave.cs = cs; - spi->freq = max_hz; - spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE; - spi->mode = mode; - - return &spi->slave; -} - -void spi_free_slave(struct spi_slave *slave) -{ - struct tegra_spi_slave *spi = to_tegra_spi(slave); - - free(spi); -} - -void spi_init(void) -{ - /* do nothing */ -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_tegra *regs = spi->regs; - u32 reg; - - /* Change SPI clock to correct frequency, PLLP_OUT0 source */ - clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, spi->freq); - - /* Clear stale status here */ - reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ - SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; - writel(reg, ®s->status); - debug("spi_init: STATUS = %08x\n", readl(®s->status)); - - /* - * Use sw-controlled CS, so we can clock in data after ReadID, etc. - */ - reg = (spi->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; - if (spi->mode & 2) - reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; - clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | - SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); - debug("spi_init: COMMAND = %08x\n", readl(®s->command)); - - /* - * SPI pins on Tegra2 are muxed - change pinmux later due to UART - * issue. - */ - pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); - pinmux_tristate_disable(PINGRP_LSPI); - -#ifndef CONFIG_SPI_UART_SWITCH - /* - * NOTE: - * Only set PinMux bits 3:2 to SPI here on boards that don't have the - * SPI UART switch or subsequent UART data won't go out! See - * spi_uart_switch(). - */ - /* TODO: pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); */ -#endif - return 0; -} - -void spi_release_bus(struct spi_slave *slave) -{ - /* - * We can't release UART_DISABLE and set pinmux to UART4 here since - * some code (e,g, spi_flash_probe) uses printf() while the SPI - * bus is held. That is arguably bad, but it has the advantage of - * already being in the source tree. - */ -} - -void spi_cs_activate(struct spi_slave *slave) -{ - struct tegra_spi_slave *spi = to_tegra_spi(slave); - - pinmux_select_spi(); - - /* CS is negated on Tegra, so drive a 1 to get a 0 */ - setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); - - corrupt_delay(); /* Let UART settle */ -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct tegra_spi_slave *spi = to_tegra_spi(slave); - - pinmux_select_uart(); - - /* CS is negated on Tegra, so drive a 0 to get a 1 */ - clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); - - corrupt_delay(); /* Let SPI settle */ -} - -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *data_out, void *data_in, unsigned long flags) -{ - struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_tegra *regs = spi->regs; - u32 reg, tmpdout, tmpdin = 0; - const u8 *dout = data_out; - u8 *din = data_in; - int num_bytes; - int ret; - - debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", - slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); - if (bitlen % 8) - return -1; - num_bytes = bitlen / 8; - - ret = 0; - - reg = readl(®s->status); - writel(reg, ®s->status); /* Clear all SPI events via R/W */ - debug("spi_xfer entry: STATUS = %08x\n", reg); - - reg = readl(®s->command); - reg |= SPI_CMD_TXEN | SPI_CMD_RXEN; - writel(reg, ®s->command); - debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); - - if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); - - /* handle data in 32-bit chunks */ - while (num_bytes > 0) { - int bytes; - int is_read = 0; - int tm, i; - - tmpdout = 0; - bytes = (num_bytes > 4) ? 4 : num_bytes; - - if (dout != NULL) { - for (i = 0; i < bytes; ++i) - tmpdout = (tmpdout << 8) | dout[i]; - } - - num_bytes -= bytes; - if (dout) - dout += bytes; - - clrsetbits_le32(®s->command, SPI_CMD_BIT_LENGTH_MASK, - bytes * 8 - 1); - writel(tmpdout, ®s->tx_fifo); - setbits_le32(®s->command, SPI_CMD_GO); - - /* - * Wait for SPI transmit FIFO to empty, or to time out. - * The RX FIFO status will be read and cleared last - */ - for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { - u32 status; - - status = readl(®s->status); - - /* We can exit when we've had both RX and TX activity */ - if (is_read && (status & SPI_STAT_TXF_EMPTY)) - break; - - if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) != - SPI_STAT_RDY) - tm++; - - else if (!(status & SPI_STAT_RXF_EMPTY)) { - tmpdin = readl(®s->rx_fifo); - is_read = 1; - - /* swap bytes read in */ - if (din != NULL) { - for (i = bytes - 1; i >= 0; --i) { - din[i] = tmpdin & 0xff; - tmpdin >>= 8; - } - din += bytes; - } - } - } - - if (tm >= SPI_TIMEOUT) - ret = tm; - - /* clear ACK RDY, etc. bits */ - writel(readl(®s->status), ®s->status); - } - - if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); - - debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", - tmpdin, readl(®s->status)); - - if (ret) { - printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret); - return -1; - } - - return 0; -} diff --git a/drivers/spi/tegra_spi.c b/drivers/spi/tegra_spi.c new file mode 100644 index 0000000..4a3e799 --- /dev/null +++ b/drivers/spi/tegra_spi.c @@ -0,0 +1,290 @@ +/* + * Copyright (c) 2010-2012 NVIDIA Corporation + * With help from the mpc8xxx SPI driver + * With more help from omap3_spi SPI driver + * + * 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_SPI_CORRUPTS_UART) + #define corrupt_delay() udelay(CONFIG_SPI_CORRUPTS_UART_DLY); +#else + #define corrupt_delay() +#endif + +struct tegra_spi_slave { + struct spi_slave slave; + struct spi_tegra *regs; + unsigned int freq; + unsigned int mode; +}; + +static inline struct tegra_spi_slave *to_tegra_spi(struct spi_slave *slave) +{ + return container_of(slave, struct tegra_spi_slave, slave); +} + +int spi_cs_is_valid(unsigned int bus, unsigned int cs) +{ + /* Tegra2 SPI-Flash - only 1 device ('bus/cs') */ + if (bus != 0 || cs != 0) + return 0; + else + return 1; +} + +struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, + unsigned int max_hz, unsigned int mode) +{ + struct tegra_spi_slave *spi; + + if (!spi_cs_is_valid(bus, cs)) { + printf("SPI error: unsupported bus %d / chip select %d\n", + bus, cs); + return NULL; + } + + if (max_hz > TEGRA2_SPI_MAX_FREQ) { + printf("SPI error: unsupported frequency %d Hz. Max frequency" + " is %d Hz\n", max_hz, TEGRA2_SPI_MAX_FREQ); + return NULL; + } + + spi = malloc(sizeof(struct tegra_spi_slave)); + if (!spi) { + printf("SPI error: malloc of SPI structure failed\n"); + return NULL; + } + spi->slave.bus = bus; + spi->slave.cs = cs; + spi->freq = max_hz; + spi->regs = (struct spi_tegra *)TEGRA2_SPI_BASE; + spi->mode = mode; + + return &spi->slave; +} + +void spi_free_slave(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + free(spi); +} + +void spi_init(void) +{ + /* do nothing */ +} + +int spi_claim_bus(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_tegra *regs = spi->regs; + u32 reg; + + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(PERIPH_ID_SPI1, CLOCK_ID_PERIPH, spi->freq); + + /* Clear stale status here */ + reg = SPI_STAT_RDY | SPI_STAT_RXF_FLUSH | SPI_STAT_TXF_FLUSH | \ + SPI_STAT_RXF_UNR | SPI_STAT_TXF_OVF; + writel(reg, ®s->status); + debug("spi_init: STATUS = %08x\n", readl(®s->status)); + + /* + * Use sw-controlled CS, so we can clock in data after ReadID, etc. + */ + reg = (spi->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; + if (spi->mode & 2) + reg |= 1 << SPI_CMD_ACTIVE_SCLK_SHIFT; + clrsetbits_le32(®s->command, SPI_CMD_ACTIVE_SCLK_MASK | + SPI_CMD_ACTIVE_SDA_MASK, SPI_CMD_CS_SOFT | reg); + debug("spi_init: COMMAND = %08x\n", readl(®s->command)); + + /* + * SPI pins on Tegra2 are muxed - change pinmux later due to UART + * issue. + */ + pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); + pinmux_tristate_disable(PINGRP_LSPI); + +#ifndef CONFIG_SPI_UART_SWITCH + /* + * NOTE: + * Only set PinMux bits 3:2 to SPI here on boards that don't have the + * SPI UART switch or subsequent UART data won't go out! See + * spi_uart_switch(). + */ + /* TODO: pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); */ +#endif + return 0; +} + +void spi_release_bus(struct spi_slave *slave) +{ + /* + * We can't release UART_DISABLE and set pinmux to UART4 here since + * some code (e,g, spi_flash_probe) uses printf() while the SPI + * bus is held. That is arguably bad, but it has the advantage of + * already being in the source tree. + */ +} + +void spi_cs_activate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + pinmux_select_spi(); + + /* CS is negated on Tegra, so drive a 1 to get a 0 */ + setbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); + + corrupt_delay(); /* Let UART settle */ +} + +void spi_cs_deactivate(struct spi_slave *slave) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + + pinmux_select_uart(); + + /* CS is negated on Tegra, so drive a 0 to get a 1 */ + clrbits_le32(&spi->regs->command, SPI_CMD_CS_VAL); + + corrupt_delay(); /* Let SPI settle */ +} + +int spi_xfer(struct spi_slave *slave, unsigned int bitlen, + const void *data_out, void *data_in, unsigned long flags) +{ + struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct spi_tegra *regs = spi->regs; + u32 reg, tmpdout, tmpdin = 0; + const u8 *dout = data_out; + u8 *din = data_in; + int num_bytes; + int ret; + + debug("spi_xfer: slave %u:%u dout %08X din %08X bitlen %u\n", + slave->bus, slave->cs, *(u8 *)dout, *(u8 *)din, bitlen); + if (bitlen % 8) + return -1; + num_bytes = bitlen / 8; + + ret = 0; + + reg = readl(®s->status); + writel(reg, ®s->status); /* Clear all SPI events via R/W */ + debug("spi_xfer entry: STATUS = %08x\n", reg); + + reg = readl(®s->command); + reg |= SPI_CMD_TXEN | SPI_CMD_RXEN; + writel(reg, ®s->command); + debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); + + if (flags & SPI_XFER_BEGIN) + spi_cs_activate(slave); + + /* handle data in 32-bit chunks */ + while (num_bytes > 0) { + int bytes; + int is_read = 0; + int tm, i; + + tmpdout = 0; + bytes = (num_bytes > 4) ? 4 : num_bytes; + + if (dout != NULL) { + for (i = 0; i < bytes; ++i) + tmpdout = (tmpdout << 8) | dout[i]; + } + + num_bytes -= bytes; + if (dout) + dout += bytes; + + clrsetbits_le32(®s->command, SPI_CMD_BIT_LENGTH_MASK, + bytes * 8 - 1); + writel(tmpdout, ®s->tx_fifo); + setbits_le32(®s->command, SPI_CMD_GO); + + /* + * Wait for SPI transmit FIFO to empty, or to time out. + * The RX FIFO status will be read and cleared last + */ + for (tm = 0, is_read = 0; tm < SPI_TIMEOUT; ++tm) { + u32 status; + + status = readl(®s->status); + + /* We can exit when we've had both RX and TX activity */ + if (is_read && (status & SPI_STAT_TXF_EMPTY)) + break; + + if ((status & (SPI_STAT_BSY | SPI_STAT_RDY)) != + SPI_STAT_RDY) + tm++; + + else if (!(status & SPI_STAT_RXF_EMPTY)) { + tmpdin = readl(®s->rx_fifo); + is_read = 1; + + /* swap bytes read in */ + if (din != NULL) { + for (i = bytes - 1; i >= 0; --i) { + din[i] = tmpdin & 0xff; + tmpdin >>= 8; + } + din += bytes; + } + } + } + + if (tm >= SPI_TIMEOUT) + ret = tm; + + /* clear ACK RDY, etc. bits */ + writel(readl(®s->status), ®s->status); + } + + if (flags & SPI_XFER_END) + spi_cs_deactivate(slave); + + debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", + tmpdin, readl(®s->status)); + + if (ret) { + printf("spi_xfer: timeout during SPI transfer, tm %d\n", ret); + return -1; + } + + return 0; +} -- cgit v1.1 From 3f82d89d3de76acf86279815a7752f7c04d043a2 Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Tue, 22 May 2012 11:44:48 +0000 Subject: mmc: tegra2: rename tegra2_mmc.* to tegra_mmc.* In anticipation of Tegra3 support, continue removing/renaming Tegra2-specific files. No functional changes (yet). Updated copyrights to 2012. Signed-off-by: Tom Warren --- drivers/mmc/Makefile | 2 +- drivers/mmc/tegra2_mmc.c | 570 ----------------------------------------------- drivers/mmc/tegra2_mmc.h | 131 ----------- drivers/mmc/tegra_mmc.c | 570 +++++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/tegra_mmc.h | 131 +++++++++++ 5 files changed, 702 insertions(+), 702 deletions(-) delete mode 100644 drivers/mmc/tegra2_mmc.c delete mode 100644 drivers/mmc/tegra2_mmc.h create mode 100644 drivers/mmc/tegra_mmc.c create mode 100644 drivers/mmc/tegra_mmc.h (limited to 'drivers') diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index a8e681c..c567737 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -42,7 +42,7 @@ COBJS-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o COBJS-$(CONFIG_SDHCI) += sdhci.o COBJS-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o COBJS-$(CONFIG_SH_MMCIF) += sh_mmcif.o -COBJS-$(CONFIG_TEGRA2_MMC) += tegra2_mmc.o +COBJS-$(CONFIG_TEGRA_MMC) += tegra_mmc.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mmc/tegra2_mmc.c b/drivers/mmc/tegra2_mmc.c deleted file mode 100644 index fb8a57d..0000000 --- a/drivers/mmc/tegra2_mmc.c +++ /dev/null @@ -1,570 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang - * Jaehoon Chung - * Portions Copyright 2011 NVIDIA Corporation - * - * 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 -#include -#include -#include -#include -#include -#include "tegra2_mmc.h" - -/* support 4 mmc hosts */ -struct mmc mmc_dev[4]; -struct mmc_host mmc_host[4]; - - -/** - * Get the host address and peripheral ID for a device. Devices are numbered - * from 0 to 3. - * - * @param host Structure to fill in (base, reg, mmc_id) - * @param dev_index Device index (0-3) - */ -static void tegra2_get_setup(struct mmc_host *host, int dev_index) -{ - debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index); - - switch (dev_index) { - case 1: - host->base = TEGRA2_SDMMC3_BASE; - host->mmc_id = PERIPH_ID_SDMMC3; - break; - case 2: - host->base = TEGRA2_SDMMC2_BASE; - host->mmc_id = PERIPH_ID_SDMMC2; - break; - case 3: - host->base = TEGRA2_SDMMC1_BASE; - host->mmc_id = PERIPH_ID_SDMMC1; - break; - case 0: - default: - host->base = TEGRA2_SDMMC4_BASE; - host->mmc_id = PERIPH_ID_SDMMC4; - break; - } - - host->reg = (struct tegra2_mmc *)host->base; -} - -static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) -{ - unsigned char ctrl; - - debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", - (u32)data->dest, data->blocks, data->blocksize); - - writel((u32)data->dest, &host->reg->sysad); - /* - * DMASEL[4:3] - * 00 = Selects SDMA - * 01 = Reserved - * 10 = Selects 32-bit Address ADMA2 - * 11 = Selects 64-bit Address ADMA2 - */ - ctrl = readb(&host->reg->hostctl); - ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; - ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; - writeb(ctrl, &host->reg->hostctl); - - /* We do not handle DMA boundaries, so set it to max (512 KiB) */ - writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); - writew(data->blocks, &host->reg->blkcnt); -} - -static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) -{ - unsigned short mode; - debug(" mmc_set_transfer_mode called\n"); - /* - * TRNMOD - * MUL1SIN0[5] : Multi/Single Block Select - * RD1WT0[4] : Data Transfer Direction Select - * 1 = read - * 0 = write - * ENACMD12[2] : Auto CMD12 Enable - * ENBLKCNT[1] : Block Count Enable - * ENDMA[0] : DMA Enable - */ - mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | - TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); - - if (data->blocks > 1) - mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; - - if (data->flags & MMC_DATA_READ) - mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; - - if (data->flags & MMC_DATA_WRITE) { - if ((uintptr_t)data->src & (ARCH_DMA_MINALIGN - 1)) - printf("Warning: unaligned write to %p may fail\n", - data->src); - flush_dcache_range((ulong)data->src, (ulong)data->src + - data->blocks * data->blocksize); - } - - writew(mode, &host->reg->trnmod); -} - -static int mmc_wait_inhibit(struct mmc_host *host, - struct mmc_cmd *cmd, - struct mmc_data *data, - unsigned int timeout) -{ - /* - * PRNSTS - * CMDINHDAT[1] : Command Inhibit (DAT) - * CMDINHCMD[0] : Command Inhibit (CMD) - */ - unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; - - /* - * We shouldn't wait for data inhibit for stop commands, even - * though they might use busy signaling - */ - if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) - mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; - - while (readl(&host->reg->prnsts) & mask) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return -1; - } - timeout--; - udelay(1000); - } - - return 0; -} - -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - int flags, i; - int result; - unsigned int mask = 0; - unsigned int retry = 0x100000; - debug(" mmc_send_cmd called\n"); - - result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); - - if (result < 0) - return result; - - if (data) - mmc_prepare_data(host, data); - - debug("cmd->arg: %08x\n", cmd->cmdarg); - writel(cmd->cmdarg, &host->reg->argument); - - if (data) - mmc_set_transfer_mode(host, data); - - if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) - return -1; - - /* - * CMDREG - * CMDIDX[13:8] : Command index - * DATAPRNT[5] : Data Present Select - * ENCMDIDX[4] : Command Index Check Enable - * ENCMDCRC[3] : Command CRC Check Enable - * RSPTYP[1:0] - * 00 = No Response - * 01 = Length 136 - * 10 = Length 48 - * 11 = Length 48 Check busy after response - */ - if (!(cmd->resp_type & MMC_RSP_PRESENT)) - flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; - else if (cmd->resp_type & MMC_RSP_136) - flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; - else if (cmd->resp_type & MMC_RSP_BUSY) - flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; - else - flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; - - if (cmd->resp_type & MMC_RSP_CRC) - flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; - if (cmd->resp_type & MMC_RSP_OPCODE) - flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; - if (data) - flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; - - debug("cmd: %d\n", cmd->cmdidx); - - writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); - - for (i = 0; i < retry; i++) { - mask = readl(&host->reg->norintsts); - /* Command Complete */ - if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { - if (!data) - writel(mask, &host->reg->norintsts); - break; - } - } - - if (i == retry) { - printf("%s: waiting for status update\n", __func__); - writel(mask, &host->reg->norintsts); - return TIMEOUT; - } - - if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { - /* Timeout Error */ - debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); - writel(mask, &host->reg->norintsts); - return TIMEOUT; - } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { - /* Error Interrupt */ - debug("error: %08x cmd %d\n", mask, cmd->cmdidx); - writel(mask, &host->reg->norintsts); - return -1; - } - - if (cmd->resp_type & MMC_RSP_PRESENT) { - if (cmd->resp_type & MMC_RSP_136) { - /* CRC is stripped so we need to do some shifting. */ - for (i = 0; i < 4; i++) { - unsigned int offset = - (unsigned int)(&host->reg->rspreg3 - i); - cmd->response[i] = readl(offset) << 8; - - if (i != 3) { - cmd->response[i] |= - readb(offset - 1); - } - debug("cmd->resp[%d]: %08x\n", - i, cmd->response[i]); - } - } else if (cmd->resp_type & MMC_RSP_BUSY) { - for (i = 0; i < retry; i++) { - /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ - if (readl(&host->reg->prnsts) - & (1 << 20)) /* DAT[0] */ - break; - } - - if (i == retry) { - printf("%s: card is still busy\n", __func__); - writel(mask, &host->reg->norintsts); - return TIMEOUT; - } - - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } else { - cmd->response[0] = readl(&host->reg->rspreg0); - debug("cmd->resp[0]: %08x\n", cmd->response[0]); - } - } - - if (data) { - unsigned long start = get_timer(0); - - while (1) { - mask = readl(&host->reg->norintsts); - - if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { - /* Error Interrupt */ - writel(mask, &host->reg->norintsts); - printf("%s: error during transfer: 0x%08x\n", - __func__, mask); - return -1; - } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { - /* - * DMA Interrupt, restart the transfer where - * it was interrupted. - */ - unsigned int address = readl(&host->reg->sysad); - - debug("DMA end\n"); - writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, - &host->reg->norintsts); - writel(address, &host->reg->sysad); - } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { - /* Transfer Complete */ - debug("r/w is done\n"); - break; - } else if (get_timer(start) > 2000UL) { - writel(mask, &host->reg->norintsts); - printf("%s: MMC Timeout\n" - " Interrupt status 0x%08x\n" - " Interrupt status enable 0x%08x\n" - " Interrupt signal enable 0x%08x\n" - " Present status 0x%08x\n", - __func__, mask, - readl(&host->reg->norintstsen), - readl(&host->reg->norintsigen), - readl(&host->reg->prnsts)); - return -1; - } - } - writel(mask, &host->reg->norintsts); - if (data->flags & MMC_DATA_READ) { - if ((uintptr_t)data->dest & (ARCH_DMA_MINALIGN - 1)) - printf("Warning: unaligned read from %p " - "may fail\n", data->dest); - invalidate_dcache_range((ulong)data->dest, - (ulong)data->dest + - data->blocks * data->blocksize); - } - } - - udelay(1000); - return 0; -} - -static void mmc_change_clock(struct mmc_host *host, uint clock) -{ - int div; - unsigned short clk; - unsigned long timeout; - - debug(" mmc_change_clock called\n"); - - /* - * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz, - * PLLP_OUT0 - */ - if (clock == 0) - goto out; - clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, - &div); - debug("div = %d\n", div); - - writew(0, &host->reg->clkcon); - - /* - * CLKCON - * SELFREQ[15:8] : base clock divided by value - * ENSDCLK[2] : SD Clock Enable - * STBLINTCLK[1] : Internal Clock Stable - * ENINTCLK[0] : Internal Clock Enable - */ - div >>= 1; - clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | - TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); - writew(clk, &host->reg->clkcon); - - /* Wait max 10 ms */ - timeout = 10; - while (!(readw(&host->reg->clkcon) & - TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } - - clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; - writew(clk, &host->reg->clkcon); - - debug("mmc_change_clock: clkcon = %08X\n", clk); - -out: - host->clock = clock; -} - -static void mmc_set_ios(struct mmc *mmc) -{ - struct mmc_host *host = mmc->priv; - unsigned char ctrl; - debug(" mmc_set_ios called\n"); - - debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); - - /* Change clock first */ - mmc_change_clock(host, mmc->clock); - - ctrl = readb(&host->reg->hostctl); - - /* - * WIDE8[5] - * 0 = Depend on WIDE4 - * 1 = 8-bit mode - * WIDE4[1] - * 1 = 4-bit mode - * 0 = 1-bit mode - */ - if (mmc->bus_width == 8) - ctrl |= (1 << 5); - else if (mmc->bus_width == 4) - ctrl |= (1 << 1); - else - ctrl &= ~(1 << 1); - - writeb(ctrl, &host->reg->hostctl); - debug("mmc_set_ios: hostctl = %08X\n", ctrl); -} - -static void mmc_reset(struct mmc_host *host) -{ - unsigned int timeout; - debug(" mmc_reset called\n"); - - /* - * RSTALL[0] : Software reset for all - * 1 = reset - * 0 = work - */ - writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); - - host->clock = 0; - - /* Wait max 100 ms */ - timeout = 100; - - /* hw clears the bit when it's done */ - while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { - if (timeout == 0) { - printf("%s: timeout error\n", __func__); - return; - } - timeout--; - udelay(1000); - } -} - -static int mmc_core_init(struct mmc *mmc) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - unsigned int mask; - debug(" mmc_core_init called\n"); - - mmc_reset(host); - - host->version = readw(&host->reg->hcver); - debug("host version = %x\n", host->version); - - /* mask all */ - writel(0xffffffff, &host->reg->norintstsen); - writel(0xffffffff, &host->reg->norintsigen); - - writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ - /* - * NORMAL Interrupt Status Enable Register init - * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable - * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable - * [3] ENSTADMAINT : DMA boundary interrupt - * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable - * [0] ENSTACMDCMPLT : Command Complete Status Enable - */ - mask = readl(&host->reg->norintstsen); - mask &= ~(0xffff); - mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | - TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | - TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | - TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | - TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); - writel(mask, &host->reg->norintstsen); - - /* - * NORMAL Interrupt Signal Enable Register init - * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable - */ - mask = readl(&host->reg->norintsigen); - mask &= ~(0xffff); - mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; - writel(mask, &host->reg->norintsigen); - - return 0; -} - -int tegra2_mmc_getcd(struct mmc *mmc) -{ - struct mmc_host *host = (struct mmc_host *)mmc->priv; - - debug("tegra2_mmc_getcd called\n"); - - if (host->cd_gpio >= 0) - return !gpio_get_value(host->cd_gpio); - - return 1; -} - -int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) -{ - struct mmc_host *host; - char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ - struct mmc *mmc; - - debug(" tegra2_mmc_init: index %d, bus width %d " - "pwr_gpio %d cd_gpio %d\n", - dev_index, bus_width, pwr_gpio, cd_gpio); - - host = &mmc_host[dev_index]; - - host->clock = 0; - host->pwr_gpio = pwr_gpio; - host->cd_gpio = cd_gpio; - tegra2_get_setup(host, dev_index); - - clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); - - if (host->pwr_gpio >= 0) { - sprintf(gpusage, "SD/MMC%d PWR", dev_index); - gpio_request(host->pwr_gpio, gpusage); - gpio_direction_output(host->pwr_gpio, 1); - } - - if (host->cd_gpio >= 0) { - sprintf(gpusage, "SD/MMC%d CD", dev_index); - gpio_request(host->cd_gpio, gpusage); - gpio_direction_input(host->cd_gpio); - } - - mmc = &mmc_dev[dev_index]; - - sprintf(mmc->name, "Tegra2 SD/MMC"); - mmc->priv = host; - mmc->send_cmd = mmc_send_cmd; - mmc->set_ios = mmc_set_ios; - mmc->init = mmc_core_init; - mmc->getcd = tegra2_mmc_getcd; - - mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - if (bus_width == 8) - mmc->host_caps = MMC_MODE_8BIT; - else - mmc->host_caps = MMC_MODE_4BIT; - mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; - - /* - * min freq is for card identification, and is the highest - * low-speed SDIO card frequency (actually 400KHz) - * max freq is highest HS eMMC clock as per the SD/MMC spec - * (actually 52MHz) - * Both of these are the closest equivalents w/216MHz source - * clock and Tegra2 SDMMC divisors. - */ - mmc->f_min = 375000; - mmc->f_max = 48000000; - - mmc_register(mmc); - - return 0; -} diff --git a/drivers/mmc/tegra2_mmc.h b/drivers/mmc/tegra2_mmc.h deleted file mode 100644 index 67c00db..0000000 --- a/drivers/mmc/tegra2_mmc.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * (C) Copyright 2009 SAMSUNG Electronics - * Minkyu Kang - * Portions Copyright (C) 2011 NVIDIA Corporation - * - * 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 - * - */ - -#ifndef __TEGRA2_MMC_H_ -#define __TEGRA2_MMC_H_ - -#define TEGRA2_SDMMC1_BASE 0xC8000000 -#define TEGRA2_SDMMC2_BASE 0xC8000200 -#define TEGRA2_SDMMC3_BASE 0xC8000400 -#define TEGRA2_SDMMC4_BASE 0xC8000600 - -#ifndef __ASSEMBLY__ -struct tegra2_mmc { - unsigned int sysad; /* _SYSTEM_ADDRESS_0 */ - unsigned short blksize; /* _BLOCK_SIZE_BLOCK_COUNT_0 15:00 */ - unsigned short blkcnt; /* _BLOCK_SIZE_BLOCK_COUNT_0 31:16 */ - unsigned int argument; /* _ARGUMENT_0 */ - unsigned short trnmod; /* _CMD_XFER_MODE_0 15:00 xfer mode */ - unsigned short cmdreg; /* _CMD_XFER_MODE_0 31:16 cmd reg */ - unsigned int rspreg0; /* _RESPONSE_R0_R1_0 CMD RESP 31:00 */ - unsigned int rspreg1; /* _RESPONSE_R2_R3_0 CMD RESP 63:32 */ - unsigned int rspreg2; /* _RESPONSE_R4_R5_0 CMD RESP 95:64 */ - unsigned int rspreg3; /* _RESPONSE_R6_R7_0 CMD RESP 127:96 */ - unsigned int bdata; /* _BUFFER_DATA_PORT_0 */ - unsigned int prnsts; /* _PRESENT_STATE_0 */ - unsigned char hostctl; /* _POWER_CONTROL_HOST_0 7:00 */ - unsigned char pwrcon; /* _POWER_CONTROL_HOST_0 15:8 */ - unsigned char blkgap; /* _POWER_CONTROL_HOST_9 23:16 */ - unsigned char wakcon; /* _POWER_CONTROL_HOST_0 31:24 */ - unsigned short clkcon; /* _CLOCK_CONTROL_0 15:00 */ - unsigned char timeoutcon; /* _TIMEOUT_CTRL 23:16 */ - unsigned char swrst; /* _SW_RESET_ 31:24 */ - unsigned int norintsts; /* _INTERRUPT_STATUS_0 */ - unsigned int norintstsen; /* _INTERRUPT_STATUS_ENABLE_0 */ - unsigned int norintsigen; /* _INTERRUPT_SIGNAL_ENABLE_0 */ - unsigned short acmd12errsts; /* _AUTO_CMD12_ERR_STATUS_0 15:00 */ - unsigned char res1[2]; /* _RESERVED 31:16 */ - unsigned int capareg; /* _CAPABILITIES_0 */ - unsigned char res2[4]; /* RESERVED, offset 44h-47h */ - unsigned int maxcurr; /* _MAXIMUM_CURRENT_0 */ - unsigned char res3[4]; /* RESERVED, offset 4Ch-4Fh */ - unsigned short setacmd12err; /* offset 50h */ - unsigned short setinterr; /* offset 52h */ - unsigned char admaerr; /* offset 54h */ - unsigned char res4[3]; /* RESERVED, offset 55h-57h */ - unsigned long admaaddr; /* offset 58h-5Fh */ - unsigned char res5[0x9c]; /* RESERVED, offset 60h-FBh */ - unsigned short slotintstatus; /* offset FCh */ - unsigned short hcver; /* HOST Version */ - unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */ -}; - -#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) -#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3) - -#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0) -#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1) -#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4) -#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4) -#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5) - -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0) -#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0) - -#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3) -#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4) -#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5) - -#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0) -#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1) - -#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0) -#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1) -#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2) - -#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8 -#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8) - -#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0) -#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1) -#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2) - -#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0) -#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1) -#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3) -#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15) -#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16) - -#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0) -#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1) -#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3) -#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4) -#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5) - -#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1) - -struct mmc_host { - struct tegra2_mmc *reg; - unsigned int version; /* SDHCI spec. version */ - unsigned int clock; /* Current clock (MHz) */ - unsigned int base; /* Base address, SDMMC1/2/3/4 */ - enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */ - int pwr_gpio; /* Power GPIO */ - int cd_gpio; /* Change Detect GPIO */ -}; - -#endif /* __ASSEMBLY__ */ -#endif /* __TEGRA2_MMC_H_ */ diff --git a/drivers/mmc/tegra_mmc.c b/drivers/mmc/tegra_mmc.c new file mode 100644 index 0000000..29bf583 --- /dev/null +++ b/drivers/mmc/tegra_mmc.c @@ -0,0 +1,570 @@ +/* + * (C) Copyright 2009 SAMSUNG Electronics + * Minkyu Kang + * Jaehoon Chung + * Portions Copyright 2011-2012 NVIDIA Corporation + * + * 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 +#include +#include +#include +#include +#include +#include "tegra_mmc.h" + +/* support 4 mmc hosts */ +struct mmc mmc_dev[4]; +struct mmc_host mmc_host[4]; + + +/** + * Get the host address and peripheral ID for a device. Devices are numbered + * from 0 to 3. + * + * @param host Structure to fill in (base, reg, mmc_id) + * @param dev_index Device index (0-3) + */ +static void tegra2_get_setup(struct mmc_host *host, int dev_index) +{ + debug("tegra2_get_base_mmc: dev_index = %d\n", dev_index); + + switch (dev_index) { + case 1: + host->base = TEGRA2_SDMMC3_BASE; + host->mmc_id = PERIPH_ID_SDMMC3; + break; + case 2: + host->base = TEGRA2_SDMMC2_BASE; + host->mmc_id = PERIPH_ID_SDMMC2; + break; + case 3: + host->base = TEGRA2_SDMMC1_BASE; + host->mmc_id = PERIPH_ID_SDMMC1; + break; + case 0: + default: + host->base = TEGRA2_SDMMC4_BASE; + host->mmc_id = PERIPH_ID_SDMMC4; + break; + } + + host->reg = (struct tegra2_mmc *)host->base; +} + +static void mmc_prepare_data(struct mmc_host *host, struct mmc_data *data) +{ + unsigned char ctrl; + + debug("data->dest: %08X, data->blocks: %u, data->blocksize: %u\n", + (u32)data->dest, data->blocks, data->blocksize); + + writel((u32)data->dest, &host->reg->sysad); + /* + * DMASEL[4:3] + * 00 = Selects SDMA + * 01 = Reserved + * 10 = Selects 32-bit Address ADMA2 + * 11 = Selects 64-bit Address ADMA2 + */ + ctrl = readb(&host->reg->hostctl); + ctrl &= ~TEGRA_MMC_HOSTCTL_DMASEL_MASK; + ctrl |= TEGRA_MMC_HOSTCTL_DMASEL_SDMA; + writeb(ctrl, &host->reg->hostctl); + + /* We do not handle DMA boundaries, so set it to max (512 KiB) */ + writew((7 << 12) | (data->blocksize & 0xFFF), &host->reg->blksize); + writew(data->blocks, &host->reg->blkcnt); +} + +static void mmc_set_transfer_mode(struct mmc_host *host, struct mmc_data *data) +{ + unsigned short mode; + debug(" mmc_set_transfer_mode called\n"); + /* + * TRNMOD + * MUL1SIN0[5] : Multi/Single Block Select + * RD1WT0[4] : Data Transfer Direction Select + * 1 = read + * 0 = write + * ENACMD12[2] : Auto CMD12 Enable + * ENBLKCNT[1] : Block Count Enable + * ENDMA[0] : DMA Enable + */ + mode = (TEGRA_MMC_TRNMOD_DMA_ENABLE | + TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE); + + if (data->blocks > 1) + mode |= TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT; + + if (data->flags & MMC_DATA_READ) + mode |= TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ; + + if (data->flags & MMC_DATA_WRITE) { + if ((uintptr_t)data->src & (ARCH_DMA_MINALIGN - 1)) + printf("Warning: unaligned write to %p may fail\n", + data->src); + flush_dcache_range((ulong)data->src, (ulong)data->src + + data->blocks * data->blocksize); + } + + writew(mode, &host->reg->trnmod); +} + +static int mmc_wait_inhibit(struct mmc_host *host, + struct mmc_cmd *cmd, + struct mmc_data *data, + unsigned int timeout) +{ + /* + * PRNSTS + * CMDINHDAT[1] : Command Inhibit (DAT) + * CMDINHCMD[0] : Command Inhibit (CMD) + */ + unsigned int mask = TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD; + + /* + * We shouldn't wait for data inhibit for stop commands, even + * though they might use busy signaling + */ + if ((data == NULL) && (cmd->resp_type & MMC_RSP_BUSY)) + mask |= TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT; + + while (readl(&host->reg->prnsts) & mask) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return -1; + } + timeout--; + udelay(1000); + } + + return 0; +} + +static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc_host *host = (struct mmc_host *)mmc->priv; + int flags, i; + int result; + unsigned int mask = 0; + unsigned int retry = 0x100000; + debug(" mmc_send_cmd called\n"); + + result = mmc_wait_inhibit(host, cmd, data, 10 /* ms */); + + if (result < 0) + return result; + + if (data) + mmc_prepare_data(host, data); + + debug("cmd->arg: %08x\n", cmd->cmdarg); + writel(cmd->cmdarg, &host->reg->argument); + + if (data) + mmc_set_transfer_mode(host, data); + + if ((cmd->resp_type & MMC_RSP_136) && (cmd->resp_type & MMC_RSP_BUSY)) + return -1; + + /* + * CMDREG + * CMDIDX[13:8] : Command index + * DATAPRNT[5] : Data Present Select + * ENCMDIDX[4] : Command Index Check Enable + * ENCMDCRC[3] : Command CRC Check Enable + * RSPTYP[1:0] + * 00 = No Response + * 01 = Length 136 + * 10 = Length 48 + * 11 = Length 48 Check busy after response + */ + if (!(cmd->resp_type & MMC_RSP_PRESENT)) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE; + else if (cmd->resp_type & MMC_RSP_136) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136; + else if (cmd->resp_type & MMC_RSP_BUSY) + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY; + else + flags = TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48; + + if (cmd->resp_type & MMC_RSP_CRC) + flags |= TEGRA_MMC_TRNMOD_CMD_CRC_CHECK; + if (cmd->resp_type & MMC_RSP_OPCODE) + flags |= TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK; + if (data) + flags |= TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER; + + debug("cmd: %d\n", cmd->cmdidx); + + writew((cmd->cmdidx << 8) | flags, &host->reg->cmdreg); + + for (i = 0; i < retry; i++) { + mask = readl(&host->reg->norintsts); + /* Command Complete */ + if (mask & TEGRA_MMC_NORINTSTS_CMD_COMPLETE) { + if (!data) + writel(mask, &host->reg->norintsts); + break; + } + } + + if (i == retry) { + printf("%s: waiting for status update\n", __func__); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } + + if (mask & TEGRA_MMC_NORINTSTS_CMD_TIMEOUT) { + /* Timeout Error */ + debug("timeout: %08x cmd %d\n", mask, cmd->cmdidx); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } else if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { + /* Error Interrupt */ + debug("error: %08x cmd %d\n", mask, cmd->cmdidx); + writel(mask, &host->reg->norintsts); + return -1; + } + + if (cmd->resp_type & MMC_RSP_PRESENT) { + if (cmd->resp_type & MMC_RSP_136) { + /* CRC is stripped so we need to do some shifting. */ + for (i = 0; i < 4; i++) { + unsigned int offset = + (unsigned int)(&host->reg->rspreg3 - i); + cmd->response[i] = readl(offset) << 8; + + if (i != 3) { + cmd->response[i] |= + readb(offset - 1); + } + debug("cmd->resp[%d]: %08x\n", + i, cmd->response[i]); + } + } else if (cmd->resp_type & MMC_RSP_BUSY) { + for (i = 0; i < retry; i++) { + /* PRNTDATA[23:20] : DAT[3:0] Line Signal */ + if (readl(&host->reg->prnsts) + & (1 << 20)) /* DAT[0] */ + break; + } + + if (i == retry) { + printf("%s: card is still busy\n", __func__); + writel(mask, &host->reg->norintsts); + return TIMEOUT; + } + + cmd->response[0] = readl(&host->reg->rspreg0); + debug("cmd->resp[0]: %08x\n", cmd->response[0]); + } else { + cmd->response[0] = readl(&host->reg->rspreg0); + debug("cmd->resp[0]: %08x\n", cmd->response[0]); + } + } + + if (data) { + unsigned long start = get_timer(0); + + while (1) { + mask = readl(&host->reg->norintsts); + + if (mask & TEGRA_MMC_NORINTSTS_ERR_INTERRUPT) { + /* Error Interrupt */ + writel(mask, &host->reg->norintsts); + printf("%s: error during transfer: 0x%08x\n", + __func__, mask); + return -1; + } else if (mask & TEGRA_MMC_NORINTSTS_DMA_INTERRUPT) { + /* + * DMA Interrupt, restart the transfer where + * it was interrupted. + */ + unsigned int address = readl(&host->reg->sysad); + + debug("DMA end\n"); + writel(TEGRA_MMC_NORINTSTS_DMA_INTERRUPT, + &host->reg->norintsts); + writel(address, &host->reg->sysad); + } else if (mask & TEGRA_MMC_NORINTSTS_XFER_COMPLETE) { + /* Transfer Complete */ + debug("r/w is done\n"); + break; + } else if (get_timer(start) > 2000UL) { + writel(mask, &host->reg->norintsts); + printf("%s: MMC Timeout\n" + " Interrupt status 0x%08x\n" + " Interrupt status enable 0x%08x\n" + " Interrupt signal enable 0x%08x\n" + " Present status 0x%08x\n", + __func__, mask, + readl(&host->reg->norintstsen), + readl(&host->reg->norintsigen), + readl(&host->reg->prnsts)); + return -1; + } + } + writel(mask, &host->reg->norintsts); + if (data->flags & MMC_DATA_READ) { + if ((uintptr_t)data->dest & (ARCH_DMA_MINALIGN - 1)) + printf("Warning: unaligned read from %p " + "may fail\n", data->dest); + invalidate_dcache_range((ulong)data->dest, + (ulong)data->dest + + data->blocks * data->blocksize); + } + } + + udelay(1000); + return 0; +} + +static void mmc_change_clock(struct mmc_host *host, uint clock) +{ + int div; + unsigned short clk; + unsigned long timeout; + + debug(" mmc_change_clock called\n"); + + /* + * Change Tegra2 SDMMCx clock divisor here. Source is 216MHz, + * PLLP_OUT0 + */ + if (clock == 0) + goto out; + clock_adjust_periph_pll_div(host->mmc_id, CLOCK_ID_PERIPH, clock, + &div); + debug("div = %d\n", div); + + writew(0, &host->reg->clkcon); + + /* + * CLKCON + * SELFREQ[15:8] : base clock divided by value + * ENSDCLK[2] : SD Clock Enable + * STBLINTCLK[1] : Internal Clock Stable + * ENINTCLK[0] : Internal Clock Enable + */ + div >>= 1; + clk = ((div << TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT) | + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE); + writew(clk, &host->reg->clkcon); + + /* Wait max 10 ms */ + timeout = 10; + while (!(readw(&host->reg->clkcon) & + TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE)) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return; + } + timeout--; + udelay(1000); + } + + clk |= TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE; + writew(clk, &host->reg->clkcon); + + debug("mmc_change_clock: clkcon = %08X\n", clk); + +out: + host->clock = clock; +} + +static void mmc_set_ios(struct mmc *mmc) +{ + struct mmc_host *host = mmc->priv; + unsigned char ctrl; + debug(" mmc_set_ios called\n"); + + debug("bus_width: %x, clock: %d\n", mmc->bus_width, mmc->clock); + + /* Change clock first */ + mmc_change_clock(host, mmc->clock); + + ctrl = readb(&host->reg->hostctl); + + /* + * WIDE8[5] + * 0 = Depend on WIDE4 + * 1 = 8-bit mode + * WIDE4[1] + * 1 = 4-bit mode + * 0 = 1-bit mode + */ + if (mmc->bus_width == 8) + ctrl |= (1 << 5); + else if (mmc->bus_width == 4) + ctrl |= (1 << 1); + else + ctrl &= ~(1 << 1); + + writeb(ctrl, &host->reg->hostctl); + debug("mmc_set_ios: hostctl = %08X\n", ctrl); +} + +static void mmc_reset(struct mmc_host *host) +{ + unsigned int timeout; + debug(" mmc_reset called\n"); + + /* + * RSTALL[0] : Software reset for all + * 1 = reset + * 0 = work + */ + writeb(TEGRA_MMC_SWRST_SW_RESET_FOR_ALL, &host->reg->swrst); + + host->clock = 0; + + /* Wait max 100 ms */ + timeout = 100; + + /* hw clears the bit when it's done */ + while (readb(&host->reg->swrst) & TEGRA_MMC_SWRST_SW_RESET_FOR_ALL) { + if (timeout == 0) { + printf("%s: timeout error\n", __func__); + return; + } + timeout--; + udelay(1000); + } +} + +static int mmc_core_init(struct mmc *mmc) +{ + struct mmc_host *host = (struct mmc_host *)mmc->priv; + unsigned int mask; + debug(" mmc_core_init called\n"); + + mmc_reset(host); + + host->version = readw(&host->reg->hcver); + debug("host version = %x\n", host->version); + + /* mask all */ + writel(0xffffffff, &host->reg->norintstsen); + writel(0xffffffff, &host->reg->norintsigen); + + writeb(0xe, &host->reg->timeoutcon); /* TMCLK * 2^27 */ + /* + * NORMAL Interrupt Status Enable Register init + * [5] ENSTABUFRDRDY : Buffer Read Ready Status Enable + * [4] ENSTABUFWTRDY : Buffer write Ready Status Enable + * [3] ENSTADMAINT : DMA boundary interrupt + * [1] ENSTASTANSCMPLT : Transfre Complete Status Enable + * [0] ENSTACMDCMPLT : Command Complete Status Enable + */ + mask = readl(&host->reg->norintstsen); + mask &= ~(0xffff); + mask |= (TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE | + TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE | + TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT | + TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY | + TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY); + writel(mask, &host->reg->norintstsen); + + /* + * NORMAL Interrupt Signal Enable Register init + * [1] ENSTACMDCMPLT : Transfer Complete Signal Enable + */ + mask = readl(&host->reg->norintsigen); + mask &= ~(0xffff); + mask |= TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE; + writel(mask, &host->reg->norintsigen); + + return 0; +} + +int tegra2_mmc_getcd(struct mmc *mmc) +{ + struct mmc_host *host = (struct mmc_host *)mmc->priv; + + debug("tegra2_mmc_getcd called\n"); + + if (host->cd_gpio >= 0) + return !gpio_get_value(host->cd_gpio); + + return 1; +} + +int tegra2_mmc_init(int dev_index, int bus_width, int pwr_gpio, int cd_gpio) +{ + struct mmc_host *host; + char gpusage[12]; /* "SD/MMCn PWR" or "SD/MMCn CD" */ + struct mmc *mmc; + + debug(" tegra2_mmc_init: index %d, bus width %d " + "pwr_gpio %d cd_gpio %d\n", + dev_index, bus_width, pwr_gpio, cd_gpio); + + host = &mmc_host[dev_index]; + + host->clock = 0; + host->pwr_gpio = pwr_gpio; + host->cd_gpio = cd_gpio; + tegra2_get_setup(host, dev_index); + + clock_start_periph_pll(host->mmc_id, CLOCK_ID_PERIPH, 20000000); + + if (host->pwr_gpio >= 0) { + sprintf(gpusage, "SD/MMC%d PWR", dev_index); + gpio_request(host->pwr_gpio, gpusage); + gpio_direction_output(host->pwr_gpio, 1); + } + + if (host->cd_gpio >= 0) { + sprintf(gpusage, "SD/MMC%d CD", dev_index); + gpio_request(host->cd_gpio, gpusage); + gpio_direction_input(host->cd_gpio); + } + + mmc = &mmc_dev[dev_index]; + + sprintf(mmc->name, "Tegra2 SD/MMC"); + mmc->priv = host; + mmc->send_cmd = mmc_send_cmd; + mmc->set_ios = mmc_set_ios; + mmc->init = mmc_core_init; + mmc->getcd = tegra2_mmc_getcd; + + mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; + if (bus_width == 8) + mmc->host_caps = MMC_MODE_8BIT; + else + mmc->host_caps = MMC_MODE_4BIT; + mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS | MMC_MODE_HC; + + /* + * min freq is for card identification, and is the highest + * low-speed SDIO card frequency (actually 400KHz) + * max freq is highest HS eMMC clock as per the SD/MMC spec + * (actually 52MHz) + * Both of these are the closest equivalents w/216MHz source + * clock and Tegra2 SDMMC divisors. + */ + mmc->f_min = 375000; + mmc->f_max = 48000000; + + mmc_register(mmc); + + return 0; +} diff --git a/drivers/mmc/tegra_mmc.h b/drivers/mmc/tegra_mmc.h new file mode 100644 index 0000000..f9cdcaa --- /dev/null +++ b/drivers/mmc/tegra_mmc.h @@ -0,0 +1,131 @@ +/* + * (C) Copyright 2009 SAMSUNG Electronics + * Minkyu Kang + * Portions Copyright (C) 2011-2012 NVIDIA Corporation + * + * 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 + * + */ + +#ifndef __TEGRA_MMC_H_ +#define __TEGRA_MMC_H_ + +#define TEGRA2_SDMMC1_BASE 0xC8000000 +#define TEGRA2_SDMMC2_BASE 0xC8000200 +#define TEGRA2_SDMMC3_BASE 0xC8000400 +#define TEGRA2_SDMMC4_BASE 0xC8000600 + +#ifndef __ASSEMBLY__ +struct tegra2_mmc { + unsigned int sysad; /* _SYSTEM_ADDRESS_0 */ + unsigned short blksize; /* _BLOCK_SIZE_BLOCK_COUNT_0 15:00 */ + unsigned short blkcnt; /* _BLOCK_SIZE_BLOCK_COUNT_0 31:16 */ + unsigned int argument; /* _ARGUMENT_0 */ + unsigned short trnmod; /* _CMD_XFER_MODE_0 15:00 xfer mode */ + unsigned short cmdreg; /* _CMD_XFER_MODE_0 31:16 cmd reg */ + unsigned int rspreg0; /* _RESPONSE_R0_R1_0 CMD RESP 31:00 */ + unsigned int rspreg1; /* _RESPONSE_R2_R3_0 CMD RESP 63:32 */ + unsigned int rspreg2; /* _RESPONSE_R4_R5_0 CMD RESP 95:64 */ + unsigned int rspreg3; /* _RESPONSE_R6_R7_0 CMD RESP 127:96 */ + unsigned int bdata; /* _BUFFER_DATA_PORT_0 */ + unsigned int prnsts; /* _PRESENT_STATE_0 */ + unsigned char hostctl; /* _POWER_CONTROL_HOST_0 7:00 */ + unsigned char pwrcon; /* _POWER_CONTROL_HOST_0 15:8 */ + unsigned char blkgap; /* _POWER_CONTROL_HOST_9 23:16 */ + unsigned char wakcon; /* _POWER_CONTROL_HOST_0 31:24 */ + unsigned short clkcon; /* _CLOCK_CONTROL_0 15:00 */ + unsigned char timeoutcon; /* _TIMEOUT_CTRL 23:16 */ + unsigned char swrst; /* _SW_RESET_ 31:24 */ + unsigned int norintsts; /* _INTERRUPT_STATUS_0 */ + unsigned int norintstsen; /* _INTERRUPT_STATUS_ENABLE_0 */ + unsigned int norintsigen; /* _INTERRUPT_SIGNAL_ENABLE_0 */ + unsigned short acmd12errsts; /* _AUTO_CMD12_ERR_STATUS_0 15:00 */ + unsigned char res1[2]; /* _RESERVED 31:16 */ + unsigned int capareg; /* _CAPABILITIES_0 */ + unsigned char res2[4]; /* RESERVED, offset 44h-47h */ + unsigned int maxcurr; /* _MAXIMUM_CURRENT_0 */ + unsigned char res3[4]; /* RESERVED, offset 4Ch-4Fh */ + unsigned short setacmd12err; /* offset 50h */ + unsigned short setinterr; /* offset 52h */ + unsigned char admaerr; /* offset 54h */ + unsigned char res4[3]; /* RESERVED, offset 55h-57h */ + unsigned long admaaddr; /* offset 58h-5Fh */ + unsigned char res5[0x9c]; /* RESERVED, offset 60h-FBh */ + unsigned short slotintstatus; /* offset FCh */ + unsigned short hcver; /* HOST Version */ + unsigned char res6[0x100]; /* RESERVED, offset 100h-1FFh */ +}; + +#define TEGRA_MMC_HOSTCTL_DMASEL_MASK (3 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_SDMA (0 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_32BIT (2 << 3) +#define TEGRA_MMC_HOSTCTL_DMASEL_ADMA2_64BIT (3 << 3) + +#define TEGRA_MMC_TRNMOD_DMA_ENABLE (1 << 0) +#define TEGRA_MMC_TRNMOD_BLOCK_COUNT_ENABLE (1 << 1) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_WRITE (0 << 4) +#define TEGRA_MMC_TRNMOD_DATA_XFER_DIR_SEL_READ (1 << 4) +#define TEGRA_MMC_TRNMOD_MULTI_BLOCK_SELECT (1 << 5) + +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_MASK (3 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_NO_RESPONSE (0 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_136 (1 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48 (2 << 0) +#define TEGRA_MMC_CMDREG_RESP_TYPE_SELECT_LENGTH_48_BUSY (3 << 0) + +#define TEGRA_MMC_TRNMOD_CMD_CRC_CHECK (1 << 3) +#define TEGRA_MMC_TRNMOD_CMD_INDEX_CHECK (1 << 4) +#define TEGRA_MMC_TRNMOD_DATA_PRESENT_SELECT_DATA_TRANSFER (1 << 5) + +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_CMD (1 << 0) +#define TEGRA_MMC_PRNSTS_CMD_INHIBIT_DAT (1 << 1) + +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_ENABLE (1 << 0) +#define TEGRA_MMC_CLKCON_INTERNAL_CLOCK_STABLE (1 << 1) +#define TEGRA_MMC_CLKCON_SD_CLOCK_ENABLE (1 << 2) + +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_SHIFT 8 +#define TEGRA_MMC_CLKCON_SDCLK_FREQ_SEL_MASK (0xff << 8) + +#define TEGRA_MMC_SWRST_SW_RESET_FOR_ALL (1 << 0) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_CMD_LINE (1 << 1) +#define TEGRA_MMC_SWRST_SW_RESET_FOR_DAT_LINE (1 << 2) + +#define TEGRA_MMC_NORINTSTS_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTS_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTS_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTS_ERR_INTERRUPT (1 << 15) +#define TEGRA_MMC_NORINTSTS_CMD_TIMEOUT (1 << 16) + +#define TEGRA_MMC_NORINTSTSEN_CMD_COMPLETE (1 << 0) +#define TEGRA_MMC_NORINTSTSEN_XFER_COMPLETE (1 << 1) +#define TEGRA_MMC_NORINTSTSEN_DMA_INTERRUPT (1 << 3) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_WRITE_READY (1 << 4) +#define TEGRA_MMC_NORINTSTSEN_BUFFER_READ_READY (1 << 5) + +#define TEGRA_MMC_NORINTSIGEN_XFER_COMPLETE (1 << 1) + +struct mmc_host { + struct tegra2_mmc *reg; + unsigned int version; /* SDHCI spec. version */ + unsigned int clock; /* Current clock (MHz) */ + unsigned int base; /* Base address, SDMMC1/2/3/4 */ + enum periph_id mmc_id; /* Peripheral ID: PERIPH_ID_... */ + int pwr_gpio; /* Power GPIO */ + int cd_gpio; /* Change Detect GPIO */ +}; + +#endif /* __ASSEMBLY__ */ +#endif /* __TEGRA_MMC_H_ */ -- cgit v1.1 From 52a8b82074d1c3a3dcde8e3d90e6a04f7eb3a1f8 Mon Sep 17 00:00:00 2001 From: Tom Warren Date: Tue, 22 May 2012 12:19:25 +0000 Subject: gpio: tegra2: rename tegra2_gpio.* to tegra_gpio.* In anticipation of Tegra3 support, continue removing/renaming Tegra2-specific files. No functional changes (yet). Updated copyrights to 2012. Signed-off-by: Tom Warren --- drivers/gpio/Makefile | 2 +- drivers/gpio/tegra2_gpio.c | 262 --------------------------------------------- drivers/gpio/tegra_gpio.c | 262 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+), 263 deletions(-) delete mode 100644 drivers/gpio/tegra2_gpio.c create mode 100644 drivers/gpio/tegra_gpio.c (limited to 'drivers') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index fb3b09a..5ae68d5 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -35,7 +35,7 @@ COBJS-$(CONFIG_PCA953X) += pca953x.o COBJS-$(CONFIG_PCA9698) += pca9698.o COBJS-$(CONFIG_S5P) += s5p_gpio.o COBJS-$(CONFIG_SANDBOX_GPIO) += sandbox.o -COBJS-$(CONFIG_TEGRA2_GPIO) += tegra2_gpio.o +COBJS-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o COBJS-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o COBJS-$(CONFIG_ALTERA_PIO) += altera_pio.o COBJS-$(CONFIG_MPC83XX_GPIO) += mpc83xx_gpio.o diff --git a/drivers/gpio/tegra2_gpio.c b/drivers/gpio/tegra2_gpio.c deleted file mode 100644 index 70ca46f..0000000 --- a/drivers/gpio/tegra2_gpio.c +++ /dev/null @@ -1,262 +0,0 @@ -/* - * NVIDIA Tegra2 GPIO handling. - * (C) Copyright 2010,2011 - * NVIDIA Corporation - * - * 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 - */ - -/* - * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver. - * Tom Warren (twarren@nvidia.com) - */ - -#include -#include -#include -#include -#include - -enum { - TEGRA2_CMD_INFO, - TEGRA2_CMD_PORT, - TEGRA2_CMD_OUTPUT, - TEGRA2_CMD_INPUT, -}; - -static struct gpio_names { - char name[GPIO_NAME_SIZE]; -} gpio_names[MAX_NUM_GPIOS]; - -static char *get_name(int i) -{ - return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN"; -} - -/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */ -static int get_config(unsigned gpio) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - u32 u; - int type; - - u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); - type = (u >> GPIO_BIT(gpio)) & 1; - - debug("get_config: port = %d, bit = %d is %s\n", - GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); - - return type; -} - -/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */ -static void set_config(unsigned gpio, int type) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - u32 u; - - debug("set_config: port = %d, bit = %d, %s\n", - GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); - - u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); - if (type) /* GPIO */ - u |= 1 << GPIO_BIT(gpio); - else - u &= ~(1 << GPIO_BIT(gpio)); - writel(u, &bank->gpio_config[GPIO_PORT(gpio)]); -} - -/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */ -static int get_direction(unsigned gpio) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - u32 u; - int dir; - - u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); - dir = (u >> GPIO_BIT(gpio)) & 1; - - debug("get_direction: port = %d, bit = %d, %s\n", - GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN"); - - return dir; -} - -/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */ -static void set_direction(unsigned gpio, int output) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - u32 u; - - debug("set_direction: port = %d, bit = %d, %s\n", - GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN"); - - u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); - if (output) - u |= 1 << GPIO_BIT(gpio); - else - u &= ~(1 << GPIO_BIT(gpio)); - writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]); -} - -/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */ -static void set_level(unsigned gpio, int high) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - u32 u; - - debug("set_level: port = %d, bit %d == %d\n", - GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high); - - u = readl(&bank->gpio_out[GPIO_PORT(gpio)]); - if (high) - u |= 1 << GPIO_BIT(gpio); - else - u &= ~(1 << GPIO_BIT(gpio)); - writel(u, &bank->gpio_out[GPIO_PORT(gpio)]); -} - -/* - * Generic_GPIO primitives. - */ - -int gpio_request(unsigned gpio, const char *label) -{ - if (gpio >= MAX_NUM_GPIOS) - return -1; - - if (label != NULL) { - strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE); - gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0'; - } - - /* Configure as a GPIO */ - set_config(gpio, 1); - - return 0; -} - -int gpio_free(unsigned gpio) -{ - if (gpio >= MAX_NUM_GPIOS) - return -1; - - gpio_names[gpio].name[0] = '\0'; - /* Do not configure as input or change pin mux here */ - return 0; -} - -/* read GPIO OUT value of pin 'gpio' */ -static int gpio_get_output_value(unsigned gpio) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - int val; - - debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); - - val = readl(&bank->gpio_out[GPIO_PORT(gpio)]); - - return (val >> GPIO_BIT(gpio)) & 1; -} - -/* set GPIO pin 'gpio' as an input */ -int gpio_direction_input(unsigned gpio) -{ - debug("gpio_direction_input: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); - - /* Configure GPIO direction as input. */ - set_direction(gpio, 0); - - return 0; -} - -/* set GPIO pin 'gpio' as an output, with polarity 'value' */ -int gpio_direction_output(unsigned gpio, int value) -{ - debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), - value ? "HIGH" : "LOW"); - - /* Configure GPIO output value. */ - set_level(gpio, value); - - /* Configure GPIO direction as output. */ - set_direction(gpio, 1); - - return 0; -} - -/* read GPIO IN value of pin 'gpio' */ -int gpio_get_value(unsigned gpio) -{ - struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; - struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; - int val; - - debug("gpio_get_value: pin = %d (port %d:bit %d)\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); - - val = readl(&bank->gpio_in[GPIO_PORT(gpio)]); - - return (val >> GPIO_BIT(gpio)) & 1; -} - -/* write GPIO OUT value to pin 'gpio' */ -int gpio_set_value(unsigned gpio, int value) -{ - debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", - gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); - - /* Configure GPIO output value. */ - set_level(gpio, value); - - return 0; -} - -/* - * Display Tegra GPIO information - */ -void gpio_info(void) -{ - unsigned c; - int type; - - for (c = 0; c < MAX_NUM_GPIOS; c++) { - type = get_config(c); /* GPIO, not SFPIO */ - if (type) { - printf("GPIO_%d:\t%s is an %s, ", c, - get_name(c), - get_direction(c) ? "OUTPUT" : "INPUT"); - if (get_direction(c)) - printf("value = %d", gpio_get_output_value(c)); - else - printf("value = %d", gpio_get_value(c)); - printf("\n"); - } else - continue; - } -} diff --git a/drivers/gpio/tegra_gpio.c b/drivers/gpio/tegra_gpio.c new file mode 100644 index 0000000..60ec6e3 --- /dev/null +++ b/drivers/gpio/tegra_gpio.c @@ -0,0 +1,262 @@ +/* + * NVIDIA Tegra2 GPIO handling. + * (C) Copyright 2010-2012 + * NVIDIA Corporation + * + * 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 + */ + +/* + * Based on (mostly copied from) kw_gpio.c based Linux 2.6 kernel driver. + * Tom Warren (twarren@nvidia.com) + */ + +#include +#include +#include +#include +#include + +enum { + TEGRA2_CMD_INFO, + TEGRA2_CMD_PORT, + TEGRA2_CMD_OUTPUT, + TEGRA2_CMD_INPUT, +}; + +static struct gpio_names { + char name[GPIO_NAME_SIZE]; +} gpio_names[MAX_NUM_GPIOS]; + +static char *get_name(int i) +{ + return *gpio_names[i].name ? gpio_names[i].name : "UNKNOWN"; +} + +/* Return config of pin 'gpio' as GPIO (1) or SFPIO (0) */ +static int get_config(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + int type; + + u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); + type = (u >> GPIO_BIT(gpio)) & 1; + + debug("get_config: port = %d, bit = %d is %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); + + return type; +} + +/* Config pin 'gpio' as GPIO or SFPIO, based on 'type' */ +static void set_config(unsigned gpio, int type) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_config: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), type ? "GPIO" : "SFPIO"); + + u = readl(&bank->gpio_config[GPIO_PORT(gpio)]); + if (type) /* GPIO */ + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_config[GPIO_PORT(gpio)]); +} + +/* Return GPIO pin 'gpio' direction - 0 = input or 1 = output */ +static int get_direction(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + int dir; + + u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); + dir = (u >> GPIO_BIT(gpio)) & 1; + + debug("get_direction: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), dir ? "OUT" : "IN"); + + return dir; +} + +/* Config GPIO pin 'gpio' as input or output (OE) as per 'output' */ +static void set_direction(unsigned gpio, int output) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_direction: port = %d, bit = %d, %s\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), output ? "OUT" : "IN"); + + u = readl(&bank->gpio_dir_out[GPIO_PORT(gpio)]); + if (output) + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_dir_out[GPIO_PORT(gpio)]); +} + +/* set GPIO pin 'gpio' output bit as 0 or 1 as per 'high' */ +static void set_level(unsigned gpio, int high) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + u32 u; + + debug("set_level: port = %d, bit %d == %d\n", + GPIO_FULLPORT(gpio), GPIO_BIT(gpio), high); + + u = readl(&bank->gpio_out[GPIO_PORT(gpio)]); + if (high) + u |= 1 << GPIO_BIT(gpio); + else + u &= ~(1 << GPIO_BIT(gpio)); + writel(u, &bank->gpio_out[GPIO_PORT(gpio)]); +} + +/* + * Generic_GPIO primitives. + */ + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + if (label != NULL) { + strncpy(gpio_names[gpio].name, label, GPIO_NAME_SIZE); + gpio_names[gpio].name[GPIO_NAME_SIZE - 1] = '\0'; + } + + /* Configure as a GPIO */ + set_config(gpio, 1); + + return 0; +} + +int gpio_free(unsigned gpio) +{ + if (gpio >= MAX_NUM_GPIOS) + return -1; + + gpio_names[gpio].name[0] = '\0'; + /* Do not configure as input or change pin mux here */ + return 0; +} + +/* read GPIO OUT value of pin 'gpio' */ +static int gpio_get_output_value(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + int val; + + debug("gpio_get_output_value: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + val = readl(&bank->gpio_out[GPIO_PORT(gpio)]); + + return (val >> GPIO_BIT(gpio)) & 1; +} + +/* set GPIO pin 'gpio' as an input */ +int gpio_direction_input(unsigned gpio) +{ + debug("gpio_direction_input: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + /* Configure GPIO direction as input. */ + set_direction(gpio, 0); + + return 0; +} + +/* set GPIO pin 'gpio' as an output, with polarity 'value' */ +int gpio_direction_output(unsigned gpio, int value) +{ + debug("gpio_direction_output: pin = %d (port %d:bit %d) = %s\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), + value ? "HIGH" : "LOW"); + + /* Configure GPIO output value. */ + set_level(gpio, value); + + /* Configure GPIO direction as output. */ + set_direction(gpio, 1); + + return 0; +} + +/* read GPIO IN value of pin 'gpio' */ +int gpio_get_value(unsigned gpio) +{ + struct gpio_ctlr *ctlr = (struct gpio_ctlr *)NV_PA_GPIO_BASE; + struct gpio_ctlr_bank *bank = &ctlr->gpio_bank[GPIO_BANK(gpio)]; + int val; + + debug("gpio_get_value: pin = %d (port %d:bit %d)\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio)); + + val = readl(&bank->gpio_in[GPIO_PORT(gpio)]); + + return (val >> GPIO_BIT(gpio)) & 1; +} + +/* write GPIO OUT value to pin 'gpio' */ +int gpio_set_value(unsigned gpio, int value) +{ + debug("gpio_set_value: pin = %d (port %d:bit %d), value = %d\n", + gpio, GPIO_FULLPORT(gpio), GPIO_BIT(gpio), value); + + /* Configure GPIO output value. */ + set_level(gpio, value); + + return 0; +} + +/* + * Display Tegra GPIO information + */ +void gpio_info(void) +{ + unsigned c; + int type; + + for (c = 0; c < MAX_NUM_GPIOS; c++) { + type = get_config(c); /* GPIO, not SFPIO */ + if (type) { + printf("GPIO_%d:\t%s is an %s, ", c, + get_name(c), + get_direction(c) ? "OUTPUT" : "INPUT"); + if (get_direction(c)) + printf("value = %d", gpio_get_output_value(c)); + else + printf("value = %d", gpio_get_value(c)); + printf("\n"); + } else + continue; + } +} -- cgit v1.1 From 168a5acb81a8d92a7effcb2727aaa89367b97e05 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 21 May 2012 06:46:29 +0000 Subject: Revert "I2C: OMAP: detect more devices when probing an i2c bus" This reverts commit 0e57968a215d1b9d271f3fa5bebeddeaea0c8075. The short version of the original commit is that some i2c devices cannot be probed via read as they NAK the first cycle, so try and probe via a write that we abort before it writes to the device. This however is not allowed by the TRM for any of these parts. The section on I2C_CON (table 17-35 I2C_CON for am/dm37x for example) says you must not change the register while STT has been set. On these parts, the unpredictable behavior that the chip exhibits is not problematic. On OMAP4 however it results in the chip being in a bad state: Panda # i2c probe Valid chip addresses: 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D 1E 1F 20 21 22 23 24 25 26 27 28 29 2A 2B 2C 2D 2E 2F 30 31 32 33 34 35 36 37 38 39 3A 3B 3C 3D 3E 3F 40 41 42 43 44 45 46 47 48 49 4A 4B 4C 4D 4E 4F 50 51 52 53 54 55 56 57 58 59 5A 5B 5C 5D 5E 5F 60 61 62 63 64 65 66 67 68 69 6A 6B 6C 6D 6E 6F 70 71 72 73 74 75 76 77 78 79 7A 7B 7C 7D 7E 7F Panda # i2c md 50 0 timed out in wait_for_pin: I2C_STAT=0 I2C read: I/O error Error reading the chip. We must revert the original behavior to bring probe back into line with the TRM. Cc: Nick Thompson Cc: Heiko Schocher Signed-off-by: Tom Rini Acked-by: Heiko Schocher --- drivers/i2c/omap24xx_i2c.c | 42 +++++++++++++++++++++++++++++++----------- 1 file changed, 31 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index a7ffd95..3a50417 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -255,23 +255,43 @@ int i2c_probe(uchar chip) /* wait until bus not busy */ wait_for_bb(); - /* try to write one byte */ + /* try to read one byte */ writew(1, &i2c_base->cnt); /* set slave address */ writew(chip, &i2c_base->sa); /* stop bit needed here */ - writew(I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_TRX | - I2C_CON_STP, &i2c_base->con); - - status = wait_for_pin(); + writew (I2C_CON_EN | I2C_CON_MST | I2C_CON_STT | I2C_CON_STP, &i2c_base->con); - /* check for ACK (!NAK) */ - if (!(status & I2C_STAT_NACK)) - res = 0; - - /* abort transfer (force idle state) */ - writew(0, &i2c_base->con); + while (1) { + status = wait_for_pin(); + if (status == 0 || status & I2C_STAT_AL) { + res = 1; + goto probe_exit; + } + if (status & I2C_STAT_NACK) { + res = 1; + writew(0xff, &i2c_base->stat); + writew (readw (&i2c_base->con) | I2C_CON_STP, &i2c_base->con); + wait_for_bb (); + break; + } + if (status & I2C_STAT_ARDY) { + writew(I2C_STAT_ARDY, &i2c_base->stat); + break; + } + if (status & I2C_STAT_RRDY) { + res = 0; +#if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ + defined(CONFIG_OMAP44XX) + readb(&i2c_base->data); +#else + readw(&i2c_base->data); +#endif + writew(I2C_STAT_RRDY, &i2c_base->stat); + } + } +probe_exit: flush_fifo(); /* don't allow any more data in... we don't want it. */ writew(0, &i2c_base->cnt); -- cgit v1.1 From 402d17fc4c406fe49a7b2d94aee640f389a68738 Mon Sep 17 00:00:00 2001 From: Tom Rini Date: Mon, 21 May 2012 06:46:33 +0000 Subject: omap24xx_i2c: Add AM33XX support The same places that check for CONFIG_OMAP44XX need to check for CONFIG_AM33XX as we share the same i2c block. Cc: Heiko Schocher Signed-off-by: Tom Rini Acked-by: Heiko Schocher --- drivers/i2c/omap24xx_i2c.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/omap24xx_i2c.c b/drivers/i2c/omap24xx_i2c.c index 3a50417..81193b0 100644 --- a/drivers/i2c/omap24xx_i2c.c +++ b/drivers/i2c/omap24xx_i2c.c @@ -202,7 +202,7 @@ static int i2c_read_byte(u8 devaddr, u8 regoffset, u8 *value) } if (status & I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) *value = readb(&i2c_base->data); #else *value = readw(&i2c_base->data); @@ -232,7 +232,7 @@ static void flush_fifo(void) stat = readw(&i2c_base->stat); if (stat == I2C_STAT_RRDY) { #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) readb(&i2c_base->data); #else readw(&i2c_base->data); @@ -282,7 +282,7 @@ int i2c_probe(uchar chip) if (status & I2C_STAT_RRDY) { res = 0; #if defined(CONFIG_OMAP243X) || defined(CONFIG_OMAP34XX) || \ - defined(CONFIG_OMAP44XX) + defined(CONFIG_OMAP44XX) || defined(CONFIG_AM33XX) readb(&i2c_base->data); #else readw(&i2c_base->data); -- cgit v1.1 From c20545691a3861ece0319cf5ad257714f1b502fd Mon Sep 17 00:00:00 2001 From: Donghwa Lee Date: Wed, 25 Apr 2012 13:29:39 +0000 Subject: TRATS: initialize panel_info data structure in board file panel_info data structure is gloable variable, so, I have initialized it in board file. If it is initialized in init_panel_info() like existing, it can't be used in drv_lcd_init() in common/lcd.c because init_panel_info() is called after drv_lcd_init(). Signed-off-by: Donghwa Lee Signed-off-by: Kyungmin Park Acked-by: Anatolij Gustschin Signed-off-by: Minkyu Kang --- drivers/video/exynos_fb.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/video/exynos_fb.c b/drivers/video/exynos_fb.c index 92be4ea..49fdfec 100644 --- a/drivers/video/exynos_fb.c +++ b/drivers/video/exynos_fb.c @@ -44,9 +44,6 @@ short console_row; static unsigned int panel_width, panel_height; -/* LCD Panel data */ -vidinfo_t panel_info; - static void exynos_lcd_init_mem(void *lcdbase, vidinfo_t *vid) { unsigned long palette_size; -- cgit v1.1 From cc30b7803d3a3237fbcef860b9320e9b4bfbffaf Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Wed, 27 Jun 2012 21:58:20 +0000 Subject: Atmel : usb : add EHCI driver for Atmel SoC MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some Atmel SoC support USB EHCI, add the EHCI driver to support it. To enable the USB EHCI, add the following configuration options into board relative configuration file and remove USB OHCI options. #define CONFIG_USB_EHCI #define CONFIG_USB_EHCI_ATMEL #define CONFIG_SYS_USB_EHCI_MAX_ROOT_PORTS 2 Signed-off-by: Bo Shen Acked-by: Marek Vasut Signed-off-by: Andreas Bießmann --- drivers/usb/host/Makefile | 1 + drivers/usb/host/ehci-atmel.c | 89 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+) create mode 100644 drivers/usb/host/ehci-atmel.c (limited to 'drivers') diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 59c3e57..4547f37 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -36,6 +36,7 @@ COBJS-$(CONFIG_USB_SL811HS) += sl811-hcd.o # echi COBJS-$(CONFIG_USB_EHCI) += ehci-hcd.o COBJS-$(CONFIG_USB_EHCI_ARMADA100) += ehci-armada100.o utmi-armada100.o +COBJS-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o ifdef CONFIG_MPC512X COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-mpc512x.o else diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c new file mode 100644 index 0000000..532db22 --- /dev/null +++ b/drivers/usb/host/ehci-atmel.c @@ -0,0 +1,89 @@ +/* + * (C) Copyright 2012 + * Atmel Semiconductor + * Written-by: Bo Shen + * + * 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., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ehci.h" +#include "ehci-core.h" + +/* Enable UTMI PLL time out 500us + * 10 times as datasheet specified + */ +#define EN_UPLL_TIMEOUT 500UL + +int ehci_hcd_init(void) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + ulong start_time, tmp_time; + + start_time = get_timer(0); + /* Enable UTMI PLL */ + writel(AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr); + while (readl(&pmc->sr) & AT91_PMC_LOCKU != AT91_PMC_LOCKU) { + WATCHDOG_RESET(); + tmp_time = get_timer(0); + if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { + printf("ERROR: failed to enable UPLL\n"); + return -1; + } + } + + /* Enable USB Host clock */ + writel(1 << ATMEL_ID_UHPHS, &pmc->pcer); + + hccr = (struct ehci_hccr *)ATMEL_BASE_EHCI; + hcor = (struct ehci_hcor *)((uint32_t)hccr + + HC_LENGTH(ehci_readl(&hccr->cr_capbase))); + + return 0; +} + +int ehci_hcd_stop(void) +{ + at91_pmc_t *pmc = (at91_pmc_t *)ATMEL_BASE_PMC; + ulong start_time, tmp_time; + + /* Disable USB Host Clock */ + writel(1 << ATMEL_ID_UHPHS, &pmc->pcdr); + + start_time = get_timer(0); + /* Disable UTMI PLL */ + writel(readl(&pmc->uckr) & ~AT91_PMC_UPLLEN, &pmc->uckr); + while (readl(&pmc->sr) & AT91_PMC_LOCKU == AT91_PMC_LOCKU) { + WATCHDOG_RESET(); + tmp_time = get_timer(0); + if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { + printf("ERROR: failed to stop UPLL\n"); + return -1; + } + } + + return 0; +} -- cgit v1.1 From 81f40345d1d5c5ba04620873e3ca0bc60b6b2fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20Bie=C3=9Fmann?= Date: Thu, 28 Jun 2012 02:50:37 +0000 Subject: ehci-atmel: fix compiler warning MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 7a101e946cba55e32d3d1265e30456c810046da3 introduced following warning: ---8<--- ehci-atmel.c: In function 'ehci_hcd_init': ehci-atmel.c:49:2: warning: suggest parentheses around comparison in operand of '&' [-Wparentheses] ehci-atmel.c: In function 'ehci_hcd_stop': ehci-atmel.c:79:2: warning: suggest parentheses around comparison in operand of '&' [-Wparentheses] --->8--- This patch fixes it. Signed-off-by: Andreas Bießmann cc: Bo Shen cc: Marek Vasut --- drivers/usb/host/ehci-atmel.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/usb/host/ehci-atmel.c b/drivers/usb/host/ehci-atmel.c index 532db22..15b9b60 100644 --- a/drivers/usb/host/ehci-atmel.c +++ b/drivers/usb/host/ehci-atmel.c @@ -46,7 +46,7 @@ int ehci_hcd_init(void) start_time = get_timer(0); /* Enable UTMI PLL */ writel(AT91_PMC_UPLLEN | AT91_PMC_BIASEN, &pmc->uckr); - while (readl(&pmc->sr) & AT91_PMC_LOCKU != AT91_PMC_LOCKU) { + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) != AT91_PMC_LOCKU) { WATCHDOG_RESET(); tmp_time = get_timer(0); if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { @@ -76,7 +76,7 @@ int ehci_hcd_stop(void) start_time = get_timer(0); /* Disable UTMI PLL */ writel(readl(&pmc->uckr) & ~AT91_PMC_UPLLEN, &pmc->uckr); - while (readl(&pmc->sr) & AT91_PMC_LOCKU == AT91_PMC_LOCKU) { + while ((readl(&pmc->sr) & AT91_PMC_LOCKU) == AT91_PMC_LOCKU) { WATCHDOG_RESET(); tmp_time = get_timer(0); if ((tmp_time - start_time) > EN_UPLL_TIMEOUT) { -- cgit v1.1 From 2b3b1c668be539d897d2a9352bc8dca54467f425 Mon Sep 17 00:00:00 2001 From: Bo Shen Date: Sun, 20 May 2012 15:50:00 +0000 Subject: ATMEL/PIO: Enable new feature of PIO on Atmel device MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Enable new PIO feature supported by Atmel SoC. Using CPU_HAS_PIO3 micro to enable PIO new feature. Signed-off-by: Bo Shen Signed-off-by: Andreas Bießmann --- drivers/gpio/at91_gpio.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 123 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/gpio/at91_gpio.c b/drivers/gpio/at91_gpio.c index be2a026..ac3b322 100644 --- a/drivers/gpio/at91_gpio.c +++ b/drivers/gpio/at91_gpio.c @@ -86,7 +86,14 @@ int at91_set_a_periph(unsigned port, unsigned pin, int use_pullup) mask = 1 << pin; writel(mask, &pio->port[port].idr); at91_set_pio_pullup(port, pin, use_pullup); +#if defined(CPU_HAS_PIO3) + writel(readl(&pio->port[port].abcdsr1) & ~mask, + &pio->port[port].abcdsr1); + writel(readl(&pio->port[port].abcdsr2) & ~mask, + &pio->port[port].abcdsr2); +#else writel(mask, &pio->port[port].asr); +#endif writel(mask, &pio->port[port].pdr); } return 0; @@ -104,12 +111,63 @@ int at91_set_b_periph(unsigned port, unsigned pin, int use_pullup) mask = 1 << pin; writel(mask, &pio->port[port].idr); at91_set_pio_pullup(port, pin, use_pullup); +#if defined(CPU_HAS_PIO3) + writel(readl(&pio->port[port].abcdsr1) | mask, + &pio->port[port].abcdsr1); + writel(readl(&pio->port[port].abcdsr2) & ~mask, + &pio->port[port].abcdsr2); +#else writel(mask, &pio->port[port].bsr); +#endif writel(mask, &pio->port[port].pdr); } return 0; } +#if defined(CPU_HAS_PIO3) +/* + * mux the pin to the "C" internal peripheral role. + */ +int at91_set_c_periph(unsigned port, unsigned pin, int use_pullup) +{ + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + writel(mask, &pio->port[port].idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(readl(&pio->port[port].abcdsr1) & ~mask, + &pio->port[port].abcdsr1); + writel(readl(&pio->port[port].abcdsr2) | mask, + &pio->port[port].abcdsr2); + writel(mask, &pio->port[port].pdr); + } + return 0; +} + +/* + * mux the pin to the "D" internal peripheral role. + */ +int at91_set_d_periph(unsigned port, unsigned pin, int use_pullup) +{ + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + writel(mask, &pio->port[port].idr); + at91_set_pio_pullup(port, pin, use_pullup); + writel(readl(&pio->port[port].abcdsr1) | mask, + &pio->port[port].abcdsr1); + writel(readl(&pio->port[port].abcdsr2) | mask, + &pio->port[port].abcdsr2); + writel(mask, &pio->port[port].pdr); + } + return 0; +} +#endif + /* * mux the pin to the gpio controller (instead of "A" or "B" peripheral), and * configure it for an input. @@ -162,13 +220,76 @@ int at91_set_pio_deglitch(unsigned port, unsigned pin, int is_on) if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { mask = 1 << pin; - if (is_on) + if (is_on) { +#if defined(CPU_HAS_PIO3) + writel(mask, &pio->port[port].ifscdr); +#endif writel(mask, &pio->port[port].ifer); - else + } else { writel(mask, &pio->port[port].ifdr); + } + } + return 0; +} + +#if defined(CPU_HAS_PIO3) +/* + * enable/disable the debounce filter. + */ +int at91_set_pio_debounce(unsigned port, unsigned pin, int is_on, int div) +{ + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + if (is_on) { + writel(mask, &pio->port[port].ifscer); + writel(div & PIO_SCDR_DIV, &pio->port[port].scdr); + writel(mask, &pio->port[port].ifer); + } else { + writel(mask, &pio->port[port].ifdr); + } + } + return 0; +} + +/* + * enable/disable the pull-down. + * If pull-up already enabled while calling the function, we disable it. + */ +int at91_set_pio_pulldown(unsigned port, unsigned pin, int is_on) +{ + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + writel(mask, &pio->port[port].pudr); + if (is_on) + writel(mask, &pio->port[port].ppder); + else + writel(mask, &pio->port[port].ppddr); + } + return 0; +} + +/* + * disable Schmitt trigger + */ +int at91_set_pio_disable_schmitt_trig(unsigned port, unsigned pin) +{ + at91_pio_t *pio = (at91_pio_t *) ATMEL_BASE_PIOA; + u32 mask; + + if ((port < ATMEL_PIO_PORTS) && (pin < 32)) { + mask = 1 << pin; + writel(readl(&pio->port[port].schmitt) | mask, + &pio->port[port].schmitt); } return 0; } +#endif /* * enable/disable the multi-driver. This is only valid for output and -- cgit v1.1 From ca880679dda63f8d58393526c7e3257f772ccabe Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Fri, 1 Jun 2012 01:31:01 +0000 Subject: kw_spi: backup and reset the MPP of the chosen CS pin This was not done before, and in the case of a shared pin (for MPP0 between NF_IO[2] and CSn) this could lead to problems. Signed-off-by: Valentin Longchamp cc: Holger Brunck cc: Prafulla Wadaskar --- drivers/spi/kirkwood_spi.c | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index db8ba8b..01e1d11 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -34,16 +34,14 @@ static struct kwspi_registers *spireg = (struct kwspi_registers *)KW_SPI_BASE; +u32 cs_spi_mpp_back[2]; + struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct spi_slave *slave; u32 data; - u32 kwspi_mpp_config[] = { - MPP0_GPIO, - MPP7_SPI_SCn, - 0 - }; + u32 kwspi_mpp_config[] = { 0, 0 }; if (!spi_cs_is_valid(bus, cs)) return NULL; @@ -70,19 +68,18 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, /* program mpp registers to select SPI_CSn */ if (cs) { - kwspi_mpp_config[0] = MPP0_GPIO; - kwspi_mpp_config[1] = MPP7_SPI_SCn; + kwspi_mpp_config[0] = MPP7_SPI_SCn; } else { kwspi_mpp_config[0] = MPP0_SPI_SCn; - kwspi_mpp_config[1] = MPP7_GPO; } - kirkwood_mpp_conf(kwspi_mpp_config); + kirkwood_mpp_conf(kwspi_mpp_config, cs_spi_mpp_back); return slave; } void spi_free_slave(struct spi_slave *slave) { + kirkwood_mpp_conf(cs_spi_mpp_back, NULL); free(slave); } -- cgit v1.1 From ac486e3ba16c37514d135c359f61ed6e32a2921d Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Fri, 1 Jun 2012 01:31:02 +0000 Subject: kw_spi: support spi_claim/release_bus functions These two function nows ensure that the MPP is configured correctly for the SPI controller before any SPI access, and restore the initial configuration when the access is over. Since the used pins for the SPI controller can differ (2 possibilities for each signal), the used pins are configured with CONFIG_SYS_KW_SPI_MPP. Signed-off-by: Valentin Longchamp cc: Holger Brunck cc: Prafulla Wadaskar --- drivers/spi/kirkwood_spi.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index 01e1d11..db4bb0a 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -83,13 +83,49 @@ void spi_free_slave(struct spi_slave *slave) free(slave); } +#if defined(CONFIG_SYS_KW_SPI_MPP) +u32 spi_mpp_backup[4]; +#endif + int spi_claim_bus(struct spi_slave *slave) { +#if defined(CONFIG_SYS_KW_SPI_MPP) + u32 config; + u32 spi_mpp_config[4]; + + config = CONFIG_SYS_KW_SPI_MPP; + + if (config & MOSI_MPP6) + spi_mpp_config[0] = MPP6_SPI_MOSI; + else + spi_mpp_config[0] = MPP1_SPI_MOSI; + + if (config & SCK_MPP10) + spi_mpp_config[1] = MPP10_SPI_SCK; + else + spi_mpp_config[1] = MPP2_SPI_SCK; + + if (config & MISO_MPP11) + spi_mpp_config[2] = MPP11_SPI_MISO; + else + spi_mpp_config[2] = MPP3_SPI_MISO; + + spi_mpp_config[3] = 0; + spi_mpp_backup[3] = 0; + + /* set new spi mpp and save current mpp config */ + kirkwood_mpp_conf(spi_mpp_config, spi_mpp_backup); + +#endif + return 0; } void spi_release_bus(struct spi_slave *slave) { +#if defined(CONFIG_SYS_KW_SPI_MPP) + kirkwood_mpp_conf(spi_mpp_backup, NULL); +#endif } #ifndef CONFIG_SPI_CS_IS_VALID -- cgit v1.1 From 24934fea0c6248ac33ccf398dd1a86e0b9904eeb Mon Sep 17 00:00:00 2001 From: Valentin Longchamp Date: Fri, 1 Jun 2012 01:31:03 +0000 Subject: kw_spi: add weak functions board_spi_claim/release_bus This allows a final, board specific, step in the claim/relase_bus function for the SPI controller, which may be needed for some hardware designs. Signed-off-by: Valentin Longchamp cc: Holger Brunck cc: Prafulla Wadaskar --- drivers/spi/kirkwood_spi.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/spi/kirkwood_spi.c b/drivers/spi/kirkwood_spi.c index db4bb0a..f4523a3 100644 --- a/drivers/spi/kirkwood_spi.c +++ b/drivers/spi/kirkwood_spi.c @@ -87,6 +87,11 @@ void spi_free_slave(struct spi_slave *slave) u32 spi_mpp_backup[4]; #endif +__attribute__((weak)) int board_spi_claim_bus(struct spi_slave *slave) +{ + return 0; +} + int spi_claim_bus(struct spi_slave *slave) { #if defined(CONFIG_SYS_KW_SPI_MPP) @@ -118,7 +123,11 @@ int spi_claim_bus(struct spi_slave *slave) #endif - return 0; + return board_spi_claim_bus(slave); +} + +__attribute__((weak)) void board_spi_release_bus(struct spi_slave *slave) +{ } void spi_release_bus(struct spi_slave *slave) @@ -126,6 +135,8 @@ void spi_release_bus(struct spi_slave *slave) #if defined(CONFIG_SYS_KW_SPI_MPP) kirkwood_mpp_conf(spi_mpp_backup, NULL); #endif + + board_spi_release_bus(slave); } #ifndef CONFIG_SPI_CS_IS_VALID -- cgit v1.1 From b78375a806ed04eb22b963255cfdef8df702de47 Mon Sep 17 00:00:00 2001 From: "Rajashekhara, Sudhakar" Date: Thu, 7 Jun 2012 00:27:44 +0000 Subject: da850/omap-l138: Enable auto negotiation in RMII mode On DA850/OMAP-L138 it was observed that in RMII mode, auto negotiation was not performed. This patch enables auto negotiation in RMII mode. Without this patch, EMAC initialization takes more time and sometimes tftp fails in RMII mode. Signed-off-by: Rajashekhara, Sudhakar Signed-off-by: Lad, Prabhakar Signed-off-by: Hadli, Manjunath --- drivers/net/davinci_emac.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers') diff --git a/drivers/net/davinci_emac.c b/drivers/net/davinci_emac.c index e471d2c..b2516d1 100644 --- a/drivers/net/davinci_emac.c +++ b/drivers/net/davinci_emac.c @@ -895,5 +895,13 @@ int davinci_emac_initialize(void) miiphy_register(phy[i].name, davinci_mii_phy_read, davinci_mii_phy_write); } + +#if defined(CONFIG_DRIVER_TI_EMAC_USE_RMII) && \ + defined(CONFIG_MACH_DAVINCI_DA850_EVM) + for (i = 0; i < num_phy; i++) { + if (phy[i].is_phy_connected(i)) + phy[i].auto_negotiate(i); + } +#endif return(1); } -- cgit v1.1 From 7f0730a02e412015b0fc4e78dc41aef14e8a3d8c Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Tue, 22 May 2012 00:15:54 +0000 Subject: mtd/NAND: Add FSMC driver support Flexible static memory controller is a peripheral provided by ST, which controls the access to NAND chips along with many other memory device chips eg NOR, SRAM. This patch adds the driver support for FSMC controller interfacing with NAND memory. Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese Acked-by: Scott Wood --- drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/fsmc_nand.c | 486 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 487 insertions(+) create mode 100644 drivers/mtd/nand/fsmc_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1d1b628..29dc20e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -49,6 +49,7 @@ COBJS-$(CONFIG_NAND_DAVINCI) += davinci_nand.o COBJS-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_nand.o COBJS-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_nand.o COBJS-$(CONFIG_NAND_FSL_UPM) += fsl_upm.o +COBJS-$(CONFIG_NAND_FSMC) += fsmc_nand.o COBJS-$(CONFIG_NAND_JZ4740) += jz4740_nand.o COBJS-$(CONFIG_NAND_KB9202) += kb9202_nand.o COBJS-$(CONFIG_NAND_KIRKWOOD) += kirkwood_nand.o diff --git a/drivers/mtd/nand/fsmc_nand.c b/drivers/mtd/nand/fsmc_nand.c new file mode 100644 index 0000000..7a61d88 --- /dev/null +++ b/drivers/mtd/nand/fsmc_nand.c @@ -0,0 +1,486 @@ +/* + * (C) Copyright 2010 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.com. + * + * (C) Copyright 2012 + * Amit Virdi, ST Microelectronics, amit.virdi@st.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 +#include +#include +#include +#include +#include +#include +#include + +static u32 fsmc_version; +static struct fsmc_regs *const fsmc_regs_p = (struct fsmc_regs *) + CONFIG_SYS_FSMC_BASE; + +/* + * ECC4 and ECC1 have 13 bytes and 3 bytes of ecc respectively for 512 bytes of + * data. ECC4 can correct up to 8 bits in 512 bytes of data while ECC1 can + * correct 1 bit in 512 bytes + */ + +static struct nand_ecclayout fsmc_ecc4_lp_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 1} + } +}; + +/* + * ECC4 layout for NAND of pagesize 4096 bytes & OOBsize 224 bytes. 13*8 bytes + * of OOB size is reserved for ECC, Byte no. 0 & 1 reserved for bad block & 118 + * bytes are free for use. + */ +static struct nand_ecclayout fsmc_ecc4_224_layout = { + .eccbytes = 104, + .eccpos = { 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 12, 13, 14, + 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, + 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, + 50, 51, 52, 53, 54, 55, 56, + 57, 58, 59, 60, 61, 62, + 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, + 82, 83, 84, 85, 86, 87, 88, + 89, 90, 91, 92, 93, 94, + 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, + 114, 115, 116, 117, 118, 119, 120, + 121, 122, 123, 124, 125, 126 + }, + .oobfree = { + {.offset = 15, .length = 3}, + {.offset = 31, .length = 3}, + {.offset = 47, .length = 3}, + {.offset = 63, .length = 3}, + {.offset = 79, .length = 3}, + {.offset = 95, .length = 3}, + {.offset = 111, .length = 3}, + {.offset = 127, .length = 97} + } +}; + +/* + * ECC placement definitions in oobfree type format + * There are 13 bytes of ecc for every 512 byte block and it has to be read + * consecutively and immediately after the 512 byte data block for hardware to + * generate the error bit offsets in 512 byte data + * Managing the ecc bytes in the following way makes it easier for software to + * read ecc bytes consecutive to data bytes. This way is similar to + * oobfree structure maintained already in u-boot nand driver + */ +static struct fsmc_eccplace fsmc_eccpl_lp = { + .eccplace = { + {.offset = 2, .length = 13}, + {.offset = 18, .length = 13}, + {.offset = 34, .length = 13}, + {.offset = 50, .length = 13}, + {.offset = 66, .length = 13}, + {.offset = 82, .length = 13}, + {.offset = 98, .length = 13}, + {.offset = 114, .length = 13} + } +}; + +static struct nand_ecclayout fsmc_ecc4_sp_layout = { + .eccbytes = 13, + .eccpos = { 0, 1, 2, 3, 6, 7, 8, + 9, 10, 11, 12, 13, 14 + }, + .oobfree = { + {.offset = 15, .length = 1}, + } +}; + +static struct fsmc_eccplace fsmc_eccpl_sp = { + .eccplace = { + {.offset = 0, .length = 4}, + {.offset = 6, .length = 9} + } +}; + +static struct nand_ecclayout fsmc_ecc1_layout = { + .eccbytes = 24, + .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, + 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, + .oobfree = { + {.offset = 8, .length = 8}, + {.offset = 24, .length = 8}, + {.offset = 40, .length = 8}, + {.offset = 56, .length = 8}, + {.offset = 72, .length = 8}, + {.offset = 88, .length = 8}, + {.offset = 104, .length = 8}, + {.offset = 120, .length = 8} + } +}; + +/* Count the number of 0's in buff upto a max of max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static void fsmc_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) +{ + struct nand_chip *this = mtd->priv; + ulong IO_ADDR_W; + + if (ctrl & NAND_CTRL_CHANGE) { + IO_ADDR_W = (ulong)this->IO_ADDR_W; + + IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); + if (ctrl & NAND_CLE) + IO_ADDR_W |= CONFIG_SYS_NAND_CLE; + if (ctrl & NAND_ALE) + IO_ADDR_W |= CONFIG_SYS_NAND_ALE; + + if (ctrl & NAND_NCE) { + writel(readl(&fsmc_regs_p->pc) | + FSMC_ENABLE, &fsmc_regs_p->pc); + } else { + writel(readl(&fsmc_regs_p->pc) & + ~FSMC_ENABLE, &fsmc_regs_p->pc); + } + this->IO_ADDR_W = (void *)IO_ADDR_W; + } + + if (cmd != NAND_CMD_NONE) + writeb(cmd, this->IO_ADDR_W); +} + +static int fsmc_bch8_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + /* The calculated ecc is actually the correction index in data */ + u32 err_idx[8]; + u32 num_err, i; + u32 ecc1, ecc2, ecc3, ecc4; + + num_err = (readl(&fsmc_regs_p->sts) >> 10) & 0xF; + + if (likely(num_err == 0)) + return 0; + + if (unlikely(num_err > 8)) { + /* + * This is a temporary erase check. A newly erased page read + * would result in an ecc error because the oob data is also + * erased to FF and the calculated ecc for an FF data is not + * FF..FF. + * This is a workaround to skip performing correction in case + * data is FF..FF + * + * Logic: + * For every page, each bit written as 0 is counted until these + * number of bits are greater than 8 (the maximum correction + * capability of FSMC for each 512 + 13 bytes) + */ + + int bits_ecc = count_written_bits(read_ecc, 13, 8); + int bits_data = count_written_bits(dat, 512, 8); + + if ((bits_ecc + bits_data) <= 8) { + if (bits_data) + memset(dat, 0xff, 512); + return bits_data + bits_ecc; + } + + return -EBADMSG; + } + + ecc1 = readl(&fsmc_regs_p->ecc1); + ecc2 = readl(&fsmc_regs_p->ecc2); + ecc3 = readl(&fsmc_regs_p->ecc3); + ecc4 = readl(&fsmc_regs_p->sts); + + err_idx[0] = (ecc1 >> 0) & 0x1FFF; + err_idx[1] = (ecc1 >> 13) & 0x1FFF; + err_idx[2] = (((ecc2 >> 0) & 0x7F) << 6) | ((ecc1 >> 26) & 0x3F); + err_idx[3] = (ecc2 >> 7) & 0x1FFF; + err_idx[4] = (((ecc3 >> 0) & 0x1) << 12) | ((ecc2 >> 20) & 0xFFF); + err_idx[5] = (ecc3 >> 1) & 0x1FFF; + err_idx[6] = (ecc3 >> 14) & 0x1FFF; + err_idx[7] = (((ecc4 >> 16) & 0xFF) << 5) | ((ecc3 >> 27) & 0x1F); + + i = 0; + while (i < num_err) { + err_idx[i] ^= 3; + + if (err_idx[i] < 512 * 8) + __change_bit(err_idx[i], dat); + + i++; + } + + return num_err; +} + +static int fsmc_read_hwecc(struct mtd_info *mtd, + const u_char *data, u_char *ecc) +{ + u_int ecc_tmp; + int timeout = CONFIG_SYS_HZ; + ulong start; + + switch (fsmc_version) { + case FSMC_VER8: + start = get_timer(0); + while (get_timer(start) < timeout) { + /* + * Busy waiting for ecc computation + * to finish for 512 bytes + */ + if (readl(&fsmc_regs_p->sts) & FSMC_CODE_RDY) + break; + } + + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + ecc[3] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc2); + ecc[4] = (u_char) (ecc_tmp >> 0); + ecc[5] = (u_char) (ecc_tmp >> 8); + ecc[6] = (u_char) (ecc_tmp >> 16); + ecc[7] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->ecc3); + ecc[8] = (u_char) (ecc_tmp >> 0); + ecc[9] = (u_char) (ecc_tmp >> 8); + ecc[10] = (u_char) (ecc_tmp >> 16); + ecc[11] = (u_char) (ecc_tmp >> 24); + + ecc_tmp = readl(&fsmc_regs_p->sts); + ecc[12] = (u_char) (ecc_tmp >> 16); + break; + + default: + ecc_tmp = readl(&fsmc_regs_p->ecc1); + ecc[0] = (u_char) (ecc_tmp >> 0); + ecc[1] = (u_char) (ecc_tmp >> 8); + ecc[2] = (u_char) (ecc_tmp >> 16); + break; + } + + return 0; +} + +void fsmc_enable_hwecc(struct mtd_info *mtd, int mode) +{ + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCPLEN_256, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) & ~FSMC_ECCEN, + &fsmc_regs_p->pc); + writel(readl(&fsmc_regs_p->pc) | FSMC_ECCEN, + &fsmc_regs_p->pc); +} + +/* + * fsmc_read_page_hwecc + * @mtd: mtd info structure + * @chip: nand chip info structure + * @buf: buffer to store read data + * @page: page number to read + * + * This routine is needed for fsmc verison 8 as reading from NAND chip has to be + * performed in a strict sequence as follows: + * data(512 byte) -> ecc(13 byte) + * After this read, fsmc hardware generates and reports error data bits(upto a + * max of 8 bits) + */ +static int fsmc_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct fsmc_eccplace *fsmc_eccpl; + int i, j, s, stat, eccsize = chip->ecc.size; + int eccbytes = chip->ecc.bytes; + int eccsteps = chip->ecc.steps; + uint8_t *p = buf; + uint8_t *ecc_calc = chip->buffers->ecccalc; + uint8_t *ecc_code = chip->buffers->ecccode; + int off, len, group = 0; + uint8_t oob[13] __attribute__ ((aligned (2))); + + /* Differentiate between small and large page ecc place definitions */ + if (mtd->writesize == 512) + fsmc_eccpl = &fsmc_eccpl_sp; + else + fsmc_eccpl = &fsmc_eccpl_lp; + + for (i = 0, s = 0; s < eccsteps; s++, i += eccbytes, p += eccsize) { + + chip->cmdfunc(mtd, NAND_CMD_READ0, s * eccsize, page); + chip->ecc.hwctl(mtd, NAND_ECC_READ); + chip->read_buf(mtd, p, eccsize); + + for (j = 0; j < eccbytes;) { + off = fsmc_eccpl->eccplace[group].offset; + len = fsmc_eccpl->eccplace[group].length; + group++; + + /* + * length is intentionally kept a higher multiple of 2 + * to read at least 13 bytes even in case of 16 bit NAND + * devices + */ + if (chip->options & NAND_BUSWIDTH_16) + len = roundup(len, 2); + chip->cmdfunc(mtd, NAND_CMD_READOOB, off, page); + chip->read_buf(mtd, oob + j, len); + j += len; + } + + memcpy(&ecc_code[i], oob, 13); + chip->ecc.calculate(mtd, p, &ecc_calc[i]); + + stat = chip->ecc.correct(mtd, p, &ecc_code[i], + &ecc_calc[i]); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + } + + return 0; +} + +int fsmc_nand_init(struct nand_chip *nand) +{ + static int chip_nr; + struct mtd_info *mtd; + int i; + u32 peripid2 = readl(&fsmc_regs_p->peripid2); + + fsmc_version = (peripid2 >> FSMC_REVISION_SHFT) & + FSMC_REVISION_MSK; + + writel(readl(&fsmc_regs_p->ctrl) | FSMC_WP, &fsmc_regs_p->ctrl); + +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + writel(FSMC_DEVWID_16 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#elif defined(CONFIG_SYS_FSMC_NAND_8BIT) + writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, + &fsmc_regs_p->pc); +#else +#error Please define CONFIG_SYS_FSMC_NAND_16BIT or CONFIG_SYS_FSMC_NAND_8BIT +#endif + writel(readl(&fsmc_regs_p->pc) | FSMC_TCLR_1 | FSMC_TAR_1, + &fsmc_regs_p->pc); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->comm); + writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, + &fsmc_regs_p->attrib); + + nand->options = 0; +#if defined(CONFIG_SYS_FSMC_NAND_16BIT) + nand->options |= NAND_BUSWIDTH_16; +#endif + nand->ecc.mode = NAND_ECC_HW; + nand->ecc.size = 512; + nand->ecc.calculate = fsmc_read_hwecc; + nand->ecc.hwctl = fsmc_enable_hwecc; + nand->cmd_ctrl = fsmc_nand_hwcontrol; + nand->IO_ADDR_R = nand->IO_ADDR_W = + (void __iomem *)CONFIG_SYS_NAND_BASE; + nand->badblockbits = 7; + + mtd = &nand_info[chip_nr++]; + mtd->priv = nand; + + switch (fsmc_version) { + case FSMC_VER8: + nand->ecc.bytes = 13; + nand->ecc.correct = fsmc_bch8_correct_data; + nand->ecc.read_page = fsmc_read_page_hwecc; + if (mtd->writesize == 512) + nand->ecc.layout = &fsmc_ecc4_sp_layout; + else { + if (mtd->oobsize == 224) + nand->ecc.layout = &fsmc_ecc4_224_layout; + else + nand->ecc.layout = &fsmc_ecc4_lp_layout; + } + + break; + default: + nand->ecc.bytes = 3; + nand->ecc.layout = &fsmc_ecc1_layout; + nand->ecc.correct = nand_correct_data; + break; + } + + /* Detect NAND chips */ + if (nand_scan_ident(mtd, CONFIG_SYS_MAX_NAND_DEVICE, NULL)) + return -ENXIO; + + if (nand_scan_tail(mtd)) + return -ENXIO; + + for (i = 0; i < CONFIG_SYS_MAX_NAND_DEVICE; i++) + if (nand_register(i)) + return -ENXIO; + + return 0; +} -- cgit v1.1 From 0def98e7be905e4e02eb22cb312bb4e3b327d2fa Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Tue, 22 May 2012 00:15:56 +0000 Subject: mtd/NAND: Remove obsolete SPEAr specific NAND drivers Since, SPEAr platform uses generic FSMC driver now, so spear specific files drivers/mtd/nand/spr_nand.c, arch/arm/include/asm/arch-spear/spr_nand.h are removed Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese Acked-by: Scott Wood --- drivers/mtd/nand/spr_nand.c | 124 -------------------------------------------- 1 file changed, 124 deletions(-) delete mode 100644 drivers/mtd/nand/spr_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/spr_nand.c b/drivers/mtd/nand/spr_nand.c deleted file mode 100644 index 097d0c6..0000000 --- a/drivers/mtd/nand/spr_nand.c +++ /dev/null @@ -1,124 +0,0 @@ -/* - * (C) Copyright 2009 - * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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 -#include -#include -#include -#include -#include - -static struct fsmc_regs *const fsmc_regs_p = - (struct fsmc_regs *)CONFIG_SPEAR_FSMCBASE; - -static struct nand_ecclayout spear_nand_ecclayout = { - .eccbytes = 24, - .eccpos = {2, 3, 4, 18, 19, 20, 34, 35, 36, 50, 51, 52, - 66, 67, 68, 82, 83, 84, 98, 99, 100, 114, 115, 116}, - .oobfree = { - {.offset = 8, .length = 8}, - {.offset = 24, .length = 8}, - {.offset = 40, .length = 8}, - {.offset = 56, .length = 8}, - {.offset = 72, .length = 8}, - {.offset = 88, .length = 8}, - {.offset = 104, .length = 8}, - {.offset = 120, .length = 8} - } -}; - -static void spear_nand_hwcontrol(struct mtd_info *mtd, int cmd, uint ctrl) -{ - struct nand_chip *this = mtd->priv; - ulong IO_ADDR_W; - - if (ctrl & NAND_CTRL_CHANGE) { - IO_ADDR_W = (ulong)this->IO_ADDR_W; - - IO_ADDR_W &= ~(CONFIG_SYS_NAND_CLE | CONFIG_SYS_NAND_ALE); - if (ctrl & NAND_CLE) - IO_ADDR_W |= CONFIG_SYS_NAND_CLE; - if (ctrl & NAND_ALE) - IO_ADDR_W |= CONFIG_SYS_NAND_ALE; - - if (ctrl & NAND_NCE) { - writel(readl(&fsmc_regs_p->genmemctrl_pc) | - FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); - } else { - writel(readl(&fsmc_regs_p->genmemctrl_pc) & - ~FSMC_ENABLE, &fsmc_regs_p->genmemctrl_pc); - } - this->IO_ADDR_W = (void *)IO_ADDR_W; - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, this->IO_ADDR_W); -} - -static int spear_read_hwecc(struct mtd_info *mtd, - const u_char *data, u_char ecc[3]) -{ - u_int ecc_tmp; - - /* read the h/w ECC */ - ecc_tmp = readl(&fsmc_regs_p->genmemctrl_ecc); - - ecc[0] = (u_char) (ecc_tmp & 0xFF); - ecc[1] = (u_char) ((ecc_tmp & 0xFF00) >> 8); - ecc[2] = (u_char) ((ecc_tmp & 0xFF0000) >> 16); - - return 0; -} - -void spear_enable_hwecc(struct mtd_info *mtd, int mode) -{ - writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~0x80, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) & ~FSMC_ECCEN, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_ECCEN, - &fsmc_regs_p->genmemctrl_pc); -} - -int spear_nand_init(struct nand_chip *nand) -{ - writel(FSMC_DEVWID_8 | FSMC_DEVTYPE_NAND | FSMC_ENABLE | FSMC_WAITON, - &fsmc_regs_p->genmemctrl_pc); - writel(readl(&fsmc_regs_p->genmemctrl_pc) | FSMC_TCLR_1 | FSMC_TAR_1, - &fsmc_regs_p->genmemctrl_pc); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->genmemctrl_comm); - writel(FSMC_THIZ_1 | FSMC_THOLD_4 | FSMC_TWAIT_6 | FSMC_TSET_0, - &fsmc_regs_p->genmemctrl_attrib); - - nand->options = 0; - nand->ecc.mode = NAND_ECC_HW; - nand->ecc.layout = &spear_nand_ecclayout; - nand->ecc.size = 512; - nand->ecc.bytes = 3; - nand->ecc.calculate = spear_read_hwecc; - nand->ecc.hwctl = spear_enable_hwecc; - nand->ecc.correct = nand_correct_data; - nand->cmd_ctrl = spear_nand_hwcontrol; - return 0; -} -- cgit v1.1 From f3fcf92d595b297b47a1b58b8ec39f93f40ef912 Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Mon, 7 May 2012 13:00:19 +0530 Subject: st_smi: Add support for SPEAr SMI driver SMI is the serial memory interface controller provided by ST. Earlier, a driver exists in the u-boot source code for the SMI IP. However, it was specific to spear platforms. This commit converts the same driver to a more generic driver. As a result, the driver files are renamed to st_smi.c and st_smi.h and moved into drivers/mtd folder for reusability by other platforms using smi controller peripheral. Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/Makefile | 2 +- drivers/mtd/spr_smi.c | 518 ------------------------------------------------- drivers/mtd/st_smi.c | 519 ++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 520 insertions(+), 519 deletions(-) delete mode 100644 drivers/mtd/spr_smi.c create mode 100644 drivers/mtd/st_smi.c (limited to 'drivers') diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 5a5ecdf..543c845 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -35,7 +35,7 @@ COBJS-$(CONFIG_HAS_DATAFLASH) += dataflash.o COBJS-$(CONFIG_FTSMC020) += ftsmc020.o COBJS-$(CONFIG_FLASH_CFI_LEGACY) += jedec_flash.o COBJS-$(CONFIG_MW_EEPROM) += mw_eeprom.o -COBJS-$(CONFIG_SPEARSMI) += spr_smi.o +COBJS-$(CONFIG_ST_SMI) += st_smi.o COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) diff --git a/drivers/mtd/spr_smi.c b/drivers/mtd/spr_smi.c deleted file mode 100644 index 6d4257a..0000000 --- a/drivers/mtd/spr_smi.c +++ /dev/null @@ -1,518 +0,0 @@ -/* - * (C) Copyright 2009 - * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.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 -#include -#include - -#include -#include -#include - -#if !defined(CONFIG_SYS_NO_FLASH) - -static struct smi_regs *const smicntl = - (struct smi_regs * const)CONFIG_SYS_SMI_BASE; -static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = - CONFIG_SYS_FLASH_ADDR_BASE; -flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; - -#define ST_M25Pxx_ID 0x00002020 - -static struct flash_dev flash_ids[] = { - {0x10, 0x10000, 2}, /* 64K Byte */ - {0x11, 0x20000, 4}, /* 128K Byte */ - {0x12, 0x40000, 4}, /* 256K Byte */ - {0x13, 0x80000, 8}, /* 512K Byte */ - {0x14, 0x100000, 16}, /* 1M Byte */ - {0x15, 0x200000, 32}, /* 2M Byte */ - {0x16, 0x400000, 64}, /* 4M Byte */ - {0x17, 0x800000, 128}, /* 8M Byte */ - {0x18, 0x1000000, 64}, /* 16M Byte */ - {0x00,} -}; - -/* - * smi_wait_xfer_finish - Wait until TFF is set in status register - * @timeout: timeout in milliseconds - * - * Wait until TFF is set in status register - */ -static void smi_wait_xfer_finish(int timeout) -{ - while (timeout--) { - if (readl(&smicntl->smi_sr) & TFF) - break; - udelay(1000); - } -} - -/* - * smi_read_id - Read flash id - * @info: flash_info structure pointer - * @banknum: bank number - * - * Read the flash id present at bank #banknum - */ -static unsigned int smi_read_id(flash_info_t *info, int banknum) -{ - unsigned int value; - - writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); - writel(READ_ID, &smicntl->smi_tr); - writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, - &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); - - value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); - - writel(readl(&smicntl->smi_sr) & ~TFF, &smicntl->smi_sr); - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); - - return value; -} - -/* - * flash_get_size - Detect the SMI flash by reading the ID. - * @base: Base address of the flash area bank #banknum - * @banknum: Bank number - * - * Detect the SMI flash by reading the ID. Initializes the flash_info structure - * with size, sector count etc. - */ -static ulong flash_get_size(ulong base, int banknum) -{ - flash_info_t *info = &flash_info[banknum]; - struct flash_dev *dev; - unsigned int value; - unsigned int density; - int i; - - value = smi_read_id(info, banknum); - density = (value >> 16) & 0xff; - - for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; - i++, dev = &flash_ids[i]) { - if (dev->density == density) { - info->size = dev->size; - info->sector_count = dev->sector_count; - break; - } - } - - if (dev->density == 0x0) - return 0; - - info->flash_id = value & 0xffff; - info->start[0] = base; - - return info->size; -} - -/* - * smi_read_sr - Read status register of SMI - * @bank: bank number - * - * This routine will get the status register of the flash chip present at the - * given bank - */ -static unsigned int smi_read_sr(int bank) -{ - u32 ctrlreg1; - - /* store the CTRL REG1 state */ - ctrlreg1 = readl(&smicntl->smi_cr1); - - /* Program SMI in HW Mode */ - writel(readl(&smicntl->smi_cr1) & ~(SW_MODE | WB_MODE), - &smicntl->smi_cr1); - - /* Performing a RSR instruction in HW mode */ - writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); - - smi_wait_xfer_finish(XFER_FINISH_TOUT); - - /* Restore the CTRL REG1 state */ - writel(ctrlreg1, &smicntl->smi_cr1); - - return readl(&smicntl->smi_sr); -} - -/* - * smi_wait_till_ready - Wait till last operation is over. - * @bank: bank number shifted. - * @timeout: timeout in milliseconds. - * - * This routine checks for WIP(write in progress)bit in Status register(SMSR-b0) - * The routine checks for #timeout loops, each at interval of 1 milli-second. - * If successful the routine returns 0. - */ -static int smi_wait_till_ready(int bank, int timeout) -{ - int count; - unsigned int sr; - - /* One chip guarantees max 5 msec wait here after page writes, - but potentially three seconds (!) after page erase. */ - for (count = 0; count < timeout; count++) { - - sr = smi_read_sr(bank); - if (sr < 0) - break; - else if (!(sr & WIP_BIT)) - return 0; - - /* Try again after 1m-sec */ - udelay(1000); - } - printf("SMI controller is still in wait, timeout=%d\n", timeout); - return -EIO; -} - -/* - * smi_write_enable - Enable the flash to do write operation - * @bank: bank number - * - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static int smi_write_enable(int bank) -{ - u32 ctrlreg1; - int timeout = WMODE_TOUT; - - /* Store the CTRL REG1 state */ - ctrlreg1 = readl(&smicntl->smi_cr1); - - /* Program SMI in H/W Mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); - - /* Give the Flash, Write Enable command */ - writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); - - smi_wait_xfer_finish(XFER_FINISH_TOUT); - - /* Restore the CTRL REG1 state */ - writel(ctrlreg1, &smicntl->smi_cr1); - - while (timeout--) { - if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) - break; - udelay(1000); - } - - if (timeout) - return 0; - - return -1; -} - -/* - * smi_init - SMI initialization routine - * - * SMI initialization routine. Sets SMI control register1. - */ -static void smi_init(void) -{ - /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ - writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, - &smicntl->smi_cr1); -} - -/* - * smi_sector_erase - Erase flash sector - * @info: flash_info structure pointer - * @sector: sector number - * - * Set write enable latch with Write Enable command. - * Returns negative if error occurred. - */ -static int smi_sector_erase(flash_info_t *info, unsigned int sector) -{ - int bank; - unsigned int sect_add; - unsigned int instruction; - - switch (info->start[0]) { - case SMIBANK0_BASE: - bank = BANK0; - break; - case SMIBANK1_BASE: - bank = BANK1; - break; - case SMIBANK2_BASE: - bank = BANK2; - break; - case SMIBANK3_BASE: - bank = BANK3; - break; - default: - return -1; - } - - sect_add = sector * (info->size / info->sector_count); - instruction = ((sect_add >> 8) & 0x0000FF00) | SECTOR_ERASE; - - writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); - - if (info->flash_id == ST_M25Pxx_ID) { - /* Wait until finished previous write command. */ - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; - - /* Send write enable, before erase commands. */ - if (smi_write_enable(bank)) - return -EIO; - - /* Put SMI in SW mode */ - writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); - - /* Send Sector Erase command in SW Mode */ - writel(instruction, &smicntl->smi_tr); - writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, - &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); - - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; - - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, - &smicntl->smi_cr1); - - return 0; - } else { - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, - &smicntl->smi_cr1); - return -EINVAL; - } -} - -/* - * smi_write - Write to SMI flash - * @src_addr: source buffer - * @dst_addr: destination buffer - * @length: length to write in words - * @bank: bank base address - * - * Write to SMI flash - */ -static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, - unsigned int length, ulong bank_addr) -{ - int banknum; - - switch (bank_addr) { - case SMIBANK0_BASE: - banknum = BANK0; - break; - case SMIBANK1_BASE: - banknum = BANK1; - break; - case SMIBANK2_BASE: - banknum = BANK2; - break; - case SMIBANK3_BASE: - banknum = BANK3; - break; - default: - return -1; - } - - if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) - return -EBUSY; - - /* Set SMI in Hardware Mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); - - if (smi_write_enable(banknum)) - return -EIO; - - /* Perform the write command */ - while (length--) { - if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { - if (smi_wait_till_ready(banknum, - CONFIG_SYS_FLASH_WRITE_TOUT)) - return -EBUSY; - - if (smi_write_enable(banknum)) - return -EIO; - } - - *dst_addr++ = *src_addr++; - - if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) - return -EIO; - } - - if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) - return -EBUSY; - - writel(readl(&smicntl->smi_sr) & ~(WCF), &smicntl->smi_sr); - - return 0; -} - -/* - * write_buff - Write to SMI flash - * @info: flash info structure - * @src: source buffer - * @dest_addr: destination buffer - * @length: length to write in words - * - * Write to SMI flash - */ -int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) -{ - return smi_write((unsigned int *)src, (unsigned int *)dest_addr, - (length + 3) / 4, info->start[0]); -} - -/* - * flash_init - SMI flash initialization - * - * SMI flash initialization - */ -unsigned long flash_init(void) -{ - unsigned long size = 0; - int i, j; - - smi_init(); - - for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { - flash_info[i].flash_id = FLASH_UNKNOWN; - size += flash_info[i].size = flash_get_size(bank_base[i], i); - } - - for (j = 0; j < CONFIG_SYS_MAX_FLASH_BANKS; j++) { - for (i = 1; i < flash_info[j].sector_count; i++) - flash_info[j].start[i] = - flash_info[j].start[i - 1] + - flash_info->size / flash_info->sector_count; - - } - - return size; -} - -/* - * flash_print_info - Print SMI flash information - * - * Print SMI flash information - */ -void flash_print_info(flash_info_t *info) -{ - int i; - if (info->flash_id == FLASH_UNKNOWN) { - puts("missing or unknown FLASH type\n"); - return; - } - printf(" Size: %ld MB in %d Sectors\n", - info->size >> 20, info->sector_count); - - puts(" Sector Start Addresses:"); - for (i = 0; i < info->sector_count; ++i) { -#ifdef CONFIG_SYS_FLASH_EMPTY_INFO - int size; - int erased; - u32 *flash; - - /* - * Check if whole sector is erased - */ - size = (info->size) / (info->sector_count); - flash = (u32 *) info->start[i]; - size = size / sizeof(int); - - while ((size--) && (*flash++ == ~0)) - ; - - size++; - if (size) - erased = 0; - else - erased = 1; - - if ((i % 5) == 0) - printf("\n"); - - printf(" %08lX%s%s", - info->start[i], - erased ? " E" : " ", info->protect[i] ? "RO " : " "); -#else - if ((i % 5) == 0) - printf("\n "); - printf(" %08lX%s", - info->start[i], info->protect[i] ? " (RO) " : " "); -#endif - } - putc('\n'); - return; -} - -/* - * flash_erase - Erase SMI flash - * - * Erase SMI flash - */ -int flash_erase(flash_info_t *info, int s_first, int s_last) -{ - int rcode = 0; - int prot = 0; - flash_sect_t sect; - - if (info->flash_id != ST_M25Pxx_ID) { - puts("Can't erase unknown flash type - aborted\n"); - return 1; - } - - if ((s_first < 0) || (s_first > s_last)) { - puts("- no sectors to erase\n"); - return 1; - } - - for (sect = s_first; sect <= s_last; ++sect) { - if (info->protect[sect]) - prot++; - } - if (prot) { - printf("- Warning: %d protected sectors will not be erased!\n", - prot); - } else { - putc('\n'); - } - - for (sect = s_first; sect <= s_last; sect++) { - if (info->protect[sect] == 0) { - if (smi_sector_erase(info, sect)) - rcode = 1; - else - putc('.'); - } - } - puts(" done\n"); - return rcode; -} -#endif diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c new file mode 100644 index 0000000..db08ab9 --- /dev/null +++ b/drivers/mtd/st_smi.c @@ -0,0 +1,519 @@ +/* + * (C) Copyright 2009 + * Vipin Kumar, ST Microelectronics, vipin.kumar@st.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 +#include +#include +#include + +#include +#include + +#if !defined(CONFIG_SYS_NO_FLASH) + +static struct smi_regs *const smicntl = + (struct smi_regs * const)CONFIG_SYS_SMI_BASE; +static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = + CONFIG_SYS_FLASH_ADDR_BASE; +flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; + +#define ST_M25Pxx_ID 0x00002020 + +static struct flash_dev flash_ids[] = { + {0x10, 0x10000, 2}, /* 64K Byte */ + {0x11, 0x20000, 4}, /* 128K Byte */ + {0x12, 0x40000, 4}, /* 256K Byte */ + {0x13, 0x80000, 8}, /* 512K Byte */ + {0x14, 0x100000, 16}, /* 1M Byte */ + {0x15, 0x200000, 32}, /* 2M Byte */ + {0x16, 0x400000, 64}, /* 4M Byte */ + {0x17, 0x800000, 128}, /* 8M Byte */ + {0x18, 0x1000000, 64}, /* 16M Byte */ + {0x00,} +}; + +/* + * smi_wait_xfer_finish - Wait until TFF is set in status register + * @timeout: timeout in milliseconds + * + * Wait until TFF is set in status register + */ +static void smi_wait_xfer_finish(int timeout) +{ + while (timeout--) { + if (readl(&smicntl->smi_sr) & TFF) + break; + udelay(1000); + } +} + +/* + * smi_read_id - Read flash id + * @info: flash_info structure pointer + * @banknum: bank number + * + * Read the flash id present at bank #banknum + */ +static unsigned int smi_read_id(flash_info_t *info, int banknum) +{ + unsigned int value; + + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + writel(READ_ID, &smicntl->smi_tr); + writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, + &smicntl->smi_cr2); + + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); + + writel(readl(&smicntl->smi_sr) & ~TFF, &smicntl->smi_sr); + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + return value; +} + +/* + * flash_get_size - Detect the SMI flash by reading the ID. + * @base: Base address of the flash area bank #banknum + * @banknum: Bank number + * + * Detect the SMI flash by reading the ID. Initializes the flash_info structure + * with size, sector count etc. + */ +static ulong flash_get_size(ulong base, int banknum) +{ + flash_info_t *info = &flash_info[banknum]; + struct flash_dev *dev; + unsigned int value; + unsigned int density; + int i; + + value = smi_read_id(info, banknum); + density = (value >> 16) & 0xff; + + for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; + i++, dev = &flash_ids[i]) { + if (dev->density == density) { + info->size = dev->size; + info->sector_count = dev->sector_count; + break; + } + } + + if (dev->density == 0x0) + return 0; + + info->flash_id = value & 0xffff; + info->start[0] = base; + + return info->size; +} + +/* + * smi_read_sr - Read status register of SMI + * @bank: bank number + * + * This routine will get the status register of the flash chip present at the + * given bank + */ +static unsigned int smi_read_sr(int bank) +{ + u32 ctrlreg1; + + /* store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in HW Mode */ + writel(readl(&smicntl->smi_cr1) & ~(SW_MODE | WB_MODE), + &smicntl->smi_cr1); + + /* Performing a RSR instruction in HW mode */ + writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); + + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + return readl(&smicntl->smi_sr); +} + +/* + * smi_wait_till_ready - Wait till last operation is over. + * @bank: bank number shifted. + * @timeout: timeout in milliseconds. + * + * This routine checks for WIP(write in progress)bit in Status register(SMSR-b0) + * The routine checks for #timeout loops, each at interval of 1 milli-second. + * If successful the routine returns 0. + */ +static int smi_wait_till_ready(int bank, int timeout) +{ + int count; + unsigned int sr; + + /* One chip guarantees max 5 msec wait here after page writes, + but potentially three seconds (!) after page erase. */ + for (count = 0; count < timeout; count++) { + + sr = smi_read_sr(bank); + if (sr < 0) + break; + else if (!(sr & WIP_BIT)) + return 0; + + /* Try again after 1m-sec */ + udelay(1000); + } + printf("SMI controller is still in wait, timeout=%d\n", timeout); + return -EIO; +} + +/* + * smi_write_enable - Enable the flash to do write operation + * @bank: bank number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_write_enable(int bank) +{ + u32 ctrlreg1; + int timeout = WMODE_TOUT; + + /* Store the CTRL REG1 state */ + ctrlreg1 = readl(&smicntl->smi_cr1); + + /* Program SMI in H/W Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + /* Give the Flash, Write Enable command */ + writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); + + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + /* Restore the CTRL REG1 state */ + writel(ctrlreg1, &smicntl->smi_cr1); + + while (timeout--) { + if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) + break; + udelay(1000); + } + + if (timeout) + return 0; + + return -1; +} + +/* + * smi_init - SMI initialization routine + * + * SMI initialization routine. Sets SMI control register1. + */ +void smi_init(void) +{ + /* Setting the fast mode values. SMI working at 166/4 = 41.5 MHz */ + writel(HOLD1 | FAST_MODE | BANK_EN | DSEL_TIME | PRESCAL4, + &smicntl->smi_cr1); +} + +/* + * smi_sector_erase - Erase flash sector + * @info: flash_info structure pointer + * @sector: sector number + * + * Set write enable latch with Write Enable command. + * Returns negative if error occurred. + */ +static int smi_sector_erase(flash_info_t *info, unsigned int sector) +{ + int bank; + unsigned int sect_add; + unsigned int instruction; + + switch (info->start[0]) { + case SMIBANK0_BASE: + bank = BANK0; + break; + case SMIBANK1_BASE: + bank = BANK1; + break; + case SMIBANK2_BASE: + bank = BANK2; + break; + case SMIBANK3_BASE: + bank = BANK3; + break; + default: + return -1; + } + + sect_add = sector * (info->size / info->sector_count); + instruction = ((sect_add >> 8) & 0x0000FF00) | SECTOR_ERASE; + + writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); + + if (info->flash_id == ST_M25Pxx_ID) { + /* Wait until finished previous write command. */ + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Send write enable, before erase commands. */ + if (smi_write_enable(bank)) + return -EIO; + + /* Put SMI in SW mode */ + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + + /* Send Sector Erase command in SW Mode */ + writel(instruction, &smicntl->smi_tr); + writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, + &smicntl->smi_cr2); + smi_wait_xfer_finish(XFER_FINISH_TOUT); + + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; + + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + &smicntl->smi_cr1); + + return 0; + } else { + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + &smicntl->smi_cr1); + return -EINVAL; + } +} + +/* + * smi_write - Write to SMI flash + * @src_addr: source buffer + * @dst_addr: destination buffer + * @length: length to write in words + * @bank: bank base address + * + * Write to SMI flash + */ +static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, + unsigned int length, ulong bank_addr) +{ + int banknum; + + switch (bank_addr) { + case SMIBANK0_BASE: + banknum = BANK0; + break; + case SMIBANK1_BASE: + banknum = BANK1; + break; + case SMIBANK2_BASE: + banknum = BANK2; + break; + case SMIBANK3_BASE: + banknum = BANK3; + break; + default: + return -1; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + /* Set SMI in Hardware Mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); + + if (smi_write_enable(banknum)) + return -EIO; + + /* Perform the write command */ + while (length--) { + if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { + if (smi_wait_till_ready(banknum, + CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + if (smi_write_enable(banknum)) + return -EIO; + } + + *dst_addr++ = *src_addr++; + + if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) + return -EIO; + } + + if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) + return -EBUSY; + + writel(readl(&smicntl->smi_sr) & ~(WCF), &smicntl->smi_sr); + + return 0; +} + +/* + * write_buff - Write to SMI flash + * @info: flash info structure + * @src: source buffer + * @dest_addr: destination buffer + * @length: length to write in words + * + * Write to SMI flash + */ +int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) +{ + return smi_write((unsigned int *)src, (unsigned int *)dest_addr, + (length + 3) / 4, info->start[0]); +} + +/* + * flash_init - SMI flash initialization + * + * SMI flash initialization + */ +unsigned long flash_init(void) +{ + unsigned long size = 0; + int i, j; + + smi_init(); + + for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; i++) { + flash_info[i].flash_id = FLASH_UNKNOWN; + size += flash_info[i].size = flash_get_size(bank_base[i], i); + } + + for (j = 0; j < CONFIG_SYS_MAX_FLASH_BANKS; j++) { + for (i = 1; i < flash_info[j].sector_count; i++) + flash_info[j].start[i] = + flash_info[j].start[i - 1] + + flash_info->size / flash_info->sector_count; + + } + + return size; +} + +/* + * flash_print_info - Print SMI flash information + * + * Print SMI flash information + */ +void flash_print_info(flash_info_t *info) +{ + int i; + if (info->flash_id == FLASH_UNKNOWN) { + puts("missing or unknown FLASH type\n"); + return; + } + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + + puts(" Sector Start Addresses:"); + for (i = 0; i < info->sector_count; ++i) { +#ifdef CONFIG_SYS_FLASH_EMPTY_INFO + int size; + int erased; + u32 *flash; + + /* + * Check if whole sector is erased + */ + size = (info->size) / (info->sector_count); + flash = (u32 *) info->start[i]; + size = size / sizeof(int); + + while ((size--) && (*flash++ == ~0)) + ; + + size++; + if (size) + erased = 0; + else + erased = 1; + + if ((i % 5) == 0) + printf("\n"); + + printf(" %08lX%s%s", + info->start[i], + erased ? " E" : " ", info->protect[i] ? "RO " : " "); +#else + if ((i % 5) == 0) + printf("\n "); + printf(" %08lX%s", + info->start[i], info->protect[i] ? " (RO) " : " "); +#endif + } + putc('\n'); + return; +} + +/* + * flash_erase - Erase SMI flash + * + * Erase SMI flash + */ +int flash_erase(flash_info_t *info, int s_first, int s_last) +{ + int rcode = 0; + int prot = 0; + flash_sect_t sect; + + if (info->flash_id != ST_M25Pxx_ID) { + puts("Can't erase unknown flash type - aborted\n"); + return 1; + } + + if ((s_first < 0) || (s_first > s_last)) { + puts("- no sectors to erase\n"); + return 1; + } + + for (sect = s_first; sect <= s_last; ++sect) { + if (info->protect[sect]) + prot++; + } + if (prot) { + printf("- Warning: %d protected sectors will not be erased!\n", + prot); + } else { + putc('\n'); + } + + for (sect = s_first; sect <= s_last; sect++) { + if (info->protect[sect] == 0) { + if (smi_sector_erase(info, sect)) + rcode = 1; + else + putc('.'); + } + } + puts(" done\n"); + return rcode; +} +#endif -- cgit v1.1 From 5c16c54124ba7a21979e1eb656e696b7b6d881ec Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Mon, 7 May 2012 13:00:20 +0530 Subject: st_smi: Return error in case TFF is not set Curently the code makes wrong assumption that the Transfer finished flag shall be set within the stipulated time. However, there may occur a scenario in which the TFF flag is not set. Return error in that case. Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index db08ab9..9769b28 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -58,13 +58,15 @@ static struct flash_dev flash_ids[] = { * * Wait until TFF is set in status register */ -static void smi_wait_xfer_finish(int timeout) +static int smi_wait_xfer_finish(int timeout) { - while (timeout--) { + do { if (readl(&smicntl->smi_sr) & TFF) - break; + return 0; udelay(1000); - } + } while (timeout--); + + return -1; } /* @@ -83,7 +85,8 @@ static unsigned int smi_read_id(flash_info_t *info, int banknum) writel((banknum << BANKSEL_SHIFT) | SEND | TX_LEN_1 | RX_LEN_3, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; value = (readl(&smicntl->smi_rr) & 0x00FFFFFF); @@ -151,7 +154,8 @@ static unsigned int smi_read_sr(int bank) /* Performing a RSR instruction in HW mode */ writel((bank << BANKSEL_SHIFT) | RD_STATUS_REG, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); @@ -211,7 +215,8 @@ static int smi_write_enable(int bank) /* Give the Flash, Write Enable command */ writel((bank << BANKSEL_SHIFT) | WE, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -1; /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); @@ -292,7 +297,8 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) writel(instruction, &smicntl->smi_tr); writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, &smicntl->smi_cr2); - smi_wait_xfer_finish(XFER_FINISH_TOUT); + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) return -EBUSY; -- cgit v1.1 From 69fcb55f71c9e2c40a0f1de6f70b26a9ec02cfb8 Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Mon, 7 May 2012 13:00:22 +0530 Subject: st_smi: Enhance the error handling This commit does the following: - Reports error if SNOR flash is not found on the board - Changes smi_read_sr to return error using which a retry mechanism is implemented for reading flash status Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 9769b28..2c6d59d 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -108,11 +108,17 @@ static ulong flash_get_size(ulong base, int banknum) { flash_info_t *info = &flash_info[banknum]; struct flash_dev *dev; - unsigned int value; + int value; unsigned int density; int i; value = smi_read_id(info, banknum); + + if (value < 0) { + printf("Flash id could not be read\n"); + return 0; + } + density = (value >> 16) & 0xff; for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; @@ -140,7 +146,7 @@ static ulong flash_get_size(ulong base, int banknum) * This routine will get the status register of the flash chip present at the * given bank */ -static unsigned int smi_read_sr(int bank) +static int smi_read_sr(int bank) { u32 ctrlreg1; @@ -174,13 +180,11 @@ static unsigned int smi_read_sr(int bank) */ static int smi_wait_till_ready(int bank, int timeout) { - int count; - unsigned int sr; + int sr; /* One chip guarantees max 5 msec wait here after page writes, but potentially three seconds (!) after page erase. */ - for (count = 0; count < timeout; count++) { - + do { sr = smi_read_sr(bank); if (sr < 0) break; @@ -189,7 +193,8 @@ static int smi_wait_till_ready(int bank, int timeout) /* Try again after 1m-sec */ udelay(1000); - } + } while (timeout--); + printf("SMI controller is still in wait, timeout=%d\n", timeout); return -EIO; } @@ -205,6 +210,7 @@ static int smi_write_enable(int bank) { u32 ctrlreg1; int timeout = WMODE_TOUT; + int sr; /* Store the CTRL REG1 state */ ctrlreg1 = readl(&smicntl->smi_cr1); @@ -221,14 +227,16 @@ static int smi_write_enable(int bank) /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); - while (timeout--) { - if (smi_read_sr(bank) & (1 << (bank + WM_SHIFT))) + do { + sr = smi_read_sr(bank); + if (sr < 0) break; - udelay(1000); - } + else if (sr & (1 << (bank + WM_SHIFT))) + return 0; - if (timeout) - return 0; + /* Try again after 1m-sec */ + udelay(1000); + } while (timeout--); return -1; } -- cgit v1.1 From 0befe7d7a396339067ccb7018ab69a097ff62cf4 Mon Sep 17 00:00:00 2001 From: Vipin KUMAR Date: Mon, 7 May 2012 13:00:23 +0530 Subject: st_smi: Read status until timeout happens SMI driver read status fails because the control register could not be overwritten. Instead, the read status should be tried until timeout. Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 2c6d59d..088c7c7 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -187,7 +187,7 @@ static int smi_wait_till_ready(int bank, int timeout) do { sr = smi_read_sr(bank); if (sr < 0) - break; + continue; /* try until timeout */ else if (!(sr & WIP_BIT)) return 0; -- cgit v1.1 From a59c7b37b908639f9daa7fe50d4646b3947e2d62 Mon Sep 17 00:00:00 2001 From: Shiraz Hashim Date: Mon, 7 May 2012 13:00:24 +0530 Subject: st_smi: Move status register read before modifying ctrl register Signed-off-by: Shiraz Hashim Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 088c7c7..8c27603 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -148,7 +148,7 @@ static ulong flash_get_size(ulong base, int banknum) */ static int smi_read_sr(int bank) { - u32 ctrlreg1; + u32 ctrlreg1, val; /* store the CTRL REG1 state */ ctrlreg1 = readl(&smicntl->smi_cr1); @@ -163,10 +163,12 @@ static int smi_read_sr(int bank) if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) return -1; + val = readl(&smicntl->smi_sr); + /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); - return readl(&smicntl->smi_sr); + return val; } /* -- cgit v1.1 From a5ad7ccd74de244f91d75fb3879eaa1929304e89 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Mon, 7 May 2012 13:00:25 +0530 Subject: st_smi: Fix smi read status smi_read_sr fails sometimes because of TFF not getting set within assumed time. This condition may arise because of, for example, smi memory being in a erase mode. This fix is to enable reading the status register until timeout. Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 8c27603..38f22b7 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -188,9 +188,7 @@ static int smi_wait_till_ready(int bank, int timeout) but potentially three seconds (!) after page erase. */ do { sr = smi_read_sr(bank); - if (sr < 0) - continue; /* try until timeout */ - else if (!(sr & WIP_BIT)) + if ((sr >= 0) && (!(sr & WIP_BIT))) return 0; /* Try again after 1m-sec */ @@ -231,9 +229,7 @@ static int smi_write_enable(int bank) do { sr = smi_read_sr(bank); - if (sr < 0) - break; - else if (sr & (1 << (bank + WM_SHIFT))) + if ((sr >= 0) && (sr & (1 << (bank + WM_SHIFT)))) return 0; /* Try again after 1m-sec */ -- cgit v1.1 From ae3e0cc924579bcea0906a6888a7bc01a19157e4 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 7 May 2012 13:00:26 +0530 Subject: st_smi: Removed no needed dependency on ST_M25Pxx_ID Since the smi erase code is very generic and works for any kind of flash, there is no need to test for ST_M25Pxx_ID flash types like m25p40 flashes). Signed-off-by: Armando Visconti Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 50 ++++++++++++++++++-------------------------------- 1 file changed, 18 insertions(+), 32 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 38f22b7..83a57d2 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -37,8 +37,6 @@ static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = CONFIG_SYS_FLASH_ADDR_BASE; flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; -#define ST_M25Pxx_ID 0x00002020 - static struct flash_dev flash_ids[] = { {0x10, 0x10000, 2}, /* 64K Byte */ {0x11, 0x20000, 4}, /* 128K Byte */ @@ -287,39 +285,32 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) writel(readl(&smicntl->smi_sr) & ~(ERF1 | ERF2), &smicntl->smi_sr); - if (info->flash_id == ST_M25Pxx_ID) { - /* Wait until finished previous write command. */ - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; + /* Wait until finished previous write command. */ + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; - /* Send write enable, before erase commands. */ - if (smi_write_enable(bank)) - return -EIO; + /* Send write enable, before erase commands. */ + if (smi_write_enable(bank)) + return -EIO; - /* Put SMI in SW mode */ - writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); + /* Put SMI in SW mode */ + writel(readl(&smicntl->smi_cr1) | SW_MODE, &smicntl->smi_cr1); - /* Send Sector Erase command in SW Mode */ - writel(instruction, &smicntl->smi_tr); - writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, + /* Send Sector Erase command in SW Mode */ + writel(instruction, &smicntl->smi_tr); + writel((bank << BANKSEL_SHIFT) | SEND | TX_LEN_4, &smicntl->smi_cr2); - if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) - return -EIO; + if (smi_wait_xfer_finish(XFER_FINISH_TOUT)) + return -EIO; - if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) - return -EBUSY; + if (smi_wait_till_ready(bank, CONFIG_SYS_FLASH_ERASE_TOUT)) + return -EBUSY; - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, + /* Put SMI in HW mode */ + writel(readl(&smicntl->smi_cr1) & ~SW_MODE, &smicntl->smi_cr1); - return 0; - } else { - /* Put SMI in HW mode */ - writel(readl(&smicntl->smi_cr1) & ~SW_MODE, - &smicntl->smi_cr1); - return -EINVAL; - } + return 0; } /* @@ -496,11 +487,6 @@ int flash_erase(flash_info_t *info, int s_first, int s_last) int prot = 0; flash_sect_t sect; - if (info->flash_id != ST_M25Pxx_ID) { - puts("Can't erase unknown flash type - aborted\n"); - return 1; - } - if ((s_first < 0) || (s_first > s_last)) { puts("- no sectors to erase\n"); return 1; -- cgit v1.1 From 6d6d23c143f50389018fd1f5091a7101247d79d5 Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 7 May 2012 13:00:27 +0530 Subject: st_smi: Change the flash probing method THis patch introduces a new methodology for flash probing in which flash_devices[] table, looked-up thru the dev_id, is used to locate the flash geometry and information. Signed-off-by: Armando Visconti Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 92 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 65 insertions(+), 27 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 83a57d2..976b097 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -37,17 +37,61 @@ static ulong bank_base[CONFIG_SYS_MAX_FLASH_BANKS] = CONFIG_SYS_FLASH_ADDR_BASE; flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS]; -static struct flash_dev flash_ids[] = { - {0x10, 0x10000, 2}, /* 64K Byte */ - {0x11, 0x20000, 4}, /* 128K Byte */ - {0x12, 0x40000, 4}, /* 256K Byte */ - {0x13, 0x80000, 8}, /* 512K Byte */ - {0x14, 0x100000, 16}, /* 1M Byte */ - {0x15, 0x200000, 32}, /* 2M Byte */ - {0x16, 0x400000, 64}, /* 4M Byte */ - {0x17, 0x800000, 128}, /* 8M Byte */ - {0x18, 0x1000000, 64}, /* 16M Byte */ - {0x00,} +/* data structure to maintain flash ids from different vendors */ +struct flash_device { + char *name; + u8 erase_cmd; + u32 device_id; + u32 pagesize; + unsigned long sectorsize; + unsigned long size_in_bytes; +}; + +#define FLASH_ID(n, es, id, psize, ssize, size) \ +{ \ + .name = n, \ + .erase_cmd = es, \ + .device_id = id, \ + .pagesize = psize, \ + .sectorsize = ssize, \ + .size_in_bytes = size \ +} + +/* + * List of supported flash devices. + * Currently the erase_cmd field is not used in this driver. + */ +static struct flash_device flash_devices[] = { + FLASH_ID("st m25p16" , 0xd8, 0x00152020, 0x100, 0x10000, 0x200000), + FLASH_ID("st m25p32" , 0xd8, 0x00162020, 0x100, 0x10000, 0x400000), + FLASH_ID("st m25p64" , 0xd8, 0x00172020, 0x100, 0x10000, 0x800000), + FLASH_ID("st m25p128" , 0xd8, 0x00182020, 0x100, 0x40000, 0x1000000), + FLASH_ID("st m25p05" , 0xd8, 0x00102020, 0x80 , 0x8000 , 0x10000), + FLASH_ID("st m25p10" , 0xd8, 0x00112020, 0x80 , 0x8000 , 0x20000), + FLASH_ID("st m25p20" , 0xd8, 0x00122020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m25p40" , 0xd8, 0x00132020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m25p80" , 0xd8, 0x00142020, 0x100, 0x10000, 0x100000), + FLASH_ID("st m45pe10" , 0xd8, 0x00114020, 0x100, 0x10000, 0x20000), + FLASH_ID("st m45pe20" , 0xd8, 0x00124020, 0x100, 0x10000, 0x40000), + FLASH_ID("st m45pe40" , 0xd8, 0x00134020, 0x100, 0x10000, 0x80000), + FLASH_ID("st m45pe80" , 0xd8, 0x00144020, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl004" , 0xd8, 0x00120201, 0x100, 0x10000, 0x80000), + FLASH_ID("sp s25fl008" , 0xd8, 0x00130201, 0x100, 0x10000, 0x100000), + FLASH_ID("sp s25fl016" , 0xd8, 0x00140201, 0x100, 0x10000, 0x200000), + FLASH_ID("sp s25fl032" , 0xd8, 0x00150201, 0x100, 0x10000, 0x400000), + FLASH_ID("sp s25fl064" , 0xd8, 0x00160201, 0x100, 0x10000, 0x800000), + FLASH_ID("mac 25l512" , 0xd8, 0x001020C2, 0x010, 0x10000, 0x10000), + FLASH_ID("mac 25l1005" , 0xd8, 0x001120C2, 0x010, 0x10000, 0x20000), + FLASH_ID("mac 25l2005" , 0xd8, 0x001220C2, 0x010, 0x10000, 0x40000), + FLASH_ID("mac 25l4005" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l4005a" , 0xd8, 0x001320C2, 0x010, 0x10000, 0x80000), + FLASH_ID("mac 25l8005" , 0xd8, 0x001420C2, 0x010, 0x10000, 0x100000), + FLASH_ID("mac 25l1605" , 0xd8, 0x001520C2, 0x100, 0x10000, 0x200000), + FLASH_ID("mac 25l1605a" , 0xd8, 0x001520C2, 0x010, 0x10000, 0x200000), + FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), + FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), + FLASH_ID("wbd w25q128" , 0xd8, 0x001840EF, 0x1000, 0x10000, 0x1000000), }; /* @@ -105,9 +149,7 @@ static unsigned int smi_read_id(flash_info_t *info, int banknum) static ulong flash_get_size(ulong base, int banknum) { flash_info_t *info = &flash_info[banknum]; - struct flash_dev *dev; int value; - unsigned int density; int i; value = smi_read_id(info, banknum); @@ -117,24 +159,20 @@ static ulong flash_get_size(ulong base, int banknum) return 0; } - density = (value >> 16) & 0xff; + /* Matches chip-id to entire list of 'serial-nor flash' ids */ + for (i = 0; i < ARRAY_SIZE(flash_devices); i++) { + if (flash_devices[i].device_id == value) { + info->size = flash_devices[i].size_in_bytes; + info->flash_id = value; + info->start[0] = base; + info->sector_count = + info->size/flash_devices[i].sectorsize; - for (i = 0, dev = &flash_ids[0]; dev->density != 0x0; - i++, dev = &flash_ids[i]) { - if (dev->density == density) { - info->size = dev->size; - info->sector_count = dev->sector_count; - break; + return info->size; } } - if (dev->density == 0x0) - return 0; - - info->flash_id = value & 0xffff; - info->start[0] = base; - - return info->size; + return 0; } /* -- cgit v1.1 From cf9026deb8d83b00c37b19ec7b63491e9338d49f Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 7 May 2012 13:00:28 +0530 Subject: st_smi: Fix bug in flash_print_info() If the flash size was smaller than 1MB then flash_print_info() was erroneously reporting 0 MB. Signed-off-by: Armando Visconti Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 976b097..5378b57 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -471,8 +471,13 @@ void flash_print_info(flash_info_t *info) puts("missing or unknown FLASH type\n"); return; } - printf(" Size: %ld MB in %d Sectors\n", - info->size >> 20, info->sector_count); + + if (info->size >= 0x100000) + printf(" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); + else + printf(" Size: %ld KB in %d Sectors\n", + info->size >> 10, info->sector_count); puts(" Sector Start Addresses:"); for (i = 0; i < info->sector_count; ++i) { -- cgit v1.1 From b5992fac88f8068f4f1af1de2e76889465e45693 Mon Sep 17 00:00:00 2001 From: Amit Virdi Date: Mon, 7 May 2012 13:00:29 +0530 Subject: st_smi: Change timeout loop implementation There are two problems in the current timeout loop implementation: 1. In case initial test failing, there will always be a delay of 1 ms 2. The delay duration is not tunable The new implementation addresses both these limitations. Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 5378b57..75ae9aa 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -102,11 +102,15 @@ static struct flash_device flash_devices[] = { */ static int smi_wait_xfer_finish(int timeout) { - do { + ulong start = get_timer(0); + + while (get_timer(start) < timeout) { if (readl(&smicntl->smi_sr) & TFF) return 0; - udelay(1000); - } while (timeout--); + + /* Try after 10 ms */ + udelay(10); + }; return -1; } @@ -219,16 +223,17 @@ static int smi_read_sr(int bank) static int smi_wait_till_ready(int bank, int timeout) { int sr; + ulong start = get_timer(0); /* One chip guarantees max 5 msec wait here after page writes, but potentially three seconds (!) after page erase. */ - do { + while (get_timer(start) < timeout) { sr = smi_read_sr(bank); if ((sr >= 0) && (!(sr & WIP_BIT))) return 0; - /* Try again after 1m-sec */ - udelay(1000); + /* Try again after 10 usec */ + udelay(10); } while (timeout--); printf("SMI controller is still in wait, timeout=%d\n", timeout); @@ -245,6 +250,7 @@ static int smi_wait_till_ready(int bank, int timeout) static int smi_write_enable(int bank) { u32 ctrlreg1; + u32 start; int timeout = WMODE_TOUT; int sr; @@ -263,14 +269,15 @@ static int smi_write_enable(int bank) /* Restore the CTRL REG1 state */ writel(ctrlreg1, &smicntl->smi_cr1); - do { + start = get_timer(0); + while (get_timer(start) < timeout) { sr = smi_read_sr(bank); if ((sr >= 0) && (sr & (1 << (bank + WM_SHIFT)))) return 0; - /* Try again after 1m-sec */ - udelay(1000); - } while (timeout--); + /* Try again after 10 usec */ + udelay(10); + }; return -1; } -- cgit v1.1 From 9264077635f4053a646d90bd8ebd5287c602d18d Mon Sep 17 00:00:00 2001 From: Armando Visconti Date: Mon, 7 May 2012 13:00:30 +0530 Subject: st_smi: Fixed page size for Winbond W25Q128FV flash Signed-off-by: Armando Visconti Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/mtd/st_smi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 75ae9aa..77e36af 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -91,7 +91,7 @@ static struct flash_device flash_devices[] = { FLASH_ID("mac 25l3205" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), FLASH_ID("mac 25l3205a" , 0xd8, 0x001620C2, 0x100, 0x10000, 0x400000), FLASH_ID("mac 25l6405" , 0xd8, 0x001720C2, 0x100, 0x10000, 0x800000), - FLASH_ID("wbd w25q128" , 0xd8, 0x001840EF, 0x1000, 0x10000, 0x1000000), + FLASH_ID("wbd w25q128" , 0xd8, 0x001840EF, 0x100, 0x10000, 0x1000000), }; /* -- cgit v1.1 From 9afc1af01f2d4b033e0da5df105b399949976a12 Mon Sep 17 00:00:00 2001 From: Vipin Kumar Date: Mon, 7 May 2012 13:06:44 +0530 Subject: SPEAr: Add interface information in initialization Few Designware peripheral registers need to be modified based on the ethernet interface selected by the board. This patch supports interface information in ethernet driver Signed-off-by: Vipin Kumar Signed-off-by: Amit Virdi Signed-off-by: Stefan Roese --- drivers/net/designware.c | 10 +++++++++- drivers/net/designware.h | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 9b17db4..8f22e00 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -171,6 +171,13 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) if (priv->speed != SPEED_1000M) conf |= MII_PORTSELECT; + if ((priv->interface != PHY_INTERFACE_MODE_MII) && + (priv->interface != PHY_INTERFACE_MODE_GMII)) { + + if (priv->speed == SPEED_100M) + conf |= FES_100; + } + if (priv->duplex == FULL_DUPLEX) conf |= FULLDPLXMODE; @@ -531,7 +538,7 @@ static int dw_mii_write(const char *devname, u8 addr, u8 reg, u16 val) } #endif -int designware_initialize(u32 id, ulong base_addr, u32 phy_addr) +int designware_initialize(u32 id, ulong base_addr, u32 phy_addr, u32 interface) { struct eth_device *dev; struct dw_eth_dev *priv; @@ -565,6 +572,7 @@ int designware_initialize(u32 id, ulong base_addr, u32 phy_addr) DW_DMA_BASE_OFFSET); priv->address = phy_addr; priv->phy_configured = 0; + priv->interface = interface; if (mac_reset(dev) < 0) return -1; diff --git a/drivers/net/designware.h b/drivers/net/designware.h index abf729d..40020bf 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -234,6 +234,7 @@ struct dmamacdescr { struct dw_eth_dev { u32 address; + u32 interface; u32 speed; u32 duplex; u32 tx_currdescnum; -- cgit v1.1 From ef76025a99247cdb8f927a2c9f15400678dfb599 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Mon, 7 May 2012 12:04:25 +0200 Subject: net: Multiple updates/enhancements to designware.c This patch adds the following changes to designware ethernet driver found on the ST SPEAr SoC: - Don't init MAC & PHY upon startup. This causes a delay, waiting for the auto negotiation to complete. And we don't want this delay to always happen. Especially not on platforms where ethernet is not used at all (e.g. booting via flash). Instead postpone the MAC / PHY configuration to the stage, where ethernet is first used. - Add possibility for board specific PHY init code. This is needed for example on the X600 board, where the Vitesse PHY needs to be configured for GMII mode. This board specific PHY init is done via the function designware_board_phy_init(). And this driver now adds a weak default which can be overridden by board code. - Use common functions miiphy_speed() & miiphy_duplex() to read link status from PHY. - Print status and progress of auto negotiation. - Print link status (speed, dupex) upon first usage. Signed-off-by: Stefan Roese Cc: Amit Virdi Cc: Vipin Kumar Cc: Joe Hershberger Acked-by: Joe Hershberger --- drivers/net/designware.c | 118 ++++++++++++++++++++--------------------------- drivers/net/designware.h | 1 + 2 files changed, 50 insertions(+), 69 deletions(-) (limited to 'drivers') diff --git a/drivers/net/designware.c b/drivers/net/designware.c index 8f22e00..326d550 100644 --- a/drivers/net/designware.c +++ b/drivers/net/designware.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "designware.h" @@ -153,6 +154,13 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) if (priv->phy_configured != 1) configure_phy(dev); + /* Print link status only once */ + if (!priv->link_printed) { + printf("ENET Speed is %d Mbps - %s duplex connection\n", + priv->speed, (priv->duplex == HALF) ? "HALF" : "FULL"); + priv->link_printed = 1; + } + /* Reset ethernet hardware */ if (mac_reset(dev) < 0) return -1; @@ -168,17 +176,17 @@ static int dw_eth_init(struct eth_device *dev, bd_t *bis) conf = FRAMEBURSTENABLE | DISABLERXOWN; - if (priv->speed != SPEED_1000M) + if (priv->speed != 1000) conf |= MII_PORTSELECT; if ((priv->interface != PHY_INTERFACE_MODE_MII) && (priv->interface != PHY_INTERFACE_MODE_GMII)) { - if (priv->speed == SPEED_100M) + if (priv->speed == 100) conf |= FES_100; } - if (priv->duplex == FULL_DUPLEX) + if (priv->duplex == FULL) conf |= FULLDPLXMODE; writel(conf, &mac_p->conf); @@ -396,6 +404,16 @@ static int dw_reset_phy(struct eth_device *dev) return 0; } +/* + * Add weak default function for board specific PHY configuration + */ +int __weak designware_board_phy_init(struct eth_device *dev, int phy_addr, + int (*mii_write)(struct eth_device *, u8, u8, u16), + int dw_reset_phy(struct eth_device *)) +{ + return 0; +} + static int configure_phy(struct eth_device *dev) { struct dw_eth_dev *priv = dev->priv; @@ -405,9 +423,6 @@ static int configure_phy(struct eth_device *dev) u16 bmsr; u32 timeout; ulong start; - u16 anlpar, btsr; -#else - u16 ctrl; #endif #if defined(CONFIG_DW_SEARCH_PHY) @@ -419,6 +434,16 @@ static int configure_phy(struct eth_device *dev) #else phy_addr = priv->address; #endif + + /* + * Some boards need board specific PHY initialization. This is + * after the main driver init code but before the auto negotiation + * is run. + */ + if (designware_board_phy_init(dev, phy_addr, + eth_mdio_write, dw_reset_phy) < 0) + return -1; + if (dw_reset_phy(dev) < 0) return -1; @@ -444,72 +469,32 @@ static int configure_phy(struct eth_device *dev) #if defined(CONFIG_DW_AUTONEG) timeout = CONFIG_AUTONEG_TIMEOUT; start = get_timer(0); - + puts("Waiting for PHY auto negotiation to complete"); while (get_timer(start) < timeout) { eth_mdio_read(dev, phy_addr, MII_BMSR, &bmsr); - if (bmsr & BMSR_ANEGCOMPLETE) + if (bmsr & BMSR_ANEGCOMPLETE) { + priv->phy_configured = 1; break; - - /* Try again after 10usec */ - udelay(10); - }; - - eth_mdio_read(dev, phy_addr, MII_LPA, &anlpar); - eth_mdio_read(dev, phy_addr, MII_STAT1000, &btsr); - - if (bmsr & BMSR_ANEGCOMPLETE) { - if (btsr & PHY_1000BTSR_1000FD) { - priv->speed = SPEED_1000M; - bmcr |= BMCR_SPEED1000; - priv->duplex = FULL_DUPLEX; - bmcr |= BMCR_FULLDPLX; - } else if (btsr & PHY_1000BTSR_1000HD) { - priv->speed = SPEED_1000M; - bmcr |= BMCR_SPEED1000; - priv->duplex = HALF_DUPLEX; - bmcr &= ~BMCR_FULLDPLX; - } else if (anlpar & LPA_100FULL) { - priv->speed = SPEED_100M; - bmcr |= BMCR_SPEED100; - priv->duplex = FULL_DUPLEX; - bmcr |= BMCR_FULLDPLX; - } else if (anlpar & LPA_100HALF) { - priv->speed = SPEED_100M; - bmcr |= BMCR_SPEED100; - priv->duplex = HALF_DUPLEX; - bmcr &= ~BMCR_FULLDPLX; - } else if (anlpar & LPA_10FULL) { - priv->speed = SPEED_10M; - bmcr &= ~BMCR_SPEED100; - priv->duplex = FULL_DUPLEX; - bmcr |= BMCR_FULLDPLX; - } else { - priv->speed = SPEED_10M; - bmcr &= ~BMCR_SPEED100; - priv->duplex = HALF_DUPLEX; - bmcr &= ~BMCR_FULLDPLX; } - if (eth_mdio_write(dev, phy_addr, MII_BMCR, bmcr) < 0) - return -1; - } else - return -1; -#else - if (eth_mdio_read(dev, phy_addr, MII_BMCR, &ctrl) < 0) - return -1; - if (ctrl & BMCR_FULLDPLX) - priv->duplex = FULL_DUPLEX; - else - priv->duplex = HALF_DUPLEX; + /* Print dot all 1s to show progress */ + if ((get_timer(start) % 1000) == 0) + putc('.'); + + /* Try again after 1msec */ + udelay(1000); + }; - if (ctrl & BMCR_SPEED1000) - priv->speed = SPEED_1000M; - else if (ctrl & BMCR_SPEED100) - priv->speed = SPEED_100M; + if (!(bmsr & BMSR_ANEGCOMPLETE)) + puts(" TIMEOUT!\n"); else - priv->speed = SPEED_10M; -#endif + puts(" done\n"); +#else priv->phy_configured = 1; +#endif + + priv->speed = miiphy_speed(dev->name, phy_addr); + priv->duplex = miiphy_duplex(dev->name, phy_addr); return 0; } @@ -574,11 +559,6 @@ int designware_initialize(u32 id, ulong base_addr, u32 phy_addr, u32 interface) priv->phy_configured = 0; priv->interface = interface; - if (mac_reset(dev) < 0) - return -1; - - configure_phy(dev); - dev->init = dw_eth_init; dev->send = dw_eth_send; dev->recv = dw_eth_recv; diff --git a/drivers/net/designware.h b/drivers/net/designware.h index 40020bf..d668f8f 100644 --- a/drivers/net/designware.h +++ b/drivers/net/designware.h @@ -240,6 +240,7 @@ struct dw_eth_dev { u32 tx_currdescnum; u32 rx_currdescnum; u32 phy_configured; + int link_printed; u32 padding; struct dmamacdescr tx_mac_descrtable[CONFIG_TX_DESCR_NUM]; -- cgit v1.1 From 2cb06a4fda4e7a88c734adaec9eeb5059e50f76b Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Tue, 14 Feb 2012 14:01:36 +0100 Subject: GPIO: Add SPEAr GPIO driver Tested on x600 (SPEAr600). Signed-off-by: Stefan Roese --- drivers/gpio/Makefile | 1 + drivers/gpio/spear_gpio.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 drivers/gpio/spear_gpio.c (limited to 'drivers') diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 5ae68d5..32a2474 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -35,6 +35,7 @@ COBJS-$(CONFIG_PCA953X) += pca953x.o COBJS-$(CONFIG_PCA9698) += pca9698.o COBJS-$(CONFIG_S5P) += s5p_gpio.o COBJS-$(CONFIG_SANDBOX_GPIO) += sandbox.o +COBJS-$(CONFIG_SPEAR_GPIO) += spear_gpio.o COBJS-$(CONFIG_TEGRA_GPIO) += tegra_gpio.o COBJS-$(CONFIG_DA8XX_GPIO) += da8xx_gpio.o COBJS-$(CONFIG_ALTERA_PIO) += altera_pio.o diff --git a/drivers/gpio/spear_gpio.c b/drivers/gpio/spear_gpio.c new file mode 100644 index 0000000..d3c728e --- /dev/null +++ b/drivers/gpio/spear_gpio.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2012 Stefan Roese + * + * 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 + */ + +/* + * Driver for SPEAr600 GPIO controller + */ + +#include +#include +#include +#include +#include + +static int gpio_direction(unsigned gpio, + enum gpio_direction direction) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodir); + + if (direction == GPIO_DIRECTION_OUT) + val |= 1 << gpio; + else + val &= ~(1 << gpio); + + writel(val, ®s->gpiodir); + + return 0; +} + +int gpio_set_value(unsigned gpio, int value) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + + writel(1 << gpio, ®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return 0; +} + +int gpio_get_value(unsigned gpio) +{ + struct gpio_regs *regs = (struct gpio_regs *)CONFIG_GPIO_BASE; + u32 val; + + val = readl(®s->gpiodata[DATA_REG_ADDR(gpio)]); + + return !!val; +} + +int gpio_request(unsigned gpio, const char *label) +{ + if (gpio >= SPEAR_GPIO_COUNT) + return -EINVAL; + + return 0; +} + +int gpio_free(unsigned gpio) +{ + return 0; +} + +void gpio_toggle_value(unsigned gpio) +{ + gpio_set_value(gpio, !gpio_get_value(gpio)); +} + +int gpio_direction_input(unsigned gpio) +{ + return gpio_direction(gpio, GPIO_DIRECTION_IN); +} + +int gpio_direction_output(unsigned gpio, int value) +{ + int ret = gpio_direction(gpio, GPIO_DIRECTION_OUT); + + if (ret < 0) + return ret; + + gpio_set_value(gpio, value); + return 0; +} -- cgit v1.1 From 038f3c54ba9a7ab4552943f47efe8f05736b1732 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 20 Jan 2012 11:47:47 +0100 Subject: rtc/m41t62: Add support for M41T82 with HT (Halt Update) Add support for the M41T82 RTC to the m41t62 driver. The only difference that needs to be handled by this driver, is to clear the HT (Halt Update) bit upon reset. This bit is not used on the M41T62, so its save to clear this bit always. The M41T82 support will be used by the X600 (SPEAr600) board support. Signed-off-by: Stefan Roese --- drivers/rtc/m41t62.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/rtc/m41t62.c b/drivers/rtc/m41t62.c index 62c2446..8721e74 100644 --- a/drivers/rtc/m41t62.c +++ b/drivers/rtc/m41t62.c @@ -63,6 +63,8 @@ #define M41T62_FEATURE_HT (1 << 0) #define M41T62_FEATURE_BL (1 << 1) +#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */ + int rtc_get(struct rtc_time *tm) { u8 buf[M41T62_DATETIME_REG_SIZE]; @@ -132,9 +134,15 @@ int rtc_set(struct rtc_time *tm) void rtc_reset(void) { + u8 val; + /* - * Nothing to do + * M41T82: Make sure HT (Halt Update) bit is cleared. + * This bit is 0 in M41T62 so its save to clear it always. */ + i2c_read(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); + val &= ~M41T80_ALHOUR_HT; + i2c_write(CONFIG_SYS_I2C_RTC_ADDR, M41T62_REG_ALARM_HOUR, 1, &val, 1); } #endif -- cgit v1.1 From 496ba48f5ec60187d9ff7e4298e3d37677d74530 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Fri, 20 Jan 2012 11:52:33 +0100 Subject: i2c: designware_i2c.c: Add support for the "i2c probe" command i2c_probe() is changed to reinit the i2c bus upon read failure. This is naturally the case upon i2c bus probing. Also, some printf messages upon read failure are removed. As they would interfere with the "i2c probe" command. Additionally, i2c_set_bus_speed() now returns 0, so that the "i2c speed" command can be used. Signed-off-by: Stefan Roese Cc: Amit Virdi Cc: Vipin Kumar --- drivers/i2c/designware_i2c.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/i2c/designware_i2c.c b/drivers/i2c/designware_i2c.c index 6d118ac..bf64a2a 100644 --- a/drivers/i2c/designware_i2c.c +++ b/drivers/i2c/designware_i2c.c @@ -90,7 +90,7 @@ static void set_speed(int i2c_spd) * * Set the i2c speed. */ -void i2c_set_bus_speed(int speed) +int i2c_set_bus_speed(int speed) { if (speed >= I2C_MAX_SPEED) set_speed(IC_SPEED_MODE_MAX); @@ -98,6 +98,8 @@ void i2c_set_bus_speed(int speed) set_speed(IC_SPEED_MODE_FAST); else set_speed(IC_SPEED_MODE_STANDARD); + + return 0; } /* @@ -215,10 +217,8 @@ static int check_params(uint addr, int alen, uchar *buffer, int len) static int i2c_xfer_init(uchar chip, uint addr) { - if (i2c_wait_for_bb()) { - printf("Timed out waiting for bus\n"); + if (i2c_wait_for_bb()) return 1; - } i2c_setaddress(chip); writel(addr, &i2c_regs_p->ic_cmd_data); @@ -282,7 +282,6 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) start_time_rx = get_timer(0); } else if (get_timer(start_time_rx) > I2C_BYTE_TO) { - printf("Timed out. i2c read Failed\n"); return 1; } } @@ -334,9 +333,14 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) int i2c_probe(uchar chip) { u32 tmp; + int ret; /* * Try to read the first location of the chip. */ - return i2c_read(chip, 0, 1, (uchar *)&tmp, 1); + ret = i2c_read(chip, 0, 1, (uchar *)&tmp, 1); + if (ret) + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + return ret; } -- cgit v1.1 From 185b3b76a6a2e4eacb9e74a633950231e6787384 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 2 Feb 2012 13:25:45 +0100 Subject: MTD: SPEAr SMI: Add write support for length < 4 bytes Needed for redundant environment for example. Signed-off-by: Stefan Roese Cc: Amit Virdi Cc: Vipin Kumar --- drivers/mtd/st_smi.c | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/st_smi.c b/drivers/mtd/st_smi.c index 77e36af..7507e5d 100644 --- a/drivers/mtd/st_smi.c +++ b/drivers/mtd/st_smi.c @@ -362,7 +362,7 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) * smi_write - Write to SMI flash * @src_addr: source buffer * @dst_addr: destination buffer - * @length: length to write in words + * @length: length to write in bytes * @bank: bank base address * * Write to SMI flash @@ -370,7 +370,10 @@ static int smi_sector_erase(flash_info_t *info, unsigned int sector) static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, unsigned int length, ulong bank_addr) { + u8 *src_addr8 = (u8 *)src_addr; + u8 *dst_addr8 = (u8 *)dst_addr; int banknum; + int i; switch (bank_addr) { case SMIBANK0_BASE: @@ -399,7 +402,7 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -EIO; /* Perform the write command */ - while (length--) { + for (i = 0; i < length; i += 4) { if (((ulong) (dst_addr) % SFLASH_PAGE_SIZE) == 0) { if (smi_wait_till_ready(banknum, CONFIG_SYS_FLASH_WRITE_TOUT)) @@ -409,7 +412,18 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, return -EIO; } - *dst_addr++ = *src_addr++; + if (length < 4) { + int k; + + /* + * Handle special case, where length < 4 (redundant env) + */ + for (k = 0; k < length; k++) + *dst_addr8++ = *src_addr8++; + } else { + /* Normal 32bit write */ + *dst_addr++ = *src_addr++; + } if ((readl(&smicntl->smi_sr) & (ERF1 | ERF2))) return -EIO; @@ -435,7 +449,7 @@ static int smi_write(unsigned int *src_addr, unsigned int *dst_addr, int write_buff(flash_info_t *info, uchar *src, ulong dest_addr, ulong length) { return smi_write((unsigned int *)src, (unsigned int *)dest_addr, - (length + 3) / 4, info->start[0]); + length, info->start[0]); } /* -- cgit v1.1