Blame view

drivers/spi/spi-orion.c 18.2 KB
60cadec9d   Shadi Ammouri   spi: new orion_sp...
1
  /*
ca632f556   Grant Likely   spi: reorganize d...
2
   * Marvell Orion SPI controller driver
60cadec9d   Shadi Ammouri   spi: new orion_sp...
3
4
5
6
7
8
9
10
   *
   * Author: Shadi Ammouri <shadi@marvell.com>
   * Copyright (C) 2007-2008 Marvell Ltd.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   */
60cadec9d   Shadi Ammouri   spi: new orion_sp...
11
12
13
14
15
16
  #include <linux/interrupt.h>
  #include <linux/delay.h>
  #include <linux/platform_device.h>
  #include <linux/err.h>
  #include <linux/io.h>
  #include <linux/spi/spi.h>
d7614de42   Paul Gortmaker   spi: Add module.h...
17
  #include <linux/module.h>
5c6786945   Russell King   spi: spi-orion: a...
18
  #include <linux/pm_runtime.h>
f814f9ac5   Andrew Lunn   spi/orion: add de...
19
  #include <linux/of.h>
b3c195b3a   Stefan Roese   spi: orion: Add d...
20
  #include <linux/of_address.h>
df59fa7f4   Greg Ungerer   spi: orion: suppo...
21
  #include <linux/of_device.h>
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
22
  #include <linux/clk.h>
895248f85   Mark Brown   spi/orion: Direct...
23
  #include <linux/sizes.h>
60cadec9d   Shadi Ammouri   spi: new orion_sp...
24
25
26
  #include <asm/unaligned.h>
  
  #define DRIVER_NAME			"orion_spi"
5c6786945   Russell King   spi: spi-orion: a...
27
28
  /* Runtime PM autosuspend timeout: PM is fairly light on this driver */
  #define SPI_AUTOSUSPEND_TIMEOUT		200
23244404e   Ken Wilson   spi: orion: Add m...
29
30
31
32
33
  /* Some SoCs using this driver support up to 8 chip selects.
   * It is up to the implementer to only use the chip selects
   * that are available.
   */
  #define ORION_NUM_CHIPSELECTS		8
60cadec9d   Shadi Ammouri   spi: new orion_sp...
34
35
36
37
38
39
40
  #define ORION_SPI_WAIT_RDY_MAX_LOOP	2000 /* in usec */
  
  #define ORION_SPI_IF_CTRL_REG		0x00
  #define ORION_SPI_IF_CONFIG_REG		0x04
  #define ORION_SPI_DATA_OUT_REG		0x08
  #define ORION_SPI_DATA_IN_REG		0x0c
  #define ORION_SPI_INT_CAUSE_REG		0x10
38d6211e8   Nadav Haklai   spi: orion: On a3...
41
  #define ORION_SPI_TIMING_PARAMS_REG	0x18
b3c195b3a   Stefan Roese   spi: orion: Add d...
42
43
  /* Register for the "Direct Mode" */
  #define SPI_DIRECT_WRITE_CONFIG_REG	0x20
38d6211e8   Nadav Haklai   spi: orion: On a3...
44
45
46
  #define ORION_SPI_TMISO_SAMPLE_MASK	(0x3 << 6)
  #define ORION_SPI_TMISO_SAMPLE_1	(1 << 6)
  #define ORION_SPI_TMISO_SAMPLE_2	(2 << 6)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
47

b15d5d700   Jason Gunthorpe   spi/orion: Add SP...
48
49
  #define ORION_SPI_MODE_CPOL		(1 << 11)
  #define ORION_SPI_MODE_CPHA		(1 << 12)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
50
51
  #define ORION_SPI_IF_8_16_BIT_MODE	(1 << 5)
  #define ORION_SPI_CLK_PRESCALE_MASK	0x1F
df59fa7f4   Greg Ungerer   spi: orion: suppo...
52
  #define ARMADA_SPI_CLK_PRESCALE_MASK	0xDF
b15d5d700   Jason Gunthorpe   spi/orion: Add SP...
53
54
  #define ORION_SPI_MODE_MASK		(ORION_SPI_MODE_CPOL | \
  					 ORION_SPI_MODE_CPHA)
23244404e   Ken Wilson   spi: orion: Add m...
55
56
57
58
  #define ORION_SPI_CS_MASK	0x1C
  #define ORION_SPI_CS_SHIFT	2
  #define ORION_SPI_CS(cs)	((cs << ORION_SPI_CS_SHIFT) & \
  					ORION_SPI_CS_MASK)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
59

df59fa7f4   Greg Ungerer   spi: orion: suppo...
60
61
62
63
64
65
66
  enum orion_spi_type {
  	ORION_SPI,
  	ARMADA_SPI,
  };
  
  struct orion_spi_dev {
  	enum orion_spi_type	typ;
ce2f6ea1c   Gregory CLEMENT   spi: orion: Fix m...
67
68
69
70
71
72
  	/*
  	 * min_divisor and max_hz should be exclusive, the only we can
  	 * have both is for managing the armada-370-spi case with old
  	 * device tree
  	 */
  	unsigned long		max_hz;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
73
74
75
  	unsigned int		min_divisor;
  	unsigned int		max_divisor;
  	u32			prescale_mask;
38d6211e8   Nadav Haklai   spi: orion: On a3...
76
  	bool			is_errata_50mhz_ac;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
77
  };
b3c195b3a   Stefan Roese   spi: orion: Add d...
78
79
80
81
  struct orion_direct_acc {
  	void __iomem		*vaddr;
  	u32			size;
  };
60cadec9d   Shadi Ammouri   spi: new orion_sp...
82
  struct orion_spi {
60cadec9d   Shadi Ammouri   spi: new orion_sp...
83
84
  	struct spi_master	*master;
  	void __iomem		*base;
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
85
  	struct clk              *clk;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
86
  	const struct orion_spi_dev *devdata;
b3c195b3a   Stefan Roese   spi: orion: Add d...
87
88
  
  	struct orion_direct_acc	direct_access[ORION_NUM_CHIPSELECTS];
60cadec9d   Shadi Ammouri   spi: new orion_sp...
89
  };
60cadec9d   Shadi Ammouri   spi: new orion_sp...
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
  static inline void __iomem *spi_reg(struct orion_spi *orion_spi, u32 reg)
  {
  	return orion_spi->base + reg;
  }
  
  static inline void
  orion_spi_setbits(struct orion_spi *orion_spi, u32 reg, u32 mask)
  {
  	void __iomem *reg_addr = spi_reg(orion_spi, reg);
  	u32 val;
  
  	val = readl(reg_addr);
  	val |= mask;
  	writel(val, reg_addr);
  }
  
  static inline void
  orion_spi_clrbits(struct orion_spi *orion_spi, u32 reg, u32 mask)
  {
  	void __iomem *reg_addr = spi_reg(orion_spi, reg);
  	u32 val;
  
  	val = readl(reg_addr);
  	val &= ~mask;
  	writel(val, reg_addr);
  }
60cadec9d   Shadi Ammouri   spi: new orion_sp...
116
117
118
119
120
121
122
  static int orion_spi_baudrate_set(struct spi_device *spi, unsigned int speed)
  {
  	u32 tclk_hz;
  	u32 rate;
  	u32 prescale;
  	u32 reg;
  	struct orion_spi *orion_spi;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
123
  	const struct orion_spi_dev *devdata;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
124
125
  
  	orion_spi = spi_master_get_devdata(spi->master);
df59fa7f4   Greg Ungerer   spi: orion: suppo...
126
  	devdata = orion_spi->devdata;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
127

4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
128
  	tclk_hz = clk_get_rate(orion_spi->clk);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
129

df59fa7f4   Greg Ungerer   spi: orion: suppo...
130
131
132
  	if (devdata->typ == ARMADA_SPI) {
  		unsigned int clk, spr, sppr, sppr2, err;
  		unsigned int best_spr, best_sppr, best_err;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
133

df59fa7f4   Greg Ungerer   spi: orion: suppo...
134
135
136
  		best_err = speed;
  		best_spr = 0;
  		best_sppr = 0;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
137

df59fa7f4   Greg Ungerer   spi: orion: suppo...
138
139
140
141
142
143
144
145
  		/* Iterate over the valid range looking for best fit */
  		for (sppr = 0; sppr < 8; sppr++) {
  			sppr2 = 0x1 << sppr;
  
  			spr = tclk_hz / sppr2;
  			spr = DIV_ROUND_UP(spr, speed);
  			if ((spr == 0) || (spr > 15))
  				continue;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
146

df59fa7f4   Greg Ungerer   spi: orion: suppo...
147
148
  			clk = tclk_hz / (spr * sppr2);
  			err = speed - clk;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
149

df59fa7f4   Greg Ungerer   spi: orion: suppo...
150
151
152
153
154
155
  			if (err < best_err) {
  				best_spr = spr;
  				best_sppr = sppr;
  				best_err = err;
  			}
  		}
60cadec9d   Shadi Ammouri   spi: new orion_sp...
156

df59fa7f4   Greg Ungerer   spi: orion: suppo...
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
  		if ((best_sppr == 0) && (best_spr == 0))
  			return -EINVAL;
  
  		prescale = ((best_sppr & 0x6) << 5) |
  			((best_sppr & 0x1) << 4) | best_spr;
  	} else {
  		/*
  		 * the supported rates are: 4,6,8...30
  		 * round up as we look for equal or less speed
  		 */
  		rate = DIV_ROUND_UP(tclk_hz, speed);
  		rate = roundup(rate, 2);
  
  		/* check if requested speed is too small */
  		if (rate > 30)
  			return -EINVAL;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
173

df59fa7f4   Greg Ungerer   spi: orion: suppo...
174
175
176
177
178
179
  		if (rate < 4)
  			rate = 4;
  
  		/* Convert the rate to SPI clock divisor value.	*/
  		prescale = 0x10 + rate/2;
  	}
60cadec9d   Shadi Ammouri   spi: new orion_sp...
180
181
  
  	reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
df59fa7f4   Greg Ungerer   spi: orion: suppo...
182
  	reg = ((reg & ~devdata->prescale_mask) | prescale);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
183
184
185
186
  	writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
  
  	return 0;
  }
b15d5d700   Jason Gunthorpe   spi/orion: Add SP...
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
  static void
  orion_spi_mode_set(struct spi_device *spi)
  {
  	u32 reg;
  	struct orion_spi *orion_spi;
  
  	orion_spi = spi_master_get_devdata(spi->master);
  
  	reg = readl(spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
  	reg &= ~ORION_SPI_MODE_MASK;
  	if (spi->mode & SPI_CPOL)
  		reg |= ORION_SPI_MODE_CPOL;
  	if (spi->mode & SPI_CPHA)
  		reg |= ORION_SPI_MODE_CPHA;
  	writel(reg, spi_reg(orion_spi, ORION_SPI_IF_CONFIG_REG));
  }
38d6211e8   Nadav Haklai   spi: orion: On a3...
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  static void
  orion_spi_50mhz_ac_timing_erratum(struct spi_device *spi, unsigned int speed)
  {
  	u32 reg;
  	struct orion_spi *orion_spi;
  
  	orion_spi = spi_master_get_devdata(spi->master);
  
  	/*
  	 * Erratum description: (Erratum NO. FE-9144572) The device
  	 * SPI interface supports frequencies of up to 50 MHz.
  	 * However, due to this erratum, when the device core clock is
  	 * 250 MHz and the SPI interfaces is configured for 50MHz SPI
  	 * clock and CPOL=CPHA=1 there might occur data corruption on
  	 * reads from the SPI device.
  	 * Erratum Workaround:
  	 * Work in one of the following configurations:
  	 * 1. Set CPOL=CPHA=0 in "SPI Interface Configuration
  	 * Register".
  	 * 2. Set TMISO_SAMPLE value to 0x2 in "SPI Timing Parameters 1
  	 * Register" before setting the interface.
  	 */
  	reg = readl(spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG));
  	reg &= ~ORION_SPI_TMISO_SAMPLE_MASK;
  
  	if (clk_get_rate(orion_spi->clk) == 250000000 &&
  			speed == 50000000 && spi->mode & SPI_CPOL &&
  			spi->mode & SPI_CPHA)
  		reg |= ORION_SPI_TMISO_SAMPLE_2;
  	else
  		reg |= ORION_SPI_TMISO_SAMPLE_1; /* This is the default value */
  
  	writel(reg, spi_reg(orion_spi, ORION_SPI_TIMING_PARAMS_REG));
  }
60cadec9d   Shadi Ammouri   spi: new orion_sp...
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
  /*
   * called only when no transfer is active on the bus
   */
  static int
  orion_spi_setup_transfer(struct spi_device *spi, struct spi_transfer *t)
  {
  	struct orion_spi *orion_spi;
  	unsigned int speed = spi->max_speed_hz;
  	unsigned int bits_per_word = spi->bits_per_word;
  	int	rc;
  
  	orion_spi = spi_master_get_devdata(spi->master);
  
  	if ((t != NULL) && t->speed_hz)
  		speed = t->speed_hz;
  
  	if ((t != NULL) && t->bits_per_word)
  		bits_per_word = t->bits_per_word;
b15d5d700   Jason Gunthorpe   spi/orion: Add SP...
255
  	orion_spi_mode_set(spi);
38d6211e8   Nadav Haklai   spi: orion: On a3...
256
257
  	if (orion_spi->devdata->is_errata_50mhz_ac)
  		orion_spi_50mhz_ac_timing_erratum(spi, speed);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
258
259
260
  	rc = orion_spi_baudrate_set(spi, speed);
  	if (rc)
  		return rc;
495b33588   Axel Lin   spi: orion: Conve...
261
262
263
264
265
266
267
268
  	if (bits_per_word == 16)
  		orion_spi_setbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
  				  ORION_SPI_IF_8_16_BIT_MODE);
  	else
  		orion_spi_clrbits(orion_spi, ORION_SPI_IF_CONFIG_REG,
  				  ORION_SPI_IF_8_16_BIT_MODE);
  
  	return 0;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
269
  }
75872ebe9   Ken Wilson   spi: orion: Chang...
270
  static void orion_spi_set_cs(struct spi_device *spi, bool enable)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
271
  {
75872ebe9   Ken Wilson   spi: orion: Chang...
272
273
274
  	struct orion_spi *orion_spi;
  
  	orion_spi = spi_master_get_devdata(spi->master);
23244404e   Ken Wilson   spi: orion: Add m...
275
276
277
  	orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, ORION_SPI_CS_MASK);
  	orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG,
  				ORION_SPI_CS(spi->chip_select));
75872ebe9   Ken Wilson   spi: orion: Chang...
278
279
  	/* Chip select logic is inverted from spi_set_cs */
  	if (!enable)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
280
281
282
283
284
285
286
287
288
289
290
291
  		orion_spi_setbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
  	else
  		orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
  }
  
  static inline int orion_spi_wait_till_ready(struct orion_spi *orion_spi)
  {
  	int i;
  
  	for (i = 0; i < ORION_SPI_WAIT_RDY_MAX_LOOP; i++) {
  		if (readl(spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG)))
  			return 1;
b8434048d   Jingoo Han   spi: orion: Fix c...
292
293
  
  		udelay(1);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
  	}
  
  	return -1;
  }
  
  static inline int
  orion_spi_write_read_8bit(struct spi_device *spi,
  			  const u8 **tx_buf, u8 **rx_buf)
  {
  	void __iomem *tx_reg, *rx_reg, *int_reg;
  	struct orion_spi *orion_spi;
  
  	orion_spi = spi_master_get_devdata(spi->master);
  	tx_reg = spi_reg(orion_spi, ORION_SPI_DATA_OUT_REG);
  	rx_reg = spi_reg(orion_spi, ORION_SPI_DATA_IN_REG);
  	int_reg = spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG);
  
  	/* clear the interrupt cause register */
  	writel(0x0, int_reg);
  
  	if (tx_buf && *tx_buf)
  		writel(*(*tx_buf)++, tx_reg);
  	else
  		writel(0, tx_reg);
  
  	if (orion_spi_wait_till_ready(orion_spi) < 0) {
  		dev_err(&spi->dev, "TXS timed out
  ");
  		return -1;
  	}
  
  	if (rx_buf && *rx_buf)
  		*(*rx_buf)++ = readl(rx_reg);
  
  	return 1;
  }
  
  static inline int
  orion_spi_write_read_16bit(struct spi_device *spi,
  			   const u16 **tx_buf, u16 **rx_buf)
  {
  	void __iomem *tx_reg, *rx_reg, *int_reg;
  	struct orion_spi *orion_spi;
  
  	orion_spi = spi_master_get_devdata(spi->master);
  	tx_reg = spi_reg(orion_spi, ORION_SPI_DATA_OUT_REG);
  	rx_reg = spi_reg(orion_spi, ORION_SPI_DATA_IN_REG);
  	int_reg = spi_reg(orion_spi, ORION_SPI_INT_CAUSE_REG);
  
  	/* clear the interrupt cause register */
  	writel(0x0, int_reg);
  
  	if (tx_buf && *tx_buf)
  		writel(__cpu_to_le16(get_unaligned((*tx_buf)++)), tx_reg);
  	else
  		writel(0, tx_reg);
  
  	if (orion_spi_wait_till_ready(orion_spi) < 0) {
  		dev_err(&spi->dev, "TXS timed out
  ");
  		return -1;
  	}
  
  	if (rx_buf && *rx_buf)
  		put_unaligned(__le16_to_cpu(readl(rx_reg)), (*rx_buf)++);
  
  	return 1;
  }
  
  static unsigned int
  orion_spi_write_read(struct spi_device *spi, struct spi_transfer *xfer)
  {
60cadec9d   Shadi Ammouri   spi: new orion_sp...
366
367
  	unsigned int count;
  	int word_len;
b3c195b3a   Stefan Roese   spi: orion: Add d...
368
369
  	struct orion_spi *orion_spi;
  	int cs = spi->chip_select;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
370

60cadec9d   Shadi Ammouri   spi: new orion_sp...
371
372
  	word_len = spi->bits_per_word;
  	count = xfer->len;
b3c195b3a   Stefan Roese   spi: orion: Add d...
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
  	orion_spi = spi_master_get_devdata(spi->master);
  
  	/*
  	 * Use SPI direct write mode if base address is available. Otherwise
  	 * fall back to PIO mode for this transfer.
  	 */
  	if ((orion_spi->direct_access[cs].vaddr) && (xfer->tx_buf) &&
  	    (word_len == 8)) {
  		unsigned int cnt = count / 4;
  		unsigned int rem = count % 4;
  
  		/*
  		 * Send the TX-data to the SPI device via the direct
  		 * mapped address window
  		 */
  		iowrite32_rep(orion_spi->direct_access[cs].vaddr,
  			      xfer->tx_buf, cnt);
  		if (rem) {
  			u32 *buf = (u32 *)xfer->tx_buf;
  
  			iowrite8_rep(orion_spi->direct_access[cs].vaddr,
  				     &buf[cnt], rem);
  		}
  
  		return count;
  	}
60cadec9d   Shadi Ammouri   spi: new orion_sp...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
  	if (word_len == 8) {
  		const u8 *tx = xfer->tx_buf;
  		u8 *rx = xfer->rx_buf;
  
  		do {
  			if (orion_spi_write_read_8bit(spi, &tx, &rx) < 0)
  				goto out;
  			count--;
  		} while (count);
  	} else if (word_len == 16) {
  		const u16 *tx = xfer->tx_buf;
  		u16 *rx = xfer->rx_buf;
  
  		do {
  			if (orion_spi_write_read_16bit(spi, &tx, &rx) < 0)
  				goto out;
  			count -= 2;
  		} while (count);
  	}
  
  out:
  	return xfer->len - count;
  }
75872ebe9   Ken Wilson   spi: orion: Chang...
422
423
424
  static int orion_spi_transfer_one(struct spi_master *master,
  					struct spi_device *spi,
  					struct spi_transfer *t)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
425
  {
ba59a8078   Andrew Lunn   spi: Refactor spi...
426
  	int status = 0;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
427

75872ebe9   Ken Wilson   spi: orion: Chang...
428
  	status = orion_spi_setup_transfer(spi, t);
ba59a8078   Andrew Lunn   spi: Refactor spi...
429
  	if (status < 0)
75872ebe9   Ken Wilson   spi: orion: Chang...
430
  		return status;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
431

75872ebe9   Ken Wilson   spi: orion: Chang...
432
433
  	if (t->len)
  		orion_spi_write_read(spi, t);
ba59a8078   Andrew Lunn   spi: Refactor spi...
434

75872ebe9   Ken Wilson   spi: orion: Chang...
435
436
  	return status;
  }
ba59a8078   Andrew Lunn   spi: Refactor spi...
437

75872ebe9   Ken Wilson   spi: orion: Chang...
438
439
440
  static int orion_spi_setup(struct spi_device *spi)
  {
  	return orion_spi_setup_transfer(spi, NULL);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
441
  }
2deff8d60   Grant Likely   spi: Remove erron...
442
  static int orion_spi_reset(struct orion_spi *orion_spi)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
443
444
  {
  	/* Verify that the CS is deasserted */
75872ebe9   Ken Wilson   spi: orion: Chang...
445
  	orion_spi_clrbits(orion_spi, ORION_SPI_IF_CTRL_REG, 0x1);
b3c195b3a   Stefan Roese   spi: orion: Add d...
446
447
448
  
  	/* Don't deassert CS between the direct mapped SPI transfers */
  	writel(0, spi_reg(orion_spi, SPI_DIRECT_WRITE_CONFIG_REG));
60cadec9d   Shadi Ammouri   spi: new orion_sp...
449
450
  	return 0;
  }
df59fa7f4   Greg Ungerer   spi: orion: suppo...
451
452
453
454
455
456
  static const struct orion_spi_dev orion_spi_dev_data = {
  	.typ = ORION_SPI,
  	.min_divisor = 4,
  	.max_divisor = 30,
  	.prescale_mask = ORION_SPI_CLK_PRESCALE_MASK,
  };
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
457
  static const struct orion_spi_dev armada_370_spi_dev_data = {
df59fa7f4   Greg Ungerer   spi: orion: suppo...
458
  	.typ = ARMADA_SPI,
ce2f6ea1c   Gregory CLEMENT   spi: orion: Fix m...
459
  	.min_divisor = 4,
df59fa7f4   Greg Ungerer   spi: orion: suppo...
460
  	.max_divisor = 1920,
ce2f6ea1c   Gregory CLEMENT   spi: orion: Fix m...
461
  	.max_hz = 50000000,
df59fa7f4   Greg Ungerer   spi: orion: suppo...
462
463
  	.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
  };
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
464
465
466
467
468
469
470
471
472
473
474
475
476
  static const struct orion_spi_dev armada_xp_spi_dev_data = {
  	.typ = ARMADA_SPI,
  	.max_hz = 50000000,
  	.max_divisor = 1920,
  	.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
  };
  
  static const struct orion_spi_dev armada_375_spi_dev_data = {
  	.typ = ARMADA_SPI,
  	.min_divisor = 15,
  	.max_divisor = 1920,
  	.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
  };
38d6211e8   Nadav Haklai   spi: orion: On a3...
477
478
479
480
481
482
483
  static const struct orion_spi_dev armada_380_spi_dev_data = {
  	.typ = ARMADA_SPI,
  	.max_hz = 50000000,
  	.max_divisor = 1920,
  	.prescale_mask = ARMADA_SPI_CLK_PRESCALE_MASK,
  	.is_errata_50mhz_ac = true,
  };
df59fa7f4   Greg Ungerer   spi: orion: suppo...
484
  static const struct of_device_id orion_spi_of_match_table[] = {
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
485
486
487
488
489
490
491
492
493
494
495
496
497
498
  	{
  		.compatible = "marvell,orion-spi",
  		.data = &orion_spi_dev_data,
  	},
  	{
  		.compatible = "marvell,armada-370-spi",
  		.data = &armada_370_spi_dev_data,
  	},
  	{
  		.compatible = "marvell,armada-375-spi",
  		.data = &armada_375_spi_dev_data,
  	},
  	{
  		.compatible = "marvell,armada-380-spi",
38d6211e8   Nadav Haklai   spi: orion: On a3...
499
  		.data = &armada_380_spi_dev_data,
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
500
501
502
503
504
505
506
507
508
  	},
  	{
  		.compatible = "marvell,armada-390-spi",
  		.data = &armada_xp_spi_dev_data,
  	},
  	{
  		.compatible = "marvell,armada-xp-spi",
  		.data = &armada_xp_spi_dev_data,
  	},
df59fa7f4   Greg Ungerer   spi: orion: suppo...
509
510
511
  	{}
  };
  MODULE_DEVICE_TABLE(of, orion_spi_of_match_table);
2deff8d60   Grant Likely   spi: Remove erron...
512
  static int orion_spi_probe(struct platform_device *pdev)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
513
  {
df59fa7f4   Greg Ungerer   spi: orion: suppo...
514
515
  	const struct of_device_id *of_id;
  	const struct orion_spi_dev *devdata;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
516
517
518
  	struct spi_master *master;
  	struct orion_spi *spi;
  	struct resource *r;
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
519
  	unsigned long tclk_hz;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
520
  	int status = 0;
b3c195b3a   Stefan Roese   spi: orion: Add d...
521
  	struct device_node *np;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
522

3fed8068f   Jingoo Han   spi: orion: Fix c...
523
  	master = spi_alloc_master(&pdev->dev, sizeof(*spi));
60cadec9d   Shadi Ammouri   spi: new orion_sp...
524
525
526
527
528
529
530
531
  	if (master == NULL) {
  		dev_dbg(&pdev->dev, "master allocation failed
  ");
  		return -ENOMEM;
  	}
  
  	if (pdev->id != -1)
  		master->bus_num = pdev->id;
f814f9ac5   Andrew Lunn   spi/orion: add de...
532
  	if (pdev->dev.of_node) {
e06871cd2   Thomas Petazzoni   spi: orion: fix i...
533
  		u32 cell_index;
b8434048d   Jingoo Han   spi: orion: Fix c...
534

e06871cd2   Thomas Petazzoni   spi: orion: fix i...
535
536
537
  		if (!of_property_read_u32(pdev->dev.of_node, "cell-index",
  					  &cell_index))
  			master->bus_num = cell_index;
f814f9ac5   Andrew Lunn   spi/orion: add de...
538
  	}
60cadec9d   Shadi Ammouri   spi: new orion_sp...
539

e7db06b5d   David Brownell   spi: move more sp...
540
  	/* we support only mode 0, and no options */
b15d5d700   Jason Gunthorpe   spi/orion: Add SP...
541
  	master->mode_bits = SPI_CPHA | SPI_CPOL;
75872ebe9   Ken Wilson   spi: orion: Chang...
542
543
  	master->set_cs = orion_spi_set_cs;
  	master->transfer_one = orion_spi_transfer_one;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
544
  	master->num_chipselect = ORION_NUM_CHIPSELECTS;
75872ebe9   Ken Wilson   spi: orion: Chang...
545
  	master->setup = orion_spi_setup;
495b33588   Axel Lin   spi: orion: Conve...
546
  	master->bits_per_word_mask = SPI_BPW_MASK(8) | SPI_BPW_MASK(16);
5c6786945   Russell King   spi: spi-orion: a...
547
  	master->auto_runtime_pm = true;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
548

24b5a82cf   Jingoo Han   spi: use platform...
549
  	platform_set_drvdata(pdev, master);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
550
551
552
  
  	spi = spi_master_get_devdata(master);
  	spi->master = master;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
553

df59fa7f4   Greg Ungerer   spi: orion: suppo...
554
  	of_id = of_match_device(orion_spi_of_match_table, &pdev->dev);
9a2d36355   Greg Ungerer   spi: orion: fix p...
555
  	devdata = (of_id) ? of_id->data : &orion_spi_dev_data;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
556
  	spi->devdata = devdata;
bb489841b   Jingoo Han   spi: orion: Use d...
557
  	spi->clk = devm_clk_get(&pdev->dev, NULL);
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
558
559
560
561
  	if (IS_ERR(spi->clk)) {
  		status = PTR_ERR(spi->clk);
  		goto out;
  	}
c85012add   Russell King   spi: spi-orion: c...
562
563
564
  	status = clk_prepare_enable(spi->clk);
  	if (status)
  		goto out;
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
565
  	tclk_hz = clk_get_rate(spi->clk);
ce2f6ea1c   Gregory CLEMENT   spi: orion: Fix m...
566
567
568
569
570
571
572
573
574
575
576
577
  
  	/*
  	 * With old device tree, armada-370-spi could be used with
  	 * Armada XP, however for this SoC the maximum frequency is
  	 * 50MHz instead of tclk/4. On Armada 370, tclk cannot be
  	 * higher than 200MHz. So, in order to be able to handle both
  	 * SoCs, we can take the minimum of 50MHz and tclk/4.
  	 */
  	if (of_device_is_compatible(pdev->dev.of_node,
  					"marvell,armada-370-spi"))
  		master->max_speed_hz = min(devdata->max_hz,
  				DIV_ROUND_UP(tclk_hz, devdata->min_divisor));
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
578
  	else if (devdata->min_divisor)
ce2f6ea1c   Gregory CLEMENT   spi: orion: Fix m...
579
580
  		master->max_speed_hz =
  			DIV_ROUND_UP(tclk_hz, devdata->min_divisor);
4dacccfac   Gregory CLEMENT   spi: orion: Fix e...
581
582
  	else
  		master->max_speed_hz = devdata->max_hz;
df59fa7f4   Greg Ungerer   spi: orion: suppo...
583
  	master->min_speed_hz = DIV_ROUND_UP(tclk_hz, devdata->max_divisor);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
584
585
  
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1729ce344   Mark Brown   spi/orion: Conver...
586
587
588
  	spi->base = devm_ioremap_resource(&pdev->dev, r);
  	if (IS_ERR(spi->base)) {
  		status = PTR_ERR(spi->base);
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
589
  		goto out_rel_clk;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
590
  	}
b3c195b3a   Stefan Roese   spi: orion: Add d...
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
  	/* Scan all SPI devices of this controller for direct mapped devices */
  	for_each_available_child_of_node(pdev->dev.of_node, np) {
  		u32 cs;
  
  		/* Get chip-select number from the "reg" property */
  		status = of_property_read_u32(np, "reg", &cs);
  		if (status) {
  			dev_err(&pdev->dev,
  				"%s has no valid 'reg' property (%d)
  ",
  				np->full_name, status);
  			status = 0;
  			continue;
  		}
  
  		/*
  		 * Check if an address is configured for this SPI device. If
  		 * not, the MBus mapping via the 'ranges' property in the 'soc'
  		 * node is not configured and this device should not use the
  		 * direct mode. In this case, just continue with the next
  		 * device.
  		 */
  		status = of_address_to_resource(pdev->dev.of_node, cs + 1, r);
  		if (status)
  			continue;
  
  		/*
  		 * Only map one page for direct access. This is enough for the
  		 * simple TX transfer which only writes to the first word.
  		 * This needs to get extended for the direct SPI-NOR / SPI-NAND
  		 * support, once this gets implemented.
  		 */
  		spi->direct_access[cs].vaddr = devm_ioremap(&pdev->dev,
  							    r->start,
  							    PAGE_SIZE);
57c624ae1   Wei Yongjun   spi: orion: Fix r...
626
627
  		if (!spi->direct_access[cs].vaddr) {
  			status = -ENOMEM;
b3c195b3a   Stefan Roese   spi: orion: Add d...
628
629
630
631
632
633
634
  			goto out_rel_clk;
  		}
  		spi->direct_access[cs].size = PAGE_SIZE;
  
  		dev_info(&pdev->dev, "CS%d configured for direct access
  ", cs);
  	}
5c6786945   Russell King   spi: spi-orion: a...
635
636
637
638
  	pm_runtime_set_active(&pdev->dev);
  	pm_runtime_use_autosuspend(&pdev->dev);
  	pm_runtime_set_autosuspend_delay(&pdev->dev, SPI_AUTOSUSPEND_TIMEOUT);
  	pm_runtime_enable(&pdev->dev);
140338163   Wei Yongjun   spi: orion: Fix e...
639
640
  	status = orion_spi_reset(spi);
  	if (status < 0)
5c6786945   Russell King   spi: spi-orion: a...
641
642
643
644
  		goto out_rel_pm;
  
  	pm_runtime_mark_last_busy(&pdev->dev);
  	pm_runtime_put_autosuspend(&pdev->dev);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
645

f814f9ac5   Andrew Lunn   spi/orion: add de...
646
  	master->dev.of_node = pdev->dev.of_node;
5c6786945   Russell King   spi: spi-orion: a...
647
  	status = spi_register_master(master);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
648
  	if (status < 0)
5c6786945   Russell King   spi: spi-orion: a...
649
  		goto out_rel_pm;
60cadec9d   Shadi Ammouri   spi: new orion_sp...
650
651
  
  	return status;
5c6786945   Russell King   spi: spi-orion: a...
652
653
  out_rel_pm:
  	pm_runtime_disable(&pdev->dev);
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
654
655
  out_rel_clk:
  	clk_disable_unprepare(spi->clk);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
656
657
658
659
  out:
  	spi_master_put(master);
  	return status;
  }
2deff8d60   Grant Likely   spi: Remove erron...
660
  static int orion_spi_remove(struct platform_device *pdev)
60cadec9d   Shadi Ammouri   spi: new orion_sp...
661
  {
5c6786945   Russell King   spi: spi-orion: a...
662
663
  	struct spi_master *master = platform_get_drvdata(pdev);
  	struct orion_spi *spi = spi_master_get_devdata(master);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
664

5c6786945   Russell King   spi: spi-orion: a...
665
  	pm_runtime_get_sync(&pdev->dev);
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
666
  	clk_disable_unprepare(spi->clk);
4574b8866   Andrew Lunn   ARM: Orion: SPI: ...
667

5c6786945   Russell King   spi: spi-orion: a...
668
669
  	spi_unregister_master(master);
  	pm_runtime_disable(&pdev->dev);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
670
671
672
673
  	return 0;
  }
  
  MODULE_ALIAS("platform:" DRIVER_NAME);
ec8330503   Rafael J. Wysocki   spi: Replace CONF...
674
  #ifdef CONFIG_PM
5c6786945   Russell King   spi: spi-orion: a...
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
  static int orion_spi_runtime_suspend(struct device *dev)
  {
  	struct spi_master *master = dev_get_drvdata(dev);
  	struct orion_spi *spi = spi_master_get_devdata(master);
  
  	clk_disable_unprepare(spi->clk);
  	return 0;
  }
  
  static int orion_spi_runtime_resume(struct device *dev)
  {
  	struct spi_master *master = dev_get_drvdata(dev);
  	struct orion_spi *spi = spi_master_get_devdata(master);
  
  	return clk_prepare_enable(spi->clk);
  }
  #endif
  
  static const struct dev_pm_ops orion_spi_pm_ops = {
  	SET_RUNTIME_PM_OPS(orion_spi_runtime_suspend,
  			   orion_spi_runtime_resume,
  			   NULL)
  };
60cadec9d   Shadi Ammouri   spi: new orion_sp...
698
699
700
  static struct platform_driver orion_spi_driver = {
  	.driver = {
  		.name	= DRIVER_NAME,
5c6786945   Russell King   spi: spi-orion: a...
701
  		.pm	= &orion_spi_pm_ops,
f814f9ac5   Andrew Lunn   spi/orion: add de...
702
  		.of_match_table = of_match_ptr(orion_spi_of_match_table),
60cadec9d   Shadi Ammouri   spi: new orion_sp...
703
  	},
41ab724aa   Ezequiel Garcia   spi/orion: Use mo...
704
  	.probe		= orion_spi_probe,
2deff8d60   Grant Likely   spi: Remove erron...
705
  	.remove		= orion_spi_remove,
60cadec9d   Shadi Ammouri   spi: new orion_sp...
706
  };
41ab724aa   Ezequiel Garcia   spi/orion: Use mo...
707
  module_platform_driver(orion_spi_driver);
60cadec9d   Shadi Ammouri   spi: new orion_sp...
708
709
710
711
  
  MODULE_DESCRIPTION("Orion SPI driver");
  MODULE_AUTHOR("Shadi Ammouri <shadi@marvell.com>");
  MODULE_LICENSE("GPL");