Blame view

drivers/spi/spi-omap2-mcspi.c 32.4 KB
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1
2
3
4
5
  /*
   * OMAP2 McSPI controller driver
   *
   * Copyright (C) 2005, 2006 Nokia Corporation
   * Author:	Samuel Ortiz <samuel.ortiz@nokia.com> and
1a5d81905   Charulatha V   OMAP: devices: Mo...
6
   *		Juha Yrj�l� <juha.yrjola@nokia.com>
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation; either version 2 of the License, or
   * (at your option) any later version.
   *
   * This program is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with this program; if not, write to the Free Software
   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/init.h>
  #include <linux/interrupt.h>
  #include <linux/module.h>
  #include <linux/device.h>
  #include <linux/delay.h>
  #include <linux/dma-mapping.h>
  #include <linux/platform_device.h>
  #include <linux/err.h>
  #include <linux/clk.h>
  #include <linux/io.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
35
  #include <linux/slab.h>
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
36
  #include <linux/pm_runtime.h>
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
37
38
  
  #include <linux/spi/spi.h>
ce491cf85   Tony Lindgren   omap: headers: Mo...
39
40
  #include <plat/dma.h>
  #include <plat/clock.h>
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
41
  #include <plat/mcspi.h>
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
42
43
  
  #define OMAP2_MCSPI_MAX_FREQ		48000000
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
44
45
  /* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */
  #define OMAP2_MCSPI_MAX_CTRL 		4
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
46
  #define OMAP2_MCSPI_REVISION		0x00
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  #define OMAP2_MCSPI_SYSSTATUS		0x14
  #define OMAP2_MCSPI_IRQSTATUS		0x18
  #define OMAP2_MCSPI_IRQENABLE		0x1c
  #define OMAP2_MCSPI_WAKEUPENABLE	0x20
  #define OMAP2_MCSPI_SYST		0x24
  #define OMAP2_MCSPI_MODULCTRL		0x28
  
  /* per-channel banks, 0x14 bytes each, first is: */
  #define OMAP2_MCSPI_CHCONF0		0x2c
  #define OMAP2_MCSPI_CHSTAT0		0x30
  #define OMAP2_MCSPI_CHCTRL0		0x34
  #define OMAP2_MCSPI_TX0			0x38
  #define OMAP2_MCSPI_RX0			0x3c
  
  /* per-register bitmasks: */
7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
62
63
64
  #define OMAP2_MCSPI_MODULCTRL_SINGLE	BIT(0)
  #define OMAP2_MCSPI_MODULCTRL_MS	BIT(2)
  #define OMAP2_MCSPI_MODULCTRL_STEST	BIT(3)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
65

7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
66
67
  #define OMAP2_MCSPI_CHCONF_PHA		BIT(0)
  #define OMAP2_MCSPI_CHCONF_POL		BIT(1)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
68
  #define OMAP2_MCSPI_CHCONF_CLKD_MASK	(0x0f << 2)
7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
69
  #define OMAP2_MCSPI_CHCONF_EPOL		BIT(6)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
70
  #define OMAP2_MCSPI_CHCONF_WL_MASK	(0x1f << 7)
7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
71
72
  #define OMAP2_MCSPI_CHCONF_TRM_RX_ONLY	BIT(12)
  #define OMAP2_MCSPI_CHCONF_TRM_TX_ONLY	BIT(13)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
73
  #define OMAP2_MCSPI_CHCONF_TRM_MASK	(0x03 << 12)
7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
74
75
76
77
78
79
80
  #define OMAP2_MCSPI_CHCONF_DMAW		BIT(14)
  #define OMAP2_MCSPI_CHCONF_DMAR		BIT(15)
  #define OMAP2_MCSPI_CHCONF_DPE0		BIT(16)
  #define OMAP2_MCSPI_CHCONF_DPE1		BIT(17)
  #define OMAP2_MCSPI_CHCONF_IS		BIT(18)
  #define OMAP2_MCSPI_CHCONF_TURBO	BIT(19)
  #define OMAP2_MCSPI_CHCONF_FORCE	BIT(20)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
81

7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
82
83
84
  #define OMAP2_MCSPI_CHSTAT_RXS		BIT(0)
  #define OMAP2_MCSPI_CHSTAT_TXS		BIT(1)
  #define OMAP2_MCSPI_CHSTAT_EOT		BIT(2)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
85

7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
86
  #define OMAP2_MCSPI_CHCTRL_EN		BIT(0)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
87

7a8fa725b   Jouni Hogander   spi: omap2_mcspi ...
88
  #define OMAP2_MCSPI_WAKEUPENABLE_WKEN	BIT(0)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
  
  /* We have 2 DMA channels per CS, one for RX and one for TX */
  struct omap2_mcspi_dma {
  	int dma_tx_channel;
  	int dma_rx_channel;
  
  	int dma_tx_sync_dev;
  	int dma_rx_sync_dev;
  
  	struct completion dma_tx_completion;
  	struct completion dma_rx_completion;
  };
  
  /* use PIO for small transfers, avoiding DMA setup/teardown overhead and
   * cache operations; better heuristics consider wordsize and bitrate.
   */
8b66c1347   Roman Tereshonkov   spi/omap2_mcspi: ...
105
  #define DMA_MIN_BYTES			160
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
106
107
108
109
110
111
112
113
  
  
  struct omap2_mcspi {
  	struct work_struct	work;
  	/* lock protects queue and registers */
  	spinlock_t		lock;
  	struct list_head	msg_queue;
  	struct spi_master	*master;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
114
115
  	/* Virtual base address of the controller */
  	void __iomem		*base;
e5480b739   Russell King   [ARM] omap: remov...
116
  	unsigned long		phys;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
117
118
  	/* SPI1 has 4 channels, while SPI2 has 2 */
  	struct omap2_mcspi_dma	*dma_channels;
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
119
  	struct  device		*dev;
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
120
  	struct workqueue_struct *wq;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
121
122
123
124
  };
  
  struct omap2_mcspi_cs {
  	void __iomem		*base;
e5480b739   Russell King   [ARM] omap: remov...
125
  	unsigned long		phys;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
126
  	int			word_len;
89c05372d   Tero Kristo   spi: McSPI saves ...
127
  	struct list_head	node;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
128
129
130
131
132
133
134
135
  	/* Context save and restore shadow register */
  	u32			chconf0;
  };
  
  /* used for context save and restore, structure members to be updated whenever
   * corresponding registers are modified.
   */
  struct omap2_mcspi_regs {
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
136
137
  	u32 modulctrl;
  	u32 wakeupenable;
89c05372d   Tero Kristo   spi: McSPI saves ...
138
  	struct list_head cs;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
139
  };
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
140
  static struct omap2_mcspi_regs omap2_mcspi_ctx[OMAP2_MCSPI_MAX_CTRL];
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
  #define MOD_REG_BIT(val, mask, set) do { \
  	if (set) \
  		val |= mask; \
  	else \
  		val &= ~mask; \
  } while (0)
  
  static inline void mcspi_write_reg(struct spi_master *master,
  		int idx, u32 val)
  {
  	struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
  
  	__raw_writel(val, mcspi->base + idx);
  }
  
  static inline u32 mcspi_read_reg(struct spi_master *master, int idx)
  {
  	struct omap2_mcspi *mcspi = spi_master_get_devdata(master);
  
  	return __raw_readl(mcspi->base + idx);
  }
  
  static inline void mcspi_write_cs_reg(const struct spi_device *spi,
  		int idx, u32 val)
  {
  	struct omap2_mcspi_cs	*cs = spi->controller_state;
  
  	__raw_writel(val, cs->base +  idx);
  }
  
  static inline u32 mcspi_read_cs_reg(const struct spi_device *spi, int idx)
  {
  	struct omap2_mcspi_cs	*cs = spi->controller_state;
  
  	return __raw_readl(cs->base + idx);
  }
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
177
178
179
180
181
182
183
184
185
186
187
188
189
  static inline u32 mcspi_cached_chconf0(const struct spi_device *spi)
  {
  	struct omap2_mcspi_cs *cs = spi->controller_state;
  
  	return cs->chconf0;
  }
  
  static inline void mcspi_write_chconf0(const struct spi_device *spi, u32 val)
  {
  	struct omap2_mcspi_cs *cs = spi->controller_state;
  
  	cs->chconf0 = val;
  	mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCONF0, val);
a330ce200   Roman Tereshonkov   omap2_mcspi: Flus...
190
  	mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCONF0);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
191
  }
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
192
193
194
195
  static void omap2_mcspi_set_dma_req(const struct spi_device *spi,
  		int is_read, int enable)
  {
  	u32 l, rw;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
196
  	l = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
197
198
199
200
201
202
203
  
  	if (is_read) /* 1 is read, 0 write */
  		rw = OMAP2_MCSPI_CHCONF_DMAR;
  	else
  		rw = OMAP2_MCSPI_CHCONF_DMAW;
  
  	MOD_REG_BIT(l, rw, enable);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
204
  	mcspi_write_chconf0(spi, l);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
205
206
207
208
209
210
211
212
  }
  
  static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable)
  {
  	u32 l;
  
  	l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0;
  	mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l);
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
213
214
  	/* Flash post-writes */
  	mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
215
216
217
218
219
  }
  
  static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active)
  {
  	u32 l;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
220
  	l = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
221
  	MOD_REG_BIT(l, OMAP2_MCSPI_CHCONF_FORCE, cs_active);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
222
  	mcspi_write_chconf0(spi, l);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
223
224
225
226
227
228
229
230
231
232
233
234
235
236
  }
  
  static void omap2_mcspi_set_master_mode(struct spi_master *master)
  {
  	u32 l;
  
  	/* setup when switching from (reset default) slave mode
  	 * to single-channel master mode
  	 */
  	l = mcspi_read_reg(master, OMAP2_MCSPI_MODULCTRL);
  	MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_STEST, 0);
  	MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_MS, 0);
  	MOD_REG_BIT(l, OMAP2_MCSPI_MODULCTRL_SINGLE, 1);
  	mcspi_write_reg(master, OMAP2_MCSPI_MODULCTRL, l);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
237
238
239
240
241
242
243
  
  	omap2_mcspi_ctx[master->bus_num - 1].modulctrl = l;
  }
  
  static void omap2_mcspi_restore_ctx(struct omap2_mcspi *mcspi)
  {
  	struct spi_master *spi_cntrl;
89c05372d   Tero Kristo   spi: McSPI saves ...
244
  	struct omap2_mcspi_cs *cs;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
245
246
247
248
249
  	spi_cntrl = mcspi->master;
  
  	/* McSPI: context restore */
  	mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_MODULCTRL,
  			omap2_mcspi_ctx[spi_cntrl->bus_num - 1].modulctrl);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
250
251
  	mcspi_write_reg(spi_cntrl, OMAP2_MCSPI_WAKEUPENABLE,
  			omap2_mcspi_ctx[spi_cntrl->bus_num - 1].wakeupenable);
89c05372d   Tero Kristo   spi: McSPI saves ...
252
253
254
255
  
  	list_for_each_entry(cs, &omap2_mcspi_ctx[spi_cntrl->bus_num - 1].cs,
  			node)
  		__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
256
257
258
  }
  static void omap2_mcspi_disable_clocks(struct omap2_mcspi *mcspi)
  {
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
259
  	pm_runtime_put_sync(mcspi->dev);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
260
261
262
263
  }
  
  static int omap2_mcspi_enable_clocks(struct omap2_mcspi *mcspi)
  {
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
264
  	return pm_runtime_get_sync(mcspi->dev);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
265
  }
2764c500b   Ilkka Koskinen   spi/omap2_mcspi: ...
266
267
268
269
270
271
272
273
274
275
276
277
  static int mcspi_wait_for_reg_bit(void __iomem *reg, unsigned long bit)
  {
  	unsigned long timeout;
  
  	timeout = jiffies + msecs_to_jiffies(1000);
  	while (!(__raw_readl(reg) & bit)) {
  		if (time_after(jiffies, timeout))
  			return -1;
  		cpu_relax();
  	}
  	return 0;
  }
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
278
279
280
281
282
283
284
285
286
  static unsigned
  omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer)
  {
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_cs	*cs = spi->controller_state;
  	struct omap2_mcspi_dma  *mcspi_dma;
  	unsigned int		count, c;
  	unsigned long		base, tx_reg, rx_reg;
  	int			word_len, data_type, element_count;
8b20c8cb8   Govindraj.R   spi/omap2: fix un...
287
  	int			elements = 0;
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
288
  	u32			l;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
289
290
  	u8			* rx;
  	const u8		* tx;
2764c500b   Ilkka Koskinen   spi/omap2_mcspi: ...
291
  	void __iomem		*chstat_reg;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
292
293
294
  
  	mcspi = spi_master_get_devdata(spi->master);
  	mcspi_dma = &mcspi->dma_channels[spi->chip_select];
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
295
  	l = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
296

2764c500b   Ilkka Koskinen   spi/omap2_mcspi: ...
297
  	chstat_reg = cs->base + OMAP2_MCSPI_CHSTAT0;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
298
299
300
  	count = xfer->len;
  	c = count;
  	word_len = cs->word_len;
e5480b739   Russell King   [ARM] omap: remov...
301
  	base = cs->phys;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
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
  	tx_reg = base + OMAP2_MCSPI_TX0;
  	rx_reg = base + OMAP2_MCSPI_RX0;
  	rx = xfer->rx_buf;
  	tx = xfer->tx_buf;
  
  	if (word_len <= 8) {
  		data_type = OMAP_DMA_DATA_TYPE_S8;
  		element_count = count;
  	} else if (word_len <= 16) {
  		data_type = OMAP_DMA_DATA_TYPE_S16;
  		element_count = count >> 1;
  	} else /* word_len <= 32 */ {
  		data_type = OMAP_DMA_DATA_TYPE_S32;
  		element_count = count >> 2;
  	}
  
  	if (tx != NULL) {
  		omap_set_dma_transfer_params(mcspi_dma->dma_tx_channel,
  				data_type, element_count, 1,
  				OMAP_DMA_SYNC_ELEMENT,
  				mcspi_dma->dma_tx_sync_dev, 0);
  
  		omap_set_dma_dest_params(mcspi_dma->dma_tx_channel, 0,
  				OMAP_DMA_AMODE_CONSTANT,
  				tx_reg, 0, 0);
  
  		omap_set_dma_src_params(mcspi_dma->dma_tx_channel, 0,
  				OMAP_DMA_AMODE_POST_INC,
  				xfer->tx_dma, 0, 0);
  	}
  
  	if (rx != NULL) {
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
334
335
336
  		elements = element_count - 1;
  		if (l & OMAP2_MCSPI_CHCONF_TURBO)
  			elements--;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
337
  		omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel,
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
338
  				data_type, elements, 1,
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
  				OMAP_DMA_SYNC_ELEMENT,
  				mcspi_dma->dma_rx_sync_dev, 1);
  
  		omap_set_dma_src_params(mcspi_dma->dma_rx_channel, 0,
  				OMAP_DMA_AMODE_CONSTANT,
  				rx_reg, 0, 0);
  
  		omap_set_dma_dest_params(mcspi_dma->dma_rx_channel, 0,
  				OMAP_DMA_AMODE_POST_INC,
  				xfer->rx_dma, 0, 0);
  	}
  
  	if (tx != NULL) {
  		omap_start_dma(mcspi_dma->dma_tx_channel);
  		omap2_mcspi_set_dma_req(spi, 0, 1);
  	}
  
  	if (rx != NULL) {
  		omap_start_dma(mcspi_dma->dma_rx_channel);
  		omap2_mcspi_set_dma_req(spi, 1, 1);
  	}
  
  	if (tx != NULL) {
  		wait_for_completion(&mcspi_dma->dma_tx_completion);
07fe03517   Russell King - ARM Linux   spi/omap: Fix DMA...
363
  		dma_unmap_single(&spi->dev, xfer->tx_dma, count, DMA_TO_DEVICE);
2764c500b   Ilkka Koskinen   spi/omap2_mcspi: ...
364
365
366
367
368
369
370
371
372
373
374
375
  
  		/* for TX_ONLY mode, be sure all words have shifted out */
  		if (rx == NULL) {
  			if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_TXS) < 0)
  				dev_err(&spi->dev, "TXS timed out
  ");
  			else if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_EOT) < 0)
  				dev_err(&spi->dev, "EOT timed out
  ");
  		}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
376
377
378
379
  	}
  
  	if (rx != NULL) {
  		wait_for_completion(&mcspi_dma->dma_rx_completion);
07fe03517   Russell King - ARM Linux   spi/omap: Fix DMA...
380
  		dma_unmap_single(&spi->dev, xfer->rx_dma, count, DMA_FROM_DEVICE);
57c5c28db   Eero Nurkkala   spi: omap2_mcspi ...
381
  		omap2_mcspi_set_enable(spi, 0);
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
  
  		if (l & OMAP2_MCSPI_CHCONF_TURBO) {
  
  			if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
  				   & OMAP2_MCSPI_CHSTAT_RXS)) {
  				u32 w;
  
  				w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
  				if (word_len <= 8)
  					((u8 *)xfer->rx_buf)[elements++] = w;
  				else if (word_len <= 16)
  					((u16 *)xfer->rx_buf)[elements++] = w;
  				else /* word_len <= 32 */
  					((u32 *)xfer->rx_buf)[elements++] = w;
  			} else {
  				dev_err(&spi->dev,
  					"DMA RX penultimate word empty");
  				count -= (word_len <= 8)  ? 2 :
  					(word_len <= 16) ? 4 :
  					/* word_len <= 32 */ 8;
  				omap2_mcspi_set_enable(spi, 1);
  				return count;
  			}
  		}
57c5c28db   Eero Nurkkala   spi: omap2_mcspi ...
406
407
408
409
410
411
  		if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0)
  				& OMAP2_MCSPI_CHSTAT_RXS)) {
  			u32 w;
  
  			w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0);
  			if (word_len <= 8)
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
412
  				((u8 *)xfer->rx_buf)[elements] = w;
57c5c28db   Eero Nurkkala   spi: omap2_mcspi ...
413
  			else if (word_len <= 16)
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
414
  				((u16 *)xfer->rx_buf)[elements] = w;
57c5c28db   Eero Nurkkala   spi: omap2_mcspi ...
415
  			else /* word_len <= 32 */
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
416
  				((u32 *)xfer->rx_buf)[elements] = w;
57c5c28db   Eero Nurkkala   spi: omap2_mcspi ...
417
418
419
420
421
422
423
  		} else {
  			dev_err(&spi->dev, "DMA RX last word empty");
  			count -= (word_len <= 8)  ? 1 :
  				 (word_len <= 16) ? 2 :
  			       /* word_len <= 32 */ 4;
  		}
  		omap2_mcspi_set_enable(spi, 1);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
424
425
426
  	}
  	return count;
  }
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
  static unsigned
  omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
  {
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_cs	*cs = spi->controller_state;
  	unsigned int		count, c;
  	u32			l;
  	void __iomem		*base = cs->base;
  	void __iomem		*tx_reg;
  	void __iomem		*rx_reg;
  	void __iomem		*chstat_reg;
  	int			word_len;
  
  	mcspi = spi_master_get_devdata(spi->master);
  	count = xfer->len;
  	c = count;
  	word_len = cs->word_len;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
444
  	l = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
445
446
447
448
449
450
  
  	/* We store the pre-calculated register addresses on stack to speed
  	 * up the transfer loop. */
  	tx_reg		= base + OMAP2_MCSPI_TX0;
  	rx_reg		= base + OMAP2_MCSPI_RX0;
  	chstat_reg	= base + OMAP2_MCSPI_CHSTAT0;
adef658dd   Michael Jones   spi/omap_mcspi: c...
451
452
  	if (c < (word_len>>3))
  		return 0;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
453
454
455
456
457
458
459
460
  	if (word_len <= 8) {
  		u8		*rx;
  		const u8	*tx;
  
  		rx = xfer->rx_buf;
  		tx = xfer->tx_buf;
  
  		do {
feed9bab7   Kalle Valo   spi: omap2_mcspi ...
461
  			c -= 1;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
462
463
464
465
466
467
468
  			if (tx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_TXS) < 0) {
  					dev_err(&spi->dev, "TXS timed out
  ");
  					goto out;
  				}
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
469
470
  				dev_vdbg(&spi->dev, "write-%d %02x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
471
  						word_len, *tx);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
472
473
474
475
476
477
478
479
480
  				__raw_writel(*tx++, tx_reg);
  			}
  			if (rx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  					dev_err(&spi->dev, "RXS timed out
  ");
  					goto out;
  				}
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
481
482
483
484
485
  
  				if (c == 1 && tx == NULL &&
  				    (l & OMAP2_MCSPI_CHCONF_TURBO)) {
  					omap2_mcspi_set_enable(spi, 0);
  					*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
486
487
  					dev_vdbg(&spi->dev, "read-%d %02x
  ",
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
488
  						    word_len, *(rx - 1));
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
489
490
491
492
493
494
495
496
497
498
499
  					if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  						dev_err(&spi->dev,
  							"RXS timed out
  ");
  						goto out;
  					}
  					c = 0;
  				} else if (c == 0 && tx == NULL) {
  					omap2_mcspi_set_enable(spi, 0);
  				}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
500
  				*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
501
502
  				dev_vdbg(&spi->dev, "read-%d %02x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
503
  						word_len, *(rx - 1));
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
504
  			}
95c5c3ab7   Jarkko Nikula   spi/omap_mcspi: F...
505
  		} while (c);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
506
507
508
509
510
511
512
  	} else if (word_len <= 16) {
  		u16		*rx;
  		const u16	*tx;
  
  		rx = xfer->rx_buf;
  		tx = xfer->tx_buf;
  		do {
feed9bab7   Kalle Valo   spi: omap2_mcspi ...
513
  			c -= 2;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
514
515
516
517
518
519
520
  			if (tx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_TXS) < 0) {
  					dev_err(&spi->dev, "TXS timed out
  ");
  					goto out;
  				}
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
521
522
  				dev_vdbg(&spi->dev, "write-%d %04x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
523
  						word_len, *tx);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
524
525
526
527
528
529
530
531
532
  				__raw_writel(*tx++, tx_reg);
  			}
  			if (rx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  					dev_err(&spi->dev, "RXS timed out
  ");
  					goto out;
  				}
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
533
534
535
536
537
  
  				if (c == 2 && tx == NULL &&
  				    (l & OMAP2_MCSPI_CHCONF_TURBO)) {
  					omap2_mcspi_set_enable(spi, 0);
  					*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
538
539
  					dev_vdbg(&spi->dev, "read-%d %04x
  ",
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
540
  						    word_len, *(rx - 1));
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
541
542
543
544
545
546
547
548
549
550
551
  					if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  						dev_err(&spi->dev,
  							"RXS timed out
  ");
  						goto out;
  					}
  					c = 0;
  				} else if (c == 0 && tx == NULL) {
  					omap2_mcspi_set_enable(spi, 0);
  				}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
552
  				*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
553
554
  				dev_vdbg(&spi->dev, "read-%d %04x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
555
  						word_len, *(rx - 1));
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
556
  			}
95c5c3ab7   Jarkko Nikula   spi/omap_mcspi: F...
557
  		} while (c >= 2);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
558
559
560
561
562
563
564
  	} else if (word_len <= 32) {
  		u32		*rx;
  		const u32	*tx;
  
  		rx = xfer->rx_buf;
  		tx = xfer->tx_buf;
  		do {
feed9bab7   Kalle Valo   spi: omap2_mcspi ...
565
  			c -= 4;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
566
567
568
569
570
571
572
  			if (tx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_TXS) < 0) {
  					dev_err(&spi->dev, "TXS timed out
  ");
  					goto out;
  				}
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
573
574
  				dev_vdbg(&spi->dev, "write-%d %08x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
575
  						word_len, *tx);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
576
577
578
579
580
581
582
583
584
  				__raw_writel(*tx++, tx_reg);
  			}
  			if (rx != NULL) {
  				if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  					dev_err(&spi->dev, "RXS timed out
  ");
  					goto out;
  				}
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
585
586
587
588
589
  
  				if (c == 4 && tx == NULL &&
  				    (l & OMAP2_MCSPI_CHCONF_TURBO)) {
  					omap2_mcspi_set_enable(spi, 0);
  					*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
590
591
  					dev_vdbg(&spi->dev, "read-%d %08x
  ",
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
592
  						    word_len, *(rx - 1));
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
593
594
595
596
597
598
599
600
601
602
603
  					if (mcspi_wait_for_reg_bit(chstat_reg,
  						OMAP2_MCSPI_CHSTAT_RXS) < 0) {
  						dev_err(&spi->dev,
  							"RXS timed out
  ");
  						goto out;
  					}
  					c = 0;
  				} else if (c == 0 && tx == NULL) {
  					omap2_mcspi_set_enable(spi, 0);
  				}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
604
  				*rx++ = __raw_readl(rx_reg);
079a176d8   Felipe Balbi   spi: omap2_mcspi:...
605
606
  				dev_vdbg(&spi->dev, "read-%d %08x
  ",
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
607
  						word_len, *(rx - 1));
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
608
  			}
95c5c3ab7   Jarkko Nikula   spi/omap_mcspi: F...
609
  		} while (c >= 4);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
610
611
612
613
614
615
616
617
618
619
620
621
  	}
  
  	/* for TX_ONLY mode, be sure all words have shifted out */
  	if (xfer->rx_buf == NULL) {
  		if (mcspi_wait_for_reg_bit(chstat_reg,
  				OMAP2_MCSPI_CHSTAT_TXS) < 0) {
  			dev_err(&spi->dev, "TXS timed out
  ");
  		} else if (mcspi_wait_for_reg_bit(chstat_reg,
  				OMAP2_MCSPI_CHSTAT_EOT) < 0)
  			dev_err(&spi->dev, "EOT timed out
  ");
e1993ed64   Jason Wang   spi/omap2_mcspi: ...
622
623
624
625
626
627
  
  		/* disable chan to purge rx datas received in TX_ONLY transfer,
  		 * otherwise these rx datas will affect the direct following
  		 * RX_ONLY transfer.
  		 */
  		omap2_mcspi_set_enable(spi, 0);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
628
629
  	}
  out:
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
630
  	omap2_mcspi_set_enable(spi, 1);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
631
632
  	return count - c;
  }
57d9c10dd   Hannu Heikkinen   spi/omap_mcspi: O...
633
634
635
636
637
638
639
640
641
642
  static u32 omap2_mcspi_calc_divisor(u32 speed_hz)
  {
  	u32 div;
  
  	for (div = 0; div < 15; div++)
  		if (speed_hz >= (OMAP2_MCSPI_MAX_FREQ >> div))
  			return div;
  
  	return 15;
  }
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
643
644
645
646
647
648
  /* called only when no transfer is active to this device */
  static int omap2_mcspi_setup_transfer(struct spi_device *spi,
  		struct spi_transfer *t)
  {
  	struct omap2_mcspi_cs *cs = spi->controller_state;
  	struct omap2_mcspi *mcspi;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
649
  	struct spi_master *spi_cntrl;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
650
651
  	u32 l = 0, div = 0;
  	u8 word_len = spi->bits_per_word;
9bd4517dd   Scott Ellis   spi/omap2_mcspi: ...
652
  	u32 speed_hz = spi->max_speed_hz;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
653
654
  
  	mcspi = spi_master_get_devdata(spi->master);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
655
  	spi_cntrl = mcspi->master;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
656
657
658
659
660
  
  	if (t != NULL && t->bits_per_word)
  		word_len = t->bits_per_word;
  
  	cs->word_len = word_len;
9bd4517dd   Scott Ellis   spi/omap2_mcspi: ...
661
662
  	if (t && t->speed_hz)
  		speed_hz = t->speed_hz;
57d9c10dd   Hannu Heikkinen   spi/omap_mcspi: O...
663
664
  	speed_hz = min_t(u32, speed_hz, OMAP2_MCSPI_MAX_FREQ);
  	div = omap2_mcspi_calc_divisor(speed_hz);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
665

a41ae1ad9   Hemanth V   spi: McSPI off-mo...
666
  	l = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
  
  	/* standard 4-wire master mode:  SCK, MOSI/out, MISO/in, nCS
  	 * REVISIT: this controller could support SPI_3WIRE mode.
  	 */
  	l &= ~(OMAP2_MCSPI_CHCONF_IS|OMAP2_MCSPI_CHCONF_DPE1);
  	l |= OMAP2_MCSPI_CHCONF_DPE0;
  
  	/* wordlength */
  	l &= ~OMAP2_MCSPI_CHCONF_WL_MASK;
  	l |= (word_len - 1) << 7;
  
  	/* set chipselect polarity; manage with FORCE */
  	if (!(spi->mode & SPI_CS_HIGH))
  		l |= OMAP2_MCSPI_CHCONF_EPOL;	/* active-low; normal */
  	else
  		l &= ~OMAP2_MCSPI_CHCONF_EPOL;
  
  	/* set clock divisor */
  	l &= ~OMAP2_MCSPI_CHCONF_CLKD_MASK;
  	l |= div << 2;
  
  	/* set SPI mode 0..3 */
  	if (spi->mode & SPI_CPOL)
  		l |= OMAP2_MCSPI_CHCONF_POL;
  	else
  		l &= ~OMAP2_MCSPI_CHCONF_POL;
  	if (spi->mode & SPI_CPHA)
  		l |= OMAP2_MCSPI_CHCONF_PHA;
  	else
  		l &= ~OMAP2_MCSPI_CHCONF_PHA;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
697
  	mcspi_write_chconf0(spi, l);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
698
699
700
  
  	dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s
  ",
57d9c10dd   Hannu Heikkinen   spi/omap_mcspi: O...
701
  			OMAP2_MCSPI_MAX_FREQ >> div,
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
  			(spi->mode & SPI_CPHA) ? "trailing" : "leading",
  			(spi->mode & SPI_CPOL) ? "inverted" : "normal");
  
  	return 0;
  }
  
  static void omap2_mcspi_dma_rx_callback(int lch, u16 ch_status, void *data)
  {
  	struct spi_device	*spi = data;
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*mcspi_dma;
  
  	mcspi = spi_master_get_devdata(spi->master);
  	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
  
  	complete(&mcspi_dma->dma_rx_completion);
  
  	/* We must disable the DMA RX request */
  	omap2_mcspi_set_dma_req(spi, 1, 0);
  }
  
  static void omap2_mcspi_dma_tx_callback(int lch, u16 ch_status, void *data)
  {
  	struct spi_device	*spi = data;
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*mcspi_dma;
  
  	mcspi = spi_master_get_devdata(spi->master);
  	mcspi_dma = &(mcspi->dma_channels[spi->chip_select]);
  
  	complete(&mcspi_dma->dma_tx_completion);
  
  	/* We must disable the DMA TX request */
  	omap2_mcspi_set_dma_req(spi, 0, 0);
  }
  
  static int omap2_mcspi_request_dma(struct spi_device *spi)
  {
  	struct spi_master	*master = spi->master;
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*mcspi_dma;
  
  	mcspi = spi_master_get_devdata(master);
  	mcspi_dma = mcspi->dma_channels + spi->chip_select;
  
  	if (omap_request_dma(mcspi_dma->dma_rx_sync_dev, "McSPI RX",
  			omap2_mcspi_dma_rx_callback, spi,
  			&mcspi_dma->dma_rx_channel)) {
  		dev_err(&spi->dev, "no RX DMA channel for McSPI
  ");
  		return -EAGAIN;
  	}
  
  	if (omap_request_dma(mcspi_dma->dma_tx_sync_dev, "McSPI TX",
  			omap2_mcspi_dma_tx_callback, spi,
  			&mcspi_dma->dma_tx_channel)) {
  		omap_free_dma(mcspi_dma->dma_rx_channel);
  		mcspi_dma->dma_rx_channel = -1;
  		dev_err(&spi->dev, "no TX DMA channel for McSPI
  ");
  		return -EAGAIN;
  	}
  
  	init_completion(&mcspi_dma->dma_rx_completion);
  	init_completion(&mcspi_dma->dma_tx_completion);
  
  	return 0;
  }
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
770
771
772
773
774
775
  static int omap2_mcspi_setup(struct spi_device *spi)
  {
  	int			ret;
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*mcspi_dma;
  	struct omap2_mcspi_cs	*cs = spi->controller_state;
7d0771970   David Brownell   spi: move common ...
776
  	if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
777
778
779
780
781
782
783
784
785
786
787
788
789
790
  		dev_dbg(&spi->dev, "setup: unsupported %d bit words
  ",
  			spi->bits_per_word);
  		return -EINVAL;
  	}
  
  	mcspi = spi_master_get_devdata(spi->master);
  	mcspi_dma = &mcspi->dma_channels[spi->chip_select];
  
  	if (!cs) {
  		cs = kzalloc(sizeof *cs, GFP_KERNEL);
  		if (!cs)
  			return -ENOMEM;
  		cs->base = mcspi->base + spi->chip_select * 0x14;
e5480b739   Russell King   [ARM] omap: remov...
791
  		cs->phys = mcspi->phys + spi->chip_select * 0x14;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
792
  		cs->chconf0 = 0;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
793
  		spi->controller_state = cs;
89c05372d   Tero Kristo   spi: McSPI saves ...
794
795
796
  		/* Link this to context save list */
  		list_add_tail(&cs->node,
  			&omap2_mcspi_ctx[mcspi->master->bus_num - 1].cs);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
797
798
799
800
801
802
803
804
  	}
  
  	if (mcspi_dma->dma_rx_channel == -1
  			|| mcspi_dma->dma_tx_channel == -1) {
  		ret = omap2_mcspi_request_dma(spi);
  		if (ret < 0)
  			return ret;
  	}
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
805
806
807
  	ret = omap2_mcspi_enable_clocks(mcspi);
  	if (ret < 0)
  		return ret;
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
808

86eeb6fe7   Kyungmin Park   OMAP2 McSPI code ...
809
  	ret = omap2_mcspi_setup_transfer(spi, NULL);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
810
  	omap2_mcspi_disable_clocks(mcspi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
811
812
813
814
815
816
817
818
  
  	return ret;
  }
  
  static void omap2_mcspi_cleanup(struct spi_device *spi)
  {
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*mcspi_dma;
89c05372d   Tero Kristo   spi: McSPI saves ...
819
  	struct omap2_mcspi_cs	*cs;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
820
821
  
  	mcspi = spi_master_get_devdata(spi->master);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
822

5e7749436   Scott Ellis   spi/omap2_mcspi: ...
823
824
825
826
  	if (spi->controller_state) {
  		/* Unlink controller state from context save list */
  		cs = spi->controller_state;
  		list_del(&cs->node);
89c05372d   Tero Kristo   spi: McSPI saves ...
827

5e7749436   Scott Ellis   spi/omap2_mcspi: ...
828
829
  		kfree(spi->controller_state);
  	}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
830

99f1a43f4   Scott Ellis   spi/omap2_mcspi: ...
831
832
833
834
835
836
837
838
839
840
841
  	if (spi->chip_select < spi->master->num_chipselect) {
  		mcspi_dma = &mcspi->dma_channels[spi->chip_select];
  
  		if (mcspi_dma->dma_rx_channel != -1) {
  			omap_free_dma(mcspi_dma->dma_rx_channel);
  			mcspi_dma->dma_rx_channel = -1;
  		}
  		if (mcspi_dma->dma_tx_channel != -1) {
  			omap_free_dma(mcspi_dma->dma_tx_channel);
  			mcspi_dma->dma_tx_channel = -1;
  		}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
842
843
844
845
846
847
848
849
  	}
  }
  
  static void omap2_mcspi_work(struct work_struct *work)
  {
  	struct omap2_mcspi	*mcspi;
  
  	mcspi = container_of(work, struct omap2_mcspi, work);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
850

1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
851
852
853
854
  	if (omap2_mcspi_enable_clocks(mcspi) < 0)
  		return;
  
  	spin_lock_irq(&mcspi->lock);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
855
856
857
858
859
860
861
862
863
864
865
866
  
  	/* We only enable one channel at a time -- the one whose message is
  	 * at the head of the queue -- although this controller would gladly
  	 * arbitrate among multiple channels.  This corresponds to "single
  	 * channel" master mode.  As a side effect, we need to manage the
  	 * chipselect with the FORCE bit ... CS != channel enable.
  	 */
  	while (!list_empty(&mcspi->msg_queue)) {
  		struct spi_message		*m;
  		struct spi_device		*spi;
  		struct spi_transfer		*t = NULL;
  		int				cs_active = 0;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
867
  		struct omap2_mcspi_cs		*cs;
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
868
  		struct omap2_mcspi_device_config *cd;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
869
870
871
872
873
874
875
876
877
878
879
  		int				par_override = 0;
  		int				status = 0;
  		u32				chconf;
  
  		m = container_of(mcspi->msg_queue.next, struct spi_message,
  				 queue);
  
  		list_del_init(&m->queue);
  		spin_unlock_irq(&mcspi->lock);
  
  		spi = m->spi;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
880
  		cs = spi->controller_state;
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
881
  		cd = spi->controller_data;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
  
  		omap2_mcspi_set_enable(spi, 1);
  		list_for_each_entry(t, &m->transfers, transfer_list) {
  			if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
  				status = -EINVAL;
  				break;
  			}
  			if (par_override || t->speed_hz || t->bits_per_word) {
  				par_override = 1;
  				status = omap2_mcspi_setup_transfer(spi, t);
  				if (status < 0)
  					break;
  				if (!t->speed_hz && !t->bits_per_word)
  					par_override = 0;
  			}
  
  			if (!cs_active) {
  				omap2_mcspi_force_cs(spi, 1);
  				cs_active = 1;
  			}
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
902
  			chconf = mcspi_cached_chconf0(spi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
903
  			chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK;
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
904
  			chconf &= ~OMAP2_MCSPI_CHCONF_TURBO;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
905
906
907
908
  			if (t->tx_buf == NULL)
  				chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY;
  			else if (t->rx_buf == NULL)
  				chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY;
4743a0f88   Roman Tereshonkov   spi/omap2_mcspi: ...
909
910
911
912
913
914
  
  			if (cd && cd->turbo_mode && t->tx_buf == NULL) {
  				/* Turbo mode is for more than one word */
  				if (t->len > ((cs->word_len + 7) >> 3))
  					chconf |= OMAP2_MCSPI_CHCONF_TURBO;
  			}
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
915
  			mcspi_write_chconf0(spi, chconf);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
  
  			if (t->len) {
  				unsigned	count;
  
  				/* RX_ONLY mode needs dummy data in TX reg */
  				if (t->tx_buf == NULL)
  					__raw_writel(0, cs->base
  							+ OMAP2_MCSPI_TX0);
  
  				if (m->is_dma_mapped || t->len >= DMA_MIN_BYTES)
  					count = omap2_mcspi_txrx_dma(spi, t);
  				else
  					count = omap2_mcspi_txrx_pio(spi, t);
  				m->actual_length += count;
  
  				if (count != t->len) {
  					status = -EIO;
  					break;
  				}
  			}
  
  			if (t->delay_usecs)
  				udelay(t->delay_usecs);
  
  			/* ignore the "leave it on after last xfer" hint */
  			if (t->cs_change) {
  				omap2_mcspi_force_cs(spi, 0);
  				cs_active = 0;
  			}
  		}
  
  		/* Restore defaults if they were overriden */
  		if (par_override) {
  			par_override = 0;
  			status = omap2_mcspi_setup_transfer(spi, NULL);
  		}
  
  		if (cs_active)
  			omap2_mcspi_force_cs(spi, 0);
  
  		omap2_mcspi_set_enable(spi, 0);
  
  		m->status = status;
  		m->complete(m->context);
  
  		spin_lock_irq(&mcspi->lock);
  	}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
963
  	spin_unlock_irq(&mcspi->lock);
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
964
965
  
  	omap2_mcspi_disable_clocks(mcspi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
  }
  
  static int omap2_mcspi_transfer(struct spi_device *spi, struct spi_message *m)
  {
  	struct omap2_mcspi	*mcspi;
  	unsigned long		flags;
  	struct spi_transfer	*t;
  
  	m->actual_length = 0;
  	m->status = 0;
  
  	/* reject invalid messages and transfers */
  	if (list_empty(&m->transfers) || !m->complete)
  		return -EINVAL;
  	list_for_each_entry(t, &m->transfers, transfer_list) {
  		const void	*tx_buf = t->tx_buf;
  		void		*rx_buf = t->rx_buf;
  		unsigned	len = t->len;
  
  		if (t->speed_hz > OMAP2_MCSPI_MAX_FREQ
  				|| (len && !(rx_buf || tx_buf))
  				|| (t->bits_per_word &&
  					(  t->bits_per_word < 4
  					|| t->bits_per_word > 32))) {
  			dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw
  ",
  					t->speed_hz,
  					len,
  					tx_buf ? "tx" : "",
  					rx_buf ? "rx" : "",
  					t->bits_per_word);
  			return -EINVAL;
  		}
57d9c10dd   Hannu Heikkinen   spi/omap_mcspi: O...
999
1000
1001
1002
1003
  		if (t->speed_hz && t->speed_hz < (OMAP2_MCSPI_MAX_FREQ >> 15)) {
  			dev_dbg(&spi->dev, "speed_hz %d below minimum %d Hz
  ",
  				t->speed_hz,
  				OMAP2_MCSPI_MAX_FREQ >> 15);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1004
1005
1006
1007
1008
  			return -EINVAL;
  		}
  
  		if (m->is_dma_mapped || len < DMA_MIN_BYTES)
  			continue;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1009
1010
1011
  		if (tx_buf != NULL) {
  			t->tx_dma = dma_map_single(&spi->dev, (void *) tx_buf,
  					len, DMA_TO_DEVICE);
8d8bb39b9   FUJITA Tomonori   dma-mapping: add ...
1012
  			if (dma_mapping_error(&spi->dev, t->tx_dma)) {
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1013
1014
1015
1016
1017
1018
1019
1020
1021
  				dev_dbg(&spi->dev, "dma %cX %d bytes error
  ",
  						'T', len);
  				return -EINVAL;
  			}
  		}
  		if (rx_buf != NULL) {
  			t->rx_dma = dma_map_single(&spi->dev, rx_buf, t->len,
  					DMA_FROM_DEVICE);
8d8bb39b9   FUJITA Tomonori   dma-mapping: add ...
1022
  			if (dma_mapping_error(&spi->dev, t->rx_dma)) {
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1023
1024
1025
1026
  				dev_dbg(&spi->dev, "dma %cX %d bytes error
  ",
  						'R', len);
  				if (tx_buf != NULL)
07fe03517   Russell King - ARM Linux   spi/omap: Fix DMA...
1027
  					dma_unmap_single(&spi->dev, t->tx_dma,
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
  							len, DMA_TO_DEVICE);
  				return -EINVAL;
  			}
  		}
  	}
  
  	mcspi = spi_master_get_devdata(spi->master);
  
  	spin_lock_irqsave(&mcspi->lock, flags);
  	list_add_tail(&m->queue, &mcspi->msg_queue);
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
1038
  	queue_work(mcspi->wq, &mcspi->work);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1039
1040
1041
1042
  	spin_unlock_irqrestore(&mcspi->lock, flags);
  
  	return 0;
  }
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1043
  static int __init omap2_mcspi_master_setup(struct omap2_mcspi *mcspi)
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1044
1045
1046
  {
  	struct spi_master	*master = mcspi->master;
  	u32			tmp;
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1047
  	int ret = 0;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1048

1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1049
1050
1051
  	ret = omap2_mcspi_enable_clocks(mcspi);
  	if (ret < 0)
  		return ret;
ddb22195c   Jouni Hogander   spi: omap2_mcspi ...
1052

a41ae1ad9   Hemanth V   spi: McSPI off-mo...
1053
1054
1055
  	tmp = OMAP2_MCSPI_WAKEUPENABLE_WKEN;
  	mcspi_write_reg(master, OMAP2_MCSPI_WAKEUPENABLE, tmp);
  	omap2_mcspi_ctx[master->bus_num - 1].wakeupenable = tmp;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1056
1057
  
  	omap2_mcspi_set_master_mode(master);
a41ae1ad9   Hemanth V   spi: McSPI off-mo...
1058
  	omap2_mcspi_disable_clocks(mcspi);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1059
1060
  	return 0;
  }
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
  static int omap_mcspi_runtime_resume(struct device *dev)
  {
  	struct omap2_mcspi	*mcspi;
  	struct spi_master	*master;
  
  	master = dev_get_drvdata(dev);
  	mcspi = spi_master_get_devdata(master);
  	omap2_mcspi_restore_ctx(mcspi);
  
  	return 0;
  }
ccc7baed1   Girish   spi: omap2_mcspi ...
1072

ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1073
1074
1075
  static int __init omap2_mcspi_probe(struct platform_device *pdev)
  {
  	struct spi_master	*master;
1a5d81905   Charulatha V   OMAP: devices: Mo...
1076
  	struct omap2_mcspi_platform_config *pdata = pdev->dev.platform_data;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1077
1078
1079
  	struct omap2_mcspi	*mcspi;
  	struct resource		*r;
  	int			status = 0, i;
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
1080
  	char			wq_name[20];
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1081
1082
1083
1084
1085
1086
1087
  
  	master = spi_alloc_master(&pdev->dev, sizeof *mcspi);
  	if (master == NULL) {
  		dev_dbg(&pdev->dev, "master allocation failed
  ");
  		return -ENOMEM;
  	}
e7db06b5d   David Brownell   spi: move more sp...
1088
1089
  	/* the spi->mode bits understood by this driver: */
  	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1090
1091
1092
1093
1094
1095
  	if (pdev->id != -1)
  		master->bus_num = pdev->id;
  
  	master->setup = omap2_mcspi_setup;
  	master->transfer = omap2_mcspi_transfer;
  	master->cleanup = omap2_mcspi_cleanup;
1a5d81905   Charulatha V   OMAP: devices: Mo...
1096
  	master->num_chipselect = pdata->num_cs;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1097
1098
1099
1100
1101
  
  	dev_set_drvdata(&pdev->dev, master);
  
  	mcspi = spi_master_get_devdata(master);
  	mcspi->master = master;
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
1102
1103
1104
1105
  	sprintf(wq_name, "omap2_mcspi/%d", master->bus_num);
  	mcspi->wq = alloc_workqueue(wq_name, WQ_MEM_RECLAIM, 1);
  	if (mcspi->wq == NULL) {
  		status = -ENOMEM;
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1106
  		goto free_master;
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
1107
  	}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1108
1109
1110
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (r == NULL) {
  		status = -ENODEV;
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1111
  		goto free_master;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1112
  	}
1458d160d   Shubhrajyoti D   OMAP: SPI: Fix th...
1113
1114
1115
1116
  
  	r->start += pdata->regs_offset;
  	r->end += pdata->regs_offset;
  	mcspi->phys = r->start;
8e2943c04   Joe Perches   spi: Convert uses...
1117
1118
  	if (!request_mem_region(r->start, resource_size(r),
  				dev_name(&pdev->dev))) {
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1119
  		status = -EBUSY;
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1120
  		goto free_master;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1121
  	}
8e2943c04   Joe Perches   spi: Convert uses...
1122
  	mcspi->base = ioremap(r->start, resource_size(r));
55c381e48   Russell King   [ARM] omap: conve...
1123
1124
1125
1126
  	if (!mcspi->base) {
  		dev_dbg(&pdev->dev, "can't ioremap MCSPI
  ");
  		status = -ENOMEM;
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1127
  		goto release_region;
55c381e48   Russell King   [ARM] omap: conve...
1128
  	}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1129

1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1130
  	mcspi->dev = &pdev->dev;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1131
1132
1133
1134
  	INIT_WORK(&mcspi->work, omap2_mcspi_work);
  
  	spin_lock_init(&mcspi->lock);
  	INIT_LIST_HEAD(&mcspi->msg_queue);
89c05372d   Tero Kristo   spi: McSPI saves ...
1135
  	INIT_LIST_HEAD(&omap2_mcspi_ctx[master->bus_num - 1].cs);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1136

ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1137
1138
1139
1140
1141
  	mcspi->dma_channels = kcalloc(master->num_chipselect,
  			sizeof(struct omap2_mcspi_dma),
  			GFP_KERNEL);
  
  	if (mcspi->dma_channels == NULL)
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1142
  		goto unmap_io;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1143

1a5d81905   Charulatha V   OMAP: devices: Mo...
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
  	for (i = 0; i < master->num_chipselect; i++) {
  		char dma_ch_name[14];
  		struct resource *dma_res;
  
  		sprintf(dma_ch_name, "rx%d", i);
  		dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
  							dma_ch_name);
  		if (!dma_res) {
  			dev_dbg(&pdev->dev, "cannot get DMA RX channel
  ");
  			status = -ENODEV;
  			break;
  		}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1157
  		mcspi->dma_channels[i].dma_rx_channel = -1;
1a5d81905   Charulatha V   OMAP: devices: Mo...
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
  		mcspi->dma_channels[i].dma_rx_sync_dev = dma_res->start;
  		sprintf(dma_ch_name, "tx%d", i);
  		dma_res = platform_get_resource_byname(pdev, IORESOURCE_DMA,
  							dma_ch_name);
  		if (!dma_res) {
  			dev_dbg(&pdev->dev, "cannot get DMA TX channel
  ");
  			status = -ENODEV;
  			break;
  		}
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1168
  		mcspi->dma_channels[i].dma_tx_channel = -1;
1a5d81905   Charulatha V   OMAP: devices: Mo...
1169
  		mcspi->dma_channels[i].dma_tx_sync_dev = dma_res->start;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1170
  	}
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1171
1172
  	if (status < 0)
  		goto dma_chnl_free;
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1173
1174
1175
  	pm_runtime_enable(&pdev->dev);
  
  	if (status || omap2_mcspi_master_setup(mcspi) < 0)
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1176
  		goto disable_pm;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1177
1178
1179
  
  	status = spi_register_master(master);
  	if (status < 0)
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1180
  		goto err_spi_register;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1181
1182
  
  	return status;
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1183
  err_spi_register:
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1184
  	spi_master_put(master);
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1185
  disable_pm:
751c925cb   Shubhrajyoti D   spi/omap: call pm...
1186
  	pm_runtime_disable(&pdev->dev);
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1187
  dma_chnl_free:
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1188
  	kfree(mcspi->dma_channels);
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1189
  unmap_io:
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1190
  	iounmap(mcspi->base);
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1191
1192
1193
1194
1195
  release_region:
  	release_mem_region(r->start, resource_size(r));
  free_master:
  	kfree(master);
  	platform_set_drvdata(pdev, NULL);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1196
1197
1198
1199
1200
1201
1202
1203
1204
  	return status;
  }
  
  static int __exit omap2_mcspi_remove(struct platform_device *pdev)
  {
  	struct spi_master	*master;
  	struct omap2_mcspi	*mcspi;
  	struct omap2_mcspi_dma	*dma_channels;
  	struct resource		*r;
55c381e48   Russell King   [ARM] omap: conve...
1205
  	void __iomem *base;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1206
1207
1208
1209
  
  	master = dev_get_drvdata(&pdev->dev);
  	mcspi = spi_master_get_devdata(master);
  	dma_channels = mcspi->dma_channels;
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1210
  	omap2_mcspi_disable_clocks(mcspi);
751c925cb   Shubhrajyoti D   spi/omap: call pm...
1211
  	pm_runtime_disable(&pdev->dev);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1212
  	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
8e2943c04   Joe Perches   spi: Convert uses...
1213
  	release_mem_region(r->start, resource_size(r));
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1214

55c381e48   Russell King   [ARM] omap: conve...
1215
  	base = mcspi->base;
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1216
  	spi_unregister_master(master);
55c381e48   Russell King   [ARM] omap: conve...
1217
  	iounmap(base);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1218
  	kfree(dma_channels);
2856ac13b   Shubhrajyoti D   spi/omap: Use a w...
1219
  	destroy_workqueue(mcspi->wq);
39f1b5659   Shubhrajyoti D   spi/omap: Correct...
1220
  	platform_set_drvdata(pdev, NULL);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1221
1222
1223
  
  	return 0;
  }
7e38c3c44   Kay Sievers   spi: fix platform...
1224
1225
  /* work with hotplug and coldplug */
  MODULE_ALIAS("platform:omap2_mcspi");
42ce7fd63   Gregory CLEMENT   spi/omap2_mcspi.c...
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
  #ifdef	CONFIG_SUSPEND
  /*
   * When SPI wake up from off-mode, CS is in activate state. If it was in
   * unactive state when driver was suspend, then force it to unactive state at
   * wake up.
   */
  static int omap2_mcspi_resume(struct device *dev)
  {
  	struct spi_master	*master = dev_get_drvdata(dev);
  	struct omap2_mcspi	*mcspi = spi_master_get_devdata(master);
  	struct omap2_mcspi_cs *cs;
  
  	omap2_mcspi_enable_clocks(mcspi);
  	list_for_each_entry(cs, &omap2_mcspi_ctx[master->bus_num - 1].cs,
  			    node) {
  		if ((cs->chconf0 & OMAP2_MCSPI_CHCONF_FORCE) == 0) {
  
  			/*
  			 * We need to toggle CS state for OMAP take this
  			 * change in account.
  			 */
  			MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 1);
  			__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
  			MOD_REG_BIT(cs->chconf0, OMAP2_MCSPI_CHCONF_FORCE, 0);
  			__raw_writel(cs->chconf0, cs->base + OMAP2_MCSPI_CHCONF0);
  		}
  	}
  	omap2_mcspi_disable_clocks(mcspi);
  	return 0;
  }
  #else
  #define	omap2_mcspi_resume	NULL
  #endif
  
  static const struct dev_pm_ops omap2_mcspi_pm_ops = {
  	.resume = omap2_mcspi_resume,
1f1a4384b   Govindraj.R   OMAP: runtime: Mc...
1262
  	.runtime_resume	= omap_mcspi_runtime_resume,
42ce7fd63   Gregory CLEMENT   spi/omap2_mcspi.c...
1263
  };
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1264
1265
1266
1267
  static struct platform_driver omap2_mcspi_driver = {
  	.driver = {
  		.name =		"omap2_mcspi",
  		.owner =	THIS_MODULE,
42ce7fd63   Gregory CLEMENT   spi/omap2_mcspi.c...
1268
  		.pm =		&omap2_mcspi_pm_ops
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1269
1270
1271
1272
1273
1274
1275
  	},
  	.remove =	__exit_p(omap2_mcspi_remove),
  };
  
  
  static int __init omap2_mcspi_init(void)
  {
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1276
1277
1278
1279
1280
1281
1282
  	return platform_driver_probe(&omap2_mcspi_driver, omap2_mcspi_probe);
  }
  subsys_initcall(omap2_mcspi_init);
  
  static void __exit omap2_mcspi_exit(void)
  {
  	platform_driver_unregister(&omap2_mcspi_driver);
ccdc7bf92   Samuel Ortiz   SPI: omap2_mcspi ...
1283
1284
1285
1286
  }
  module_exit(omap2_mcspi_exit);
  
  MODULE_LICENSE("GPL");