/* * Freescale i.MX28 SPI driver * * Copyright (C) 2013 Michael Grzeschik * * Copyright (C) 2011 Marek Vasut * on behalf of DENX Software Engineering GmbH * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #define MXS_SPI_MAX_TIMEOUT (10 * MSECOND) #define SPI_XFER_BEGIN 0x01 /* Assert CS before transfer */ #define SPI_XFER_END 0x02 /* Deassert CS after transfer */ struct mxs_spi { struct spi_master master; uint32_t max_khz; uint32_t mode; struct clk *clk; void __iomem *regs; }; static inline struct mxs_spi *to_mxs(struct spi_master *master) { return container_of(master, struct mxs_spi, master); } /* * Set SSP/MMC bus frequency, in kHz */ static void imx_set_ssp_busclock(struct spi_master *master, uint32_t freq) { struct mxs_spi *mxs = to_mxs(master); const uint32_t sspclk = clk_get_rate(mxs->clk); uint32_t val; uint32_t divide, rate, tgtclk; /* * SSP bit rate = SSPCLK / (CLOCK_DIVIDE * (1 + CLOCK_RATE)), * CLOCK_DIVIDE has to be an even value from 2 to 254, and * CLOCK_RATE could be any integer from 0 to 255. */ for (divide = 2; divide < 254; divide += 2) { rate = sspclk / freq / divide; if (rate <= 256) break; } tgtclk = sspclk / divide / rate; while (tgtclk > freq) { rate++; tgtclk = sspclk / divide / rate; } if (rate > 256) rate = 256; /* Always set timeout the maximum */ val = SSP_TIMING_TIMEOUT_MASK | SSP_TIMING_CLOCK_DIVIDE(divide) | SSP_TIMING_CLOCK_RATE(rate - 1); writel(val, mxs->regs + HW_SSP_TIMING); dev_dbg(master->dev, "SPI%d: Set freq rate to %d KHz (requested %d KHz)\n", master->bus_num, tgtclk, freq); } static int mxs_spi_setup(struct spi_device *spi) { struct spi_master *master = spi->master; struct mxs_spi *mxs = to_mxs(master); uint32_t val = 0; /* MXS SPI: 4 ports and 3 chip selects maximum */ if (master->bus_num > 3 || spi->chip_select > 2) { dev_err(master->dev, "mxs_spi: invalid bus %d / chip select %d\n", master->bus_num, spi->chip_select); return -EINVAL; } stmp_reset_block(mxs->regs + HW_SSP_CTRL0, 0); val |= SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select); val |= SSP_CTRL0_BUS_WIDTH(0); writel(val, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); val = SSP_CTRL1_SSP_MODE(0) | SSP_CTRL1_WORD_LENGTH(7); val |= (mxs->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0; val |= (mxs->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0; writel(val, mxs->regs + HW_SSP_CTRL1); writel(0x0, mxs->regs + HW_SSP_CMD0); writel(0x0, mxs->regs + HW_SSP_CMD1); imx_set_ssp_busclock(master, spi->max_speed_hz); return 0; } static void mxs_spi_start_xfer(struct mxs_spi *mxs) { writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); } static void mxs_spi_end_xfer(struct mxs_spi *mxs) { writel(SSP_CTRL0_LOCK_CS, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(SSP_CTRL0_IGNORE_CRC, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); } static void mxs_spi_set_cs(struct spi_device *spi) { struct mxs_spi *mxs = to_mxs(spi->master); const uint32_t mask = SSP_CTRL0_WAIT_FOR_CMD | SSP_CTRL0_WAIT_FOR_IRQ; uint32_t select = SSP_CTRL0_SSP_ASSERT_OUT(spi->chip_select); writel(mask, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); writel(select, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); } static int mxs_spi_xfer_pio(struct spi_device *spi, char *data, int length, int write, unsigned long flags) { struct mxs_spi *mxs = to_mxs(spi->master); struct spi_master *master = spi->master; if (flags & SPI_XFER_BEGIN) mxs_spi_start_xfer(mxs); mxs_spi_set_cs(spi); while (length--) { if ((flags & SPI_XFER_END) && !length) mxs_spi_end_xfer(mxs); /* We transfer 1 byte */ writel(1, mxs->regs + HW_SSP_XFER_COUNT); if (write) writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_CLR); else writel(SSP_CTRL0_READ, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); writel(SSP_CTRL0_RUN, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, (readl(mxs->regs + HW_SSP_CTRL0) & SSP_CTRL0_RUN) == SSP_CTRL0_RUN)) { dev_err(master->dev, "MXS SPI: Timeout waiting for start\n"); return -ETIMEDOUT; } if (write) writel(*data++, mxs->regs + HW_SSP_DATA); writel(SSP_CTRL0_DATA_XFER, mxs->regs + HW_SSP_CTRL0 + STMP_OFFSET_REG_SET); if (!write) { if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, !(readl(mxs->regs + HW_SSP_STATUS) & SSP_STATUS_FIFO_EMPTY))) { dev_err(master->dev, "MXS SPI: Timeout waiting for data\n"); return -ETIMEDOUT; } *data++ = readl(mxs->regs + HW_SSP_DATA) & 0xff; } if (wait_on_timeout(MXS_SPI_MAX_TIMEOUT, !(readl(mxs->regs + HW_SSP_CTRL0) & SSP_CTRL0_RUN))) { dev_err(master->dev, "MXS SPI: Timeout waiting for finish\n"); return -ETIMEDOUT; } } return 0; } static int mxs_spi_transfer(struct spi_device *spi, struct spi_message *mesg) { struct mxs_spi *mxs = to_mxs(spi->master); struct spi_master *master = spi->master; struct spi_transfer *t = NULL; char dummy; unsigned long flags = 0; int write = 0; char *data = NULL; int ret; mesg->actual_length = 0; list_for_each_entry(t, &mesg->transfers, transfer_list) { flags = 0; if (t->tx_buf) { data = (char *) t->tx_buf; write = 1; } else if (t->rx_buf) { data = (char *) t->rx_buf; write = 0; } else if (t->rx_buf && t->tx_buf) { dev_err(master->dev, "Cannot send and receive simultaneously\n"); return -EIO; } else if (!t->rx_buf && !t->tx_buf) { dev_err(master->dev, "No Data\n"); return -EIO; } if (&t->transfer_list == mesg->transfers.next) flags |= SPI_XFER_BEGIN; if (&t->transfer_list == mesg->transfers.prev) flags |= SPI_XFER_END; if (t->len == 0) { if (flags == SPI_XFER_END) { t->len = 1; t->rx_buf = (void *) &dummy; } else { return 0; } } writel(SSP_CTRL1_DMA_ENABLE, mxs->regs + HW_SSP_CTRL1 + STMP_OFFSET_REG_CLR); ret = mxs_spi_xfer_pio(spi, data, t->len, write, flags); if (ret < 0) return ret; mesg->actual_length += t->len; } return 0; } static int mxs_spi_probe(struct device_d *dev) { struct resource *iores; struct spi_master *master; struct mxs_spi *mxs; mxs = xzalloc(sizeof(*mxs)); master = &mxs->master; master->dev = dev; master->bus_num = dev->id; master->setup = mxs_spi_setup; master->transfer = mxs_spi_transfer; master->num_chipselect = 3; mxs->mode = SPI_CPOL | SPI_CPHA; iores = dev_request_mem_resource(dev, 0); if (IS_ERR(iores)) return PTR_ERR(iores); mxs->regs = IOMEM(iores->start); mxs->clk = clk_get(dev, NULL); if (IS_ERR(mxs->clk)) return PTR_ERR(mxs->clk); clk_enable(mxs->clk); spi_register_master(master); return 0; } static struct driver_d mxs_spi_driver = { .name = "mxs_spi", .probe = mxs_spi_probe, }; device_platform_driver(mxs_spi_driver); MODULE_AUTHOR("Denx Software Engeneering and Michael Grzeschik"); MODULE_DESCRIPTION("MXS SPI driver"); MODULE_LICENSE("GPL");