Linux Audio

Check our new training course

Loading...
v6.2
  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}
v6.8
  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_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			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_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		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_get_chipselect(spi, 0);
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}