Blame view

drivers/spi/spi-fsl-lpspi.c 16.1 KB
5314987de   Gao Pan   spi: imx: add lps...
1
2
3
4
5
6
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
  /*
   * Freescale i.MX7ULP LPSPI driver
   *
   * Copyright 2016 Freescale Semiconductor, Inc.
   *
   * 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.
   *
   */
  
  #include <linux/clk.h>
  #include <linux/completion.h>
  #include <linux/delay.h>
  #include <linux/err.h>
  #include <linux/interrupt.h>
  #include <linux/io.h>
  #include <linux/irq.h>
  #include <linux/kernel.h>
  #include <linux/module.h>
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/platform_device.h>
  #include <linux/slab.h>
  #include <linux/spi/spi.h>
  #include <linux/spi/spi_bitbang.h>
  #include <linux/types.h>
532f2ba5c   Han Xu   MLK-16785: spi: l...
34
  #include <linux/pm_runtime.h>
5314987de   Gao Pan   spi: imx: add lps...
35
36
  
  #define DRIVER_NAME "fsl_lpspi"
532f2ba5c   Han Xu   MLK-16785: spi: l...
37
  #define FSL_LPSPI_RPM_TIMEOUT 50 /* 50ms */
5314987de   Gao Pan   spi: imx: add lps...
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  /* i.MX7ULP LPSPI registers */
  #define IMX7ULP_VERID	0x0
  #define IMX7ULP_PARAM	0x4
  #define IMX7ULP_CR	0x10
  #define IMX7ULP_SR	0x14
  #define IMX7ULP_IER	0x18
  #define IMX7ULP_DER	0x1c
  #define IMX7ULP_CFGR0	0x20
  #define IMX7ULP_CFGR1	0x24
  #define IMX7ULP_DMR0	0x30
  #define IMX7ULP_DMR1	0x34
  #define IMX7ULP_CCR	0x40
  #define IMX7ULP_FCR	0x58
  #define IMX7ULP_FSR	0x5c
  #define IMX7ULP_TCR	0x60
  #define IMX7ULP_TDR	0x64
  #define IMX7ULP_RSR	0x70
  #define IMX7ULP_RDR	0x74
  
  /* General control register field define */
  #define CR_RRF		BIT(9)
  #define CR_RTF		BIT(8)
  #define CR_RST		BIT(1)
  #define CR_MEN		BIT(0)
  #define SR_TCF		BIT(10)
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
63
  #define SR_FCF		BIT(9)
5314987de   Gao Pan   spi: imx: add lps...
64
65
66
  #define SR_RDF		BIT(1)
  #define SR_TDF		BIT(0)
  #define IER_TCIE	BIT(10)
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
67
  #define IER_FCIE	BIT(9)
5314987de   Gao Pan   spi: imx: add lps...
68
69
70
  #define IER_RDIE	BIT(1)
  #define IER_TDIE	BIT(0)
  #define CFGR1_PCSCFG	BIT(27)
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
71
  #define CFGR1_PINCFG	(BIT(24)|BIT(25))
5314987de   Gao Pan   spi: imx: add lps...
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
  #define CFGR1_PCSPOL	BIT(8)
  #define CFGR1_NOSTALL	BIT(3)
  #define CFGR1_MASTER	BIT(0)
  #define RSR_RXEMPTY	BIT(1)
  #define TCR_CPOL	BIT(31)
  #define TCR_CPHA	BIT(30)
  #define TCR_CONT	BIT(21)
  #define TCR_CONTC	BIT(20)
  #define TCR_RXMSK	BIT(19)
  #define TCR_TXMSK	BIT(18)
  
  static int clkdivs[] = {1, 2, 4, 8, 16, 32, 64, 128};
  
  struct lpspi_config {
  	u8 bpw;
  	u8 chip_select;
  	u8 prescale;
  	u16 mode;
  	u32 speed_hz;
  };
  
  struct fsl_lpspi_data {
  	struct device *dev;
  	void __iomem *base;
cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
96
97
  	struct clk *clk_ipg;
  	struct clk *clk_per;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
98
  	bool is_slave;
5314987de   Gao Pan   spi: imx: add lps...
99
100
101
102
103
104
105
  
  	void *rx_buf;
  	const void *tx_buf;
  	void (*tx)(struct fsl_lpspi_data *);
  	void (*rx)(struct fsl_lpspi_data *);
  
  	u32 remain;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
106
  	u8 watermark;
5314987de   Gao Pan   spi: imx: add lps...
107
108
109
110
111
  	u8 txfifosize;
  	u8 rxfifosize;
  
  	struct lpspi_config config;
  	struct completion xfer_done;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
112
113
  
  	bool slave_aborted;
5314987de   Gao Pan   spi: imx: add lps...
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
  };
  
  static const struct of_device_id fsl_lpspi_dt_ids[] = {
  	{ .compatible = "fsl,imx7ulp-spi", },
  	{ /* sentinel */ }
  };
  MODULE_DEVICE_TABLE(of, fsl_lpspi_dt_ids);
  
  #define LPSPI_BUF_RX(type)						\
  static void fsl_lpspi_buf_rx_##type(struct fsl_lpspi_data *fsl_lpspi)	\
  {									\
  	unsigned int val = readl(fsl_lpspi->base + IMX7ULP_RDR);	\
  									\
  	if (fsl_lpspi->rx_buf) {					\
  		*(type *)fsl_lpspi->rx_buf = val;			\
  		fsl_lpspi->rx_buf += sizeof(type);                      \
  	}								\
  }
  
  #define LPSPI_BUF_TX(type)						\
  static void fsl_lpspi_buf_tx_##type(struct fsl_lpspi_data *fsl_lpspi)	\
  {									\
  	type val = 0;							\
  									\
  	if (fsl_lpspi->tx_buf) {					\
  		val = *(type *)fsl_lpspi->tx_buf;			\
  		fsl_lpspi->tx_buf += sizeof(type);			\
  	}								\
  									\
  	fsl_lpspi->remain -= sizeof(type);				\
  	writel(val, fsl_lpspi->base + IMX7ULP_TDR);			\
  }
  
  LPSPI_BUF_RX(u8)
  LPSPI_BUF_TX(u8)
  LPSPI_BUF_RX(u16)
  LPSPI_BUF_TX(u16)
  LPSPI_BUF_RX(u32)
  LPSPI_BUF_TX(u32)
  
  static void fsl_lpspi_intctrl(struct fsl_lpspi_data *fsl_lpspi,
  			      unsigned int enable)
  {
  	writel(enable, fsl_lpspi->base + IMX7ULP_IER);
  }
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
159
  static int lpspi_prepare_xfer_hardware(struct spi_controller *controller)
5314987de   Gao Pan   spi: imx: add lps...
160
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
161
162
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
163
  	int ret;
532f2ba5c   Han Xu   MLK-16785: spi: l...
164
165
166
167
  	ret = pm_runtime_get_sync(fsl_lpspi->dev);
  	if (ret < 0) {
  		dev_err(fsl_lpspi->dev, "failed to enable clock
  ");
cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
168
169
  		return ret;
  	}
5314987de   Gao Pan   spi: imx: add lps...
170

cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
171
  	return 0;
5314987de   Gao Pan   spi: imx: add lps...
172
  }
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
173
  static int lpspi_unprepare_xfer_hardware(struct spi_controller *controller)
5314987de   Gao Pan   spi: imx: add lps...
174
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
175
176
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
5314987de   Gao Pan   spi: imx: add lps...
177

532f2ba5c   Han Xu   MLK-16785: spi: l...
178
179
  	pm_runtime_mark_last_busy(fsl_lpspi->dev);
  	pm_runtime_put_autosuspend(fsl_lpspi->dev);
5314987de   Gao Pan   spi: imx: add lps...
180
181
182
  
  	return 0;
  }
5314987de   Gao Pan   spi: imx: add lps...
183
184
185
  static void fsl_lpspi_write_tx_fifo(struct fsl_lpspi_data *fsl_lpspi)
  {
  	u8 txfifo_cnt;
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
186
  	u32 temp;
5314987de   Gao Pan   spi: imx: add lps...
187
188
  
  	txfifo_cnt = readl(fsl_lpspi->base + IMX7ULP_FSR) & 0xff;
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
189
190
191
192
  	while (txfifo_cnt < fsl_lpspi->txfifosize) {
  		if (!fsl_lpspi->remain)
  			break;
  		fsl_lpspi->tx(fsl_lpspi);
5314987de   Gao Pan   spi: imx: add lps...
193
194
  		txfifo_cnt++;
  	}
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
195
196
197
198
199
200
201
202
203
204
205
  
  	if (txfifo_cnt < fsl_lpspi->txfifosize) {
  		if (!fsl_lpspi->is_slave) {
  			temp = readl(fsl_lpspi->base + IMX7ULP_TCR);
  			temp &= ~TCR_CONTC;
  			writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
  		}
  
  		fsl_lpspi_intctrl(fsl_lpspi, IER_FCIE);
  	} else
  		fsl_lpspi_intctrl(fsl_lpspi, IER_TDIE);
5314987de   Gao Pan   spi: imx: add lps...
206
207
208
209
210
211
212
213
214
215
216
217
  }
  
  static void fsl_lpspi_read_rx_fifo(struct fsl_lpspi_data *fsl_lpspi)
  {
  	while (!(readl(fsl_lpspi->base + IMX7ULP_RSR) & RSR_RXEMPTY))
  		fsl_lpspi->rx(fsl_lpspi);
  }
  
  static void fsl_lpspi_set_cmd(struct fsl_lpspi_data *fsl_lpspi,
  			      bool is_first_xfer)
  {
  	u32 temp = 0;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
  	if (!fsl_lpspi->is_slave) {
  		temp |= fsl_lpspi->config.bpw - 1;
  		temp |= fsl_lpspi->config.prescale << 27;
  		temp |= (fsl_lpspi->config.mode & 0x3) << 30;
  		temp |= (fsl_lpspi->config.chip_select & 0x3) << 24;
  
  		/*
  		 * Set TCR_CONT will keep SS asserted after current transfer.
  		 * For the first transfer, clear TCR_CONTC to assert SS.
  		 * For subsequent transfer, set TCR_CONTC to keep SS asserted.
  		 */
  		temp |= TCR_CONT;
  		if (is_first_xfer)
  			temp &= ~TCR_CONTC;
  		else
  			temp |= TCR_CONTC;
  	} else {
  		temp |= fsl_lpspi->config.bpw - 1;
  		temp |= (fsl_lpspi->config.mode & 0x3) << 30;
  	}
5314987de   Gao Pan   spi: imx: add lps...
238
239
240
241
242
243
244
245
  	writel(temp, fsl_lpspi->base + IMX7ULP_TCR);
  
  	dev_dbg(fsl_lpspi->dev, "TCR=0x%x
  ", temp);
  }
  
  static void fsl_lpspi_set_watermark(struct fsl_lpspi_data *fsl_lpspi)
  {
5314987de   Gao Pan   spi: imx: add lps...
246
  	u32 temp;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
247
  	temp = fsl_lpspi->watermark >> 1 | (fsl_lpspi->watermark >> 1) << 16;
5314987de   Gao Pan   spi: imx: add lps...
248
249
250
251
252
253
254
255
256
257
258
259
  
  	writel(temp, fsl_lpspi->base + IMX7ULP_FCR);
  
  	dev_dbg(fsl_lpspi->dev, "FCR=0x%x
  ", temp);
  }
  
  static int fsl_lpspi_set_bitrate(struct fsl_lpspi_data *fsl_lpspi)
  {
  	struct lpspi_config config = fsl_lpspi->config;
  	unsigned int perclk_rate, scldiv;
  	u8 prescale;
cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
260
  	perclk_rate = clk_get_rate(fsl_lpspi->clk_per);
5314987de   Gao Pan   spi: imx: add lps...
261
262
263
264
265
266
267
268
269
270
271
  	for (prescale = 0; prescale < 8; prescale++) {
  		scldiv = perclk_rate /
  			 (clkdivs[prescale] * config.speed_hz) - 2;
  		if (scldiv < 256) {
  			fsl_lpspi->config.prescale = prescale;
  			break;
  		}
  	}
  
  	if (prescale == 8 && scldiv >= 256)
  		return -EINVAL;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
272
273
  	writel(scldiv | (scldiv << 8) | ((scldiv >> 1) << 16),
  					fsl_lpspi->base + IMX7ULP_CCR);
5314987de   Gao Pan   spi: imx: add lps...
274
275
276
277
278
279
280
281
282
283
284
285
  
  	dev_dbg(fsl_lpspi->dev, "perclk=%d, speed=%d, prescale =%d, scldiv=%d
  ",
  		perclk_rate, config.speed_hz, prescale, scldiv);
  
  	return 0;
  }
  
  static int fsl_lpspi_config(struct fsl_lpspi_data *fsl_lpspi)
  {
  	u32 temp;
  	int ret;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
286
287
288
289
290
  	if (!fsl_lpspi->is_slave) {
  		ret = fsl_lpspi_set_bitrate(fsl_lpspi);
  		if (ret)
  			return ret;
  	}
5314987de   Gao Pan   spi: imx: add lps...
291
292
  
  	fsl_lpspi_set_watermark(fsl_lpspi);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
293
294
295
296
  	if (!fsl_lpspi->is_slave)
  		temp = CFGR1_MASTER;
  	else
  		temp = CFGR1_PINCFG;
5314987de   Gao Pan   spi: imx: add lps...
297
298
  	if (fsl_lpspi->config.mode & SPI_CS_HIGH)
  		temp |= CFGR1_PCSPOL;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
299

5314987de   Gao Pan   spi: imx: add lps...
300
301
302
303
304
305
306
307
308
309
310
311
  	writel(temp, fsl_lpspi->base + IMX7ULP_CFGR1);
  
  	temp = readl(fsl_lpspi->base + IMX7ULP_CR);
  	temp |= CR_RRF | CR_RTF | CR_MEN;
  	writel(temp, fsl_lpspi->base + IMX7ULP_CR);
  
  	return 0;
  }
  
  static void fsl_lpspi_setup_transfer(struct spi_device *spi,
  				     struct spi_transfer *t)
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
312
313
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(spi->controller);
5314987de   Gao Pan   spi: imx: add lps...
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
  
  	fsl_lpspi->config.mode = spi->mode;
  	fsl_lpspi->config.bpw = t ? t->bits_per_word : spi->bits_per_word;
  	fsl_lpspi->config.speed_hz = t ? t->speed_hz : spi->max_speed_hz;
  	fsl_lpspi->config.chip_select = spi->chip_select;
  
  	if (!fsl_lpspi->config.speed_hz)
  		fsl_lpspi->config.speed_hz = spi->max_speed_hz;
  	if (!fsl_lpspi->config.bpw)
  		fsl_lpspi->config.bpw = spi->bits_per_word;
  
  	/* Initialize the functions for transfer */
  	if (fsl_lpspi->config.bpw <= 8) {
  		fsl_lpspi->rx = fsl_lpspi_buf_rx_u8;
  		fsl_lpspi->tx = fsl_lpspi_buf_tx_u8;
  	} else if (fsl_lpspi->config.bpw <= 16) {
  		fsl_lpspi->rx = fsl_lpspi_buf_rx_u16;
  		fsl_lpspi->tx = fsl_lpspi_buf_tx_u16;
  	} else {
  		fsl_lpspi->rx = fsl_lpspi_buf_rx_u32;
  		fsl_lpspi->tx = fsl_lpspi_buf_tx_u32;
  	}
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
336
337
338
339
  	if (t->len <= fsl_lpspi->txfifosize)
  		fsl_lpspi->watermark = t->len;
  	else
  		fsl_lpspi->watermark = fsl_lpspi->txfifosize;
5314987de   Gao Pan   spi: imx: add lps...
340
341
  	fsl_lpspi_config(fsl_lpspi);
  }
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
  static int fsl_lpspi_slave_abort(struct spi_controller *controller)
  {
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
  
  	fsl_lpspi->slave_aborted = true;
  	complete(&fsl_lpspi->xfer_done);
  	return 0;
  }
  
  static int fsl_lpspi_wait_for_completion(struct spi_controller *controller)
  {
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
  
  	if (fsl_lpspi->is_slave) {
  		if (wait_for_completion_interruptible(&fsl_lpspi->xfer_done) ||
  			fsl_lpspi->slave_aborted) {
  			dev_dbg(fsl_lpspi->dev, "interrupted
  ");
  			return -EINTR;
  		}
  	} else {
  		if (!wait_for_completion_timeout(&fsl_lpspi->xfer_done, HZ)) {
  			dev_dbg(fsl_lpspi->dev, "wait for completion timeout
  ");
  			return -ETIMEDOUT;
  		}
  	}
  
  	return 0;
  }
a765a3a0a   Xiaoning Wang   MLK-19899 spi: lp...
374
375
376
377
378
379
380
381
382
383
384
385
386
387
  static int fsl_lpspi_reset(struct fsl_lpspi_data *fsl_lpspi)
  {
  	u32 temp;
  
  	/* W1C for all flags in SR */
  	temp = 0x3F << 8;
  	writel(temp, fsl_lpspi->base + IMX7ULP_SR);
  
  	/* Clear FIFO and disable module */
  	temp = CR_RRF | CR_RTF;
  	writel(temp, fsl_lpspi->base + IMX7ULP_CR);
  
  	return 0;
  }
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
388
  static int fsl_lpspi_transfer_one(struct spi_controller *controller,
5314987de   Gao Pan   spi: imx: add lps...
389
390
391
  				  struct spi_device *spi,
  				  struct spi_transfer *t)
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
392
393
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
5314987de   Gao Pan   spi: imx: add lps...
394
395
396
397
398
399
400
  	int ret;
  
  	fsl_lpspi->tx_buf = t->tx_buf;
  	fsl_lpspi->rx_buf = t->rx_buf;
  	fsl_lpspi->remain = t->len;
  
  	reinit_completion(&fsl_lpspi->xfer_done);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
401
  	fsl_lpspi->slave_aborted = false;
5314987de   Gao Pan   spi: imx: add lps...
402
  	fsl_lpspi_write_tx_fifo(fsl_lpspi);
d2ad0a62d   Gao Pan   spi: fsl-lpspi: u...
403

1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
404
405
406
  	ret = fsl_lpspi_wait_for_completion(controller);
  	if (ret)
  		return ret;
5314987de   Gao Pan   spi: imx: add lps...
407

a765a3a0a   Xiaoning Wang   MLK-19899 spi: lp...
408
  	fsl_lpspi_reset(fsl_lpspi);
d989eed20   Gao Pan   spi: fsl-lpspi: q...
409
  	return 0;
5314987de   Gao Pan   spi: imx: add lps...
410
  }
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
411
  static int fsl_lpspi_transfer_one_msg(struct spi_controller *controller,
5314987de   Gao Pan   spi: imx: add lps...
412
413
  				      struct spi_message *msg)
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
414
415
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
5314987de   Gao Pan   spi: imx: add lps...
416
417
418
  	struct spi_device *spi = msg->spi;
  	struct spi_transfer *xfer;
  	bool is_first_xfer = true;
cc4a7ffe0   Geert Uytterhoeven   spi: fsl-lpspi: P...
419
  	int ret = 0;
5314987de   Gao Pan   spi: imx: add lps...
420
421
422
423
424
425
426
427
428
  
  	msg->status = 0;
  	msg->actual_length = 0;
  
  	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
  		fsl_lpspi_setup_transfer(spi, xfer);
  		fsl_lpspi_set_cmd(fsl_lpspi, is_first_xfer);
  
  		is_first_xfer = false;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
429
  		ret = fsl_lpspi_transfer_one(controller, spi, xfer);
5314987de   Gao Pan   spi: imx: add lps...
430
431
432
433
434
435
436
  		if (ret < 0)
  			goto complete;
  
  		msg->actual_length += xfer->len;
  	}
  
  complete:
5314987de   Gao Pan   spi: imx: add lps...
437
  	msg->status = ret;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
438
  	spi_finalize_current_message(controller);
5314987de   Gao Pan   spi: imx: add lps...
439
440
441
442
443
444
  
  	return ret;
  }
  
  static irqreturn_t fsl_lpspi_isr(int irq, void *dev_id)
  {
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
445
  	u32 temp_SR, temp_IER;
5314987de   Gao Pan   spi: imx: add lps...
446
  	struct fsl_lpspi_data *fsl_lpspi = dev_id;
5314987de   Gao Pan   spi: imx: add lps...
447

09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
448
  	temp_IER = readl(fsl_lpspi->base + IMX7ULP_IER);
5314987de   Gao Pan   spi: imx: add lps...
449
  	fsl_lpspi_intctrl(fsl_lpspi, 0);
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
450
  	temp_SR = readl(fsl_lpspi->base + IMX7ULP_SR);
5314987de   Gao Pan   spi: imx: add lps...
451
  	fsl_lpspi_read_rx_fifo(fsl_lpspi);
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
452
  	if ((temp_SR & SR_TDF) && (temp_IER & IER_TDIE)) {
5314987de   Gao Pan   spi: imx: add lps...
453
  		fsl_lpspi_write_tx_fifo(fsl_lpspi);
09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
454
455
  		return IRQ_HANDLED;
  	}
5314987de   Gao Pan   spi: imx: add lps...
456

09db42b6d   Xiaoning Wang   MLK-20004 spi: lp...
457
458
459
  	if (temp_SR & SR_FCF && (temp_IER & IER_FCIE)) {
  		writel(SR_FCF, fsl_lpspi->base + IMX7ULP_SR);
  		complete(&fsl_lpspi->xfer_done);
5314987de   Gao Pan   spi: imx: add lps...
460
461
462
463
464
  		return IRQ_HANDLED;
  	}
  
  	return IRQ_NONE;
  }
532f2ba5c   Han Xu   MLK-16785: spi: l...
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
  int fsl_lpspi_runtime_resume(struct device *dev)
  {
  	struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev);
  	int ret;
  
  	ret = clk_prepare_enable(fsl_lpspi->clk_per);
  	if (ret)
  		return ret;
  
  	ret = clk_prepare_enable(fsl_lpspi->clk_ipg);
  	if (ret) {
  		clk_disable_unprepare(fsl_lpspi->clk_per);
  		return ret;
  	}
  
  	return 0;
  }
  
  int fsl_lpspi_runtime_suspend(struct device *dev)
  {
  	struct fsl_lpspi_data *fsl_lpspi = dev_get_drvdata(dev);
  
  	clk_disable_unprepare(fsl_lpspi->clk_per);
  	clk_disable_unprepare(fsl_lpspi->clk_ipg);
  
  	return 0;
  }
  
  static int fsl_lpspi_init_rpm(struct fsl_lpspi_data *fsl_lpspi)
  {
  	struct device *dev = fsl_lpspi->dev;
  
  	pm_runtime_enable(dev);
  	pm_runtime_set_autosuspend_delay(dev, FSL_LPSPI_RPM_TIMEOUT);
  	pm_runtime_use_autosuspend(dev);
  
  	return 0;
  }
5314987de   Gao Pan   spi: imx: add lps...
503
504
505
  static int fsl_lpspi_probe(struct platform_device *pdev)
  {
  	struct fsl_lpspi_data *fsl_lpspi;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
506
  	struct spi_controller *controller;
5314987de   Gao Pan   spi: imx: add lps...
507
508
  	struct resource *res;
  	int ret, irq;
b88a0deaa   Gao Pan   spi: fsl-lpspi: r...
509
  	u32 temp;
5314987de   Gao Pan   spi: imx: add lps...
510

1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
511
512
513
514
515
516
517
518
  	if (of_property_read_bool((&pdev->dev)->of_node, "spi-slave"))
  		controller = spi_alloc_slave(&pdev->dev,
  					sizeof(struct fsl_lpspi_data));
  	else
  		controller = spi_alloc_master(&pdev->dev,
  					sizeof(struct fsl_lpspi_data));
  
  	if (!controller)
5314987de   Gao Pan   spi: imx: add lps...
519
  		return -ENOMEM;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
520
  	platform_set_drvdata(pdev, controller);
5314987de   Gao Pan   spi: imx: add lps...
521

1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
522
523
  	controller->bits_per_word_mask = SPI_BPW_RANGE_MASK(8, 32);
  	controller->bus_num = pdev->id;
5314987de   Gao Pan   spi: imx: add lps...
524

1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
525
  	fsl_lpspi = spi_controller_get_devdata(controller);
5314987de   Gao Pan   spi: imx: add lps...
526
  	fsl_lpspi->dev = &pdev->dev;
532f2ba5c   Han Xu   MLK-16785: spi: l...
527
  	dev_set_drvdata(&pdev->dev, fsl_lpspi);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
528
529
530
531
532
533
534
535
536
537
538
  	fsl_lpspi->is_slave = of_property_read_bool((&pdev->dev)->of_node,
  						    "spi-slave");
  
  	controller->transfer_one_message = fsl_lpspi_transfer_one_msg;
  	controller->prepare_transfer_hardware = lpspi_prepare_xfer_hardware;
  	controller->unprepare_transfer_hardware = lpspi_unprepare_xfer_hardware;
  	controller->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH;
  	controller->flags = SPI_MASTER_MUST_RX | SPI_MASTER_MUST_TX;
  	controller->dev.of_node = pdev->dev.of_node;
  	controller->bus_num = pdev->id;
  	controller->slave_abort = fsl_lpspi_slave_abort;
5314987de   Gao Pan   spi: imx: add lps...
539
540
541
542
543
544
545
  
  	init_completion(&fsl_lpspi->xfer_done);
  
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	fsl_lpspi->base = devm_ioremap_resource(&pdev->dev, res);
  	if (IS_ERR(fsl_lpspi->base)) {
  		ret = PTR_ERR(fsl_lpspi->base);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
546
  		goto out_controller_put;
5314987de   Gao Pan   spi: imx: add lps...
547
548
549
550
551
  	}
  
  	irq = platform_get_irq(pdev, 0);
  	if (irq < 0) {
  		ret = irq;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
552
  		goto out_controller_put;
5314987de   Gao Pan   spi: imx: add lps...
553
554
555
556
557
558
559
  	}
  
  	ret = devm_request_irq(&pdev->dev, irq, fsl_lpspi_isr, 0,
  			       dev_name(&pdev->dev), fsl_lpspi);
  	if (ret) {
  		dev_err(&pdev->dev, "can't get irq%d: %d
  ", irq, ret);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
560
  		goto out_controller_put;
5314987de   Gao Pan   spi: imx: add lps...
561
  	}
cf2c78e51   Xiaoning Wang   MLK-19821 spi: lp...
562
563
564
565
566
567
568
569
570
571
572
  	fsl_lpspi->clk_per = devm_clk_get(&pdev->dev, "per");
  	if (IS_ERR(fsl_lpspi->clk_per)) {
  		ret = PTR_ERR(fsl_lpspi->clk_per);
  		goto out_controller_put;
  	}
  
  	fsl_lpspi->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
  	if (IS_ERR(fsl_lpspi->clk_ipg)) {
  		ret = PTR_ERR(fsl_lpspi->clk_ipg);
  		goto out_controller_put;
  	}
532f2ba5c   Han Xu   MLK-16785: spi: l...
573
574
575
  	/* enable the clock */
  	ret = fsl_lpspi_init_rpm(fsl_lpspi);
  	if (ret)
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
576
  		goto out_controller_put;
5314987de   Gao Pan   spi: imx: add lps...
577

532f2ba5c   Han Xu   MLK-16785: spi: l...
578
579
580
581
582
  	ret = pm_runtime_get_sync(fsl_lpspi->dev);
  	if (ret < 0) {
  		dev_err(fsl_lpspi->dev, "failed to enable clock
  ");
  		return ret;
b88a0deaa   Gao Pan   spi: fsl-lpspi: r...
583
584
585
586
587
  	}
  
  	temp = readl(fsl_lpspi->base + IMX7ULP_PARAM);
  	fsl_lpspi->txfifosize = 1 << (temp & 0x0f);
  	fsl_lpspi->rxfifosize = 1 << ((temp >> 8) & 0x0f);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
588
  	ret = devm_spi_register_controller(&pdev->dev, controller);
5314987de   Gao Pan   spi: imx: add lps...
589
  	if (ret < 0) {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
590
591
592
  		dev_err(&pdev->dev, "spi_register_controller error.
  ");
  		goto out_controller_put;
5314987de   Gao Pan   spi: imx: add lps...
593
594
595
  	}
  
  	return 0;
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
596
597
  out_controller_put:
  	spi_controller_put(controller);
5314987de   Gao Pan   spi: imx: add lps...
598
599
600
601
602
603
  
  	return ret;
  }
  
  static int fsl_lpspi_remove(struct platform_device *pdev)
  {
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
604
605
606
  	struct spi_controller *controller = platform_get_drvdata(pdev);
  	struct fsl_lpspi_data *fsl_lpspi =
  				spi_controller_get_devdata(controller);
5314987de   Gao Pan   spi: imx: add lps...
607

532f2ba5c   Han Xu   MLK-16785: spi: l...
608
609
610
  	pm_runtime_disable(fsl_lpspi->dev);
  
  	spi_master_put(controller);
5314987de   Gao Pan   spi: imx: add lps...
611
612
613
  
  	return 0;
  }
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
614
615
616
  #ifdef CONFIG_PM_SLEEP
  static int fsl_lpspi_suspend(struct device *dev)
  {
532f2ba5c   Han Xu   MLK-16785: spi: l...
617
  	int ret;
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
618
  	pinctrl_pm_select_sleep_state(dev);
532f2ba5c   Han Xu   MLK-16785: spi: l...
619
620
  	ret = pm_runtime_force_suspend(dev);
  	return ret;
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
621
622
623
624
  }
  
  static int fsl_lpspi_resume(struct device *dev)
  {
532f2ba5c   Han Xu   MLK-16785: spi: l...
625
626
627
628
629
630
631
632
  	int ret;
  
  	ret = pm_runtime_force_resume(dev);
  	if (ret) {
  		dev_err(dev, "Error in resume: %d
  ", ret);
  		return ret;
  	}
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
633
  	pinctrl_pm_select_default_state(dev);
532f2ba5c   Han Xu   MLK-16785: spi: l...
634

c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
635
636
  	return 0;
  }
532f2ba5c   Han Xu   MLK-16785: spi: l...
637
  #endif /* CONFIG_PM_SLEEP */
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
638

532f2ba5c   Han Xu   MLK-16785: spi: l...
639
640
641
642
643
  static const struct dev_pm_ops fsl_lpspi_pm_ops = {
  	SET_RUNTIME_PM_OPS(fsl_lpspi_runtime_suspend,
  				fsl_lpspi_runtime_resume, NULL)
  	SET_SYSTEM_SLEEP_PM_OPS(fsl_lpspi_suspend, fsl_lpspi_resume)
  };
c84cc2b0b   Gao Pan   MLK-13717-2 spi: ...
644

5314987de   Gao Pan   spi: imx: add lps...
645
646
  static struct platform_driver fsl_lpspi_driver = {
  	.driver = {
102ecc471   Gao Pan   spi: fsl-lpspi: f...
647
648
  		.name = DRIVER_NAME,
  		.of_match_table = fsl_lpspi_dt_ids,
532f2ba5c   Han Xu   MLK-16785: spi: l...
649
  		.pm = &fsl_lpspi_pm_ops,
102ecc471   Gao Pan   spi: fsl-lpspi: f...
650
  	},
5314987de   Gao Pan   spi: imx: add lps...
651
652
653
654
  	.probe = fsl_lpspi_probe,
  	.remove = fsl_lpspi_remove,
  };
  module_platform_driver(fsl_lpspi_driver);
1454f64d1   Xiaoning Wang   MLK-19452-1 SPI: ...
655
  MODULE_DESCRIPTION("LPSPI Controller driver");
5314987de   Gao Pan   spi: imx: add lps...
656
  MODULE_AUTHOR("Gao Pan <pandy.gao@nxp.com>");
b6787b680   Gao Pan   spi: fsl-lpspi: u...
657
  MODULE_LICENSE("GPL");