| /* |
| * Analog Devices SPI3 controller driver |
| * |
| * Copyright (c) 2011 Analog Devices Inc. |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License version 2 as |
| * published by the Free Software Foundation. |
| * |
| * 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., 675 Mass Ave, Cambridge, MA 02139, USA. |
| */ |
| |
| #include <common.h> |
| #include <malloc.h> |
| #include <spi.h> |
| |
| #include <asm/blackfin.h> |
| #include <asm/gpio.h> |
| #include <asm/portmux.h> |
| #include <asm/mach-common/bits/spi6xx.h> |
| |
| struct bfin_spi_slave { |
| struct spi_slave slave; |
| u32 control, clock; |
| struct bfin_spi_regs *regs; |
| int cs_pol; |
| }; |
| |
| #define to_bfin_spi_slave(s) container_of(s, struct bfin_spi_slave, slave) |
| |
| #define gpio_cs(cs) ((cs) - MAX_CTRL_CS) |
| #ifdef CONFIG_BFIN_SPI_GPIO_CS |
| # define is_gpio_cs(cs) ((cs) > MAX_CTRL_CS) |
| #else |
| # define is_gpio_cs(cs) 0 |
| #endif |
| |
| int spi_cs_is_valid(unsigned int bus, unsigned int cs) |
| { |
| if (is_gpio_cs(cs)) |
| return gpio_is_valid(gpio_cs(cs)); |
| else |
| return (cs >= 1 && cs <= MAX_CTRL_CS); |
| } |
| |
| void spi_cs_activate(struct spi_slave *slave) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| |
| if (is_gpio_cs(slave->cs)) { |
| unsigned int cs = gpio_cs(slave->cs); |
| gpio_set_value(cs, bss->cs_pol); |
| } else { |
| u32 ssel; |
| ssel = bfin_read32(&bss->regs->ssel); |
| ssel |= 1 << slave->cs; |
| if (bss->cs_pol) |
| ssel |= (1 << 8) << slave->cs; |
| else |
| ssel &= ~((1 << 8) << slave->cs); |
| bfin_write32(&bss->regs->ssel, ssel); |
| } |
| |
| SSYNC(); |
| } |
| |
| void spi_cs_deactivate(struct spi_slave *slave) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| |
| if (is_gpio_cs(slave->cs)) { |
| unsigned int cs = gpio_cs(slave->cs); |
| gpio_set_value(cs, !bss->cs_pol); |
| } else { |
| u32 ssel; |
| ssel = bfin_read32(&bss->regs->ssel); |
| if (bss->cs_pol) |
| ssel &= ~((1 << 8) << slave->cs); |
| else |
| ssel |= (1 << 8) << slave->cs; |
| /* deassert cs */ |
| bfin_write32(&bss->regs->ssel, ssel); |
| SSYNC(); |
| /* disable cs */ |
| ssel &= ~(1 << slave->cs); |
| bfin_write32(&bss->regs->ssel, ssel); |
| } |
| |
| SSYNC(); |
| } |
| |
| void spi_init() |
| { |
| } |
| |
| #define SPI_PINS(n) \ |
| { 0, P_SPI##n##_SCK, P_SPI##n##_MISO, P_SPI##n##_MOSI, 0 } |
| static unsigned short pins[][5] = { |
| #ifdef SPI0_REGBASE |
| [0] = SPI_PINS(0), |
| #endif |
| #ifdef SPI1_REGBASE |
| [1] = SPI_PINS(1), |
| #endif |
| #ifdef SPI2_REGBASE |
| [2] = SPI_PINS(2), |
| #endif |
| }; |
| |
| #define SPI_CS_PINS(n) \ |
| { \ |
| P_SPI##n##_SSEL1, P_SPI##n##_SSEL2, P_SPI##n##_SSEL3, \ |
| P_SPI##n##_SSEL4, P_SPI##n##_SSEL5, P_SPI##n##_SSEL6, \ |
| P_SPI##n##_SSEL7, \ |
| } |
| static const unsigned short cs_pins[][7] = { |
| #ifdef SPI0_REGBASE |
| [0] = SPI_CS_PINS(0), |
| #endif |
| #ifdef SPI1_REGBASE |
| [1] = SPI_CS_PINS(1), |
| #endif |
| #ifdef SPI2_REGBASE |
| [2] = SPI_CS_PINS(2), |
| #endif |
| }; |
| |
| void spi_set_speed(struct spi_slave *slave, uint hz) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| ulong sclk; |
| u32 clock; |
| |
| sclk = get_sclk1(); |
| clock = sclk / hz; |
| if (clock) |
| clock--; |
| bss->clock = clock; |
| } |
| |
| struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs, |
| unsigned int max_hz, unsigned int mode) |
| { |
| struct bfin_spi_slave *bss; |
| u32 reg_base; |
| |
| if (!spi_cs_is_valid(bus, cs)) |
| return NULL; |
| |
| switch (bus) { |
| #ifdef SPI0_REGBASE |
| case 0: |
| reg_base = SPI0_REGBASE; |
| break; |
| #endif |
| #ifdef SPI1_REGBASE |
| case 1: |
| reg_base = SPI1_REGBASE; |
| break; |
| #endif |
| #ifdef SPI2_REGBASE |
| case 2: |
| reg_base = SPI2_REGBASE; |
| break; |
| #endif |
| default: |
| debug("%s: invalid bus %u\n", __func__, bus); |
| return NULL; |
| } |
| |
| bss = spi_alloc_slave(struct bfin_spi_slave, bus, cs); |
| if (!bss) |
| return NULL; |
| |
| bss->regs = (struct bfin_spi_regs *)reg_base; |
| bss->control = SPI_CTL_EN | SPI_CTL_MSTR; |
| if (mode & SPI_CPHA) |
| bss->control |= SPI_CTL_CPHA; |
| if (mode & SPI_CPOL) |
| bss->control |= SPI_CTL_CPOL; |
| if (mode & SPI_LSB_FIRST) |
| bss->control |= SPI_CTL_LSBF; |
| bss->control &= ~SPI_CTL_ASSEL; |
| bss->cs_pol = mode & SPI_CS_HIGH ? 1 : 0; |
| spi_set_speed(&bss->slave, max_hz); |
| |
| return &bss->slave; |
| } |
| |
| void spi_free_slave(struct spi_slave *slave) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| free(bss); |
| } |
| |
| int spi_claim_bus(struct spi_slave *slave) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| |
| debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); |
| |
| if (is_gpio_cs(slave->cs)) { |
| unsigned int cs = gpio_cs(slave->cs); |
| gpio_request(cs, "bfin-spi"); |
| gpio_direction_output(cs, !bss->cs_pol); |
| pins[slave->bus][0] = P_DONTCARE; |
| } else |
| pins[slave->bus][0] = cs_pins[slave->bus][slave->cs - 1]; |
| peripheral_request_list(pins[slave->bus], "bfin-spi"); |
| |
| bfin_write32(&bss->regs->control, bss->control); |
| bfin_write32(&bss->regs->clock, bss->clock); |
| bfin_write32(&bss->regs->delay, 0x0); |
| bfin_write32(&bss->regs->rx_control, SPI_RXCTL_REN); |
| bfin_write32(&bss->regs->tx_control, SPI_TXCTL_TEN | SPI_TXCTL_TTI); |
| SSYNC(); |
| |
| return 0; |
| } |
| |
| void spi_release_bus(struct spi_slave *slave) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| |
| debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs); |
| |
| peripheral_free_list(pins[slave->bus]); |
| if (is_gpio_cs(slave->cs)) |
| gpio_free(gpio_cs(slave->cs)); |
| |
| bfin_write32(&bss->regs->rx_control, 0x0); |
| bfin_write32(&bss->regs->tx_control, 0x0); |
| bfin_write32(&bss->regs->control, 0x0); |
| SSYNC(); |
| } |
| |
| #ifndef CONFIG_BFIN_SPI_IDLE_VAL |
| # define CONFIG_BFIN_SPI_IDLE_VAL 0xff |
| #endif |
| |
| static int spi_pio_xfer(struct bfin_spi_slave *bss, const u8 *tx, u8 *rx, |
| uint bytes) |
| { |
| /* discard invalid rx data and empty rfifo */ |
| while (!(bfin_read32(&bss->regs->status) & SPI_STAT_RFE)) |
| bfin_read32(&bss->regs->rfifo); |
| |
| while (bytes--) { |
| u8 value = (tx ? *tx++ : CONFIG_BFIN_SPI_IDLE_VAL); |
| debug("%s: tx:%x ", __func__, value); |
| bfin_write32(&bss->regs->tfifo, value); |
| SSYNC(); |
| while (bfin_read32(&bss->regs->status) & SPI_STAT_RFE) |
| if (ctrlc()) |
| return -1; |
| value = bfin_read32(&bss->regs->rfifo); |
| if (rx) |
| *rx++ = value; |
| debug("rx:%x\n", value); |
| } |
| |
| return 0; |
| } |
| |
| int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, |
| void *din, unsigned long flags) |
| { |
| struct bfin_spi_slave *bss = to_bfin_spi_slave(slave); |
| const u8 *tx = dout; |
| u8 *rx = din; |
| uint bytes = bitlen / 8; |
| int ret = 0; |
| |
| debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__, |
| slave->bus, slave->cs, bitlen, bytes, flags); |
| |
| if (bitlen == 0) |
| goto done; |
| |
| /* we can only do 8 bit transfers */ |
| if (bitlen % 8) { |
| flags |= SPI_XFER_END; |
| goto done; |
| } |
| |
| if (flags & SPI_XFER_BEGIN) |
| spi_cs_activate(slave); |
| |
| ret = spi_pio_xfer(bss, tx, rx, bytes); |
| |
| done: |
| if (flags & SPI_XFER_END) |
| spi_cs_deactivate(slave); |
| |
| return ret; |
| } |