X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fspi%2Ftegra20_sflash.c;h=e70210d7abf86949e8942132caaef3eaae3be7a1;hb=118226495249268a35e604b2e6309801699f6224;hp=bb1e57d5e80040beebfba7792dc559171a9f5dc8;hpb=6b3a03e112cecda55b58f3de40f4fc760159979b;p=u-boot diff --git a/drivers/spi/tegra20_sflash.c b/drivers/spi/tegra20_sflash.c index bb1e57d5e8..e70210d7ab 100644 --- a/drivers/spi/tegra20_sflash.c +++ b/drivers/spi/tegra20_sflash.c @@ -3,69 +3,54 @@ * 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 + * SPDX-License-Identifier: GPL-2.0+ */ #include -#include +#include +#include #include #include #include #include #include -#include #include #include +#include "tegra_spi.h" DECLARE_GLOBAL_DATA_PTR; -#define SPI_CMD_GO (1 << 30) +#define SPI_CMD_GO BIT(30) #define SPI_CMD_ACTIVE_SCLK_SHIFT 26 #define SPI_CMD_ACTIVE_SCLK_MASK (3 << SPI_CMD_ACTIVE_SCLK_SHIFT) -#define SPI_CMD_CK_SDA (1 << 21) +#define SPI_CMD_CK_SDA BIT(21) #define SPI_CMD_ACTIVE_SDA_SHIFT 18 #define SPI_CMD_ACTIVE_SDA_MASK (3 << SPI_CMD_ACTIVE_SDA_SHIFT) -#define SPI_CMD_CS_POL (1 << 16) -#define SPI_CMD_TXEN (1 << 15) -#define SPI_CMD_RXEN (1 << 14) -#define SPI_CMD_CS_VAL (1 << 13) -#define SPI_CMD_CS_SOFT (1 << 12) -#define SPI_CMD_CS_DELAY (1 << 9) -#define SPI_CMD_CS3_EN (1 << 8) -#define SPI_CMD_CS2_EN (1 << 7) -#define SPI_CMD_CS1_EN (1 << 6) -#define SPI_CMD_CS0_EN (1 << 5) -#define SPI_CMD_BIT_LENGTH (1 << 4) -#define SPI_CMD_BIT_LENGTH_MASK 0x0000001F - -#define SPI_STAT_BSY (1 << 31) -#define SPI_STAT_RDY (1 << 30) -#define SPI_STAT_RXF_FLUSH (1 << 29) -#define SPI_STAT_TXF_FLUSH (1 << 28) -#define SPI_STAT_RXF_UNR (1 << 27) -#define SPI_STAT_TXF_OVF (1 << 26) -#define SPI_STAT_RXF_EMPTY (1 << 25) -#define SPI_STAT_RXF_FULL (1 << 24) -#define SPI_STAT_TXF_EMPTY (1 << 23) -#define SPI_STAT_TXF_FULL (1 << 22) -#define SPI_STAT_SEL_TXRX_N (1 << 16) -#define SPI_STAT_CUR_BLKCNT (1 << 15) +#define SPI_CMD_CS_POL BIT(16) +#define SPI_CMD_TXEN BIT(15) +#define SPI_CMD_RXEN BIT(14) +#define SPI_CMD_CS_VAL BIT(13) +#define SPI_CMD_CS_SOFT BIT(12) +#define SPI_CMD_CS_DELAY BIT(9) +#define SPI_CMD_CS3_EN BIT(8) +#define SPI_CMD_CS2_EN BIT(7) +#define SPI_CMD_CS1_EN BIT(6) +#define SPI_CMD_CS0_EN BIT(5) +#define SPI_CMD_BIT_LENGTH BIT(4) +#define SPI_CMD_BIT_LENGTH_MASK GENMASK(4, 0) + +#define SPI_STAT_BSY BIT(31) +#define SPI_STAT_RDY BIT(30) +#define SPI_STAT_RXF_FLUSH BIT(29) +#define SPI_STAT_TXF_FLUSH BIT(28) +#define SPI_STAT_RXF_UNR BIT(27) +#define SPI_STAT_TXF_OVF BIT(26) +#define SPI_STAT_RXF_EMPTY BIT(25) +#define SPI_STAT_RXF_FULL BIT(24) +#define SPI_STAT_TXF_EMPTY BIT(23) +#define SPI_STAT_TXF_FULL BIT(22) +#define SPI_STAT_SEL_TXRX_N BIT(16) +#define SPI_STAT_CUR_BLKCNT BIT(15) #define SPI_TIMEOUT 1000 #define TEGRA_SPI_MAX_FREQ 52000000 @@ -80,203 +65,156 @@ struct spi_regs { u32 rx_fifo; /* SPI_RX_FIFO_0 register */ }; -struct tegra_spi_ctrl { +struct tegra20_sflash_priv { struct spi_regs *regs; unsigned int freq; unsigned int mode; int periph_id; int valid; + int last_transaction_us; }; -struct tegra_spi_slave { - struct spi_slave slave; - struct tegra_spi_ctrl *ctrl; -}; - -/* tegra20 only supports one SFLASH controller */ -static struct tegra_spi_ctrl spi_ctrls[1]; - -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) +int tegra20_sflash_cs_info(struct udevice *bus, unsigned int cs, + struct spi_cs_info *info) { /* Tegra20 SPI-Flash - only 1 device ('bus/cs') */ - if (bus != 0 || cs != 0) - return 0; + if (cs != 0) + return -ENODEV; else - return 1; + return 0; } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static int tegra20_sflash_ofdata_to_platdata(struct udevice *bus) { - struct tegra_spi_slave *spi; + struct tegra_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = dev_of_offset(bus); - if (!spi_cs_is_valid(bus, cs)) { - printf("SPI error: unsupported bus %d / chip select %d\n", - bus, cs); - return NULL; - } + plat->base = devfdt_get_addr(bus); + plat->periph_id = clock_decode_periph_id(bus); - if (max_hz > TEGRA_SPI_MAX_FREQ) { - printf("SPI error: unsupported frequency %d Hz. Max frequency" - " is %d Hz\n", max_hz, TEGRA_SPI_MAX_FREQ); - return NULL; + if (plat->periph_id == PERIPH_ID_NONE) { + debug("%s: could not decode periph id %d\n", __func__, + plat->periph_id); + return -FDT_ERR_NOTFOUND; } - 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->ctrl = &spi_ctrls[bus]; - if (!spi->ctrl) { - printf("SPI error: could not find controller for bus %d\n", - bus); - return NULL; - } - - if (max_hz < spi->ctrl->freq) { - debug("%s: limiting frequency from %u to %u\n", __func__, - spi->ctrl->freq, max_hz); - spi->ctrl->freq = max_hz; - } - spi->ctrl->mode = mode; + /* Use 500KHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 500000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + debug("%s: base=%#08lx, periph_id=%d, max-frequency=%d, deactivate_delay=%d\n", + __func__, plat->base, plat->periph_id, plat->frequency, + plat->deactivate_delay_us); - return &spi->slave; + return 0; } -void spi_free_slave(struct spi_slave *slave) +static int tegra20_sflash_probe(struct udevice *bus) { - struct tegra_spi_slave *spi = to_tegra_spi(slave); + struct tegra_spi_platdata *plat = dev_get_platdata(bus); + struct tegra20_sflash_priv *priv = dev_get_priv(bus); - free(spi); -} + priv->regs = (struct spi_regs *)plat->base; -void spi_init(void) -{ - struct tegra_spi_ctrl *ctrl; - int i; - int node = 0; - int count; - int node_list[1]; - - count = fdtdec_find_aliases_for_id(gd->fdt_blob, "spi", - COMPAT_NVIDIA_TEGRA20_SFLASH, - node_list, - 1); - for (i = 0; i < count; i++) { - ctrl = &spi_ctrls[i]; - node = node_list[i]; - - ctrl->regs = (struct spi_regs *)fdtdec_get_addr(gd->fdt_blob, - node, "reg"); - if ((fdt_addr_t)ctrl->regs == FDT_ADDR_T_NONE) { - debug("%s: no slink register found\n", __func__); - continue; - } - ctrl->freq = fdtdec_get_int(gd->fdt_blob, node, - "spi-max-frequency", 0); - if (!ctrl->freq) { - debug("%s: no slink max frequency found\n", __func__); - continue; - } + priv->last_transaction_us = timer_get_us(); + priv->freq = plat->frequency; + priv->periph_id = plat->periph_id; - ctrl->periph_id = clock_decode_periph_id(gd->fdt_blob, node); - if (ctrl->periph_id == PERIPH_ID_NONE) { - debug("%s: could not decode periph id\n", __func__); - continue; - } - ctrl->valid = 1; + /* Change SPI clock to correct frequency, PLLP_OUT0 source */ + clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH, + priv->freq); - debug("%s: found controller at %p, freq = %u, periph_id = %d\n", - __func__, ctrl->regs, ctrl->freq, ctrl->periph_id); - } + return 0; } -int spi_claim_bus(struct spi_slave *slave) +static int tegra20_sflash_claim_bus(struct udevice *dev) { - struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_regs *regs = spi->ctrl->regs; + struct udevice *bus = dev->parent; + struct tegra20_sflash_priv *priv = dev_get_priv(bus); + struct spi_regs *regs = priv->regs; u32 reg; /* Change SPI clock to correct frequency, PLLP_OUT0 source */ - clock_start_periph_pll(spi->ctrl->periph_id, CLOCK_ID_PERIPH, - spi->ctrl->freq); + clock_start_periph_pll(priv->periph_id, CLOCK_ID_PERIPH, + priv->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)); + debug("%s: STATUS = %08x\n", __func__, readl(®s->status)); /* * Use sw-controlled CS, so we can clock in data after ReadID, etc. */ - reg = (spi->ctrl->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; - if (spi->ctrl->mode & 2) + reg = (priv->mode & 1) << SPI_CMD_ACTIVE_SDA_SHIFT; + if (priv->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)); + debug("%s: COMMAND = %08x\n", __func__, readl(®s->command)); /* * SPI pins on Tegra20 are muxed - change pinmux later due to UART * issue. */ - pinmux_set_func(PINGRP_GMD, PMUX_FUNC_SFLASH); - pinmux_tristate_disable(PINGRP_LSPI); - pinmux_set_func(PINGRP_GMC, PMUX_FUNC_SFLASH); + pinmux_set_func(PMUX_PINGRP_GMD, PMUX_FUNC_SFLASH); + pinmux_tristate_disable(PMUX_PINGRP_LSPI); + pinmux_set_func(PMUX_PINGRP_GMC, PMUX_FUNC_SFLASH); return 0; } -void spi_release_bus(struct spi_slave *slave) +static void spi_cs_activate(struct udevice *dev) { - /* - * 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); - struct spi_regs *regs = spi->ctrl->regs; + struct udevice *bus = dev->parent; + struct tegra_spi_platdata *pdata = dev_get_platdata(bus); + struct tegra20_sflash_priv *priv = dev_get_priv(bus); + + /* If it's too soon to do another transaction, wait */ + if (pdata->deactivate_delay_us && + priv->last_transaction_us) { + ulong delay_us; /* The delay completed so far */ + delay_us = timer_get_us() - priv->last_transaction_us; + if (delay_us < pdata->deactivate_delay_us) + udelay(pdata->deactivate_delay_us - delay_us); + } /* CS is negated on Tegra, so drive a 1 to get a 0 */ - setbits_le32(®s->command, SPI_CMD_CS_VAL); + setbits_le32(&priv->regs->command, SPI_CMD_CS_VAL); } -void spi_cs_deactivate(struct spi_slave *slave) +static void spi_cs_deactivate(struct udevice *dev) { - struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_regs *regs = spi->ctrl->regs; + struct udevice *bus = dev->parent; + struct tegra_spi_platdata *pdata = dev_get_platdata(bus); + struct tegra20_sflash_priv *priv = dev_get_priv(bus); /* CS is negated on Tegra, so drive a 0 to get a 1 */ - clrbits_le32(®s->command, SPI_CMD_CS_VAL); + clrbits_le32(&priv->regs->command, SPI_CMD_CS_VAL); + + /* Remember time of this transaction so we can honour the bus delay */ + if (pdata->deactivate_delay_us) + priv->last_transaction_us = timer_get_us(); } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, - const void *data_out, void *data_in, unsigned long flags) +static int tegra20_sflash_xfer(struct udevice *dev, unsigned int bitlen, + const void *data_out, void *data_in, + unsigned long flags) { - struct tegra_spi_slave *spi = to_tegra_spi(slave); - struct spi_regs *regs = spi->ctrl->regs; + struct udevice *bus = dev->parent; + struct tegra20_sflash_priv *priv = dev_get_priv(bus); + struct spi_regs *regs = priv->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); + debug("%s: slave %u:%u dout %p din %p bitlen %u\n", + __func__, bus->seq, spi_chip_select(dev), dout, din, bitlen); if (bitlen % 8) return -1; num_bytes = bitlen / 8; @@ -293,7 +231,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, debug("spi_xfer: COMMAND = %08x\n", readl(®s->command)); if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); + spi_cs_activate(dev); /* handle data in 32-bit chunks */ while (num_bytes > 0) { @@ -358,7 +296,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, } if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + spi_cs_deactivate(dev); debug("spi_xfer: transfer ended. Value=%08x, status = %08x\n", tmpdin, readl(®s->status)); @@ -370,3 +308,50 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, return 0; } + +static int tegra20_sflash_set_speed(struct udevice *bus, uint speed) +{ + struct tegra_spi_platdata *plat = bus->platdata; + struct tegra20_sflash_priv *priv = dev_get_priv(bus); + + if (speed > plat->frequency) + speed = plat->frequency; + priv->freq = speed; + debug("%s: regs=%p, speed=%d\n", __func__, priv->regs, priv->freq); + + return 0; +} + +static int tegra20_sflash_set_mode(struct udevice *bus, uint mode) +{ + struct tegra20_sflash_priv *priv = dev_get_priv(bus); + + priv->mode = mode; + debug("%s: regs=%p, mode=%d\n", __func__, priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops tegra20_sflash_ops = { + .claim_bus = tegra20_sflash_claim_bus, + .xfer = tegra20_sflash_xfer, + .set_speed = tegra20_sflash_set_speed, + .set_mode = tegra20_sflash_set_mode, + .cs_info = tegra20_sflash_cs_info, +}; + +static const struct udevice_id tegra20_sflash_ids[] = { + { .compatible = "nvidia,tegra20-sflash" }, + { } +}; + +U_BOOT_DRIVER(tegra20_sflash) = { + .name = "tegra20_sflash", + .id = UCLASS_SPI, + .of_match = tegra20_sflash_ids, + .ops = &tegra20_sflash_ops, + .ofdata_to_platdata = tegra20_sflash_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct tegra_spi_platdata), + .priv_auto_alloc_size = sizeof(struct tegra20_sflash_priv), + .probe = tegra20_sflash_probe, +};