/* * (C) Copyright 2011 * eInfochips Ltd. * Written-by: Ajay Bhargav * * (C) Copyright 2009 * Marvell Semiconductor * Based on SSP driver * Written-by: Lei Wen * * 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 #define to_armd_spi_slave(s) container_of(s, struct armd_spi_slave, slave) struct armd_spi_slave { struct spi_slave slave; struct ssp_reg *spi_reg; u32 cr0, cr1; u32 int_cr1; u32 clear_sr; const void *tx; void *rx; int gpio_cs_inverted; }; static int spi_armd_write(struct armd_spi_slave *pss) { int wait_timeout = SSP_FLUSH_NUM; while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_TNF)) ; if (!wait_timeout) { debug("%s: timeout error\n", __func__); return -1; } if (pss->tx != NULL) { writel(*(u8 *)pss->tx, &pss->spi_reg->ssdr); ++pss->tx; } else { writel(0, &pss->spi_reg->ssdr); } return 0; } static int spi_armd_read(struct armd_spi_slave *pss) { int wait_timeout = SSP_FLUSH_NUM; while (--wait_timeout && !(readl(&pss->spi_reg->sssr) & SSSR_RNE)) ; if (!wait_timeout) { debug("%s: timeout error\n", __func__); return -1; } if (pss->rx != NULL) { *(u8 *)pss->rx = readl(&pss->spi_reg->ssdr); ++pss->rx; } else { readl(&pss->spi_reg->ssdr); } return 0; } static int spi_armd_flush(struct armd_spi_slave *pss) { unsigned long limit = SSP_FLUSH_NUM; do { while (readl(&pss->spi_reg->sssr) & SSSR_RNE) readl(&pss->spi_reg->ssdr); } while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--); writel(SSSR_ROR, &pss->spi_reg->sssr); return limit; } void spi_cs_activate(struct spi_slave *slave) { struct armd_spi_slave *pss = to_armd_spi_slave(slave); gpio_set_value(slave->cs, pss->gpio_cs_inverted); } void spi_cs_deactivate(struct spi_slave *slave) { struct armd_spi_slave *pss = to_armd_spi_slave(slave); gpio_set_value(slave->cs, !pss->gpio_cs_inverted); } struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, unsigned int max_hz, unsigned int mode) { struct armd_spi_slave *pss; pss = malloc(sizeof(*pss)); if (!pss) return NULL; pss->slave.bus = bus; pss->slave.cs = cs; pss->spi_reg = (struct ssp_reg *)SSP_REG_BASE(CONFIG_SYS_SSP_PORT); pss->cr0 = SSCR0_MOTO | SSCR0_DATASIZE(DEFAULT_WORD_LEN) | SSCR0_SSE; pss->cr1 = (SSCR1_RXTRESH(RX_THRESH_DEF) & SSCR1_RFT) | (SSCR1_TXTRESH(TX_THRESH_DEF) & SSCR1_TFT); pss->cr1 &= ~(SSCR1_SPO | SSCR1_SPH); pss->cr1 |= (((mode & SPI_CPHA) != 0) ? SSCR1_SPH : 0) | (((mode & SPI_CPOL) != 0) ? SSCR1_SPO : 0); pss->int_cr1 = SSCR1_TIE | SSCR1_RIE | SSCR1_TINTE; pss->clear_sr = SSSR_ROR | SSSR_TINT; pss->gpio_cs_inverted = mode & SPI_CS_HIGH; gpio_set_value(cs, !pss->gpio_cs_inverted); return &pss->slave; } void spi_free_slave(struct spi_slave *slave) { struct armd_spi_slave *pss = to_armd_spi_slave(slave); free(pss); } int spi_claim_bus(struct spi_slave *slave) { struct armd_spi_slave *pss = to_armd_spi_slave(slave); debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); if (spi_armd_flush(pss) == 0) return -1; return 0; } void spi_release_bus(struct spi_slave *slave) { } int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { struct armd_spi_slave *pss = to_armd_spi_slave(slave); uint bytes = bitlen / 8; unsigned long limit; int ret = 0; if (bitlen == 0) goto done; /* we can only do 8 bit transfers */ if (bitlen % 8) { flags |= SPI_XFER_END; goto done; } if (dout) pss->tx = dout; else pss->tx = NULL; if (din) pss->rx = din; else pss->rx = NULL; if (flags & SPI_XFER_BEGIN) { spi_cs_activate(slave); writel(pss->cr1 | pss->int_cr1, &pss->spi_reg->sscr1); writel(TIMEOUT_DEF, &pss->spi_reg->ssto); writel(pss->cr0, &pss->spi_reg->sscr0); } while (bytes--) { limit = SSP_FLUSH_NUM; ret = spi_armd_write(pss); if (ret) break; while ((readl(&pss->spi_reg->sssr) & SSSR_BSY) && limit--) udelay(1); ret = spi_armd_read(pss); if (ret) break; } done: if (flags & SPI_XFER_END) { /* Stop SSP */ writel(pss->clear_sr, &pss->spi_reg->sssr); clrbits_le32(&pss->spi_reg->sscr1, pss->int_cr1); writel(0, &pss->spi_reg->ssto); spi_cs_deactivate(slave); } return ret; }