Blame view

drivers/spi/spi-bfin5xx.c 40.4 KB
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1
  /*
26fdc1f0d   Mike Frysinger   spi_bfin: headers...
2
   * Blackfin On-Chip SPI Driver
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
3
   *
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
4
   * Copyright 2004-2010 Analog Devices Inc.
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
5
   *
26fdc1f0d   Mike Frysinger   spi_bfin: headers...
6
   * Enter bugs at http://blackfin.uclinux.org/
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
7
   *
26fdc1f0d   Mike Frysinger   spi_bfin: headers...
8
   * Licensed under the GPL-2 or later.
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
9
10
11
12
   */
  
  #include <linux/init.h>
  #include <linux/module.h>
131b17d42   Bryan Wu   spi: initial BF54...
13
  #include <linux/delay.h>
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
14
  #include <linux/device.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
15
  #include <linux/slab.h>
131b17d42   Bryan Wu   spi: initial BF54...
16
  #include <linux/io.h>
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
17
  #include <linux/ioport.h>
131b17d42   Bryan Wu   spi: initial BF54...
18
  #include <linux/irq.h>
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
19
20
21
22
23
24
  #include <linux/errno.h>
  #include <linux/interrupt.h>
  #include <linux/platform_device.h>
  #include <linux/dma-mapping.h>
  #include <linux/spi/spi.h>
  #include <linux/workqueue.h>
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
25

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
26
  #include <asm/dma.h>
131b17d42   Bryan Wu   spi: initial BF54...
27
  #include <asm/portmux.h>
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
28
  #include <asm/bfin5xx_spi.h>
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
29
  #include <asm/cacheflush.h>
a32c691d7   Bryan Wu   spi: spi_bfin use...
30
31
  #define DRV_NAME	"bfin-spi"
  #define DRV_AUTHOR	"Bryan Wu, Luke Yang"
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
32
  #define DRV_DESC	"Blackfin on-chip SPI Controller Driver"
a32c691d7   Bryan Wu   spi: spi_bfin use...
33
34
35
36
  #define DRV_VERSION	"1.0"
  
  MODULE_AUTHOR(DRV_AUTHOR);
  MODULE_DESCRIPTION(DRV_DESC);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
37
  MODULE_LICENSE("GPL");
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
38
39
40
41
  #define START_STATE	((void *)0)
  #define RUNNING_STATE	((void *)1)
  #define DONE_STATE	((void *)2)
  #define ERROR_STATE	((void *)-1)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
42

9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
43
  struct bfin_spi_master_data;
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
44

9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
45
46
47
48
  struct bfin_spi_transfer_ops {
  	void (*write) (struct bfin_spi_master_data *);
  	void (*read) (struct bfin_spi_master_data *);
  	void (*duplex) (struct bfin_spi_master_data *);
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
49
  };
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
50
  struct bfin_spi_master_data {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
51
52
53
54
55
  	/* Driver model hookup */
  	struct platform_device *pdev;
  
  	/* SPI framework hookup */
  	struct spi_master *master;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
56
  	/* Regs base of SPI controller */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
57
  	struct bfin_spi_regs __iomem *regs;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
58

003d92261   Bryan Wu   Blackfin SPI driv...
59
60
  	/* Pin request list */
  	u16 *pin_req;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
61
62
63
64
65
66
67
68
69
  	/* BFIN hookup */
  	struct bfin5xx_spi_master *master_info;
  
  	/* Driver message queue */
  	struct workqueue_struct *workqueue;
  	struct work_struct pump_messages;
  	spinlock_t lock;
  	struct list_head queue;
  	int busy;
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
70
  	bool running;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
71
72
73
74
75
76
77
  
  	/* Message Transfer pump */
  	struct tasklet_struct pump_transfers;
  
  	/* Current message transfer state info */
  	struct spi_message *cur_msg;
  	struct spi_transfer *cur_transfer;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
78
  	struct bfin_spi_slave_data *cur_chip;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
79
80
81
82
83
84
  	size_t len_in_bytes;
  	size_t len;
  	void *tx;
  	void *tx_end;
  	void *rx;
  	void *rx_end;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
85
86
87
  
  	/* DMA stuffs */
  	int dma_channel;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
88
  	int dma_mapped;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
89
  	int dma_requested;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
90
91
  	dma_addr_t rx_dma;
  	dma_addr_t tx_dma;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
92

f6a6d9668   Yi Li   spi/bfin_spi: uti...
93
94
  	int irq_requested;
  	int spi_irq;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
95
96
97
  	size_t rx_map_len;
  	size_t tx_map_len;
  	u8 n_bytes;
b052fd0a4   Barry Song   spi/bfin_spi: sav...
98
99
  	u16 ctrl_reg;
  	u16 flag_reg;
fad91c890   Bryan Wu   spi: spi_bfin han...
100
  	int cs_change;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
101
  	const struct bfin_spi_transfer_ops *ops;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
102
  };
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
103
  struct bfin_spi_slave_data {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
104
105
106
107
108
  	u16 ctl_reg;
  	u16 baud;
  	u16 flag;
  
  	u8 chip_select_num;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
109
  	u8 enable_dma;
62310e51a   Bryan Wu   spi: spi_bfin: up...
110
  	u16 cs_chg_udelay;	/* Some devices require > 255usec delay */
42c78b2bf   Michael Hennerich   Blackfin SPI Driv...
111
  	u32 cs_gpio;
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
112
  	u16 idle_tx_val;
f6a6d9668   Yi Li   spi/bfin_spi: uti...
113
  	u8 pio_interrupt;	/* use spi data irq */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
114
  	const struct bfin_spi_transfer_ops *ops;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
115
  };
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
116
  static void bfin_spi_enable(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
117
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
118
  	bfin_write_or(&drv_data->regs->ctl, BIT_CTL_ENABLE);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
119
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
120
  static void bfin_spi_disable(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
121
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
122
  	bfin_write_and(&drv_data->regs->ctl, ~BIT_CTL_ENABLE);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
123
124
125
126
127
128
129
130
131
132
  }
  
  /* Caculate the SPI_BAUD register value based on input HZ */
  static u16 hz_to_spi_baud(u32 speed_hz)
  {
  	u_long sclk = get_sclk();
  	u16 spi_baud = (sclk / (2 * speed_hz));
  
  	if ((sclk % (2 * speed_hz)) > 0)
  		spi_baud++;
7513e006c   Michael Hennerich   Blackfin SPI Driv...
133
134
  	if (spi_baud < MIN_SPI_BAUD_VAL)
  		spi_baud = MIN_SPI_BAUD_VAL;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
135
136
  	return spi_baud;
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
137
  static int bfin_spi_flush(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
138
139
140
141
  {
  	unsigned long limit = loops_per_jiffy << 1;
  
  	/* wait for stop and clear stat */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
142
  	while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF) && --limit)
d8c05008b   Bryan Wu   Blackfin SPI driv...
143
  		cpu_relax();
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
144

47885ce81   Mike Frysinger   spi/bfin_spi: use...
145
  	bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
146
147
148
  
  	return limit;
  }
fad91c890   Bryan Wu   spi: spi_bfin han...
149
  /* Chip select operation functions for cs_change flag */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
150
  static void bfin_spi_cs_active(struct bfin_spi_master_data *drv_data, struct bfin_spi_slave_data *chip)
fad91c890   Bryan Wu   spi: spi_bfin han...
151
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
152
153
154
  	if (likely(chip->chip_select_num < MAX_CTRL_CS))
  		bfin_write_and(&drv_data->regs->flg, ~chip->flag);
  	else
42c78b2bf   Michael Hennerich   Blackfin SPI Driv...
155
  		gpio_set_value(chip->cs_gpio, 0);
fad91c890   Bryan Wu   spi: spi_bfin han...
156
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
157
158
  static void bfin_spi_cs_deactive(struct bfin_spi_master_data *drv_data,
                                   struct bfin_spi_slave_data *chip)
fad91c890   Bryan Wu   spi: spi_bfin han...
159
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
160
161
162
  	if (likely(chip->chip_select_num < MAX_CTRL_CS))
  		bfin_write_or(&drv_data->regs->flg, chip->flag);
  	else
42c78b2bf   Michael Hennerich   Blackfin SPI Driv...
163
  		gpio_set_value(chip->cs_gpio, 1);
62310e51a   Bryan Wu   spi: spi_bfin: up...
164
165
166
167
  
  	/* Move delay here for consistency */
  	if (chip->cs_chg_udelay)
  		udelay(chip->cs_chg_udelay);
fad91c890   Bryan Wu   spi: spi_bfin han...
168
  }
8221610e9   Barry Song   spi/bfin_spi: fix...
169
  /* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
170
171
  static inline void bfin_spi_cs_enable(struct bfin_spi_master_data *drv_data,
                                        struct bfin_spi_slave_data *chip)
8221610e9   Barry Song   spi/bfin_spi: fix...
172
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
173
174
  	if (chip->chip_select_num < MAX_CTRL_CS)
  		bfin_write_or(&drv_data->regs->flg, chip->flag >> 8);
8221610e9   Barry Song   spi/bfin_spi: fix...
175
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
176
177
  static inline void bfin_spi_cs_disable(struct bfin_spi_master_data *drv_data,
                                         struct bfin_spi_slave_data *chip)
8221610e9   Barry Song   spi/bfin_spi: fix...
178
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
179
180
  	if (chip->chip_select_num < MAX_CTRL_CS)
  		bfin_write_and(&drv_data->regs->flg, ~(chip->flag >> 8));
8221610e9   Barry Song   spi/bfin_spi: fix...
181
  }
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
182
  /* stop controller and re-config current chip*/
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
183
  static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
184
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
185
  	struct bfin_spi_slave_data *chip = drv_data->cur_chip;
12e17c426   Sonic Zhang   spi: spi_bfin, re...
186

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
187
  	/* Clear status and disable clock */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
188
  	bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
189
  	bfin_spi_disable(drv_data);
88b403693   Bryan Wu   Blackfin SPI: cle...
190
191
  	dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
192

9677b0de1   Barry Song   spi/bfin_spi: syn...
193
  	SSYNC();
5fec5b5a4   Bryan Wu   spi: spi_bfin cle...
194
  	/* Load the registers */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
195
196
  	bfin_write(&drv_data->regs->ctl, chip->ctl_reg);
  	bfin_write(&drv_data->regs->baud, chip->baud);
cc487e732   Sonic Zhang   spi: spi_bfin: ch...
197
198
  
  	bfin_spi_enable(drv_data);
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
199
  	bfin_spi_cs_active(drv_data, chip);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
200
  }
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
201
  /* used to kick off transfer in rx mode and read unwanted RX data */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
202
  static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
203
  {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
204
  	(void) bfin_read(&drv_data->regs->rdbr);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
205
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
206
  static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
207
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
208
209
  	/* clear RXS (we check for RXS inside the loop) */
  	bfin_spi_dummy_read(drv_data);
cc487e732   Sonic Zhang   spi: spi_bfin: ch...
210

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
211
  	while (drv_data->tx < drv_data->tx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
212
  		bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
213
214
  		/* wait until transfer finished.
  		   checking SPIF or TXS may not guarantee transfer completion */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
215
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
216
  			cpu_relax();
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
217
218
  		/* discard RX data and clear RXS */
  		bfin_spi_dummy_read(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
219
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
220
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
221
  static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
222
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
223
  	u16 tx_val = drv_data->cur_chip->idle_tx_val;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
224

93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
225
  	/* discard old RX data and clear RXS */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
226
  	bfin_spi_dummy_read(drv_data);
cc487e732   Sonic Zhang   spi: spi_bfin: ch...
227

93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
228
  	while (drv_data->rx < drv_data->rx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
229
230
  		bfin_write(&drv_data->regs->tdbr, tx_val);
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
231
  			cpu_relax();
47885ce81   Mike Frysinger   spi/bfin_spi: use...
232
  		*(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
233
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
234
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
235
  static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
236
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
237
238
  	/* discard old RX data and clear RXS */
  	bfin_spi_dummy_read(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
239
  	while (drv_data->rx < drv_data->rx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
240
241
  		bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++)));
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
242
  			cpu_relax();
47885ce81   Mike Frysinger   spi/bfin_spi: use...
243
  		*(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
244
245
  	}
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
246
  static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = {
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
247
248
249
250
  	.write  = bfin_spi_u8_writer,
  	.read   = bfin_spi_u8_reader,
  	.duplex = bfin_spi_u8_duplex,
  };
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
251
  static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
252
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
253
254
  	/* clear RXS (we check for RXS inside the loop) */
  	bfin_spi_dummy_read(drv_data);
88b403693   Bryan Wu   Blackfin SPI: cle...
255

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
256
  	while (drv_data->tx < drv_data->tx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
257
  		bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
258
  		drv_data->tx += 2;
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
259
260
  		/* wait until transfer finished.
  		   checking SPIF or TXS may not guarantee transfer completion */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
261
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
262
263
264
  			cpu_relax();
  		/* discard RX data and clear RXS */
  		bfin_spi_dummy_read(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
265
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
266
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
267
  static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
268
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
269
  	u16 tx_val = drv_data->cur_chip->idle_tx_val;
cc487e732   Sonic Zhang   spi: spi_bfin: ch...
270

93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
271
  	/* discard old RX data and clear RXS */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
272
  	bfin_spi_dummy_read(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
273

93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
274
  	while (drv_data->rx < drv_data->rx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
275
276
  		bfin_write(&drv_data->regs->tdbr, tx_val);
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
277
  			cpu_relax();
47885ce81   Mike Frysinger   spi/bfin_spi: use...
278
  		*(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
279
280
  		drv_data->rx += 2;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
281
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
282
  static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
283
  {
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
284
285
286
287
  	/* discard old RX data and clear RXS */
  	bfin_spi_dummy_read(drv_data);
  
  	while (drv_data->rx < drv_data->rx_end) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
288
  		bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx)));
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
289
  		drv_data->tx += 2;
47885ce81   Mike Frysinger   spi/bfin_spi: use...
290
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
291
  			cpu_relax();
47885ce81   Mike Frysinger   spi/bfin_spi: use...
292
  		*(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
293
  		drv_data->rx += 2;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
294
295
  	}
  }
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
296
  static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = {
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
297
298
299
300
  	.write  = bfin_spi_u16_writer,
  	.read   = bfin_spi_u16_reader,
  	.duplex = bfin_spi_u16_duplex,
  };
e35954053   Rob Maris   spi/bfin_spi: fix...
301
  /* test if there is more transfer to be done */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
302
  static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
  {
  	struct spi_message *msg = drv_data->cur_msg;
  	struct spi_transfer *trans = drv_data->cur_transfer;
  
  	/* Move to next transfer */
  	if (trans->transfer_list.next != &msg->transfers) {
  		drv_data->cur_transfer =
  		    list_entry(trans->transfer_list.next,
  			       struct spi_transfer, transfer_list);
  		return RUNNING_STATE;
  	} else
  		return DONE_STATE;
  }
  
  /*
   * caller already set message->status;
   * dma and pio irqs are blocked give finished message back
   */
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
321
  static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
322
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
323
  	struct bfin_spi_slave_data *chip = drv_data->cur_chip;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
  	struct spi_transfer *last_transfer;
  	unsigned long flags;
  	struct spi_message *msg;
  
  	spin_lock_irqsave(&drv_data->lock, flags);
  	msg = drv_data->cur_msg;
  	drv_data->cur_msg = NULL;
  	drv_data->cur_transfer = NULL;
  	drv_data->cur_chip = NULL;
  	queue_work(drv_data->workqueue, &drv_data->pump_messages);
  	spin_unlock_irqrestore(&drv_data->lock, flags);
  
  	last_transfer = list_entry(msg->transfers.prev,
  				   struct spi_transfer, transfer_list);
  
  	msg->state = NULL;
fad91c890   Bryan Wu   spi: spi_bfin han...
340
  	if (!drv_data->cs_change)
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
341
  		bfin_spi_cs_deactive(drv_data, chip);
fad91c890   Bryan Wu   spi: spi_bfin han...
342

b9b2a76a4   Yi Li   Blackfin SPI Driv...
343
344
345
  	/* Not stop spi in autobuffer mode */
  	if (drv_data->tx_dma != 0xFFFF)
  		bfin_spi_disable(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
346
347
348
  	if (msg->complete)
  		msg->complete(msg->context);
  }
f6a6d9668   Yi Li   spi/bfin_spi: uti...
349
350
351
  /* spi data irq handler */
  static irqreturn_t bfin_spi_pio_irq_handler(int irq, void *dev_id)
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
352
353
  	struct bfin_spi_master_data *drv_data = dev_id;
  	struct bfin_spi_slave_data *chip = drv_data->cur_chip;
f6a6d9668   Yi Li   spi/bfin_spi: uti...
354
355
  	struct spi_message *msg = drv_data->cur_msg;
  	int n_bytes = drv_data->n_bytes;
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
356
  	int loop = 0;
f6a6d9668   Yi Li   spi/bfin_spi: uti...
357
358
  
  	/* wait until transfer finished. */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
359
  	while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS))
f6a6d9668   Yi Li   spi/bfin_spi: uti...
360
361
362
363
364
365
366
367
  		cpu_relax();
  
  	if ((drv_data->tx && drv_data->tx >= drv_data->tx_end) ||
  		(drv_data->rx && drv_data->rx >= (drv_data->rx_end - n_bytes))) {
  		/* last read */
  		if (drv_data->rx) {
  			dev_dbg(&drv_data->pdev->dev, "last read
  ");
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
368
369
370
  			if (n_bytes % 2) {
  				u16 *buf = (u16 *)drv_data->rx;
  				for (loop = 0; loop < n_bytes / 2; loop++)
47885ce81   Mike Frysinger   spi/bfin_spi: use...
371
  					*buf++ = bfin_read(&drv_data->regs->rdbr);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
372
373
374
  			} else {
  				u8 *buf = (u8 *)drv_data->rx;
  				for (loop = 0; loop < n_bytes; loop++)
47885ce81   Mike Frysinger   spi/bfin_spi: use...
375
  					*buf++ = bfin_read(&drv_data->regs->rdbr);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
376
  			}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
377
378
379
380
381
382
383
384
  			drv_data->rx += n_bytes;
  		}
  
  		msg->actual_length += drv_data->len_in_bytes;
  		if (drv_data->cs_change)
  			bfin_spi_cs_deactive(drv_data, chip);
  		/* Move to next transfer */
  		msg->state = bfin_spi_next_transfer(drv_data);
7370ed6b9   Yi Li   spi/bfin_spi: use...
385
  		disable_irq_nosync(drv_data->spi_irq);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
386
387
388
389
390
391
392
393
394
395
  
  		/* Schedule transfer tasklet */
  		tasklet_schedule(&drv_data->pump_transfers);
  		return IRQ_HANDLED;
  	}
  
  	if (drv_data->rx && drv_data->tx) {
  		/* duplex */
  		dev_dbg(&drv_data->pdev->dev, "duplex: write_TDBR
  ");
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
396
397
398
399
  		if (n_bytes % 2) {
  			u16 *buf = (u16 *)drv_data->rx;
  			u16 *buf2 = (u16 *)drv_data->tx;
  			for (loop = 0; loop < n_bytes / 2; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
400
401
  				*buf++ = bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, *buf2++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
402
403
404
405
406
  			}
  		} else {
  			u8 *buf = (u8 *)drv_data->rx;
  			u8 *buf2 = (u8 *)drv_data->tx;
  			for (loop = 0; loop < n_bytes; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
407
408
  				*buf++ = bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, *buf2++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
409
  			}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
410
411
412
413
414
  		}
  	} else if (drv_data->rx) {
  		/* read */
  		dev_dbg(&drv_data->pdev->dev, "read: write_TDBR
  ");
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
415
416
417
  		if (n_bytes % 2) {
  			u16 *buf = (u16 *)drv_data->rx;
  			for (loop = 0; loop < n_bytes / 2; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
418
419
  				*buf++ = bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
420
421
422
423
  			}
  		} else {
  			u8 *buf = (u8 *)drv_data->rx;
  			for (loop = 0; loop < n_bytes; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
424
425
  				*buf++ = bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
426
427
  			}
  		}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
428
429
430
431
  	} else if (drv_data->tx) {
  		/* write */
  		dev_dbg(&drv_data->pdev->dev, "write: write_TDBR
  ");
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
432
433
434
  		if (n_bytes % 2) {
  			u16 *buf = (u16 *)drv_data->tx;
  			for (loop = 0; loop < n_bytes / 2; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
435
436
  				bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, *buf++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
437
438
439
440
  			}
  		} else {
  			u8 *buf = (u8 *)drv_data->tx;
  			for (loop = 0; loop < n_bytes; loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
441
442
  				bfin_read(&drv_data->regs->rdbr);
  				bfin_write(&drv_data->regs->tdbr, *buf++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
443
444
  			}
  		}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
445
446
447
448
449
450
451
452
453
  	}
  
  	if (drv_data->tx)
  		drv_data->tx += n_bytes;
  	if (drv_data->rx)
  		drv_data->rx += n_bytes;
  
  	return IRQ_HANDLED;
  }
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
454
  static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
455
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
456
457
  	struct bfin_spi_master_data *drv_data = dev_id;
  	struct bfin_spi_slave_data *chip = drv_data->cur_chip;
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
458
  	struct spi_message *msg = drv_data->cur_msg;
aaaf939c5   Mike Frysinger   Blackfin SPI Driv...
459
  	unsigned long timeout;
d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
460
  	unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel);
47885ce81   Mike Frysinger   spi/bfin_spi: use...
461
  	u16 spistat = bfin_read(&drv_data->regs->stat);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
462

d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
463
464
465
466
  	dev_dbg(&drv_data->pdev->dev,
  		"in dma_irq_handler dmastat:0x%x spistat:0x%x
  ",
  		dmastat, spistat);
782a89569   Michael Hennerich   spi/bfin_spi: han...
467
  	if (drv_data->rx != NULL) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
468
  		u16 cr = bfin_read(&drv_data->regs->ctl);
782a89569   Michael Hennerich   spi/bfin_spi: han...
469
470
  		/* discard old RX data and clear RXS */
  		bfin_spi_dummy_read(drv_data);
47885ce81   Mike Frysinger   spi/bfin_spi: use...
471
472
473
  		bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_ENABLE); /* Disable SPI */
  		bfin_write(&drv_data->regs->ctl, cr & ~BIT_CTL_TIMOD); /* Restore State */
  		bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); /* Clear Status */
782a89569   Michael Hennerich   spi/bfin_spi: han...
474
  	}
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
475
  	clear_dma_irqstat(drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
476
477
  
  	/*
d6fe89b06   Bryan Wu   Blackfin SPI driv...
478
479
480
481
  	 * wait for the last transaction shifted out.  HRM states:
  	 * at this point there may still be data in the SPI DMA FIFO waiting
  	 * to be transmitted ... software needs to poll TXS in the SPI_STAT
  	 * register until it goes low for 2 successive reads
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
482
483
  	 */
  	if (drv_data->tx != NULL) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
484
485
  		while ((bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS) ||
  		       (bfin_read(&drv_data->regs->stat) & BIT_STAT_TXS))
d8c05008b   Bryan Wu   Blackfin SPI driv...
486
  			cpu_relax();
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
487
  	}
aaaf939c5   Mike Frysinger   Blackfin SPI Driv...
488
489
490
  	dev_dbg(&drv_data->pdev->dev,
  		"in dma_irq_handler dmastat:0x%x spistat:0x%x
  ",
47885ce81   Mike Frysinger   spi/bfin_spi: use...
491
  		dmastat, bfin_read(&drv_data->regs->stat));
aaaf939c5   Mike Frysinger   Blackfin SPI Driv...
492
493
  
  	timeout = jiffies + HZ;
47885ce81   Mike Frysinger   spi/bfin_spi: use...
494
  	while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
aaaf939c5   Mike Frysinger   Blackfin SPI Driv...
495
496
497
498
499
  		if (!time_before(jiffies, timeout)) {
  			dev_warn(&drv_data->pdev->dev, "timeout waiting for SPIF");
  			break;
  		} else
  			cpu_relax();
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
500

90008a641   Mike Frysinger   spi/bfin_spi: use...
501
  	if ((dmastat & DMA_ERR) && (spistat & BIT_STAT_RBSY)) {
04b95d2f7   Mike Frysinger   Blackfin SPI Driv...
502
503
504
505
506
  		msg->state = ERROR_STATE;
  		dev_err(&drv_data->pdev->dev, "dma receive: fifo/buffer overflow
  ");
  	} else {
  		msg->actual_length += drv_data->len_in_bytes;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
507

04b95d2f7   Mike Frysinger   Blackfin SPI Driv...
508
  		if (drv_data->cs_change)
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
509
  			bfin_spi_cs_deactive(drv_data, chip);
fad91c890   Bryan Wu   spi: spi_bfin han...
510

04b95d2f7   Mike Frysinger   Blackfin SPI Driv...
511
  		/* Move to next transfer */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
512
  		msg->state = bfin_spi_next_transfer(drv_data);
04b95d2f7   Mike Frysinger   Blackfin SPI Driv...
513
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
514
515
516
517
518
  
  	/* Schedule transfer tasklet */
  	tasklet_schedule(&drv_data->pump_transfers);
  
  	/* free the irq handler before next transfer */
88b403693   Bryan Wu   Blackfin SPI: cle...
519
520
521
  	dev_dbg(&drv_data->pdev->dev,
  		"disable dma channel irq%d
  ",
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
522
  		drv_data->dma_channel);
a75bd65b2   Barry Song   spi/bfin_spi: use...
523
  	dma_disable_irq_nosync(drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
524
525
526
  
  	return IRQ_HANDLED;
  }
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
527
  static void bfin_spi_pump_transfers(unsigned long data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
528
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
529
  	struct bfin_spi_master_data *drv_data = (struct bfin_spi_master_data *)data;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
530
531
532
  	struct spi_message *message = NULL;
  	struct spi_transfer *transfer = NULL;
  	struct spi_transfer *previous = NULL;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
533
  	struct bfin_spi_slave_data *chip = NULL;
033f44bd0   Mike Frysinger   spi/bfin_spi: pus...
534
  	unsigned int bits_per_word;
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
535
  	u16 cr, cr_width, dma_width, dma_config;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
536
  	u32 tranf_success = 1;
8eeb12e5a   Vitja Makarov   spi_bfin5xx: use ...
537
  	u8 full_duplex = 0;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
538
539
540
541
542
  
  	/* Get current state information */
  	message = drv_data->cur_msg;
  	transfer = drv_data->cur_transfer;
  	chip = drv_data->cur_chip;
092e1fdaf   Bryan Wu   Blackfin SPI driv...
543

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
544
545
546
547
548
549
  	/*
  	 * if msg is error or done, report it back using complete() callback
  	 */
  
  	 /* Handle for abort */
  	if (message->state == ERROR_STATE) {
d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
550
551
  		dev_dbg(&drv_data->pdev->dev, "transfer: we've hit an error
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
552
  		message->status = -EIO;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
553
  		bfin_spi_giveback(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
554
555
556
557
558
  		return;
  	}
  
  	/* Handle end of message */
  	if (message->state == DONE_STATE) {
d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
559
560
  		dev_dbg(&drv_data->pdev->dev, "transfer: all done!
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
561
  		message->status = 0;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
562
  		bfin_spi_giveback(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
563
564
565
566
567
  		return;
  	}
  
  	/* Delay if requested at end of transfer */
  	if (message->state == RUNNING_STATE) {
d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
568
569
  		dev_dbg(&drv_data->pdev->dev, "transfer: still running ...
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
570
571
572
573
574
  		previous = list_entry(transfer->transfer_list.prev,
  				      struct spi_transfer, transfer_list);
  		if (previous->delay_usecs)
  			udelay(previous->delay_usecs);
  	}
ab09e0406   Mike Frysinger   spi/bfin_spi: fix...
575
  	/* Flush any existing transfers that may be sitting in the hardware */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
576
  	if (bfin_spi_flush(drv_data) == 0) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
577
578
579
  		dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed
  ");
  		message->status = -EIO;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
580
  		bfin_spi_giveback(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
581
582
  		return;
  	}
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
583
584
585
586
587
  	if (transfer->len == 0) {
  		/* Move to next transfer of this msg */
  		message->state = bfin_spi_next_transfer(drv_data);
  		/* Schedule next transfer tasklet */
  		tasklet_schedule(&drv_data->pump_transfers);
1974eba60   Sonic Zhang   spi/bfin_spi: ret...
588
  		return;
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
589
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
590
591
592
  	if (transfer->tx_buf != NULL) {
  		drv_data->tx = (void *)transfer->tx_buf;
  		drv_data->tx_end = drv_data->tx + transfer->len;
88b403693   Bryan Wu   Blackfin SPI: cle...
593
594
595
  		dev_dbg(&drv_data->pdev->dev, "tx_buf is %p, tx_end is %p
  ",
  			transfer->tx_buf, drv_data->tx_end);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
596
597
598
599
600
  	} else {
  		drv_data->tx = NULL;
  	}
  
  	if (transfer->rx_buf != NULL) {
8eeb12e5a   Vitja Makarov   spi_bfin5xx: use ...
601
  		full_duplex = transfer->tx_buf != NULL;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
602
603
  		drv_data->rx = transfer->rx_buf;
  		drv_data->rx_end = drv_data->rx + transfer->len;
88b403693   Bryan Wu   Blackfin SPI: cle...
604
605
606
  		dev_dbg(&drv_data->pdev->dev, "rx_buf is %p, rx_end is %p
  ",
  			transfer->rx_buf, drv_data->rx_end);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
607
608
609
610
611
612
613
  	} else {
  		drv_data->rx = NULL;
  	}
  
  	drv_data->rx_dma = transfer->rx_dma;
  	drv_data->tx_dma = transfer->tx_dma;
  	drv_data->len_in_bytes = transfer->len;
fad91c890   Bryan Wu   spi: spi_bfin han...
614
  	drv_data->cs_change = transfer->cs_change;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
615

092e1fdaf   Bryan Wu   Blackfin SPI driv...
616
  	/* Bits per word setup */
e479c6045   Mike Frysinger   spi/bfin_spi: fix...
617
618
619
  	bits_per_word = transfer->bits_per_word ? :
  		message->spi->bits_per_word ? : 8;
  	if (bits_per_word % 16 == 0) {
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
620
  		drv_data->n_bytes = bits_per_word/8;
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
621
622
  		drv_data->len = (transfer->len) >> 1;
  		cr_width = BIT_CTL_WORDSIZE;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
623
  		drv_data->ops = &bfin_bfin_spi_transfer_ops_u16;
e479c6045   Mike Frysinger   spi/bfin_spi: fix...
624
  	} else if (bits_per_word % 8 == 0) {
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
625
626
627
628
  		drv_data->n_bytes = bits_per_word/8;
  		drv_data->len = transfer->len;
  		cr_width = 0;
  		drv_data->ops = &bfin_bfin_spi_transfer_ops_u8;
2e768659d   Bob Liu   spi/bfin_spi: che...
629
630
631
632
633
634
  	} else {
  		dev_err(&drv_data->pdev->dev, "transfer: unsupported bits_per_word
  ");
  		message->status = -EINVAL;
  		bfin_spi_giveback(drv_data);
  		return;
092e1fdaf   Bryan Wu   Blackfin SPI driv...
635
  	}
47885ce81   Mike Frysinger   spi/bfin_spi: use...
636
  	cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE);
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
637
  	cr |= cr_width;
47885ce81   Mike Frysinger   spi/bfin_spi: use...
638
  	bfin_write(&drv_data->regs->ctl, cr);
092e1fdaf   Bryan Wu   Blackfin SPI driv...
639

4fb98efac   Mike Frysinger   spi: spi_bfin5xx ...
640
  	dev_dbg(&drv_data->pdev->dev,
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
641
642
  		"transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p
  ",
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
643
  		drv_data->ops, chip->ops, &bfin_bfin_spi_transfer_ops_u8);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
644

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
645
646
  	message->state = RUNNING_STATE;
  	dma_config = 0;
092e1fdaf   Bryan Wu   Blackfin SPI driv...
647
648
  	/* Speed setup (surely valid because already checked) */
  	if (transfer->speed_hz)
47885ce81   Mike Frysinger   spi/bfin_spi: use...
649
  		bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz));
092e1fdaf   Bryan Wu   Blackfin SPI driv...
650
  	else
47885ce81   Mike Frysinger   spi/bfin_spi: use...
651
  		bfin_write(&drv_data->regs->baud, chip->baud);
092e1fdaf   Bryan Wu   Blackfin SPI driv...
652

47885ce81   Mike Frysinger   spi/bfin_spi: use...
653
  	bfin_write(&drv_data->regs->stat, BIT_STAT_CLR);
e72dcde72   Rob Maris   spi/bfin_spi: cs ...
654
  	bfin_spi_cs_active(drv_data, chip);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
655

88b403693   Bryan Wu   Blackfin SPI: cle...
656
657
658
  	dev_dbg(&drv_data->pdev->dev,
  		"now pumping a transfer: width is %d, len is %d
  ",
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
659
  		cr_width, transfer->len);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
660
661
  
  	/*
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
662
663
664
665
  	 * Try to map dma buffer and do a dma transfer.  If successful use,
  	 * different way to r/w according to the enable_dma settings and if
  	 * we are not doing a full duplex transfer (since the hardware does
  	 * not support full duplex DMA transfers).
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
666
  	 */
8eeb12e5a   Vitja Makarov   spi_bfin5xx: use ...
667
668
  	if (!full_duplex && drv_data->cur_chip->enable_dma
  				&& drv_data->len > 6) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
669

11d6f5995   Mike Frysinger   Blackfin SPI Driv...
670
  		unsigned long dma_start_addr, flags;
7aec35661   Mike Frysinger   Blackfin SPI Driv...
671

bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
672
673
  		disable_dma(drv_data->dma_channel);
  		clear_dma_irqstat(drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
674
675
  
  		/* config dma channel */
88b403693   Bryan Wu   Blackfin SPI: cle...
676
677
  		dev_dbg(&drv_data->pdev->dev, "doing dma transfer
  ");
7aec35661   Mike Frysinger   Blackfin SPI Driv...
678
  		set_dma_x_count(drv_data->dma_channel, drv_data->len);
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
679
  		if (cr_width == BIT_CTL_WORDSIZE) {
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
680
  			set_dma_x_modify(drv_data->dma_channel, 2);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
681
682
  			dma_width = WDSIZE_16;
  		} else {
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
683
  			set_dma_x_modify(drv_data->dma_channel, 1);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
684
685
  			dma_width = WDSIZE_8;
  		}
3f479a65b   Sonic Zhang   spi: spi_bfin: re...
686
  		/* poll for SPI completion before start */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
687
  		while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF))
d8c05008b   Bryan Wu   Blackfin SPI driv...
688
  			cpu_relax();
3f479a65b   Sonic Zhang   spi: spi_bfin: re...
689

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
690
691
  		/* dirty hack for autobuffer DMA mode */
  		if (drv_data->tx_dma == 0xFFFF) {
88b403693   Bryan Wu   Blackfin SPI: cle...
692
693
694
  			dev_dbg(&drv_data->pdev->dev,
  				"doing autobuffer DMA out.
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
695
696
697
698
  
  			/* no irq in autobuffer mode */
  			dma_config =
  			    (DMAFLOW_AUTO | RESTART | dma_width | DI_EN);
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
699
700
  			set_dma_config(drv_data->dma_channel, dma_config);
  			set_dma_start_addr(drv_data->dma_channel,
a32c691d7   Bryan Wu   spi: spi_bfin use...
701
  					(unsigned long)drv_data->tx);
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
702
  			enable_dma(drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
703

07612e5f2   Sonic Zhang   spi: spi_bfin: re...
704
  			/* start SPI transfer */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
705
  			bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TIMOD_DMA_TX);
07612e5f2   Sonic Zhang   spi: spi_bfin: re...
706
707
708
709
  
  			/* just return here, there can only be one transfer
  			 * in this mode
  			 */
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
710
  			message->status = 0;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
711
  			bfin_spi_giveback(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
712
713
714
715
  			return;
  		}
  
  		/* In dma mode, rx or tx must be NULL in one transfer */
7aec35661   Mike Frysinger   Blackfin SPI Driv...
716
  		dma_config = (RESTART | dma_width | DI_EN);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
717
718
  		if (drv_data->rx != NULL) {
  			/* set transfer mode, and enable SPI */
d24bd1d0d   Mike Frysinger   Blackfin SPI Driv...
719
720
721
  			dev_dbg(&drv_data->pdev->dev, "doing DMA in to %p (size %zx)
  ",
  				drv_data->rx, drv_data->len_in_bytes);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
722

8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
723
  			/* invalidate caches, if needed */
67834fa93   Jie Zhang   Blackfin: rename ...
724
  			if (bfin_addr_dcacheable((unsigned long) drv_data->rx))
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
725
726
  				invalidate_dcache_range((unsigned long) drv_data->rx,
  							(unsigned long) (drv_data->rx +
ace32865a   Mike Frysinger   Blackfin SPI Driv...
727
  							drv_data->len_in_bytes));
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
728

7aec35661   Mike Frysinger   Blackfin SPI Driv...
729
730
  			dma_config |= WNR;
  			dma_start_addr = (unsigned long)drv_data->rx;
b31e27a6d   Mike Frysinger   Blackfin SPI Driv...
731
  			cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT;
07612e5f2   Sonic Zhang   spi: spi_bfin: re...
732

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
733
  		} else if (drv_data->tx != NULL) {
88b403693   Bryan Wu   Blackfin SPI: cle...
734
735
  			dev_dbg(&drv_data->pdev->dev, "doing DMA out.
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
736

8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
737
  			/* flush caches, if needed */
67834fa93   Jie Zhang   Blackfin: rename ...
738
  			if (bfin_addr_dcacheable((unsigned long) drv_data->tx))
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
739
740
  				flush_dcache_range((unsigned long) drv_data->tx,
  						(unsigned long) (drv_data->tx +
ace32865a   Mike Frysinger   Blackfin SPI Driv...
741
  						drv_data->len_in_bytes));
8cf5858c5   Vitja Makarov   Blackfin SPI Driv...
742

7aec35661   Mike Frysinger   Blackfin SPI Driv...
743
  			dma_start_addr = (unsigned long)drv_data->tx;
b31e27a6d   Mike Frysinger   Blackfin SPI Driv...
744
  			cr |= BIT_CTL_TIMOD_DMA_TX;
7aec35661   Mike Frysinger   Blackfin SPI Driv...
745
746
747
  
  		} else
  			BUG();
11d6f5995   Mike Frysinger   Blackfin SPI Driv...
748
749
750
751
752
753
754
755
756
  		/* oh man, here there be monsters ... and i dont mean the
  		 * fluffy cute ones from pixar, i mean the kind that'll eat
  		 * your data, kick your dog, and love it all.  do *not* try
  		 * and change these lines unless you (1) heavily test DMA
  		 * with SPI flashes on a loaded system (e.g. ping floods),
  		 * (2) know just how broken the DMA engine interaction with
  		 * the SPI peripheral is, and (3) have someone else to blame
  		 * when you screw it all up anyways.
  		 */
7aec35661   Mike Frysinger   Blackfin SPI Driv...
757
  		set_dma_start_addr(drv_data->dma_channel, dma_start_addr);
11d6f5995   Mike Frysinger   Blackfin SPI Driv...
758
759
  		set_dma_config(drv_data->dma_channel, dma_config);
  		local_irq_save(flags);
a963ea83b   Mike Frysinger   Blackfin SPI Driv...
760
  		SSYNC();
47885ce81   Mike Frysinger   spi/bfin_spi: use...
761
  		bfin_write(&drv_data->regs->ctl, cr);
a963ea83b   Mike Frysinger   Blackfin SPI Driv...
762
  		enable_dma(drv_data->dma_channel);
11d6f5995   Mike Frysinger   Blackfin SPI Driv...
763
764
  		dma_enable_irq(drv_data->dma_channel);
  		local_irq_restore(flags);
07612e5f2   Sonic Zhang   spi: spi_bfin: re...
765

f6a6d9668   Yi Li   spi/bfin_spi: uti...
766
767
  		return;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
768

5e8592dca   Mike Frysinger   spi/bfin_spi: com...
769
770
771
772
773
774
  	/*
  	 * We always use SPI_WRITE mode (transfer starts with TDBR write).
  	 * SPI_READ mode (transfer starts with RDBR read) seems to have
  	 * problems with setting up the output value in TDBR prior to the
  	 * start of the transfer.
  	 */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
775
  	bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TXMOD);
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
776

f6a6d9668   Yi Li   spi/bfin_spi: uti...
777
  	if (chip->pio_interrupt) {
5e8592dca   Mike Frysinger   spi/bfin_spi: com...
778
  		/* SPI irq should have been disabled by now */
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
779

f6a6d9668   Yi Li   spi/bfin_spi: uti...
780
781
  		/* discard old RX data and clear RXS */
  		bfin_spi_dummy_read(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
782

f6a6d9668   Yi Li   spi/bfin_spi: uti...
783
784
  		/* start transfer */
  		if (drv_data->tx == NULL)
47885ce81   Mike Frysinger   spi/bfin_spi: use...
785
  			bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
786
  		else {
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
787
788
789
790
791
  			int loop;
  			if (bits_per_word % 16 == 0) {
  				u16 *buf = (u16 *)drv_data->tx;
  				for (loop = 0; loop < bits_per_word / 16;
  						loop++) {
47885ce81   Mike Frysinger   spi/bfin_spi: use...
792
  					bfin_write(&drv_data->regs->tdbr, *buf++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
793
794
795
796
  				}
  			} else if (bits_per_word % 8 == 0) {
  				u8 *buf = (u8 *)drv_data->tx;
  				for (loop = 0; loop < bits_per_word / 8; loop++)
47885ce81   Mike Frysinger   spi/bfin_spi: use...
797
  					bfin_write(&drv_data->regs->tdbr, *buf++);
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
798
  			}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
799
800
  			drv_data->tx += drv_data->n_bytes;
  		}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
801

f6a6d9668   Yi Li   spi/bfin_spi: uti...
802
803
804
805
  		/* once TDBR is empty, interrupt is triggered */
  		enable_irq(drv_data->spi_irq);
  		return;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
806

f6a6d9668   Yi Li   spi/bfin_spi: uti...
807
808
809
  	/* IO mode */
  	dev_dbg(&drv_data->pdev->dev, "doing IO transfer
  ");
f6a6d9668   Yi Li   spi/bfin_spi: uti...
810
811
812
813
814
815
816
  	if (full_duplex) {
  		/* full duplex mode */
  		BUG_ON((drv_data->tx_end - drv_data->tx) !=
  		       (drv_data->rx_end - drv_data->rx));
  		dev_dbg(&drv_data->pdev->dev,
  			"IO duplex: cr is 0x%x
  ", cr);
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
817
  		drv_data->ops->duplex(drv_data);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
818
819
820
821
822
823
824
825
  
  		if (drv_data->tx != drv_data->tx_end)
  			tranf_success = 0;
  	} else if (drv_data->tx != NULL) {
  		/* write only half duplex */
  		dev_dbg(&drv_data->pdev->dev,
  			"IO write: cr is 0x%x
  ", cr);
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
826
  		drv_data->ops->write(drv_data);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
827
828
829
830
831
832
833
834
  
  		if (drv_data->tx != drv_data->tx_end)
  			tranf_success = 0;
  	} else if (drv_data->rx != NULL) {
  		/* read only half duplex */
  		dev_dbg(&drv_data->pdev->dev,
  			"IO read: cr is 0x%x
  ", cr);
9c4542c7a   Mike Frysinger   spi/bfin_spi: con...
835
  		drv_data->ops->read(drv_data);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
836
837
838
  		if (drv_data->rx != drv_data->rx_end)
  			tranf_success = 0;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
839

f6a6d9668   Yi Li   spi/bfin_spi: uti...
840
841
842
843
844
845
  	if (!tranf_success) {
  		dev_dbg(&drv_data->pdev->dev,
  			"IO write error!
  ");
  		message->state = ERROR_STATE;
  	} else {
25985edce   Lucas De Marchi   Fix common misspe...
846
  		/* Update total byte transferred */
f6a6d9668   Yi Li   spi/bfin_spi: uti...
847
848
849
850
851
  		message->actual_length += drv_data->len_in_bytes;
  		/* Move to next transfer of this msg */
  		message->state = bfin_spi_next_transfer(drv_data);
  		if (drv_data->cs_change)
  			bfin_spi_cs_deactive(drv_data, chip);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
852
  	}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
853
854
855
  
  	/* Schedule next transfer tasklet */
  	tasklet_schedule(&drv_data->pump_transfers);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
856
857
858
  }
  
  /* pop a msg from queue and kick off real transfer */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
859
  static void bfin_spi_pump_messages(struct work_struct *work)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
860
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
861
  	struct bfin_spi_master_data *drv_data;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
862
  	unsigned long flags;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
863
  	drv_data = container_of(work, struct bfin_spi_master_data, pump_messages);
131b17d42   Bryan Wu   spi: initial BF54...
864

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
865
866
  	/* Lock queue and check for queue work */
  	spin_lock_irqsave(&drv_data->lock, flags);
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
867
  	if (list_empty(&drv_data->queue) || !drv_data->running) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
  		/* pumper kicked off but no work to do */
  		drv_data->busy = 0;
  		spin_unlock_irqrestore(&drv_data->lock, flags);
  		return;
  	}
  
  	/* Make sure we are not already running a message */
  	if (drv_data->cur_msg) {
  		spin_unlock_irqrestore(&drv_data->lock, flags);
  		return;
  	}
  
  	/* Extract head of queue */
  	drv_data->cur_msg = list_entry(drv_data->queue.next,
  				       struct spi_message, queue);
5fec5b5a4   Bryan Wu   spi: spi_bfin cle...
883
884
885
  
  	/* Setup the SSP using the per chip configuration */
  	drv_data->cur_chip = spi_get_ctldata(drv_data->cur_msg->spi);
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
886
  	bfin_spi_restore_state(drv_data);
5fec5b5a4   Bryan Wu   spi: spi_bfin cle...
887

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
888
889
890
891
892
893
  	list_del_init(&drv_data->cur_msg->queue);
  
  	/* Initial message state */
  	drv_data->cur_msg->state = START_STATE;
  	drv_data->cur_transfer = list_entry(drv_data->cur_msg->transfers.next,
  					    struct spi_transfer, transfer_list);
5fec5b5a4   Bryan Wu   spi: spi_bfin cle...
894
895
896
897
898
  	dev_dbg(&drv_data->pdev->dev, "got a message to pump, "
  		"state is set to: baud %d, flag 0x%x, ctl 0x%x
  ",
  		drv_data->cur_chip->baud, drv_data->cur_chip->flag,
  		drv_data->cur_chip->ctl_reg);
131b17d42   Bryan Wu   spi: initial BF54...
899
900
  
  	dev_dbg(&drv_data->pdev->dev,
88b403693   Bryan Wu   Blackfin SPI: cle...
901
902
903
  		"the first transfer len is %d
  ",
  		drv_data->cur_transfer->len);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
904
905
906
907
908
909
910
911
912
913
914
915
  
  	/* Mark as busy and launch transfers */
  	tasklet_schedule(&drv_data->pump_transfers);
  
  	drv_data->busy = 1;
  	spin_unlock_irqrestore(&drv_data->lock, flags);
  }
  
  /*
   * got a msg to transfer, queue it in drv_data->queue.
   * And kick off message pumper
   */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
916
  static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
917
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
918
  	struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
919
920
921
  	unsigned long flags;
  
  	spin_lock_irqsave(&drv_data->lock, flags);
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
922
  	if (!drv_data->running) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
923
924
925
926
927
928
929
  		spin_unlock_irqrestore(&drv_data->lock, flags);
  		return -ESHUTDOWN;
  	}
  
  	msg->actual_length = 0;
  	msg->status = -EINPROGRESS;
  	msg->state = START_STATE;
88b403693   Bryan Wu   Blackfin SPI: cle...
930
931
  	dev_dbg(&spi->dev, "adding an msg in transfer() 
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
932
  	list_add_tail(&msg->queue, &drv_data->queue);
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
933
  	if (drv_data->running && !drv_data->busy)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
934
935
936
937
938
939
  		queue_work(drv_data->workqueue, &drv_data->pump_messages);
  
  	spin_unlock_irqrestore(&drv_data->lock, flags);
  
  	return 0;
  }
12e17c426   Sonic Zhang   spi: spi_bfin, re...
940
  #define MAX_SPI_SSEL	7
ddc0bf13d   Mike Frysinger   spi/bfin_spi: con...
941
  static const u16 ssel[][MAX_SPI_SSEL] = {
12e17c426   Sonic Zhang   spi: spi_bfin, re...
942
943
944
945
946
947
948
949
950
951
952
953
  	{P_SPI0_SSEL1, P_SPI0_SSEL2, P_SPI0_SSEL3,
  	P_SPI0_SSEL4, P_SPI0_SSEL5,
  	P_SPI0_SSEL6, P_SPI0_SSEL7},
  
  	{P_SPI1_SSEL1, P_SPI1_SSEL2, P_SPI1_SSEL3,
  	P_SPI1_SSEL4, P_SPI1_SSEL5,
  	P_SPI1_SSEL6, P_SPI1_SSEL7},
  
  	{P_SPI2_SSEL1, P_SPI2_SSEL2, P_SPI2_SSEL3,
  	P_SPI2_SSEL4, P_SPI2_SSEL5,
  	P_SPI2_SSEL6, P_SPI2_SSEL7},
  };
ab09e0406   Mike Frysinger   spi/bfin_spi: fix...
954
  /* setup for devices (may be called multiple times -- not just first setup) */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
955
  static int bfin_spi_setup(struct spi_device *spi)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
956
  {
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
957
  	struct bfin5xx_spi_chip *chip_info;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
958
959
  	struct bfin_spi_slave_data *chip = NULL;
  	struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
5b47bcd48   Mike Frysinger   spi/bfin_spi: res...
960
  	u16 bfin_ctl_reg;
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
961
  	int ret = -EINVAL;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
962

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
963
  	/* Only alloc (or use chip_info) on first setup */
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
964
  	chip_info = NULL;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
965
966
  	chip = spi_get_ctldata(spi);
  	if (chip == NULL) {
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
967
968
969
970
971
972
973
  		chip = kzalloc(sizeof(*chip), GFP_KERNEL);
  		if (!chip) {
  			dev_err(&spi->dev, "cannot allocate chip data
  ");
  			ret = -ENOMEM;
  			goto error;
  		}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
974
975
976
977
  
  		chip->enable_dma = 0;
  		chip_info = spi->controller_data;
  	}
5b47bcd48   Mike Frysinger   spi/bfin_spi: res...
978
979
980
  	/* Let people set non-standard bits directly */
  	bfin_ctl_reg = BIT_CTL_OPENDRAIN | BIT_CTL_EMISO |
  		BIT_CTL_PSSE | BIT_CTL_GM | BIT_CTL_SZ;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
981
982
  	/* chip_info isn't always needed */
  	if (chip_info) {
2ed355165   Mike Frysinger   spi: spi_bfin, do...
983
984
  		/* Make sure people stop trying to set fields via ctl_reg
  		 * when they should actually be using common SPI framework.
90008a641   Mike Frysinger   spi/bfin_spi: use...
985
  		 * Currently we let through: WOM EMISO PSSE GM SZ.
2ed355165   Mike Frysinger   spi: spi_bfin, do...
986
987
988
  		 * Not sure if a user actually needs/uses any of these,
  		 * but let's assume (for now) they do.
  		 */
5b47bcd48   Mike Frysinger   spi/bfin_spi: res...
989
  		if (chip_info->ctl_reg & ~bfin_ctl_reg) {
2ed355165   Mike Frysinger   spi: spi_bfin, do...
990
991
992
  			dev_err(&spi->dev, "do not set bits in ctl_reg "
  				"that the SPI framework manages
  ");
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
993
  			goto error;
2ed355165   Mike Frysinger   spi: spi_bfin, do...
994
  		}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
995
996
997
  		chip->enable_dma = chip_info->enable_dma != 0
  		    && drv_data->master_info->enable_dma;
  		chip->ctl_reg = chip_info->ctl_reg;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
998
  		chip->cs_chg_udelay = chip_info->cs_chg_udelay;
93b61bddc   Wolfgang Muees   Blackfin SPI Driv...
999
  		chip->idle_tx_val = chip_info->idle_tx_val;
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1000
  		chip->pio_interrupt = chip_info->pio_interrupt;
033f44bd0   Mike Frysinger   spi/bfin_spi: pus...
1001
  		spi->bits_per_word = chip_info->bits_per_word;
5b47bcd48   Mike Frysinger   spi/bfin_spi: res...
1002
1003
1004
  	} else {
  		/* force a default base state */
  		chip->ctl_reg &= bfin_ctl_reg;
033f44bd0   Mike Frysinger   spi/bfin_spi: pus...
1005
  	}
4d676fc5c   Bob Liu   spi/bfin_spi: sup...
1006
  	if (spi->bits_per_word % 8) {
033f44bd0   Mike Frysinger   spi/bfin_spi: pus...
1007
1008
1009
1010
  		dev_err(&spi->dev, "%d bits_per_word is not supported
  ",
  				spi->bits_per_word);
  		goto error;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1011
1012
1013
  	}
  
  	/* translate common spi framework into our register */
7715aad4e   Mike Frysinger   spi/bfin_spi: rej...
1014
1015
1016
1017
1018
  	if (spi->mode & ~(SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST)) {
  		dev_err(&spi->dev, "unsupported spi modes detected
  ");
  		goto error;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1019
  	if (spi->mode & SPI_CPOL)
90008a641   Mike Frysinger   spi/bfin_spi: use...
1020
  		chip->ctl_reg |= BIT_CTL_CPOL;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1021
  	if (spi->mode & SPI_CPHA)
90008a641   Mike Frysinger   spi/bfin_spi: use...
1022
  		chip->ctl_reg |= BIT_CTL_CPHA;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1023
  	if (spi->mode & SPI_LSB_FIRST)
90008a641   Mike Frysinger   spi/bfin_spi: use...
1024
  		chip->ctl_reg |= BIT_CTL_LSBF;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1025
  	/* we dont support running in slave mode (yet?) */
90008a641   Mike Frysinger   spi/bfin_spi: use...
1026
  	chip->ctl_reg |= BIT_CTL_MASTER;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1027
1028
  
  	/*
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1029
1030
1031
1032
  	 * Notice: for blackfin, the speed_hz is the value of register
  	 * SPI_BAUD, not the real baudrate
  	 */
  	chip->baud = hz_to_spi_baud(spi->max_speed_hz);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1033
  	chip->chip_select_num = spi->chip_select;
4190f6a51   Barry Song   spi/bfin_spi: war...
1034
1035
1036
1037
1038
1039
  	if (chip->chip_select_num < MAX_CTRL_CS) {
  		if (!(spi->mode & SPI_CPHA))
  			dev_warn(&spi->dev, "Warning: SPI CPHA not set:"
  				" Slave Select not under software control!
  "
  				" See Documentation/blackfin/bfin-spi-notes.txt");
d3cc71f71   Barry Song   spi/bfin_spi: red...
1040
  		chip->flag = (1 << spi->chip_select) << 8;
4190f6a51   Barry Song   spi/bfin_spi: war...
1041
  	} else
d3cc71f71   Barry Song   spi/bfin_spi: red...
1042
  		chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1043

f6a6d9668   Yi Li   spi/bfin_spi: uti...
1044
1045
1046
1047
1048
1049
  	if (chip->enable_dma && chip->pio_interrupt) {
  		dev_err(&spi->dev, "enable_dma is set, "
  				"do not set pio_interrupt
  ");
  		goto error;
  	}
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
  	/*
  	 * if any one SPI chip is registered and wants DMA, request the
  	 * DMA channel for it
  	 */
  	if (chip->enable_dma && !drv_data->dma_requested) {
  		/* register dma irq handler */
  		ret = request_dma(drv_data->dma_channel, "BFIN_SPI_DMA");
  		if (ret) {
  			dev_err(&spi->dev,
  				"Unable to request BlackFin SPI DMA channel
  ");
  			goto error;
  		}
  		drv_data->dma_requested = 1;
  
  		ret = set_dma_callback(drv_data->dma_channel,
  			bfin_spi_dma_irq_handler, drv_data);
  		if (ret) {
  			dev_err(&spi->dev, "Unable to set dma callback
  ");
  			goto error;
  		}
  		dma_disable_irq(drv_data->dma_channel);
  	}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1074
1075
  	if (chip->pio_interrupt && !drv_data->irq_requested) {
  		ret = request_irq(drv_data->spi_irq, bfin_spi_pio_irq_handler,
38ada214f   Yong Zhang   spi: irq: Remove ...
1076
  			0, "BFIN_SPI", drv_data);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1077
1078
1079
1080
1081
1082
1083
1084
1085
  		if (ret) {
  			dev_err(&spi->dev, "Unable to register spi IRQ
  ");
  			goto error;
  		}
  		drv_data->irq_requested = 1;
  		/* we use write mode, spi irq has to be disabled here */
  		disable_irq(drv_data->spi_irq);
  	}
d3cc71f71   Barry Song   spi/bfin_spi: red...
1086
  	if (chip->chip_select_num >= MAX_CTRL_CS) {
73e1ac162   Michael Hennerich   spi/bfin_spi: onl...
1087
1088
1089
1090
1091
1092
1093
1094
1095
  		/* Only request on first setup */
  		if (spi_get_ctldata(spi) == NULL) {
  			ret = gpio_request(chip->cs_gpio, spi->modalias);
  			if (ret) {
  				dev_err(&spi->dev, "gpio_request() error
  ");
  				goto pin_error;
  			}
  			gpio_direction_output(chip->cs_gpio, 1);
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1096
  		}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1097
  	}
898eb71cb   Joe Perches   Add missing newli...
1098
1099
  	dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d
  ",
033f44bd0   Mike Frysinger   spi/bfin_spi: pus...
1100
  			spi->modalias, spi->bits_per_word, chip->enable_dma);
88b403693   Bryan Wu   Blackfin SPI: cle...
1101
1102
  	dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x
  ",
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1103
1104
1105
  			chip->ctl_reg, chip->flag);
  
  	spi_set_ctldata(spi, chip);
12e17c426   Sonic Zhang   spi: spi_bfin, re...
1106
1107
  	dev_dbg(&spi->dev, "chip select number is %d
  ", chip->chip_select_num);
d3cc71f71   Barry Song   spi/bfin_spi: red...
1108
  	if (chip->chip_select_num < MAX_CTRL_CS) {
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1109
1110
1111
1112
1113
1114
1115
1116
  		ret = peripheral_request(ssel[spi->master->bus_num]
  		                         [chip->chip_select_num-1], spi->modalias);
  		if (ret) {
  			dev_err(&spi->dev, "peripheral_request() error
  ");
  			goto pin_error;
  		}
  	}
12e17c426   Sonic Zhang   spi: spi_bfin, re...
1117

8221610e9   Barry Song   spi/bfin_spi: fix...
1118
  	bfin_spi_cs_enable(drv_data, chip);
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1119
  	bfin_spi_cs_deactive(drv_data, chip);
07612e5f2   Sonic Zhang   spi: spi_bfin: re...
1120

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1121
  	return 0;
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1122
1123
  
   pin_error:
d3cc71f71   Barry Song   spi/bfin_spi: red...
1124
  	if (chip->chip_select_num >= MAX_CTRL_CS)
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
  		gpio_free(chip->cs_gpio);
  	else
  		peripheral_free(ssel[spi->master->bus_num]
  			[chip->chip_select_num - 1]);
   error:
  	if (chip) {
  		if (drv_data->dma_requested)
  			free_dma(drv_data->dma_channel);
  		drv_data->dma_requested = 0;
  
  		kfree(chip);
  		/* prevent free 'chip' twice */
  		spi_set_ctldata(spi, NULL);
  	}
  
  	return ret;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1141
1142
1143
1144
1145
1146
  }
  
  /*
   * callback for spi framework.
   * clean driver specific data
   */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1147
  static void bfin_spi_cleanup(struct spi_device *spi)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1148
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
1149
1150
  	struct bfin_spi_slave_data *chip = spi_get_ctldata(spi);
  	struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1151

e7d02e3c9   Mike Frysinger   Blackfin SPI Driv...
1152
1153
  	if (!chip)
  		return;
d3cc71f71   Barry Song   spi/bfin_spi: red...
1154
  	if (chip->chip_select_num < MAX_CTRL_CS) {
12e17c426   Sonic Zhang   spi: spi_bfin, re...
1155
1156
  		peripheral_free(ssel[spi->master->bus_num]
  					[chip->chip_select_num-1]);
8221610e9   Barry Song   spi/bfin_spi: fix...
1157
  		bfin_spi_cs_disable(drv_data, chip);
d3cc71f71   Barry Song   spi/bfin_spi: red...
1158
  	} else
42c78b2bf   Michael Hennerich   Blackfin SPI Driv...
1159
  		gpio_free(chip->cs_gpio);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1160
  	kfree(chip);
ac01e97d6   Daniel Mack   spi/bfin_spi: fix...
1161
1162
  	/* prevent free 'chip' twice */
  	spi_set_ctldata(spi, NULL);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1163
  }
c52d4e5f3   Mike Frysinger   spi/bfin_spi: uni...
1164
  static int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1165
1166
1167
  {
  	INIT_LIST_HEAD(&drv_data->queue);
  	spin_lock_init(&drv_data->lock);
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
1168
  	drv_data->running = false;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1169
1170
1171
1172
  	drv_data->busy = 0;
  
  	/* init transfer tasklet */
  	tasklet_init(&drv_data->pump_transfers,
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1173
  		     bfin_spi_pump_transfers, (unsigned long)drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1174
1175
  
  	/* init messages workqueue */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1176
  	INIT_WORK(&drv_data->pump_messages, bfin_spi_pump_messages);
6c7377ab6   Kay Sievers   spi: struct devic...
1177
1178
  	drv_data->workqueue = create_singlethread_workqueue(
  				dev_name(drv_data->master->dev.parent));
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1179
1180
1181
1182
1183
  	if (drv_data->workqueue == NULL)
  		return -EBUSY;
  
  	return 0;
  }
c52d4e5f3   Mike Frysinger   spi/bfin_spi: uni...
1184
  static int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1185
1186
1187
1188
  {
  	unsigned long flags;
  
  	spin_lock_irqsave(&drv_data->lock, flags);
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
1189
  	if (drv_data->running || drv_data->busy) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1190
1191
1192
  		spin_unlock_irqrestore(&drv_data->lock, flags);
  		return -EBUSY;
  	}
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
1193
  	drv_data->running = true;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1194
1195
1196
1197
1198
1199
1200
1201
1202
  	drv_data->cur_msg = NULL;
  	drv_data->cur_transfer = NULL;
  	drv_data->cur_chip = NULL;
  	spin_unlock_irqrestore(&drv_data->lock, flags);
  
  	queue_work(drv_data->workqueue, &drv_data->pump_messages);
  
  	return 0;
  }
c52d4e5f3   Mike Frysinger   spi/bfin_spi: uni...
1203
  static int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
  {
  	unsigned long flags;
  	unsigned limit = 500;
  	int status = 0;
  
  	spin_lock_irqsave(&drv_data->lock, flags);
  
  	/*
  	 * This is a bit lame, but is optimized for the common execution path.
  	 * A wait_queue on the drv_data->busy could be used, but then the common
  	 * execution path (pump_messages) would be required to call wake_up or
  	 * friends on every SPI message. Do this instead
  	 */
f4f50c3ff   Mike Frysinger   spi/bfin_spi: con...
1217
  	drv_data->running = false;
850a28ecd   Vasily Khoruzhick   spi: Fix race con...
1218
  	while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) {
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
  		spin_unlock_irqrestore(&drv_data->lock, flags);
  		msleep(10);
  		spin_lock_irqsave(&drv_data->lock, flags);
  	}
  
  	if (!list_empty(&drv_data->queue) || drv_data->busy)
  		status = -EBUSY;
  
  	spin_unlock_irqrestore(&drv_data->lock, flags);
  
  	return status;
  }
c52d4e5f3   Mike Frysinger   spi/bfin_spi: uni...
1231
  static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1232
1233
  {
  	int status;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1234
  	status = bfin_spi_stop_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1235
1236
1237
1238
1239
1240
1241
  	if (status != 0)
  		return status;
  
  	destroy_workqueue(drv_data->workqueue);
  
  	return 0;
  }
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1242
  static int __init bfin_spi_probe(struct platform_device *pdev)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1243
1244
1245
1246
  {
  	struct device *dev = &pdev->dev;
  	struct bfin5xx_spi_master *platform_info;
  	struct spi_master *master;
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
1247
  	struct bfin_spi_master_data *drv_data;
a32c691d7   Bryan Wu   spi: spi_bfin use...
1248
  	struct resource *res;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1249
1250
1251
1252
1253
  	int status = 0;
  
  	platform_info = dev->platform_data;
  
  	/* Allocate master with space for drv_data */
2a045131d   Mike Frysinger   spi/bfin_spi: dro...
1254
  	master = spi_alloc_master(dev, sizeof(*drv_data));
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1255
1256
1257
1258
1259
  	if (!master) {
  		dev_err(&pdev->dev, "can not alloc spi_master
  ");
  		return -ENOMEM;
  	}
131b17d42   Bryan Wu   spi: initial BF54...
1260

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1261
1262
1263
1264
  	drv_data = spi_master_get_devdata(master);
  	drv_data->master = master;
  	drv_data->master_info = platform_info;
  	drv_data->pdev = pdev;
003d92261   Bryan Wu   Blackfin SPI driv...
1265
  	drv_data->pin_req = platform_info->pin_req;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1266

e7db06b5d   David Brownell   spi: move more sp...
1267
1268
  	/* the spi->mode bits supported by this driver: */
  	master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1269
1270
  	master->bus_num = pdev->id;
  	master->num_chipselect = platform_info->num_chipselect;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1271
1272
1273
  	master->cleanup = bfin_spi_cleanup;
  	master->setup = bfin_spi_setup;
  	master->transfer = bfin_spi_transfer;
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1274

a32c691d7   Bryan Wu   spi: spi_bfin use...
1275
1276
1277
1278
1279
1280
1281
1282
  	/* Find and map our resources */
  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  	if (res == NULL) {
  		dev_err(dev, "Cannot get IORESOURCE_MEM
  ");
  		status = -ENOENT;
  		goto out_error_get_res;
  	}
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1283
1284
  	drv_data->regs = ioremap(res->start, resource_size(res));
  	if (drv_data->regs == NULL) {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1285
1286
1287
1288
1289
  		dev_err(dev, "Cannot map IO
  ");
  		status = -ENXIO;
  		goto out_error_ioremap;
  	}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1290
1291
  	res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
  	if (res == NULL) {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1292
1293
1294
  		dev_err(dev, "No DMA channel specified
  ");
  		status = -ENOENT;
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
  		goto out_error_free_io;
  	}
  	drv_data->dma_channel = res->start;
  
  	drv_data->spi_irq = platform_get_irq(pdev, 0);
  	if (drv_data->spi_irq < 0) {
  		dev_err(dev, "No spi pio irq specified
  ");
  		status = -ENOENT;
  		goto out_error_free_io;
a32c691d7   Bryan Wu   spi: spi_bfin use...
1305
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1306
  	/* Initial and start queue */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1307
  	status = bfin_spi_init_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1308
  	if (status != 0) {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1309
1310
  		dev_err(dev, "problem initializing queue
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1311
1312
  		goto out_error_queue_alloc;
  	}
a32c691d7   Bryan Wu   spi: spi_bfin use...
1313

138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1314
  	status = bfin_spi_start_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1315
  	if (status != 0) {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1316
1317
  		dev_err(dev, "problem starting queue
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1318
1319
  		goto out_error_queue_alloc;
  	}
f9e522cae   Vitja Makarov   spi: spi_bfin5xx:...
1320
1321
1322
1323
1324
1325
  	status = peripheral_request_list(drv_data->pin_req, DRV_NAME);
  	if (status != 0) {
  		dev_err(&pdev->dev, ": Requesting Peripherals failed
  ");
  		goto out_error_queue_alloc;
  	}
bb8beecd9   Wolfgang Muees   spi/bfin_spi: for...
1326
1327
1328
  	/* Reset SPI registers. If these registers were used by the boot loader,
  	 * the sky may fall on your head if you enable the dma controller.
  	 */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1329
1330
  	bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
  	bfin_write(&drv_data->regs->flg, 0xFF00);
bb8beecd9   Wolfgang Muees   spi/bfin_spi: for...
1331

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1332
1333
1334
1335
  	/* Register with the SPI framework */
  	platform_set_drvdata(pdev, drv_data);
  	status = spi_register_master(master);
  	if (status != 0) {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1336
1337
  		dev_err(dev, "problem registering spi master
  ");
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1338
1339
  		goto out_error_queue_alloc;
  	}
a32c691d7   Bryan Wu   spi: spi_bfin use...
1340

47885ce81   Mike Frysinger   spi/bfin_spi: use...
1341
1342
1343
  	dev_info(dev, "%s, Version %s, regs@%p, dma channel@%d
  ",
  		DRV_DESC, DRV_VERSION, drv_data->regs,
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
1344
  		drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1345
  	return status;
cc2f81a69   Michael Hennerich   spi: bfin spi use...
1346
  out_error_queue_alloc:
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1347
  	bfin_spi_destroy_queue(drv_data);
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1348
  out_error_free_io:
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1349
  	iounmap(drv_data->regs);
a32c691d7   Bryan Wu   spi: spi_bfin use...
1350
1351
  out_error_ioremap:
  out_error_get_res:
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1352
  	spi_master_put(master);
cc2f81a69   Michael Hennerich   spi: bfin spi use...
1353

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1354
1355
1356
1357
  	return status;
  }
  
  /* stop hardware and remove the driver */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1358
  static int __devexit bfin_spi_remove(struct platform_device *pdev)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1359
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
1360
  	struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1361
1362
1363
1364
1365
1366
  	int status = 0;
  
  	if (!drv_data)
  		return 0;
  
  	/* Remove the queue */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1367
  	status = bfin_spi_destroy_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1368
1369
1370
1371
1372
1373
1374
1375
  	if (status != 0)
  		return status;
  
  	/* Disable the SSP at the peripheral and SOC level */
  	bfin_spi_disable(drv_data);
  
  	/* Release DMA */
  	if (drv_data->master_info->enable_dma) {
bb90eb00b   Bryan Wu   spi: spi_bfin: ha...
1376
1377
  		if (dma_channel_active(drv_data->dma_channel))
  			free_dma(drv_data->dma_channel);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1378
  	}
f6a6d9668   Yi Li   spi/bfin_spi: uti...
1379
1380
1381
1382
  	if (drv_data->irq_requested) {
  		free_irq(drv_data->spi_irq, drv_data);
  		drv_data->irq_requested = 0;
  	}
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1383
1384
  	/* Disconnect from the SPI framework */
  	spi_unregister_master(drv_data->master);
003d92261   Bryan Wu   Blackfin SPI driv...
1385
  	peripheral_free_list(drv_data->pin_req);
cc2f81a69   Michael Hennerich   spi: bfin spi use...
1386

a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1387
1388
1389
1390
1391
1392
1393
  	/* Prevent double remove */
  	platform_set_drvdata(pdev, NULL);
  
  	return 0;
  }
  
  #ifdef CONFIG_PM
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1394
  static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1395
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
1396
  	struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1397
  	int status = 0;
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1398
  	status = bfin_spi_stop_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1399
1400
  	if (status != 0)
  		return status;
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1401
1402
  	drv_data->ctrl_reg = bfin_read(&drv_data->regs->ctl);
  	drv_data->flag_reg = bfin_read(&drv_data->regs->flg);
b052fd0a4   Barry Song   spi/bfin_spi: sav...
1403
1404
1405
1406
  
  	/*
  	 * reset SPI_CTL and SPI_FLG registers
  	 */
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1407
1408
  	bfin_write(&drv_data->regs->ctl, BIT_CTL_CPHA | BIT_CTL_MASTER);
  	bfin_write(&drv_data->regs->flg, 0xFF00);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1409
1410
1411
  
  	return 0;
  }
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1412
  static int bfin_spi_resume(struct platform_device *pdev)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1413
  {
9c0a788b4   Mike Frysinger   spi/bfin_spi: nam...
1414
  	struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1415
  	int status = 0;
47885ce81   Mike Frysinger   spi/bfin_spi: use...
1416
1417
  	bfin_write(&drv_data->regs->ctl, drv_data->ctrl_reg);
  	bfin_write(&drv_data->regs->flg, drv_data->flag_reg);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1418
1419
  
  	/* Start the queue running */
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1420
  	status = bfin_spi_start_queue(drv_data);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1421
1422
1423
1424
1425
1426
1427
1428
1429
  	if (status != 0) {
  		dev_err(&pdev->dev, "problem starting queue (%d)
  ", status);
  		return status;
  	}
  
  	return 0;
  }
  #else
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1430
1431
  #define bfin_spi_suspend NULL
  #define bfin_spi_resume NULL
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1432
  #endif				/* CONFIG_PM */
7e38c3c44   Kay Sievers   spi: fix platform...
1433
  MODULE_ALIAS("platform:bfin-spi");
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1434
  static struct platform_driver bfin_spi_driver = {
fc3ba9525   David Brownell   SPI driver hotplu...
1435
  	.driver	= {
a32c691d7   Bryan Wu   spi: spi_bfin use...
1436
  		.name	= DRV_NAME,
88b403693   Bryan Wu   Blackfin SPI: cle...
1437
1438
  		.owner	= THIS_MODULE,
  	},
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1439
1440
1441
  	.suspend	= bfin_spi_suspend,
  	.resume		= bfin_spi_resume,
  	.remove		= __devexit_p(bfin_spi_remove),
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1442
  };
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1443
  static int __init bfin_spi_init(void)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1444
  {
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1445
  	return platform_driver_probe(&bfin_spi_driver, bfin_spi_probe);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1446
  }
6f7c17f4f   Michael Hennerich   spi/bfin_spi: ini...
1447
  subsys_initcall(bfin_spi_init);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1448

138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1449
  static void __exit bfin_spi_exit(void)
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1450
  {
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1451
  	platform_driver_unregister(&bfin_spi_driver);
a5f6abd4f   Wu, Bryan   Blackfin: blackfi...
1452
  }
138f97cd0   Mike Frysinger   Blackfin SPI Driv...
1453
  module_exit(bfin_spi_exit);