X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fspi%2Fzynq_spi.c;h=ed2b8cb52f6a5e6fad79061a32275d104c5257f9;hb=bbdf38698ee5c96138868d563c6825cd48bb26dc;hp=5da87591ceef3f530edf4bbc45b95263b53d28bf;hpb=6d4511b2c6734842de9de21c1bc0db4c3ea28b72;p=u-boot diff --git a/drivers/spi/zynq_spi.c b/drivers/spi/zynq_spi.c index 5da87591ce..ed2b8cb52f 100644 --- a/drivers/spi/zynq_spi.c +++ b/drivers/spi/zynq_spi.c @@ -1,30 +1,36 @@ /* - * (C) Copyright 2013 Inc. + * (C) Copyright 2013 Xilinx, Inc. + * (C) Copyright 2015 Jagan Teki * * Xilinx Zynq PS SPI controller driver (master mode only) * * SPDX-License-Identifier: GPL-2.0+ */ -#include #include +#include #include #include #include -#include + +DECLARE_GLOBAL_DATA_PTR; /* zynq spi register bit masks ZYNQ_SPI___MASK */ -#define ZYNQ_SPI_CR_MSA_MASK (1 << 15) /* Manual start enb */ -#define ZYNQ_SPI_CR_MCS_MASK (1 << 14) /* Manual chip select */ -#define ZYNQ_SPI_CR_CS_MASK (0xF << 10) /* Chip select */ -#define ZYNQ_SPI_CR_BRD_MASK (0x7 << 3) /* Baud rate div */ -#define ZYNQ_SPI_CR_CPHA_MASK (1 << 2) /* Clock phase */ -#define ZYNQ_SPI_CR_CPOL_MASK (1 << 1) /* Clock polarity */ -#define ZYNQ_SPI_CR_MSTREN_MASK (1 << 0) /* Mode select */ -#define ZYNQ_SPI_IXR_RXNEMPTY_MASK (1 << 4) /* RX_FIFO_not_empty */ -#define ZYNQ_SPI_IXR_TXOW_MASK (1 << 2) /* TX_FIFO_not_full */ -#define ZYNQ_SPI_IXR_ALL_MASK 0x7F /* All IXR bits */ -#define ZYNQ_SPI_ENR_SPI_EN_MASK (1 << 0) /* SPI Enable */ +#define ZYNQ_SPI_CR_MSA_MASK BIT(15) /* Manual start enb */ +#define ZYNQ_SPI_CR_MCS_MASK BIT(14) /* Manual chip select */ +#define ZYNQ_SPI_CR_CS_MASK GENMASK(13, 10) /* Chip select */ +#define ZYNQ_SPI_CR_BAUD_MASK GENMASK(5, 3) /* Baud rate div */ +#define ZYNQ_SPI_CR_CPHA_MASK BIT(2) /* Clock phase */ +#define ZYNQ_SPI_CR_CPOL_MASK BIT(1) /* Clock polarity */ +#define ZYNQ_SPI_CR_MSTREN_MASK BIT(0) /* Mode select */ +#define ZYNQ_SPI_IXR_RXNEMPTY_MASK BIT(4) /* RX_FIFO_not_empty */ +#define ZYNQ_SPI_IXR_TXOW_MASK BIT(2) /* TX_FIFO_not_full */ +#define ZYNQ_SPI_IXR_ALL_MASK GENMASK(6, 0) /* All IXR bits */ +#define ZYNQ_SPI_ENR_SPI_EN_MASK BIT(0) /* SPI Enable */ + +#define ZYNQ_SPI_CR_BAUD_MAX 8 /* Baud rate divisor max val */ +#define ZYNQ_SPI_CR_BAUD_SHIFT 3 /* Baud rate divisor shift */ +#define ZYNQ_SPI_CR_SS_SHIFT 10 /* Slave select shift */ #define ZYNQ_SPI_FIFO_DEPTH 128 #ifndef CONFIG_SYS_ZYNQ_SPI_WAIT @@ -44,180 +50,169 @@ struct zynq_spi_regs { u32 rxdr; /* 0x20 */ }; -/* zynq spi slave */ -struct zynq_spi_slave { - struct spi_slave slave; - struct zynq_spi_regs *base; + +/* zynq spi platform data */ +struct zynq_spi_platdata { + struct zynq_spi_regs *regs; + u32 frequency; /* input frequency */ + u32 speed_hz; + uint deactivate_delay_us; /* Delay to wait after deactivate */ + uint activate_delay_us; /* Delay to wait after activate */ +}; + +/* zynq spi priv */ +struct zynq_spi_priv { + struct zynq_spi_regs *regs; + u8 cs; u8 mode; + ulong last_transaction_us; /* Time of last transaction end */ u8 fifo_depth; - u32 speed_hz; - u32 input_hz; - u32 req_hz; + u32 freq; /* required frequency */ }; -static inline struct zynq_spi_slave *to_zynq_spi_slave(struct spi_slave *slave) +static int zynq_spi_ofdata_to_platdata(struct udevice *bus) { - return container_of(slave, struct zynq_spi_slave, slave); -} + struct zynq_spi_platdata *plat = bus->platdata; + const void *blob = gd->fdt_blob; + int node = dev_of_offset(bus); -static inline struct zynq_spi_regs *get_zynq_spi_base(int dev) -{ - if (dev) - return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR1; - else - return (struct zynq_spi_regs *)ZYNQ_SPI_BASEADDR0; + plat->regs = (struct zynq_spi_regs *)devfdt_get_addr(bus); + + /* FIXME: Use 250MHz as a suitable default */ + plat->frequency = fdtdec_get_int(blob, node, "spi-max-frequency", + 250000000); + plat->deactivate_delay_us = fdtdec_get_int(blob, node, + "spi-deactivate-delay", 0); + plat->activate_delay_us = fdtdec_get_int(blob, node, + "spi-activate-delay", 0); + plat->speed_hz = plat->frequency / 2; + + debug("%s: regs=%p max-frequency=%d\n", __func__, + plat->regs, plat->frequency); + + return 0; } -static void zynq_spi_init_hw(struct zynq_spi_slave *zslave) +static void zynq_spi_init_hw(struct zynq_spi_priv *priv) { + struct zynq_spi_regs *regs = priv->regs; u32 confr; /* Disable SPI */ - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + confr = ZYNQ_SPI_ENR_SPI_EN_MASK; + writel(~confr, ®s->enr); /* Disable Interrupts */ - writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->idr); + writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->idr); /* Clear RX FIFO */ - while (readl(&zslave->base->isr) & + while (readl(®s->isr) & ZYNQ_SPI_IXR_RXNEMPTY_MASK) - readl(&zslave->base->rxdr); + readl(®s->rxdr); /* Clear Interrupts */ - writel(ZYNQ_SPI_IXR_ALL_MASK, &zslave->base->isr); + writel(ZYNQ_SPI_IXR_ALL_MASK, ®s->isr); /* Manual slave select and Auto start */ confr = ZYNQ_SPI_CR_MCS_MASK | ZYNQ_SPI_CR_CS_MASK | ZYNQ_SPI_CR_MSTREN_MASK; confr &= ~ZYNQ_SPI_CR_MSA_MASK; - writel(confr, &zslave->base->cr); + writel(confr, ®s->cr); /* Enable SPI */ - writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); } -int spi_cs_is_valid(unsigned int bus, unsigned int cs) +static int zynq_spi_probe(struct udevice *bus) { - /* 2 bus with 3 chipselect */ - return bus < 2 && cs < 3; + struct zynq_spi_platdata *plat = dev_get_platdata(bus); + struct zynq_spi_priv *priv = dev_get_priv(bus); + + priv->regs = plat->regs; + priv->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; + + /* init the zynq spi hw */ + zynq_spi_init_hw(priv); + + return 0; } -void spi_cs_activate(struct spi_slave *slave) +static void spi_cs_activate(struct udevice *dev) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + struct udevice *bus = dev->parent; + struct zynq_spi_platdata *plat = bus->platdata; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; u32 cr; - debug("spi_cs_activate: 0x%08x\n", (u32)slave); + /* If it's too soon to do another transaction, wait */ + if (plat->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 < plat->deactivate_delay_us) + udelay(plat->deactivate_delay_us - delay_us); + } - clrbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); - cr = readl(&zslave->base->cr); + clrbits_le32(®s->cr, ZYNQ_SPI_CR_CS_MASK); + cr = readl(®s->cr); /* * CS cal logic: CS[13:10] * xxx0 - cs0 * xx01 - cs1 * x011 - cs2 */ - cr |= (~(0x1 << slave->cs) << 10) & ZYNQ_SPI_CR_CS_MASK; - writel(cr, &zslave->base->cr); -} - -void spi_cs_deactivate(struct spi_slave *slave) -{ - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + cr |= (~(1 << priv->cs) << ZYNQ_SPI_CR_SS_SHIFT) & ZYNQ_SPI_CR_CS_MASK; + writel(cr, ®s->cr); - debug("spi_cs_deactivate: 0x%08x\n", (u32)slave); - - setbits_le32(&zslave->base->cr, ZYNQ_SPI_CR_CS_MASK); -} - -void spi_init() -{ - /* nothing to do */ + if (plat->activate_delay_us) + udelay(plat->activate_delay_us); } -struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, - unsigned int max_hz, unsigned int mode) +static void spi_cs_deactivate(struct udevice *dev) { - struct zynq_spi_slave *zslave; - - if (!spi_cs_is_valid(bus, cs)) - return NULL; + struct udevice *bus = dev->parent; + struct zynq_spi_platdata *plat = bus->platdata; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; - zslave = spi_alloc_slave(struct zynq_spi_slave, bus, cs); - if (!zslave) { - printf("SPI_error: Fail to allocate zynq_spi_slave\n"); - return NULL; - } + setbits_le32(®s->cr, ZYNQ_SPI_CR_CS_MASK); - zslave->base = get_zynq_spi_base(bus); - zslave->mode = mode; - zslave->fifo_depth = ZYNQ_SPI_FIFO_DEPTH; - zslave->input_hz = 166666700; - zslave->speed_hz = zslave->input_hz / 2; - zslave->req_hz = max_hz; - - /* init the zynq spi hw */ - zynq_spi_init_hw(zslave); - - return &zslave->slave; + /* Remember time of this transaction so we can honour the bus delay */ + if (plat->deactivate_delay_us) + priv->last_transaction_us = timer_get_us(); } -void spi_free_slave(struct spi_slave *slave) +static int zynq_spi_claim_bus(struct udevice *dev) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - - debug("spi_free_slave: 0x%08x\n", (u32)slave); - free(zslave); -} - -int spi_claim_bus(struct spi_slave *slave) -{ - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); - u32 confr = 0; - u8 baud_rate_val = 0; - - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); - - /* Set the SPI Clock phase and polarities */ - confr = readl(&zslave->base->cr); - confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); - if (zslave->mode & SPI_CPHA) - confr |= ZYNQ_SPI_CR_CPHA_MASK; - if (zslave->mode & SPI_CPOL) - confr |= ZYNQ_SPI_CR_CPOL_MASK; - - /* Set the clock frequency */ - if (zslave->req_hz == 0) { - /* Set baudrate x8, if the req_hz is 0 */ - baud_rate_val = 0x2; - } else if (zslave->speed_hz != zslave->req_hz) { - while ((baud_rate_val < 8) && - ((zslave->input_hz / - (2 << baud_rate_val)) > zslave->req_hz)) - baud_rate_val++; - zslave->speed_hz = zslave->req_hz / (2 << baud_rate_val); - } - confr &= ~ZYNQ_SPI_CR_BRD_MASK; - confr |= (baud_rate_val << 3); - writel(confr, &zslave->base->cr); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; - writel(ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + writel(ZYNQ_SPI_ENR_SPI_EN_MASK, ®s->enr); return 0; } -void spi_release_bus(struct spi_slave *slave) +static int zynq_spi_release_bus(struct udevice *dev) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + u32 confr; + + confr = ZYNQ_SPI_ENR_SPI_EN_MASK; + writel(~confr, ®s->enr); - debug("spi_release_bus: 0x%08x\n", (u32)slave); - writel(~ZYNQ_SPI_ENR_SPI_EN_MASK, &zslave->base->enr); + return 0; } -int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, - void *din, unsigned long flags) +static int zynq_spi_xfer(struct udevice *dev, unsigned int bitlen, + const void *dout, void *din, unsigned long flags) { - struct zynq_spi_slave *zslave = to_zynq_spi_slave(slave); + struct udevice *bus = dev->parent; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + struct dm_spi_slave_platdata *slave_plat = dev_get_parent_platdata(dev); u32 len = bitlen / 8; u32 tx_len = len, rx_len = len, tx_tvl; const u8 *tx_buf = dout; @@ -225,56 +220,137 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, u32 ts, status; debug("spi_xfer: bus:%i cs:%i bitlen:%i len:%i flags:%lx\n", - slave->bus, slave->cs, bitlen, len, flags); - - if (bitlen == 0) - return -1; + bus->seq, slave_plat->cs, bitlen, len, flags); if (bitlen % 8) { debug("spi_xfer: Non byte aligned SPI transfer\n"); return -1; } + priv->cs = slave_plat->cs; if (flags & SPI_XFER_BEGIN) - spi_cs_activate(slave); + spi_cs_activate(dev); while (rx_len > 0) { /* Write the data into TX FIFO - tx threshold is fifo_depth */ tx_tvl = 0; - while ((tx_tvl < zslave->fifo_depth) && tx_len) { + while ((tx_tvl < priv->fifo_depth) && tx_len) { if (tx_buf) buf = *tx_buf++; else buf = 0; - writel(buf, &zslave->base->txdr); + writel(buf, ®s->txdr); tx_len--; tx_tvl++; } /* Check TX FIFO completion */ ts = get_timer(0); - status = readl(&zslave->base->isr); + status = readl(®s->isr); while (!(status & ZYNQ_SPI_IXR_TXOW_MASK)) { if (get_timer(ts) > CONFIG_SYS_ZYNQ_SPI_WAIT) { printf("spi_xfer: Timeout! TX FIFO not full\n"); return -1; } - status = readl(&zslave->base->isr); + status = readl(®s->isr); } /* Read the data from RX FIFO */ - status = readl(&zslave->base->isr); - while (status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) { - buf = readl(&zslave->base->rxdr); + status = readl(®s->isr); + while ((status & ZYNQ_SPI_IXR_RXNEMPTY_MASK) && rx_len) { + buf = readl(®s->rxdr); if (rx_buf) *rx_buf++ = buf; - status = readl(&zslave->base->isr); + status = readl(®s->isr); rx_len--; } } if (flags & SPI_XFER_END) - spi_cs_deactivate(slave); + spi_cs_deactivate(dev); + + return 0; +} + +static int zynq_spi_set_speed(struct udevice *bus, uint speed) +{ + struct zynq_spi_platdata *plat = bus->platdata; + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + uint32_t confr; + u8 baud_rate_val = 0; + + if (speed > plat->frequency) + speed = plat->frequency; + + /* Set the clock frequency */ + confr = readl(®s->cr); + if (speed == 0) { + /* Set baudrate x8, if the freq is 0 */ + baud_rate_val = 0x2; + } else if (plat->speed_hz != speed) { + while ((baud_rate_val < ZYNQ_SPI_CR_BAUD_MAX) && + ((plat->frequency / + (2 << baud_rate_val)) > speed)) + baud_rate_val++; + plat->speed_hz = speed / (2 << baud_rate_val); + } + confr &= ~ZYNQ_SPI_CR_BAUD_MASK; + confr |= (baud_rate_val << ZYNQ_SPI_CR_BAUD_SHIFT); + + writel(confr, ®s->cr); + priv->freq = speed; + + debug("zynq_spi_set_speed: regs=%p, speed=%d\n", + priv->regs, priv->freq); return 0; } + +static int zynq_spi_set_mode(struct udevice *bus, uint mode) +{ + struct zynq_spi_priv *priv = dev_get_priv(bus); + struct zynq_spi_regs *regs = priv->regs; + uint32_t confr; + + /* Set the SPI Clock phase and polarities */ + confr = readl(®s->cr); + confr &= ~(ZYNQ_SPI_CR_CPHA_MASK | ZYNQ_SPI_CR_CPOL_MASK); + + if (mode & SPI_CPHA) + confr |= ZYNQ_SPI_CR_CPHA_MASK; + if (mode & SPI_CPOL) + confr |= ZYNQ_SPI_CR_CPOL_MASK; + + writel(confr, ®s->cr); + priv->mode = mode; + + debug("zynq_spi_set_mode: regs=%p, mode=%d\n", priv->regs, priv->mode); + + return 0; +} + +static const struct dm_spi_ops zynq_spi_ops = { + .claim_bus = zynq_spi_claim_bus, + .release_bus = zynq_spi_release_bus, + .xfer = zynq_spi_xfer, + .set_speed = zynq_spi_set_speed, + .set_mode = zynq_spi_set_mode, +}; + +static const struct udevice_id zynq_spi_ids[] = { + { .compatible = "xlnx,zynq-spi-r1p6" }, + { .compatible = "cdns,spi-r1p6" }, + { } +}; + +U_BOOT_DRIVER(zynq_spi) = { + .name = "zynq_spi", + .id = UCLASS_SPI, + .of_match = zynq_spi_ids, + .ops = &zynq_spi_ops, + .ofdata_to_platdata = zynq_spi_ofdata_to_platdata, + .platdata_auto_alloc_size = sizeof(struct zynq_spi_platdata), + .priv_auto_alloc_size = sizeof(struct zynq_spi_priv), + .probe = zynq_spi_probe, +};