cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

spi-cavium.c (3564B)


      1/*
      2 * This file is subject to the terms and conditions of the GNU General Public
      3 * License.  See the file "COPYING" in the main directory of this archive
      4 * for more details.
      5 *
      6 * Copyright (C) 2011, 2012 Cavium, Inc.
      7 */
      8
      9#include <linux/spi/spi.h>
     10#include <linux/module.h>
     11#include <linux/delay.h>
     12#include <linux/io.h>
     13
     14#include "spi-cavium.h"
     15
     16static void octeon_spi_wait_ready(struct octeon_spi *p)
     17{
     18	union cvmx_mpi_sts mpi_sts;
     19	unsigned int loops = 0;
     20
     21	do {
     22		if (loops++)
     23			__delay(500);
     24		mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p));
     25	} while (mpi_sts.s.busy);
     26}
     27
     28static int octeon_spi_do_transfer(struct octeon_spi *p,
     29				  struct spi_message *msg,
     30				  struct spi_transfer *xfer,
     31				  bool last_xfer)
     32{
     33	struct spi_device *spi = msg->spi;
     34	union cvmx_mpi_cfg mpi_cfg;
     35	union cvmx_mpi_tx mpi_tx;
     36	unsigned int clkdiv;
     37	int mode;
     38	bool cpha, cpol;
     39	const u8 *tx_buf;
     40	u8 *rx_buf;
     41	int len;
     42	int i;
     43
     44	mode = spi->mode;
     45	cpha = mode & SPI_CPHA;
     46	cpol = mode & SPI_CPOL;
     47
     48	clkdiv = p->sys_freq / (2 * xfer->speed_hz);
     49
     50	mpi_cfg.u64 = 0;
     51
     52	mpi_cfg.s.clkdiv = clkdiv;
     53	mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0;
     54	mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0;
     55	mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0;
     56	mpi_cfg.s.idlelo = cpha != cpol;
     57	mpi_cfg.s.cslate = cpha ? 1 : 0;
     58	mpi_cfg.s.enable = 1;
     59
     60	if (spi->chip_select < 4)
     61		p->cs_enax |= 1ull << (12 + spi->chip_select);
     62	mpi_cfg.u64 |= p->cs_enax;
     63
     64	if (mpi_cfg.u64 != p->last_cfg) {
     65		p->last_cfg = mpi_cfg.u64;
     66		writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p));
     67	}
     68	tx_buf = xfer->tx_buf;
     69	rx_buf = xfer->rx_buf;
     70	len = xfer->len;
     71	while (len > OCTEON_SPI_MAX_BYTES) {
     72		for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
     73			u8 d;
     74			if (tx_buf)
     75				d = *tx_buf++;
     76			else
     77				d = 0;
     78			writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
     79		}
     80		mpi_tx.u64 = 0;
     81		mpi_tx.s.csid = spi->chip_select;
     82		mpi_tx.s.leavecs = 1;
     83		mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
     84		mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
     85		writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
     86
     87		octeon_spi_wait_ready(p);
     88		if (rx_buf)
     89			for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
     90				u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
     91				*rx_buf++ = (u8)v;
     92			}
     93		len -= OCTEON_SPI_MAX_BYTES;
     94	}
     95
     96	for (i = 0; i < len; i++) {
     97		u8 d;
     98		if (tx_buf)
     99			d = *tx_buf++;
    100		else
    101			d = 0;
    102		writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
    103	}
    104
    105	mpi_tx.u64 = 0;
    106	mpi_tx.s.csid = spi->chip_select;
    107	if (last_xfer)
    108		mpi_tx.s.leavecs = xfer->cs_change;
    109	else
    110		mpi_tx.s.leavecs = !xfer->cs_change;
    111	mpi_tx.s.txnum = tx_buf ? len : 0;
    112	mpi_tx.s.totnum = len;
    113	writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
    114
    115	octeon_spi_wait_ready(p);
    116	if (rx_buf)
    117		for (i = 0; i < len; i++) {
    118			u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
    119			*rx_buf++ = (u8)v;
    120		}
    121
    122	spi_transfer_delay_exec(xfer);
    123
    124	return xfer->len;
    125}
    126
    127int octeon_spi_transfer_one_message(struct spi_master *master,
    128				    struct spi_message *msg)
    129{
    130	struct octeon_spi *p = spi_master_get_devdata(master);
    131	unsigned int total_len = 0;
    132	int status = 0;
    133	struct spi_transfer *xfer;
    134
    135	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
    136		bool last_xfer = list_is_last(&xfer->transfer_list,
    137					      &msg->transfers);
    138		int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
    139		if (r < 0) {
    140			status = r;
    141			goto err;
    142		}
    143		total_len += r;
    144	}
    145err:
    146	msg->status = status;
    147	msg->actual_length = total_len;
    148	spi_finalize_current_message(master);
    149	return status;
    150}