163d49afeSJan Glauber /* 263d49afeSJan Glauber * This file is subject to the terms and conditions of the GNU General Public 363d49afeSJan Glauber * License. See the file "COPYING" in the main directory of this archive 463d49afeSJan Glauber * for more details. 563d49afeSJan Glauber * 663d49afeSJan Glauber * Copyright (C) 2011, 2012 Cavium, Inc. 763d49afeSJan Glauber */ 863d49afeSJan Glauber 963d49afeSJan Glauber #include <linux/spi/spi.h> 1063d49afeSJan Glauber #include <linux/module.h> 1163d49afeSJan Glauber #include <linux/delay.h> 1263d49afeSJan Glauber #include <linux/io.h> 1363d49afeSJan Glauber 1463d49afeSJan Glauber #include "spi-cavium.h" 1563d49afeSJan Glauber 1663d49afeSJan Glauber static void octeon_spi_wait_ready(struct octeon_spi *p) 1763d49afeSJan Glauber { 1863d49afeSJan Glauber union cvmx_mpi_sts mpi_sts; 1963d49afeSJan Glauber unsigned int loops = 0; 2063d49afeSJan Glauber 2163d49afeSJan Glauber do { 2263d49afeSJan Glauber if (loops++) 2363d49afeSJan Glauber __delay(500); 2463d49afeSJan Glauber mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p)); 2563d49afeSJan Glauber } while (mpi_sts.s.busy); 2663d49afeSJan Glauber } 2763d49afeSJan Glauber 2863d49afeSJan Glauber static int octeon_spi_do_transfer(struct octeon_spi *p, 2963d49afeSJan Glauber struct spi_message *msg, 3063d49afeSJan Glauber struct spi_transfer *xfer, 3163d49afeSJan Glauber bool last_xfer) 3263d49afeSJan Glauber { 3363d49afeSJan Glauber struct spi_device *spi = msg->spi; 3463d49afeSJan Glauber union cvmx_mpi_cfg mpi_cfg; 3563d49afeSJan Glauber union cvmx_mpi_tx mpi_tx; 3663d49afeSJan Glauber unsigned int clkdiv; 3763d49afeSJan Glauber int mode; 3863d49afeSJan Glauber bool cpha, cpol; 3963d49afeSJan Glauber const u8 *tx_buf; 4063d49afeSJan Glauber u8 *rx_buf; 4163d49afeSJan Glauber int len; 4263d49afeSJan Glauber int i; 4363d49afeSJan Glauber 4463d49afeSJan Glauber mode = spi->mode; 4563d49afeSJan Glauber cpha = mode & SPI_CPHA; 4663d49afeSJan Glauber cpol = mode & SPI_CPOL; 4763d49afeSJan Glauber 4863d49afeSJan Glauber clkdiv = p->sys_freq / (2 * xfer->speed_hz); 4963d49afeSJan Glauber 5063d49afeSJan Glauber mpi_cfg.u64 = 0; 5163d49afeSJan Glauber 5263d49afeSJan Glauber mpi_cfg.s.clkdiv = clkdiv; 5363d49afeSJan Glauber mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0; 5463d49afeSJan Glauber mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0; 5563d49afeSJan Glauber mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0; 5663d49afeSJan Glauber mpi_cfg.s.idlelo = cpha != cpol; 5763d49afeSJan Glauber mpi_cfg.s.cslate = cpha ? 1 : 0; 5863d49afeSJan Glauber mpi_cfg.s.enable = 1; 5963d49afeSJan Glauber 6063d49afeSJan Glauber if (spi->chip_select < 4) 6163d49afeSJan Glauber p->cs_enax |= 1ull << (12 + spi->chip_select); 6263d49afeSJan Glauber mpi_cfg.u64 |= p->cs_enax; 6363d49afeSJan Glauber 6463d49afeSJan Glauber if (mpi_cfg.u64 != p->last_cfg) { 6563d49afeSJan Glauber p->last_cfg = mpi_cfg.u64; 6663d49afeSJan Glauber writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p)); 6763d49afeSJan Glauber } 6863d49afeSJan Glauber tx_buf = xfer->tx_buf; 6963d49afeSJan Glauber rx_buf = xfer->rx_buf; 7063d49afeSJan Glauber len = xfer->len; 7163d49afeSJan Glauber while (len > OCTEON_SPI_MAX_BYTES) { 7263d49afeSJan Glauber for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { 7363d49afeSJan Glauber u8 d; 7463d49afeSJan Glauber if (tx_buf) 7563d49afeSJan Glauber d = *tx_buf++; 7663d49afeSJan Glauber else 7763d49afeSJan Glauber d = 0; 7863d49afeSJan Glauber writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); 7963d49afeSJan Glauber } 8063d49afeSJan Glauber mpi_tx.u64 = 0; 8163d49afeSJan Glauber mpi_tx.s.csid = spi->chip_select; 8263d49afeSJan Glauber mpi_tx.s.leavecs = 1; 8363d49afeSJan Glauber mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0; 8463d49afeSJan Glauber mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES; 8563d49afeSJan Glauber writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); 8663d49afeSJan Glauber 8763d49afeSJan Glauber octeon_spi_wait_ready(p); 8863d49afeSJan Glauber if (rx_buf) 8963d49afeSJan Glauber for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) { 9063d49afeSJan Glauber u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); 9163d49afeSJan Glauber *rx_buf++ = (u8)v; 9263d49afeSJan Glauber } 9363d49afeSJan Glauber len -= OCTEON_SPI_MAX_BYTES; 9463d49afeSJan Glauber } 9563d49afeSJan Glauber 9663d49afeSJan Glauber for (i = 0; i < len; i++) { 9763d49afeSJan Glauber u8 d; 9863d49afeSJan Glauber if (tx_buf) 9963d49afeSJan Glauber d = *tx_buf++; 10063d49afeSJan Glauber else 10163d49afeSJan Glauber d = 0; 10263d49afeSJan Glauber writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); 10363d49afeSJan Glauber } 10463d49afeSJan Glauber 10563d49afeSJan Glauber mpi_tx.u64 = 0; 10663d49afeSJan Glauber mpi_tx.s.csid = spi->chip_select; 10763d49afeSJan Glauber if (last_xfer) 10863d49afeSJan Glauber mpi_tx.s.leavecs = xfer->cs_change; 10963d49afeSJan Glauber else 11063d49afeSJan Glauber mpi_tx.s.leavecs = !xfer->cs_change; 11163d49afeSJan Glauber mpi_tx.s.txnum = tx_buf ? len : 0; 11263d49afeSJan Glauber mpi_tx.s.totnum = len; 11363d49afeSJan Glauber writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p)); 11463d49afeSJan Glauber 11563d49afeSJan Glauber octeon_spi_wait_ready(p); 11663d49afeSJan Glauber if (rx_buf) 11763d49afeSJan Glauber for (i = 0; i < len; i++) { 11863d49afeSJan Glauber u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i)); 11963d49afeSJan Glauber *rx_buf++ = (u8)v; 12063d49afeSJan Glauber } 12163d49afeSJan Glauber 122e74dc5c7SAlexandru Ardelean spi_transfer_delay_exec(xfer); 12363d49afeSJan Glauber 12463d49afeSJan Glauber return xfer->len; 12563d49afeSJan Glauber } 12663d49afeSJan Glauber 12763d49afeSJan Glauber int octeon_spi_transfer_one_message(struct spi_master *master, 12863d49afeSJan Glauber struct spi_message *msg) 12963d49afeSJan Glauber { 13063d49afeSJan Glauber struct octeon_spi *p = spi_master_get_devdata(master); 13163d49afeSJan Glauber unsigned int total_len = 0; 13263d49afeSJan Glauber int status = 0; 13363d49afeSJan Glauber struct spi_transfer *xfer; 13463d49afeSJan Glauber 13563d49afeSJan Glauber list_for_each_entry(xfer, &msg->transfers, transfer_list) { 13663d49afeSJan Glauber bool last_xfer = list_is_last(&xfer->transfer_list, 13763d49afeSJan Glauber &msg->transfers); 13863d49afeSJan Glauber int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer); 13963d49afeSJan Glauber if (r < 0) { 14063d49afeSJan Glauber status = r; 14163d49afeSJan Glauber goto err; 14263d49afeSJan Glauber } 14363d49afeSJan Glauber total_len += r; 14463d49afeSJan Glauber } 14563d49afeSJan Glauber err: 14663d49afeSJan Glauber msg->status = status; 14763d49afeSJan Glauber msg->actual_length = total_len; 14863d49afeSJan Glauber spi_finalize_current_message(master); 14963d49afeSJan Glauber return status; 15063d49afeSJan Glauber } 151