Blame view

drivers/spi/spi-cavium.c 3.5 KB
6b52c00f2   David Daney   spi: Add SPI mast...
1
2
3
4
5
6
7
  /*
   * This file is subject to the terms and conditions of the GNU General Public
   * License.  See the file "COPYING" in the main directory of this archive
   * for more details.
   *
   * Copyright (C) 2011, 2012 Cavium, Inc.
   */
6b52c00f2   David Daney   spi: Add SPI mast...
8
9
10
  #include <linux/spi/spi.h>
  #include <linux/module.h>
  #include <linux/delay.h>
6b52c00f2   David Daney   spi: Add SPI mast...
11
  #include <linux/io.h>
22cc1b6b3   Jan Glauber   spi: octeon: Move...
12
13
  
  #include "spi-cavium.h"
6b52c00f2   David Daney   spi: Add SPI mast...
14

6b52c00f2   David Daney   spi: Add SPI mast...
15
16
17
18
19
20
21
22
  static void octeon_spi_wait_ready(struct octeon_spi *p)
  {
  	union cvmx_mpi_sts mpi_sts;
  	unsigned int loops = 0;
  
  	do {
  		if (loops++)
  			__delay(500);
ee423c532   Jan Glauber   spi: octeon: Put ...
23
  		mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p));
6b52c00f2   David Daney   spi: Add SPI mast...
24
25
26
27
28
29
30
31
  	} while (mpi_sts.s.busy);
  }
  
  static int octeon_spi_do_transfer(struct octeon_spi *p,
  				  struct spi_message *msg,
  				  struct spi_transfer *xfer,
  				  bool last_xfer)
  {
85fe414d3   Axel Lin   spi: octeon: Remo...
32
  	struct spi_device *spi = msg->spi;
6b52c00f2   David Daney   spi: Add SPI mast...
33
34
35
  	union cvmx_mpi_cfg mpi_cfg;
  	union cvmx_mpi_tx mpi_tx;
  	unsigned int clkdiv;
6b52c00f2   David Daney   spi: Add SPI mast...
36
37
  	int mode;
  	bool cpha, cpol;
6b52c00f2   David Daney   spi: Add SPI mast...
38
39
40
41
  	const u8 *tx_buf;
  	u8 *rx_buf;
  	int len;
  	int i;
85fe414d3   Axel Lin   spi: octeon: Remo...
42
  	mode = spi->mode;
6b52c00f2   David Daney   spi: Add SPI mast...
43
44
  	cpha = mode & SPI_CPHA;
  	cpol = mode & SPI_CPOL;
6b52c00f2   David Daney   spi: Add SPI mast...
45

b9e64763b   Jan Glauber   spi: octeon: Stor...
46
  	clkdiv = p->sys_freq / (2 * xfer->speed_hz);
6b52c00f2   David Daney   spi: Add SPI mast...
47
48
49
50
51
52
53
54
55
56
  
  	mpi_cfg.u64 = 0;
  
  	mpi_cfg.s.clkdiv = clkdiv;
  	mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0;
  	mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0;
  	mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0;
  	mpi_cfg.s.idlelo = cpha != cpol;
  	mpi_cfg.s.cslate = cpha ? 1 : 0;
  	mpi_cfg.s.enable = 1;
85fe414d3   Axel Lin   spi: octeon: Remo...
57
58
  	if (spi->chip_select < 4)
  		p->cs_enax |= 1ull << (12 + spi->chip_select);
6b52c00f2   David Daney   spi: Add SPI mast...
59
60
61
62
  	mpi_cfg.u64 |= p->cs_enax;
  
  	if (mpi_cfg.u64 != p->last_cfg) {
  		p->last_cfg = mpi_cfg.u64;
ee423c532   Jan Glauber   spi: octeon: Put ...
63
  		writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p));
6b52c00f2   David Daney   spi: Add SPI mast...
64
65
66
67
68
69
70
71
72
73
74
  	}
  	tx_buf = xfer->tx_buf;
  	rx_buf = xfer->rx_buf;
  	len = xfer->len;
  	while (len > OCTEON_SPI_MAX_BYTES) {
  		for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
  			u8 d;
  			if (tx_buf)
  				d = *tx_buf++;
  			else
  				d = 0;
ee423c532   Jan Glauber   spi: octeon: Put ...
75
  			writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
6b52c00f2   David Daney   spi: Add SPI mast...
76
77
  		}
  		mpi_tx.u64 = 0;
85fe414d3   Axel Lin   spi: octeon: Remo...
78
  		mpi_tx.s.csid = spi->chip_select;
6b52c00f2   David Daney   spi: Add SPI mast...
79
80
81
  		mpi_tx.s.leavecs = 1;
  		mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
  		mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
ee423c532   Jan Glauber   spi: octeon: Put ...
82
  		writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
6b52c00f2   David Daney   spi: Add SPI mast...
83
84
85
86
  
  		octeon_spi_wait_ready(p);
  		if (rx_buf)
  			for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
ee423c532   Jan Glauber   spi: octeon: Put ...
87
  				u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
6b52c00f2   David Daney   spi: Add SPI mast...
88
89
90
91
92
93
94
95
96
97
98
  				*rx_buf++ = (u8)v;
  			}
  		len -= OCTEON_SPI_MAX_BYTES;
  	}
  
  	for (i = 0; i < len; i++) {
  		u8 d;
  		if (tx_buf)
  			d = *tx_buf++;
  		else
  			d = 0;
ee423c532   Jan Glauber   spi: octeon: Put ...
99
  		writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
6b52c00f2   David Daney   spi: Add SPI mast...
100
101
102
  	}
  
  	mpi_tx.u64 = 0;
85fe414d3   Axel Lin   spi: octeon: Remo...
103
  	mpi_tx.s.csid = spi->chip_select;
6b52c00f2   David Daney   spi: Add SPI mast...
104
105
106
107
108
109
  	if (last_xfer)
  		mpi_tx.s.leavecs = xfer->cs_change;
  	else
  		mpi_tx.s.leavecs = !xfer->cs_change;
  	mpi_tx.s.txnum = tx_buf ? len : 0;
  	mpi_tx.s.totnum = len;
ee423c532   Jan Glauber   spi: octeon: Put ...
110
  	writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
6b52c00f2   David Daney   spi: Add SPI mast...
111
112
113
114
  
  	octeon_spi_wait_ready(p);
  	if (rx_buf)
  		for (i = 0; i < len; i++) {
ee423c532   Jan Glauber   spi: octeon: Put ...
115
  			u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
6b52c00f2   David Daney   spi: Add SPI mast...
116
117
118
119
120
121
122
123
  			*rx_buf++ = (u8)v;
  		}
  
  	if (xfer->delay_usecs)
  		udelay(xfer->delay_usecs);
  
  	return xfer->len;
  }
63d49afef   Jan Glauber   spi: octeon: Spli...
124
125
  int octeon_spi_transfer_one_message(struct spi_master *master,
  				    struct spi_message *msg)
6b52c00f2   David Daney   spi: Add SPI mast...
126
127
128
129
130
  {
  	struct octeon_spi *p = spi_master_get_devdata(master);
  	unsigned int total_len = 0;
  	int status = 0;
  	struct spi_transfer *xfer;
6b52c00f2   David Daney   spi: Add SPI mast...
131
  	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
0a4e210e9   Axel Lin   spi: octeon: Use ...
132
133
  		bool last_xfer = list_is_last(&xfer->transfer_list,
  					      &msg->transfers);
6b52c00f2   David Daney   spi: Add SPI mast...
134
135
136
137
138
139
140
141
142
143
144
145
146
  		int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
  		if (r < 0) {
  			status = r;
  			goto err;
  		}
  		total_len += r;
  	}
  err:
  	msg->status = status;
  	msg->actual_length = total_len;
  	spi_finalize_current_message(master);
  	return status;
  }