| #include <asm/types.h> |
| #include <asm/io.h> |
| #include <linux/types.h> |
| |
| #include <linux/byteorder/generic.h> |
| #include <linux/byteorder/little_endian.h> |
| |
| #define COMCERTO_AXI_APB_CFG_BASE 0x90400000 |
| #define SPI_BASEADDR (COMCERTO_AXI_APB_CFG_BASE + 0x098000) |
| |
| #define COMCERTO_SPI_CTRLR0 0x00 |
| #define COMCERTO_SPI_CTRLR1 0x04 |
| #define COMCERTO_SPI_SSIENR 0x08 |
| #define COMCERTO_SPI_MWCR 0x0C |
| #define COMCERTO_SPI_SER 0x10 |
| #define COMCERTO_SPI_BAUDR 0x14 |
| #define COMCERTO_SPI_TXFTLR 0x18 |
| #define COMCERTO_SPI_RXFTLR 0x1C |
| #define COMCERTO_SPI_TXFLR 0x20 |
| #define COMCERTO_SPI_RXFLR 0x24 |
| #define COMCERTO_SPI_SR 0x28 |
| #define COMCERTO_SPI_IMR 0x2C |
| #define COMCERTO_SPI_ISR 0x30 |
| #define COMCERTO_SPI_RISR 0x34 |
| #define COMCERTO_SPI_TXOICR 0x38 |
| #define COMCERTO_SPI_RXOICR 0x3C |
| #define COMCERTO_SPI_RXUICR 0x40 |
| #define COMCERTO_SPI_MSTICR 0x44 |
| #define COMCERTO_SPI_ICR 0x48 |
| #define COMCERTO_SPI_DMACR 0x4C |
| #define COMCERTO_SPI_DMATDLR 0x50 |
| #define COMCERTO_SPI_DMARDLR 0x54 |
| #define COMCERTO_SPI_IDR 0x58 |
| #define COMCERTO_SPI_DR 0x60 |
| |
| /* SR - status register bits */ |
| #define BUSY (1<<0) /* SSI busy flag, serial transfer in progress */ |
| #define TFNF (1<<1) /* Transmit FIFO not full */ |
| #define TFE (1<<2) /* Transmit FIFO empty */ |
| #define RFNE (1<<3) /* Receive FIFO not empty */ |
| #define RFF (1<<4) /* Receive FIFO full */ |
| #define TXE (1<<5) /* Transmission error */ |
| #define DCOL (1<<6) /* Data collision error */ |
| |
| /* Interrupt status after being masked */ |
| #define TXEIS (1<<0) /* Transmit FIFO empty interrupt status */ |
| #define TXOIS (1<<1) /* Transmit FIFO overflow interrupt status */ |
| #define RXUIS (1<<2) /* Receive FIFO underflow interrupt status */ |
| #define RXOIS (1<<3) /* Receive FIFO overflow interrupt status */ |
| #define RXFIS (1<<4) /* Receive FIFO full interrupt status */ |
| #define MSTIS (1<<5) /* Multi-Master contention interrupt status */ |
| |
| /* Interrupt status before being masked */ |
| #define TXEIR (1<<0) /* Transmit FIFO empty interrupt status */ |
| #define TXOIR (1<<1) /* Transmit FIFO overflow interrupt status */ |
| #define RXUIR (1<<2) /* Receive FIFO underflow interrupt status */ |
| #define RXOIR (1<<3) /* Receive FIFO overflow interrupt status */ |
| #define RXFIR (1<<4) /* Receive FIFO full interrupt status */ |
| #define MSTIR (1<<5) /* Multi-Master contention interrupt status */ |
| |
| |
| /* Interrupt mask register */ |
| #define TXEIM (1<<0) /* Transmit FIFO empty interrupt status */ |
| #define TXOIM (1<<1) /* Transmit FIFO overflow interrupt status */ |
| #define RXUIM (1<<2) /* Receive FIFO underflow interrupt status */ |
| #define RXOIM (1<<3) /* Receive FIFO overflow interrupt status */ |
| #define RXFIM (1<<4) /* Receive FIFO full interrupt status */ |
| #define MSTIM (1<<5) /* Multi-Master contention interrupt status */ |
| |
| |
| #define SPI_TRANSFER_MODE_WRITE_ONLY 0x01 |
| #define SPI_TRANSFER_MODE_READ_ONLY 0x02 |
| #define SPI_TRANSFER_MODE_WRITE_READ 0x03 |
| #define SPI_TRANSFER_MODE_EEPROM_READ 0x04 |
| |
| #define SPI_CTRLR0_SCPOL (1 << 7) |
| #define SPI_CTRLR0_SCPH (1 << 6) |
| |
| #define COMCERTO_DEFAULTAXICLK 250000000 /* Hz */ |
| |
| unsigned int spi_base = SPI_BASEADDR; |
| |
| struct spi_transfer { |
| const void *tx_buf; |
| void *rx_buf; |
| unsigned len; |
| int mode; |
| }; |
| |
| |
| int do_eeprom_read(u8 fs, u8 *wbuf, u32 *wlen, u8 *rbuf, u32 *ndf) |
| { |
| u32 sr, dr; |
| u32 wlen_now = 0, rlen_now = 0; |
| int rc = 0; |
| |
| while (wlen_now < *wlen) { |
| sr = readl(spi_base + COMCERTO_SPI_SR); |
| |
| if (sr & TFNF) { |
| if (wlen_now < *wlen) { |
| writew(cpu_to_le16((u16) *wbuf), spi_base + COMCERTO_SPI_DR); |
| wbuf++; |
| wlen_now++; |
| } |
| } |
| } |
| |
| while (rlen_now < *ndf) { |
| sr = readl(spi_base + COMCERTO_SPI_SR); |
| |
| if (sr & (RFF | DCOL)) { |
| /* read overrun, data collision */ |
| rc = -1; |
| goto out; |
| } |
| |
| if (sr & RFNE) { |
| dr = readw(spi_base + COMCERTO_SPI_DR); |
| if (rlen_now < *ndf) { |
| *rbuf = (u8) (le16_to_cpu(dr) & 0xff); |
| rbuf++; |
| rlen_now++; |
| } else { |
| /* read overflow */ |
| rc = -1; |
| goto out; |
| } |
| } |
| } |
| |
| out: |
| *ndf = rlen_now; |
| *wlen = wlen_now; |
| |
| return rc; |
| } |
| |
| |
| /** |
| * do_write_read_transfer - |
| * |
| * |
| */ |
| int do_write_read_transfer(u8 fs, u8 *wbuf, u32 *wlen, u8 *rbuf, u32 *rlen) |
| { |
| u32 sr, dr; |
| u32 wlen_now = 0, rlen_now = 0; |
| int rc = 0; |
| |
| while (wlen_now < *wlen) { |
| sr = readl(spi_base + COMCERTO_SPI_SR); |
| |
| if (sr & TFNF) { |
| if (wlen_now < *wlen) { |
| writew(cpu_to_le16((u16) *wbuf), spi_base + COMCERTO_SPI_DR); |
| wbuf++; |
| wlen_now++; |
| } |
| } |
| } |
| |
| while (rlen_now < *rlen) { |
| sr = readl(spi_base + COMCERTO_SPI_SR); |
| |
| if (sr & (RFF | DCOL)) { |
| /* read overrun, data collision */ |
| printf ("%s: sr=0x%x: Read Overrun.\n", __func__, sr); |
| rc = -1; |
| goto out; |
| } |
| |
| if (sr & RFNE) { |
| dr = readw(spi_base + COMCERTO_SPI_DR); |
| if (rlen_now < *rlen) { |
| *rbuf = (u8) (le16_to_cpu(dr) & 0xff); |
| rbuf++; |
| rlen_now++; |
| } else { |
| printf ("%s: Read Overflow.\n", __func__); |
| /* read overflow */ |
| |
| rc = -1; |
| goto out; |
| } |
| } |
| } |
| |
| out: |
| *rlen = rlen_now; |
| *wlen = wlen_now; |
| |
| return rc; |
| } |
| |
| |
| /** |
| * do_write_only_transfer8 - |
| * |
| * |
| */ |
| int do_write_only_transfer8(u8 *buf, u32 *len) |
| { |
| u32 len_now; |
| int rc = 0; |
| u32 tmp = *len; |
| u32 dr = spi_base + COMCERTO_SPI_DR; |
| u32 txflr = spi_base + COMCERTO_SPI_TXFLR; |
| |
| while (tmp) |
| { |
| len_now = 8 - readl(txflr); |
| if (len_now > tmp) |
| len_now = tmp; |
| |
| tmp -= len_now; |
| |
| /* warm-up write fifo to avoid underruns */ |
| while (len_now--) |
| { |
| writew(cpu_to_le16((u16) *buf++), dr); |
| } |
| } |
| |
| *len -= tmp; |
| |
| return rc; |
| } |
| |
| /** |
| * do_write_only_transfer - |
| * |
| * |
| */ |
| int do_write_only_transfer16(u16 *buf, u32 *len) |
| { |
| u32 len_now; |
| int rc = 0; |
| u32 tmp = *len; |
| u32 dr = spi_base + COMCERTO_SPI_DR; |
| u32 txflr = spi_base + COMCERTO_SPI_TXFLR; |
| |
| while (tmp) |
| { |
| len_now = 8 - readl(txflr); |
| if (len_now > tmp) |
| len_now = tmp; |
| |
| tmp -= len_now; |
| |
| /* warm-up write fifo to avoid underruns */ |
| while (len_now--) |
| writew(cpu_to_le16(*buf++), dr); |
| } |
| |
| *len -= tmp; |
| |
| return rc; |
| } |
| |
| |
| /** |
| * do_read_only_transfer - |
| * |
| * |
| */ |
| int do_read_only_transfer8(u8 *buf, u32 *len) |
| { |
| u32 len_now; |
| int rc = 0; |
| u32 tmp = *len; |
| u32 dr = spi_base + COMCERTO_SPI_DR; |
| u32 rxflr = spi_base + COMCERTO_SPI_RXFLR; |
| |
| /* start the serial clock */ |
| writew(0, dr); |
| |
| while (tmp) |
| { |
| len_now = readl(rxflr); |
| if (len_now > tmp) |
| len_now = tmp; |
| |
| tmp -= len_now; |
| |
| while (len_now--) { |
| *buf = (u8) (le16_to_cpu(readw(dr)) & 0xff); |
| buf++; |
| } |
| } |
| |
| *len -= tmp; |
| |
| return rc; |
| } |
| |
| /** |
| * do_read_only_transfer - |
| * |
| * |
| */ |
| int do_read_only_transfer16(u16 *buf, u32 *len) |
| { |
| u32 len_now; |
| int rc = 0; |
| u32 tmp = *len; |
| u32 dr = spi_base + COMCERTO_SPI_DR; |
| u32 rxflr = spi_base + COMCERTO_SPI_RXFLR; |
| |
| /* start the serial clock */ |
| writew(0, dr); |
| |
| while (tmp) |
| { |
| len_now = readl(rxflr); |
| if (len_now > tmp) |
| len_now = tmp; |
| |
| tmp -= len_now; |
| |
| while (len_now--) { |
| *buf = le16_to_cpu(readw(dr)); |
| buf++; |
| } |
| } |
| |
| *len -= tmp; |
| |
| return rc; |
| } |
| |
| static int c2k_spi_transfer(u8 chip_select,struct spi_transfer *t) |
| { |
| unsigned int op = t->mode & 0xff; |
| u32 ctrlr0 = 0; |
| u32 baudr = 0; |
| u32 ser = 0; |
| u8 bits_per_word = 8; |
| u32 max_speed_hz = 4000000; |
| u32 hz; |
| u8 *txbuf; |
| u8 *rxbuf; |
| int spi_udelay; |
| |
| ctrlr0 |= SPI_CTRLR0_SCPOL; |
| ctrlr0 |= SPI_CTRLR0_SCPH; |
| ctrlr0 |= (bits_per_word - 1) & 0xf; |
| |
| hz = COMCERTO_DEFAULTAXICLK; |
| baudr = hz / max_speed_hz; |
| ser = (1 << chip_select) ; |
| |
| spi_udelay = 1 + ((1000000 * bits_per_word) / max_speed_hz); |
| |
| while(readl(spi_base + COMCERTO_SPI_SR) & BUSY); |
| |
| writel(0, spi_base + COMCERTO_SPI_SSIENR); |
| |
| txbuf = t->tx_buf; |
| rxbuf = t->rx_buf; |
| |
| switch(op) { |
| case SPI_TRANSFER_MODE_WRITE_ONLY: |
| |
| ctrlr0 |= (0x0001 << 8); |
| writel(ctrlr0, spi_base + COMCERTO_SPI_CTRLR0); |
| writel(baudr, spi_base + COMCERTO_SPI_BAUDR); |
| writel(ser, spi_base + COMCERTO_SPI_SER); |
| writel(8, spi_base + COMCERTO_SPI_RXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_TXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_IMR); |
| writel(1, spi_base + COMCERTO_SPI_SSIENR); |
| |
| if (bits_per_word <= 8) |
| do_write_only_transfer8(txbuf, &t->len); |
| else |
| do_write_only_transfer16((u16*)txbuf, &t->len); |
| |
| break; |
| |
| case SPI_TRANSFER_MODE_READ_ONLY: |
| |
| ctrlr0 |= (0x0002 << 8); |
| writel(ctrlr0, spi_base + COMCERTO_SPI_CTRLR0); |
| writel(baudr, spi_base + COMCERTO_SPI_BAUDR); |
| writel(ser, spi_base + COMCERTO_SPI_SER); |
| writel(8, spi_base + COMCERTO_SPI_RXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_TXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_IMR); |
| writel(1, spi_base + COMCERTO_SPI_SSIENR); |
| |
| if (bits_per_word <= 8) |
| do_read_only_transfer8(rxbuf, &t->len); |
| else |
| do_read_only_transfer16((u16 *)rxbuf, &t->len); |
| |
| break; |
| |
| case SPI_TRANSFER_MODE_WRITE_READ: |
| |
| ctrlr0 |= (0x0000 << 8); |
| writel(ctrlr0, spi_base + COMCERTO_SPI_CTRLR0); |
| writel(baudr, spi_base + COMCERTO_SPI_BAUDR); |
| writel(ser, spi_base + COMCERTO_SPI_SER); |
| writel(8, spi_base + COMCERTO_SPI_RXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_TXFTLR); |
| writel(0, spi_base + COMCERTO_SPI_IMR); |
| writel(1, spi_base + COMCERTO_SPI_SSIENR); |
| |
| do_write_read_transfer(bits_per_word, txbuf, &t->len, rxbuf, &t->len); |
| |
| break; |
| |
| default: |
| printf ("Transfer mode not supported.\n"); |
| return -1; |
| }; |
| |
| /* deassert the chip select at least for this long */ |
| udelay (spi_udelay); |
| |
| return 0; |
| |
| |
| } |
| |
| |
| int c2k_spi_write(u8 cs, const void *buf, size_t len) |
| { |
| struct spi_transfer t = { |
| .tx_buf = buf, |
| .len = len, |
| .mode = SPI_TRANSFER_MODE_WRITE_ONLY, |
| }; |
| |
| c2k_spi_transfer(cs, &t); |
| } |
| |
| int c2k_spi_read(u8 cs, void *buf, size_t len) |
| { |
| struct spi_transfer t = { |
| .rx_buf = buf, |
| .len = len, |
| .mode = SPI_TRANSFER_MODE_READ_ONLY, |
| }; |
| |
| c2k_spi_transfer(cs, &t); |
| } |
| |