brintos

brintos / linux-shallow public Read only

0
0
Text · 3.5 KiB · 26b8cd1 Raw
151 lines · c
1/*2 * This file is subject to the terms and conditions of the GNU General Public3 * License.  See the file "COPYING" in the main directory of this archive4 * 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_get_chipselect(spi, 0) < 4)61		p->cs_enax |= 1ull << (12 + spi_get_chipselect(spi, 0));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			else77				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_get_chipselect(spi, 0);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		else101			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_get_chipselect(spi, 0);107	if (last_xfer)108		mpi_tx.s.leavecs = xfer->cs_change;109	else110		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_controller *ctlr,128				    struct spi_message *msg)129{130	struct octeon_spi *p = spi_controller_get_devdata(ctlr);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(ctlr);149	return status;150}151