Blame view
drivers/spi/spi-bfin5xx.c
40.4 KB
a5f6abd4f Blackfin: blackfi... |
1 |
/* |
26fdc1f0d spi_bfin: headers... |
2 |
* Blackfin On-Chip SPI Driver |
a5f6abd4f Blackfin: blackfi... |
3 |
* |
9c0a788b4 spi/bfin_spi: nam... |
4 |
* Copyright 2004-2010 Analog Devices Inc. |
a5f6abd4f Blackfin: blackfi... |
5 |
* |
26fdc1f0d spi_bfin: headers... |
6 |
* Enter bugs at http://blackfin.uclinux.org/ |
a5f6abd4f Blackfin: blackfi... |
7 |
* |
26fdc1f0d spi_bfin: headers... |
8 |
* Licensed under the GPL-2 or later. |
a5f6abd4f Blackfin: blackfi... |
9 10 11 12 |
*/ #include <linux/init.h> #include <linux/module.h> |
131b17d42 spi: initial BF54... |
13 |
#include <linux/delay.h> |
a5f6abd4f Blackfin: blackfi... |
14 |
#include <linux/device.h> |
5a0e3ad6a include cleanup: ... |
15 |
#include <linux/slab.h> |
131b17d42 spi: initial BF54... |
16 |
#include <linux/io.h> |
a5f6abd4f Blackfin: blackfi... |
17 |
#include <linux/ioport.h> |
131b17d42 spi: initial BF54... |
18 |
#include <linux/irq.h> |
a5f6abd4f 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 Blackfin: blackfi... |
25 |
|
a5f6abd4f Blackfin: blackfi... |
26 |
#include <asm/dma.h> |
131b17d42 spi: initial BF54... |
27 |
#include <asm/portmux.h> |
a5f6abd4f Blackfin: blackfi... |
28 |
#include <asm/bfin5xx_spi.h> |
8cf5858c5 Blackfin SPI Driv... |
29 |
#include <asm/cacheflush.h> |
a32c691d7 spi: spi_bfin use... |
30 31 |
#define DRV_NAME "bfin-spi" #define DRV_AUTHOR "Bryan Wu, Luke Yang" |
138f97cd0 Blackfin SPI Driv... |
32 |
#define DRV_DESC "Blackfin on-chip SPI Controller Driver" |
a32c691d7 spi: spi_bfin use... |
33 34 35 36 |
#define DRV_VERSION "1.0" MODULE_AUTHOR(DRV_AUTHOR); MODULE_DESCRIPTION(DRV_DESC); |
a5f6abd4f Blackfin: blackfi... |
37 |
MODULE_LICENSE("GPL"); |
bb90eb00b 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 Blackfin: blackfi... |
42 |
|
9c0a788b4 spi/bfin_spi: nam... |
43 |
struct bfin_spi_master_data; |
9c4542c7a spi/bfin_spi: con... |
44 |
|
9c0a788b4 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 spi/bfin_spi: con... |
49 |
}; |
9c0a788b4 spi/bfin_spi: nam... |
50 |
struct bfin_spi_master_data { |
a5f6abd4f Blackfin: blackfi... |
51 52 53 54 55 |
/* Driver model hookup */ struct platform_device *pdev; /* SPI framework hookup */ struct spi_master *master; |
bb90eb00b spi: spi_bfin: ha... |
56 |
/* Regs base of SPI controller */ |
47885ce81 spi/bfin_spi: use... |
57 |
struct bfin_spi_regs __iomem *regs; |
bb90eb00b spi: spi_bfin: ha... |
58 |
|
003d92261 Blackfin SPI driv... |
59 60 |
/* Pin request list */ u16 *pin_req; |
a5f6abd4f 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 spi/bfin_spi: con... |
70 |
bool running; |
a5f6abd4f 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 spi/bfin_spi: nam... |
78 |
struct bfin_spi_slave_data *cur_chip; |
a5f6abd4f 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 spi: spi_bfin: ha... |
85 86 87 |
/* DMA stuffs */ int dma_channel; |
a5f6abd4f Blackfin: blackfi... |
88 |
int dma_mapped; |
bb90eb00b spi: spi_bfin: ha... |
89 |
int dma_requested; |
a5f6abd4f Blackfin: blackfi... |
90 91 |
dma_addr_t rx_dma; dma_addr_t tx_dma; |
bb90eb00b spi: spi_bfin: ha... |
92 |
|
f6a6d9668 spi/bfin_spi: uti... |
93 94 |
int irq_requested; int spi_irq; |
a5f6abd4f Blackfin: blackfi... |
95 96 97 |
size_t rx_map_len; size_t tx_map_len; u8 n_bytes; |
b052fd0a4 spi/bfin_spi: sav... |
98 99 |
u16 ctrl_reg; u16 flag_reg; |
fad91c890 spi: spi_bfin han... |
100 |
int cs_change; |
9c0a788b4 spi/bfin_spi: nam... |
101 |
const struct bfin_spi_transfer_ops *ops; |
a5f6abd4f Blackfin: blackfi... |
102 |
}; |
9c0a788b4 spi/bfin_spi: nam... |
103 |
struct bfin_spi_slave_data { |
a5f6abd4f Blackfin: blackfi... |
104 105 106 107 108 |
u16 ctl_reg; u16 baud; u16 flag; u8 chip_select_num; |
a5f6abd4f Blackfin: blackfi... |
109 |
u8 enable_dma; |
62310e51a spi: spi_bfin: up... |
110 |
u16 cs_chg_udelay; /* Some devices require > 255usec delay */ |
42c78b2bf Blackfin SPI Driv... |
111 |
u32 cs_gpio; |
93b61bddc Blackfin SPI Driv... |
112 |
u16 idle_tx_val; |
f6a6d9668 spi/bfin_spi: uti... |
113 |
u8 pio_interrupt; /* use spi data irq */ |
9c0a788b4 spi/bfin_spi: nam... |
114 |
const struct bfin_spi_transfer_ops *ops; |
a5f6abd4f Blackfin: blackfi... |
115 |
}; |
9c0a788b4 spi/bfin_spi: nam... |
116 |
static void bfin_spi_enable(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
117 |
{ |
47885ce81 spi/bfin_spi: use... |
118 |
bfin_write_or(&drv_data->regs->ctl, BIT_CTL_ENABLE); |
a5f6abd4f Blackfin: blackfi... |
119 |
} |
9c0a788b4 spi/bfin_spi: nam... |
120 |
static void bfin_spi_disable(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
121 |
{ |
47885ce81 spi/bfin_spi: use... |
122 |
bfin_write_and(&drv_data->regs->ctl, ~BIT_CTL_ENABLE); |
a5f6abd4f 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 Blackfin SPI Driv... |
133 134 |
if (spi_baud < MIN_SPI_BAUD_VAL) spi_baud = MIN_SPI_BAUD_VAL; |
a5f6abd4f Blackfin: blackfi... |
135 136 |
return spi_baud; } |
9c0a788b4 spi/bfin_spi: nam... |
137 |
static int bfin_spi_flush(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
138 139 140 141 |
{ unsigned long limit = loops_per_jiffy << 1; /* wait for stop and clear stat */ |
47885ce81 spi/bfin_spi: use... |
142 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF) && --limit) |
d8c05008b Blackfin SPI driv... |
143 |
cpu_relax(); |
a5f6abd4f Blackfin: blackfi... |
144 |
|
47885ce81 spi/bfin_spi: use... |
145 |
bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); |
a5f6abd4f Blackfin: blackfi... |
146 147 148 |
return limit; } |
fad91c890 spi: spi_bfin han... |
149 |
/* Chip select operation functions for cs_change flag */ |
9c0a788b4 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 spi: spi_bfin han... |
151 |
{ |
47885ce81 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 Blackfin SPI Driv... |
155 |
gpio_set_value(chip->cs_gpio, 0); |
fad91c890 spi: spi_bfin han... |
156 |
} |
9c0a788b4 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 spi: spi_bfin han... |
159 |
{ |
47885ce81 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 Blackfin SPI Driv... |
163 |
gpio_set_value(chip->cs_gpio, 1); |
62310e51a spi: spi_bfin: up... |
164 165 166 167 |
/* Move delay here for consistency */ if (chip->cs_chg_udelay) udelay(chip->cs_chg_udelay); |
fad91c890 spi: spi_bfin han... |
168 |
} |
8221610e9 spi/bfin_spi: fix... |
169 |
/* enable or disable the pin muxed by GPIO and SPI CS to work as SPI CS */ |
9c0a788b4 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 spi/bfin_spi: fix... |
172 |
{ |
47885ce81 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 spi/bfin_spi: fix... |
175 |
} |
9c0a788b4 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 spi/bfin_spi: fix... |
178 |
{ |
47885ce81 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 spi/bfin_spi: fix... |
181 |
} |
a5f6abd4f Blackfin: blackfi... |
182 |
/* stop controller and re-config current chip*/ |
9c0a788b4 spi/bfin_spi: nam... |
183 |
static void bfin_spi_restore_state(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
184 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
185 |
struct bfin_spi_slave_data *chip = drv_data->cur_chip; |
12e17c426 spi: spi_bfin, re... |
186 |
|
a5f6abd4f Blackfin: blackfi... |
187 |
/* Clear status and disable clock */ |
47885ce81 spi/bfin_spi: use... |
188 |
bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); |
a5f6abd4f Blackfin: blackfi... |
189 |
bfin_spi_disable(drv_data); |
88b403693 Blackfin SPI: cle... |
190 191 |
dev_dbg(&drv_data->pdev->dev, "restoring spi ctl state "); |
a5f6abd4f Blackfin: blackfi... |
192 |
|
9677b0de1 spi/bfin_spi: syn... |
193 |
SSYNC(); |
5fec5b5a4 spi: spi_bfin cle... |
194 |
/* Load the registers */ |
47885ce81 spi/bfin_spi: use... |
195 196 |
bfin_write(&drv_data->regs->ctl, chip->ctl_reg); bfin_write(&drv_data->regs->baud, chip->baud); |
cc487e732 spi: spi_bfin: ch... |
197 198 |
bfin_spi_enable(drv_data); |
138f97cd0 Blackfin SPI Driv... |
199 |
bfin_spi_cs_active(drv_data, chip); |
a5f6abd4f Blackfin: blackfi... |
200 |
} |
93b61bddc Blackfin SPI Driv... |
201 |
/* used to kick off transfer in rx mode and read unwanted RX data */ |
9c0a788b4 spi/bfin_spi: nam... |
202 |
static inline void bfin_spi_dummy_read(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
203 |
{ |
47885ce81 spi/bfin_spi: use... |
204 |
(void) bfin_read(&drv_data->regs->rdbr); |
a5f6abd4f Blackfin: blackfi... |
205 |
} |
9c0a788b4 spi/bfin_spi: nam... |
206 |
static void bfin_spi_u8_writer(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
207 |
{ |
93b61bddc Blackfin SPI Driv... |
208 209 |
/* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); |
cc487e732 spi: spi_bfin: ch... |
210 |
|
a5f6abd4f Blackfin: blackfi... |
211 |
while (drv_data->tx < drv_data->tx_end) { |
47885ce81 spi/bfin_spi: use... |
212 |
bfin_write(&drv_data->regs->tdbr, (*(u8 *) (drv_data->tx++))); |
93b61bddc Blackfin SPI Driv... |
213 214 |
/* wait until transfer finished. checking SPIF or TXS may not guarantee transfer completion */ |
47885ce81 spi/bfin_spi: use... |
215 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS)) |
d8c05008b Blackfin SPI driv... |
216 |
cpu_relax(); |
93b61bddc Blackfin SPI Driv... |
217 218 |
/* discard RX data and clear RXS */ bfin_spi_dummy_read(drv_data); |
a5f6abd4f Blackfin: blackfi... |
219 |
} |
a5f6abd4f Blackfin: blackfi... |
220 |
} |
9c0a788b4 spi/bfin_spi: nam... |
221 |
static void bfin_spi_u8_reader(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
222 |
{ |
93b61bddc Blackfin SPI Driv... |
223 |
u16 tx_val = drv_data->cur_chip->idle_tx_val; |
a5f6abd4f Blackfin: blackfi... |
224 |
|
93b61bddc Blackfin SPI Driv... |
225 |
/* discard old RX data and clear RXS */ |
138f97cd0 Blackfin SPI Driv... |
226 |
bfin_spi_dummy_read(drv_data); |
cc487e732 spi: spi_bfin: ch... |
227 |
|
93b61bddc Blackfin SPI Driv... |
228 |
while (drv_data->rx < drv_data->rx_end) { |
47885ce81 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 Blackfin SPI driv... |
231 |
cpu_relax(); |
47885ce81 spi/bfin_spi: use... |
232 |
*(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr); |
a5f6abd4f Blackfin: blackfi... |
233 |
} |
a5f6abd4f Blackfin: blackfi... |
234 |
} |
9c0a788b4 spi/bfin_spi: nam... |
235 |
static void bfin_spi_u8_duplex(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
236 |
{ |
93b61bddc Blackfin SPI Driv... |
237 238 |
/* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); |
a5f6abd4f Blackfin: blackfi... |
239 |
while (drv_data->rx < drv_data->rx_end) { |
47885ce81 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 Blackfin SPI driv... |
242 |
cpu_relax(); |
47885ce81 spi/bfin_spi: use... |
243 |
*(u8 *) (drv_data->rx++) = bfin_read(&drv_data->regs->rdbr); |
a5f6abd4f Blackfin: blackfi... |
244 245 |
} } |
9c0a788b4 spi/bfin_spi: nam... |
246 |
static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u8 = { |
9c4542c7a spi/bfin_spi: con... |
247 248 249 250 |
.write = bfin_spi_u8_writer, .read = bfin_spi_u8_reader, .duplex = bfin_spi_u8_duplex, }; |
9c0a788b4 spi/bfin_spi: nam... |
251 |
static void bfin_spi_u16_writer(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
252 |
{ |
93b61bddc Blackfin SPI Driv... |
253 254 |
/* clear RXS (we check for RXS inside the loop) */ bfin_spi_dummy_read(drv_data); |
88b403693 Blackfin SPI: cle... |
255 |
|
a5f6abd4f Blackfin: blackfi... |
256 |
while (drv_data->tx < drv_data->tx_end) { |
47885ce81 spi/bfin_spi: use... |
257 |
bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx))); |
a5f6abd4f Blackfin: blackfi... |
258 |
drv_data->tx += 2; |
93b61bddc Blackfin SPI Driv... |
259 260 |
/* wait until transfer finished. checking SPIF or TXS may not guarantee transfer completion */ |
47885ce81 spi/bfin_spi: use... |
261 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS)) |
93b61bddc Blackfin SPI Driv... |
262 263 264 |
cpu_relax(); /* discard RX data and clear RXS */ bfin_spi_dummy_read(drv_data); |
a5f6abd4f Blackfin: blackfi... |
265 |
} |
a5f6abd4f Blackfin: blackfi... |
266 |
} |
9c0a788b4 spi/bfin_spi: nam... |
267 |
static void bfin_spi_u16_reader(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
268 |
{ |
93b61bddc Blackfin SPI Driv... |
269 |
u16 tx_val = drv_data->cur_chip->idle_tx_val; |
cc487e732 spi: spi_bfin: ch... |
270 |
|
93b61bddc Blackfin SPI Driv... |
271 |
/* discard old RX data and clear RXS */ |
138f97cd0 Blackfin SPI Driv... |
272 |
bfin_spi_dummy_read(drv_data); |
a5f6abd4f Blackfin: blackfi... |
273 |
|
93b61bddc Blackfin SPI Driv... |
274 |
while (drv_data->rx < drv_data->rx_end) { |
47885ce81 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 Blackfin SPI driv... |
277 |
cpu_relax(); |
47885ce81 spi/bfin_spi: use... |
278 |
*(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr); |
a5f6abd4f Blackfin: blackfi... |
279 280 |
drv_data->rx += 2; } |
a5f6abd4f Blackfin: blackfi... |
281 |
} |
9c0a788b4 spi/bfin_spi: nam... |
282 |
static void bfin_spi_u16_duplex(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
283 |
{ |
93b61bddc 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 spi/bfin_spi: use... |
288 |
bfin_write(&drv_data->regs->tdbr, (*(u16 *) (drv_data->tx))); |
93b61bddc Blackfin SPI Driv... |
289 |
drv_data->tx += 2; |
47885ce81 spi/bfin_spi: use... |
290 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS)) |
d8c05008b Blackfin SPI driv... |
291 |
cpu_relax(); |
47885ce81 spi/bfin_spi: use... |
292 |
*(u16 *) (drv_data->rx) = bfin_read(&drv_data->regs->rdbr); |
a5f6abd4f Blackfin: blackfi... |
293 |
drv_data->rx += 2; |
a5f6abd4f Blackfin: blackfi... |
294 295 |
} } |
9c0a788b4 spi/bfin_spi: nam... |
296 |
static const struct bfin_spi_transfer_ops bfin_bfin_spi_transfer_ops_u16 = { |
9c4542c7a spi/bfin_spi: con... |
297 298 299 300 |
.write = bfin_spi_u16_writer, .read = bfin_spi_u16_reader, .duplex = bfin_spi_u16_duplex, }; |
e35954053 spi/bfin_spi: fix... |
301 |
/* test if there is more transfer to be done */ |
9c0a788b4 spi/bfin_spi: nam... |
302 |
static void *bfin_spi_next_transfer(struct bfin_spi_master_data *drv_data) |
a5f6abd4f 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 spi/bfin_spi: nam... |
321 |
static void bfin_spi_giveback(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
322 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
323 |
struct bfin_spi_slave_data *chip = drv_data->cur_chip; |
a5f6abd4f 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 spi: spi_bfin han... |
340 |
if (!drv_data->cs_change) |
138f97cd0 Blackfin SPI Driv... |
341 |
bfin_spi_cs_deactive(drv_data, chip); |
fad91c890 spi: spi_bfin han... |
342 |
|
b9b2a76a4 Blackfin SPI Driv... |
343 344 345 |
/* Not stop spi in autobuffer mode */ if (drv_data->tx_dma != 0xFFFF) bfin_spi_disable(drv_data); |
a5f6abd4f Blackfin: blackfi... |
346 347 348 |
if (msg->complete) msg->complete(msg->context); } |
f6a6d9668 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 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 spi/bfin_spi: uti... |
354 355 |
struct spi_message *msg = drv_data->cur_msg; int n_bytes = drv_data->n_bytes; |
4d676fc5c spi/bfin_spi: sup... |
356 |
int loop = 0; |
f6a6d9668 spi/bfin_spi: uti... |
357 358 |
/* wait until transfer finished. */ |
47885ce81 spi/bfin_spi: use... |
359 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_RXS)) |
f6a6d9668 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 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 spi/bfin_spi: use... |
371 |
*buf++ = bfin_read(&drv_data->regs->rdbr); |
4d676fc5c spi/bfin_spi: sup... |
372 373 374 |
} else { u8 *buf = (u8 *)drv_data->rx; for (loop = 0; loop < n_bytes; loop++) |
47885ce81 spi/bfin_spi: use... |
375 |
*buf++ = bfin_read(&drv_data->regs->rdbr); |
4d676fc5c spi/bfin_spi: sup... |
376 |
} |
f6a6d9668 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 spi/bfin_spi: use... |
385 |
disable_irq_nosync(drv_data->spi_irq); |
f6a6d9668 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 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 spi/bfin_spi: use... |
400 401 |
*buf++ = bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, *buf2++); |
4d676fc5c 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 spi/bfin_spi: use... |
407 408 |
*buf++ = bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, *buf2++); |
4d676fc5c spi/bfin_spi: sup... |
409 |
} |
f6a6d9668 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 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 spi/bfin_spi: use... |
418 419 |
*buf++ = bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val); |
4d676fc5c spi/bfin_spi: sup... |
420 421 422 423 |
} } else { u8 *buf = (u8 *)drv_data->rx; for (loop = 0; loop < n_bytes; loop++) { |
47885ce81 spi/bfin_spi: use... |
424 425 |
*buf++ = bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val); |
4d676fc5c spi/bfin_spi: sup... |
426 427 |
} } |
f6a6d9668 spi/bfin_spi: uti... |
428 429 430 431 |
} else if (drv_data->tx) { /* write */ dev_dbg(&drv_data->pdev->dev, "write: write_TDBR "); |
4d676fc5c 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 spi/bfin_spi: use... |
435 436 |
bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, *buf++); |
4d676fc5c spi/bfin_spi: sup... |
437 438 439 440 |
} } else { u8 *buf = (u8 *)drv_data->tx; for (loop = 0; loop < n_bytes; loop++) { |
47885ce81 spi/bfin_spi: use... |
441 442 |
bfin_read(&drv_data->regs->rdbr); bfin_write(&drv_data->regs->tdbr, *buf++); |
4d676fc5c spi/bfin_spi: sup... |
443 444 |
} } |
f6a6d9668 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 Blackfin SPI Driv... |
454 |
static irqreturn_t bfin_spi_dma_irq_handler(int irq, void *dev_id) |
a5f6abd4f Blackfin: blackfi... |
455 |
{ |
9c0a788b4 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 spi: spi_bfin: ha... |
458 |
struct spi_message *msg = drv_data->cur_msg; |
aaaf939c5 Blackfin SPI Driv... |
459 |
unsigned long timeout; |
d24bd1d0d Blackfin SPI Driv... |
460 |
unsigned short dmastat = get_dma_curr_irqstat(drv_data->dma_channel); |
47885ce81 spi/bfin_spi: use... |
461 |
u16 spistat = bfin_read(&drv_data->regs->stat); |
a5f6abd4f Blackfin: blackfi... |
462 |
|
d24bd1d0d 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 spi/bfin_spi: han... |
467 |
if (drv_data->rx != NULL) { |
47885ce81 spi/bfin_spi: use... |
468 |
u16 cr = bfin_read(&drv_data->regs->ctl); |
782a89569 spi/bfin_spi: han... |
469 470 |
/* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); |
47885ce81 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 spi/bfin_spi: han... |
474 |
} |
bb90eb00b spi: spi_bfin: ha... |
475 |
clear_dma_irqstat(drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
476 477 |
/* |
d6fe89b06 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 Blackfin: blackfi... |
482 483 |
*/ if (drv_data->tx != NULL) { |
47885ce81 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 Blackfin SPI driv... |
486 |
cpu_relax(); |
a5f6abd4f Blackfin: blackfi... |
487 |
} |
aaaf939c5 Blackfin SPI Driv... |
488 489 490 |
dev_dbg(&drv_data->pdev->dev, "in dma_irq_handler dmastat:0x%x spistat:0x%x ", |
47885ce81 spi/bfin_spi: use... |
491 |
dmastat, bfin_read(&drv_data->regs->stat)); |
aaaf939c5 Blackfin SPI Driv... |
492 493 |
timeout = jiffies + HZ; |
47885ce81 spi/bfin_spi: use... |
494 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF)) |
aaaf939c5 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 Blackfin: blackfi... |
500 |
|
90008a641 spi/bfin_spi: use... |
501 |
if ((dmastat & DMA_ERR) && (spistat & BIT_STAT_RBSY)) { |
04b95d2f7 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 Blackfin: blackfi... |
507 |
|
04b95d2f7 Blackfin SPI Driv... |
508 |
if (drv_data->cs_change) |
138f97cd0 Blackfin SPI Driv... |
509 |
bfin_spi_cs_deactive(drv_data, chip); |
fad91c890 spi: spi_bfin han... |
510 |
|
04b95d2f7 Blackfin SPI Driv... |
511 |
/* Move to next transfer */ |
138f97cd0 Blackfin SPI Driv... |
512 |
msg->state = bfin_spi_next_transfer(drv_data); |
04b95d2f7 Blackfin SPI Driv... |
513 |
} |
a5f6abd4f Blackfin: blackfi... |
514 515 516 517 518 |
/* Schedule transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); /* free the irq handler before next transfer */ |
88b403693 Blackfin SPI: cle... |
519 520 521 |
dev_dbg(&drv_data->pdev->dev, "disable dma channel irq%d ", |
bb90eb00b spi: spi_bfin: ha... |
522 |
drv_data->dma_channel); |
a75bd65b2 spi/bfin_spi: use... |
523 |
dma_disable_irq_nosync(drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
524 525 526 |
return IRQ_HANDLED; } |
138f97cd0 Blackfin SPI Driv... |
527 |
static void bfin_spi_pump_transfers(unsigned long data) |
a5f6abd4f Blackfin: blackfi... |
528 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
529 |
struct bfin_spi_master_data *drv_data = (struct bfin_spi_master_data *)data; |
a5f6abd4f Blackfin: blackfi... |
530 531 532 |
struct spi_message *message = NULL; struct spi_transfer *transfer = NULL; struct spi_transfer *previous = NULL; |
9c0a788b4 spi/bfin_spi: nam... |
533 |
struct bfin_spi_slave_data *chip = NULL; |
033f44bd0 spi/bfin_spi: pus... |
534 |
unsigned int bits_per_word; |
5e8592dca spi/bfin_spi: com... |
535 |
u16 cr, cr_width, dma_width, dma_config; |
a5f6abd4f Blackfin: blackfi... |
536 |
u32 tranf_success = 1; |
8eeb12e5a spi_bfin5xx: use ... |
537 |
u8 full_duplex = 0; |
a5f6abd4f 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 Blackfin SPI driv... |
543 |
|
a5f6abd4f 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 Blackfin SPI Driv... |
550 551 |
dev_dbg(&drv_data->pdev->dev, "transfer: we've hit an error "); |
a5f6abd4f Blackfin: blackfi... |
552 |
message->status = -EIO; |
138f97cd0 Blackfin SPI Driv... |
553 |
bfin_spi_giveback(drv_data); |
a5f6abd4f Blackfin: blackfi... |
554 555 556 557 558 |
return; } /* Handle end of message */ if (message->state == DONE_STATE) { |
d24bd1d0d Blackfin SPI Driv... |
559 560 |
dev_dbg(&drv_data->pdev->dev, "transfer: all done! "); |
a5f6abd4f Blackfin: blackfi... |
561 |
message->status = 0; |
138f97cd0 Blackfin SPI Driv... |
562 |
bfin_spi_giveback(drv_data); |
a5f6abd4f Blackfin: blackfi... |
563 564 565 566 567 |
return; } /* Delay if requested at end of transfer */ if (message->state == RUNNING_STATE) { |
d24bd1d0d Blackfin SPI Driv... |
568 569 |
dev_dbg(&drv_data->pdev->dev, "transfer: still running ... "); |
a5f6abd4f 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 spi/bfin_spi: fix... |
575 |
/* Flush any existing transfers that may be sitting in the hardware */ |
138f97cd0 Blackfin SPI Driv... |
576 |
if (bfin_spi_flush(drv_data) == 0) { |
a5f6abd4f Blackfin: blackfi... |
577 578 579 |
dev_err(&drv_data->pdev->dev, "pump_transfers: flush failed "); message->status = -EIO; |
138f97cd0 Blackfin SPI Driv... |
580 |
bfin_spi_giveback(drv_data); |
a5f6abd4f Blackfin: blackfi... |
581 582 |
return; } |
93b61bddc 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 spi/bfin_spi: ret... |
588 |
return; |
93b61bddc Blackfin SPI Driv... |
589 |
} |
a5f6abd4f 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 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 Blackfin: blackfi... |
596 597 598 599 600 |
} else { drv_data->tx = NULL; } if (transfer->rx_buf != NULL) { |
8eeb12e5a spi_bfin5xx: use ... |
601 |
full_duplex = transfer->tx_buf != NULL; |
a5f6abd4f Blackfin: blackfi... |
602 603 |
drv_data->rx = transfer->rx_buf; drv_data->rx_end = drv_data->rx + transfer->len; |
88b403693 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 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 spi: spi_bfin han... |
614 |
drv_data->cs_change = transfer->cs_change; |
a5f6abd4f Blackfin: blackfi... |
615 |
|
092e1fdaf Blackfin SPI driv... |
616 |
/* Bits per word setup */ |
e479c6045 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 spi/bfin_spi: sup... |
620 |
drv_data->n_bytes = bits_per_word/8; |
5e8592dca spi/bfin_spi: com... |
621 622 |
drv_data->len = (transfer->len) >> 1; cr_width = BIT_CTL_WORDSIZE; |
9c0a788b4 spi/bfin_spi: nam... |
623 |
drv_data->ops = &bfin_bfin_spi_transfer_ops_u16; |
e479c6045 spi/bfin_spi: fix... |
624 |
} else if (bits_per_word % 8 == 0) { |
4d676fc5c 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 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 Blackfin SPI driv... |
635 |
} |
47885ce81 spi/bfin_spi: use... |
636 |
cr = bfin_read(&drv_data->regs->ctl) & ~(BIT_CTL_TIMOD | BIT_CTL_WORDSIZE); |
5e8592dca spi/bfin_spi: com... |
637 |
cr |= cr_width; |
47885ce81 spi/bfin_spi: use... |
638 |
bfin_write(&drv_data->regs->ctl, cr); |
092e1fdaf Blackfin SPI driv... |
639 |
|
4fb98efac spi: spi_bfin5xx ... |
640 |
dev_dbg(&drv_data->pdev->dev, |
9c4542c7a spi/bfin_spi: con... |
641 642 |
"transfer: drv_data->ops is %p, chip->ops is %p, u8_ops is %p ", |
9c0a788b4 spi/bfin_spi: nam... |
643 |
drv_data->ops, chip->ops, &bfin_bfin_spi_transfer_ops_u8); |
a5f6abd4f Blackfin: blackfi... |
644 |
|
a5f6abd4f Blackfin: blackfi... |
645 646 |
message->state = RUNNING_STATE; dma_config = 0; |
092e1fdaf Blackfin SPI driv... |
647 648 |
/* Speed setup (surely valid because already checked) */ if (transfer->speed_hz) |
47885ce81 spi/bfin_spi: use... |
649 |
bfin_write(&drv_data->regs->baud, hz_to_spi_baud(transfer->speed_hz)); |
092e1fdaf Blackfin SPI driv... |
650 |
else |
47885ce81 spi/bfin_spi: use... |
651 |
bfin_write(&drv_data->regs->baud, chip->baud); |
092e1fdaf Blackfin SPI driv... |
652 |
|
47885ce81 spi/bfin_spi: use... |
653 |
bfin_write(&drv_data->regs->stat, BIT_STAT_CLR); |
e72dcde72 spi/bfin_spi: cs ... |
654 |
bfin_spi_cs_active(drv_data, chip); |
a5f6abd4f Blackfin: blackfi... |
655 |
|
88b403693 Blackfin SPI: cle... |
656 657 658 |
dev_dbg(&drv_data->pdev->dev, "now pumping a transfer: width is %d, len is %d ", |
5e8592dca spi/bfin_spi: com... |
659 |
cr_width, transfer->len); |
a5f6abd4f Blackfin: blackfi... |
660 661 |
/* |
8cf5858c5 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 Blackfin: blackfi... |
666 |
*/ |
8eeb12e5a spi_bfin5xx: use ... |
667 668 |
if (!full_duplex && drv_data->cur_chip->enable_dma && drv_data->len > 6) { |
a5f6abd4f Blackfin: blackfi... |
669 |
|
11d6f5995 Blackfin SPI Driv... |
670 |
unsigned long dma_start_addr, flags; |
7aec35661 Blackfin SPI Driv... |
671 |
|
bb90eb00b spi: spi_bfin: ha... |
672 673 |
disable_dma(drv_data->dma_channel); clear_dma_irqstat(drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
674 675 |
/* config dma channel */ |
88b403693 Blackfin SPI: cle... |
676 677 |
dev_dbg(&drv_data->pdev->dev, "doing dma transfer "); |
7aec35661 Blackfin SPI Driv... |
678 |
set_dma_x_count(drv_data->dma_channel, drv_data->len); |
5e8592dca spi/bfin_spi: com... |
679 |
if (cr_width == BIT_CTL_WORDSIZE) { |
bb90eb00b spi: spi_bfin: ha... |
680 |
set_dma_x_modify(drv_data->dma_channel, 2); |
a5f6abd4f Blackfin: blackfi... |
681 682 |
dma_width = WDSIZE_16; } else { |
bb90eb00b spi: spi_bfin: ha... |
683 |
set_dma_x_modify(drv_data->dma_channel, 1); |
a5f6abd4f Blackfin: blackfi... |
684 685 |
dma_width = WDSIZE_8; } |
3f479a65b spi: spi_bfin: re... |
686 |
/* poll for SPI completion before start */ |
47885ce81 spi/bfin_spi: use... |
687 |
while (!(bfin_read(&drv_data->regs->stat) & BIT_STAT_SPIF)) |
d8c05008b Blackfin SPI driv... |
688 |
cpu_relax(); |
3f479a65b spi: spi_bfin: re... |
689 |
|
a5f6abd4f Blackfin: blackfi... |
690 691 |
/* dirty hack for autobuffer DMA mode */ if (drv_data->tx_dma == 0xFFFF) { |
88b403693 Blackfin SPI: cle... |
692 693 694 |
dev_dbg(&drv_data->pdev->dev, "doing autobuffer DMA out. "); |
a5f6abd4f Blackfin: blackfi... |
695 696 697 698 |
/* no irq in autobuffer mode */ dma_config = (DMAFLOW_AUTO | RESTART | dma_width | DI_EN); |
bb90eb00b spi: spi_bfin: ha... |
699 700 |
set_dma_config(drv_data->dma_channel, dma_config); set_dma_start_addr(drv_data->dma_channel, |
a32c691d7 spi: spi_bfin use... |
701 |
(unsigned long)drv_data->tx); |
bb90eb00b spi: spi_bfin: ha... |
702 |
enable_dma(drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
703 |
|
07612e5f2 spi: spi_bfin: re... |
704 |
/* start SPI transfer */ |
47885ce81 spi/bfin_spi: use... |
705 |
bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TIMOD_DMA_TX); |
07612e5f2 spi: spi_bfin: re... |
706 707 708 709 |
/* just return here, there can only be one transfer * in this mode */ |
a5f6abd4f Blackfin: blackfi... |
710 |
message->status = 0; |
138f97cd0 Blackfin SPI Driv... |
711 |
bfin_spi_giveback(drv_data); |
a5f6abd4f Blackfin: blackfi... |
712 713 714 715 |
return; } /* In dma mode, rx or tx must be NULL in one transfer */ |
7aec35661 Blackfin SPI Driv... |
716 |
dma_config = (RESTART | dma_width | DI_EN); |
a5f6abd4f Blackfin: blackfi... |
717 718 |
if (drv_data->rx != NULL) { /* set transfer mode, and enable SPI */ |
d24bd1d0d 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 Blackfin: blackfi... |
722 |
|
8cf5858c5 Blackfin SPI Driv... |
723 |
/* invalidate caches, if needed */ |
67834fa93 Blackfin: rename ... |
724 |
if (bfin_addr_dcacheable((unsigned long) drv_data->rx)) |
8cf5858c5 Blackfin SPI Driv... |
725 726 |
invalidate_dcache_range((unsigned long) drv_data->rx, (unsigned long) (drv_data->rx + |
ace32865a Blackfin SPI Driv... |
727 |
drv_data->len_in_bytes)); |
8cf5858c5 Blackfin SPI Driv... |
728 |
|
7aec35661 Blackfin SPI Driv... |
729 730 |
dma_config |= WNR; dma_start_addr = (unsigned long)drv_data->rx; |
b31e27a6d Blackfin SPI Driv... |
731 |
cr |= BIT_CTL_TIMOD_DMA_RX | BIT_CTL_SENDOPT; |
07612e5f2 spi: spi_bfin: re... |
732 |
|
a5f6abd4f Blackfin: blackfi... |
733 |
} else if (drv_data->tx != NULL) { |
88b403693 Blackfin SPI: cle... |
734 735 |
dev_dbg(&drv_data->pdev->dev, "doing DMA out. "); |
a5f6abd4f Blackfin: blackfi... |
736 |
|
8cf5858c5 Blackfin SPI Driv... |
737 |
/* flush caches, if needed */ |
67834fa93 Blackfin: rename ... |
738 |
if (bfin_addr_dcacheable((unsigned long) drv_data->tx)) |
8cf5858c5 Blackfin SPI Driv... |
739 740 |
flush_dcache_range((unsigned long) drv_data->tx, (unsigned long) (drv_data->tx + |
ace32865a Blackfin SPI Driv... |
741 |
drv_data->len_in_bytes)); |
8cf5858c5 Blackfin SPI Driv... |
742 |
|
7aec35661 Blackfin SPI Driv... |
743 |
dma_start_addr = (unsigned long)drv_data->tx; |
b31e27a6d Blackfin SPI Driv... |
744 |
cr |= BIT_CTL_TIMOD_DMA_TX; |
7aec35661 Blackfin SPI Driv... |
745 746 747 |
} else BUG(); |
11d6f5995 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 Blackfin SPI Driv... |
757 |
set_dma_start_addr(drv_data->dma_channel, dma_start_addr); |
11d6f5995 Blackfin SPI Driv... |
758 759 |
set_dma_config(drv_data->dma_channel, dma_config); local_irq_save(flags); |
a963ea83b Blackfin SPI Driv... |
760 |
SSYNC(); |
47885ce81 spi/bfin_spi: use... |
761 |
bfin_write(&drv_data->regs->ctl, cr); |
a963ea83b Blackfin SPI Driv... |
762 |
enable_dma(drv_data->dma_channel); |
11d6f5995 Blackfin SPI Driv... |
763 764 |
dma_enable_irq(drv_data->dma_channel); local_irq_restore(flags); |
07612e5f2 spi: spi_bfin: re... |
765 |
|
f6a6d9668 spi/bfin_spi: uti... |
766 767 |
return; } |
a5f6abd4f Blackfin: blackfi... |
768 |
|
5e8592dca 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 spi/bfin_spi: use... |
775 |
bfin_write(&drv_data->regs->ctl, cr | BIT_CTL_TXMOD); |
5e8592dca spi/bfin_spi: com... |
776 |
|
f6a6d9668 spi/bfin_spi: uti... |
777 |
if (chip->pio_interrupt) { |
5e8592dca spi/bfin_spi: com... |
778 |
/* SPI irq should have been disabled by now */ |
93b61bddc Blackfin SPI Driv... |
779 |
|
f6a6d9668 spi/bfin_spi: uti... |
780 781 |
/* discard old RX data and clear RXS */ bfin_spi_dummy_read(drv_data); |
a5f6abd4f Blackfin: blackfi... |
782 |
|
f6a6d9668 spi/bfin_spi: uti... |
783 784 |
/* start transfer */ if (drv_data->tx == NULL) |
47885ce81 spi/bfin_spi: use... |
785 |
bfin_write(&drv_data->regs->tdbr, chip->idle_tx_val); |
f6a6d9668 spi/bfin_spi: uti... |
786 |
else { |
4d676fc5c 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 spi/bfin_spi: use... |
792 |
bfin_write(&drv_data->regs->tdbr, *buf++); |
4d676fc5c 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 spi/bfin_spi: use... |
797 |
bfin_write(&drv_data->regs->tdbr, *buf++); |
4d676fc5c spi/bfin_spi: sup... |
798 |
} |
f6a6d9668 spi/bfin_spi: uti... |
799 800 |
drv_data->tx += drv_data->n_bytes; } |
a5f6abd4f Blackfin: blackfi... |
801 |
|
f6a6d9668 spi/bfin_spi: uti... |
802 803 804 805 |
/* once TDBR is empty, interrupt is triggered */ enable_irq(drv_data->spi_irq); return; } |
a5f6abd4f Blackfin: blackfi... |
806 |
|
f6a6d9668 spi/bfin_spi: uti... |
807 808 809 |
/* IO mode */ dev_dbg(&drv_data->pdev->dev, "doing IO transfer "); |
f6a6d9668 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 spi/bfin_spi: con... |
817 |
drv_data->ops->duplex(drv_data); |
f6a6d9668 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 spi/bfin_spi: con... |
826 |
drv_data->ops->write(drv_data); |
f6a6d9668 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 spi/bfin_spi: con... |
835 |
drv_data->ops->read(drv_data); |
f6a6d9668 spi/bfin_spi: uti... |
836 837 838 |
if (drv_data->rx != drv_data->rx_end) tranf_success = 0; } |
a5f6abd4f Blackfin: blackfi... |
839 |
|
f6a6d9668 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 Fix common misspe... |
846 |
/* Update total byte transferred */ |
f6a6d9668 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 Blackfin: blackfi... |
852 |
} |
f6a6d9668 spi/bfin_spi: uti... |
853 854 855 |
/* Schedule next transfer tasklet */ tasklet_schedule(&drv_data->pump_transfers); |
a5f6abd4f Blackfin: blackfi... |
856 857 858 |
} /* pop a msg from queue and kick off real transfer */ |
138f97cd0 Blackfin SPI Driv... |
859 |
static void bfin_spi_pump_messages(struct work_struct *work) |
a5f6abd4f Blackfin: blackfi... |
860 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
861 |
struct bfin_spi_master_data *drv_data; |
a5f6abd4f Blackfin: blackfi... |
862 |
unsigned long flags; |
9c0a788b4 spi/bfin_spi: nam... |
863 |
drv_data = container_of(work, struct bfin_spi_master_data, pump_messages); |
131b17d42 spi: initial BF54... |
864 |
|
a5f6abd4f Blackfin: blackfi... |
865 866 |
/* Lock queue and check for queue work */ spin_lock_irqsave(&drv_data->lock, flags); |
f4f50c3ff spi/bfin_spi: con... |
867 |
if (list_empty(&drv_data->queue) || !drv_data->running) { |
a5f6abd4f 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 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 Blackfin SPI Driv... |
886 |
bfin_spi_restore_state(drv_data); |
5fec5b5a4 spi: spi_bfin cle... |
887 |
|
a5f6abd4f 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 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 spi: initial BF54... |
899 900 |
dev_dbg(&drv_data->pdev->dev, |
88b403693 Blackfin SPI: cle... |
901 902 903 |
"the first transfer len is %d ", drv_data->cur_transfer->len); |
a5f6abd4f 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 Blackfin SPI Driv... |
916 |
static int bfin_spi_transfer(struct spi_device *spi, struct spi_message *msg) |
a5f6abd4f Blackfin: blackfi... |
917 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
918 |
struct bfin_spi_master_data *drv_data = spi_master_get_devdata(spi->master); |
a5f6abd4f Blackfin: blackfi... |
919 920 921 |
unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); |
f4f50c3ff spi/bfin_spi: con... |
922 |
if (!drv_data->running) { |
a5f6abd4f 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 Blackfin SPI: cle... |
930 931 |
dev_dbg(&spi->dev, "adding an msg in transfer() "); |
a5f6abd4f Blackfin: blackfi... |
932 |
list_add_tail(&msg->queue, &drv_data->queue); |
f4f50c3ff spi/bfin_spi: con... |
933 |
if (drv_data->running && !drv_data->busy) |
a5f6abd4f 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 spi: spi_bfin, re... |
940 |
#define MAX_SPI_SSEL 7 |
ddc0bf13d spi/bfin_spi: con... |
941 |
static const u16 ssel[][MAX_SPI_SSEL] = { |
12e17c426 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 spi/bfin_spi: fix... |
954 |
/* setup for devices (may be called multiple times -- not just first setup) */ |
138f97cd0 Blackfin SPI Driv... |
955 |
static int bfin_spi_setup(struct spi_device *spi) |
a5f6abd4f Blackfin: blackfi... |
956 |
{ |
ac01e97d6 spi/bfin_spi: fix... |
957 |
struct bfin5xx_spi_chip *chip_info; |
9c0a788b4 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 spi/bfin_spi: res... |
960 |
u16 bfin_ctl_reg; |
ac01e97d6 spi/bfin_spi: fix... |
961 |
int ret = -EINVAL; |
a5f6abd4f Blackfin: blackfi... |
962 |
|
a5f6abd4f Blackfin: blackfi... |
963 |
/* Only alloc (or use chip_info) on first setup */ |
ac01e97d6 spi/bfin_spi: fix... |
964 |
chip_info = NULL; |
a5f6abd4f Blackfin: blackfi... |
965 966 |
chip = spi_get_ctldata(spi); if (chip == NULL) { |
ac01e97d6 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 Blackfin: blackfi... |
974 975 976 977 |
chip->enable_dma = 0; chip_info = spi->controller_data; } |
5b47bcd48 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 Blackfin: blackfi... |
981 982 |
/* chip_info isn't always needed */ if (chip_info) { |
2ed355165 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 spi/bfin_spi: use... |
985 |
* Currently we let through: WOM EMISO PSSE GM SZ. |
2ed355165 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 spi/bfin_spi: res... |
989 |
if (chip_info->ctl_reg & ~bfin_ctl_reg) { |
2ed355165 spi: spi_bfin, do... |
990 991 992 |
dev_err(&spi->dev, "do not set bits in ctl_reg " "that the SPI framework manages "); |
ac01e97d6 spi/bfin_spi: fix... |
993 |
goto error; |
2ed355165 spi: spi_bfin, do... |
994 |
} |
a5f6abd4f 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 Blackfin: blackfi... |
998 |
chip->cs_chg_udelay = chip_info->cs_chg_udelay; |
93b61bddc Blackfin SPI Driv... |
999 |
chip->idle_tx_val = chip_info->idle_tx_val; |
f6a6d9668 spi/bfin_spi: uti... |
1000 |
chip->pio_interrupt = chip_info->pio_interrupt; |
033f44bd0 spi/bfin_spi: pus... |
1001 |
spi->bits_per_word = chip_info->bits_per_word; |
5b47bcd48 spi/bfin_spi: res... |
1002 1003 1004 |
} else { /* force a default base state */ chip->ctl_reg &= bfin_ctl_reg; |
033f44bd0 spi/bfin_spi: pus... |
1005 |
} |
4d676fc5c spi/bfin_spi: sup... |
1006 |
if (spi->bits_per_word % 8) { |
033f44bd0 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 Blackfin: blackfi... |
1011 1012 1013 |
} /* translate common spi framework into our register */ |
7715aad4e 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 Blackfin: blackfi... |
1019 |
if (spi->mode & SPI_CPOL) |
90008a641 spi/bfin_spi: use... |
1020 |
chip->ctl_reg |= BIT_CTL_CPOL; |
a5f6abd4f Blackfin: blackfi... |
1021 |
if (spi->mode & SPI_CPHA) |
90008a641 spi/bfin_spi: use... |
1022 |
chip->ctl_reg |= BIT_CTL_CPHA; |
a5f6abd4f Blackfin: blackfi... |
1023 |
if (spi->mode & SPI_LSB_FIRST) |
90008a641 spi/bfin_spi: use... |
1024 |
chip->ctl_reg |= BIT_CTL_LSBF; |
a5f6abd4f Blackfin: blackfi... |
1025 |
/* we dont support running in slave mode (yet?) */ |
90008a641 spi/bfin_spi: use... |
1026 |
chip->ctl_reg |= BIT_CTL_MASTER; |
a5f6abd4f Blackfin: blackfi... |
1027 1028 |
/* |
a5f6abd4f 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 Blackfin: blackfi... |
1033 |
chip->chip_select_num = spi->chip_select; |
4190f6a51 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 spi/bfin_spi: red... |
1040 |
chip->flag = (1 << spi->chip_select) << 8; |
4190f6a51 spi/bfin_spi: war... |
1041 |
} else |
d3cc71f71 spi/bfin_spi: red... |
1042 |
chip->cs_gpio = chip->chip_select_num - MAX_CTRL_CS; |
a5f6abd4f Blackfin: blackfi... |
1043 |
|
f6a6d9668 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 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 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 spi: irq: Remove ... |
1076 |
0, "BFIN_SPI", drv_data); |
f6a6d9668 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 spi/bfin_spi: red... |
1086 |
if (chip->chip_select_num >= MAX_CTRL_CS) { |
73e1ac162 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 spi/bfin_spi: fix... |
1096 |
} |
a5f6abd4f Blackfin: blackfi... |
1097 |
} |
898eb71cb Add missing newli... |
1098 1099 |
dev_dbg(&spi->dev, "setup spi chip %s, width is %d, dma is %d ", |
033f44bd0 spi/bfin_spi: pus... |
1100 |
spi->modalias, spi->bits_per_word, chip->enable_dma); |
88b403693 Blackfin SPI: cle... |
1101 1102 |
dev_dbg(&spi->dev, "ctl_reg is 0x%x, flag_reg is 0x%x ", |
a5f6abd4f Blackfin: blackfi... |
1103 1104 1105 |
chip->ctl_reg, chip->flag); spi_set_ctldata(spi, chip); |
12e17c426 spi: spi_bfin, re... |
1106 1107 |
dev_dbg(&spi->dev, "chip select number is %d ", chip->chip_select_num); |
d3cc71f71 spi/bfin_spi: red... |
1108 |
if (chip->chip_select_num < MAX_CTRL_CS) { |
ac01e97d6 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 spi: spi_bfin, re... |
1117 |
|
8221610e9 spi/bfin_spi: fix... |
1118 |
bfin_spi_cs_enable(drv_data, chip); |
138f97cd0 Blackfin SPI Driv... |
1119 |
bfin_spi_cs_deactive(drv_data, chip); |
07612e5f2 spi: spi_bfin: re... |
1120 |
|
a5f6abd4f Blackfin: blackfi... |
1121 |
return 0; |
ac01e97d6 spi/bfin_spi: fix... |
1122 1123 |
pin_error: |
d3cc71f71 spi/bfin_spi: red... |
1124 |
if (chip->chip_select_num >= MAX_CTRL_CS) |
ac01e97d6 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 Blackfin: blackfi... |
1141 1142 1143 1144 1145 1146 |
} /* * callback for spi framework. * clean driver specific data */ |
138f97cd0 Blackfin SPI Driv... |
1147 |
static void bfin_spi_cleanup(struct spi_device *spi) |
a5f6abd4f Blackfin: blackfi... |
1148 |
{ |
9c0a788b4 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 Blackfin: blackfi... |
1151 |
|
e7d02e3c9 Blackfin SPI Driv... |
1152 1153 |
if (!chip) return; |
d3cc71f71 spi/bfin_spi: red... |
1154 |
if (chip->chip_select_num < MAX_CTRL_CS) { |
12e17c426 spi: spi_bfin, re... |
1155 1156 |
peripheral_free(ssel[spi->master->bus_num] [chip->chip_select_num-1]); |
8221610e9 spi/bfin_spi: fix... |
1157 |
bfin_spi_cs_disable(drv_data, chip); |
d3cc71f71 spi/bfin_spi: red... |
1158 |
} else |
42c78b2bf Blackfin SPI Driv... |
1159 |
gpio_free(chip->cs_gpio); |
a5f6abd4f Blackfin: blackfi... |
1160 |
kfree(chip); |
ac01e97d6 spi/bfin_spi: fix... |
1161 1162 |
/* prevent free 'chip' twice */ spi_set_ctldata(spi, NULL); |
a5f6abd4f Blackfin: blackfi... |
1163 |
} |
c52d4e5f3 spi/bfin_spi: uni... |
1164 |
static int bfin_spi_init_queue(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
1165 1166 1167 |
{ INIT_LIST_HEAD(&drv_data->queue); spin_lock_init(&drv_data->lock); |
f4f50c3ff spi/bfin_spi: con... |
1168 |
drv_data->running = false; |
a5f6abd4f Blackfin: blackfi... |
1169 1170 1171 1172 |
drv_data->busy = 0; /* init transfer tasklet */ tasklet_init(&drv_data->pump_transfers, |
138f97cd0 Blackfin SPI Driv... |
1173 |
bfin_spi_pump_transfers, (unsigned long)drv_data); |
a5f6abd4f Blackfin: blackfi... |
1174 1175 |
/* init messages workqueue */ |
138f97cd0 Blackfin SPI Driv... |
1176 |
INIT_WORK(&drv_data->pump_messages, bfin_spi_pump_messages); |
6c7377ab6 spi: struct devic... |
1177 1178 |
drv_data->workqueue = create_singlethread_workqueue( dev_name(drv_data->master->dev.parent)); |
a5f6abd4f Blackfin: blackfi... |
1179 1180 1181 1182 1183 |
if (drv_data->workqueue == NULL) return -EBUSY; return 0; } |
c52d4e5f3 spi/bfin_spi: uni... |
1184 |
static int bfin_spi_start_queue(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
1185 1186 1187 1188 |
{ unsigned long flags; spin_lock_irqsave(&drv_data->lock, flags); |
f4f50c3ff spi/bfin_spi: con... |
1189 |
if (drv_data->running || drv_data->busy) { |
a5f6abd4f Blackfin: blackfi... |
1190 1191 1192 |
spin_unlock_irqrestore(&drv_data->lock, flags); return -EBUSY; } |
f4f50c3ff spi/bfin_spi: con... |
1193 |
drv_data->running = true; |
a5f6abd4f 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 spi/bfin_spi: uni... |
1203 |
static int bfin_spi_stop_queue(struct bfin_spi_master_data *drv_data) |
a5f6abd4f 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 spi/bfin_spi: con... |
1217 |
drv_data->running = false; |
850a28ecd spi: Fix race con... |
1218 |
while ((!list_empty(&drv_data->queue) || drv_data->busy) && limit--) { |
a5f6abd4f 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 spi/bfin_spi: uni... |
1231 |
static int bfin_spi_destroy_queue(struct bfin_spi_master_data *drv_data) |
a5f6abd4f Blackfin: blackfi... |
1232 1233 |
{ int status; |
138f97cd0 Blackfin SPI Driv... |
1234 |
status = bfin_spi_stop_queue(drv_data); |
a5f6abd4f Blackfin: blackfi... |
1235 1236 1237 1238 1239 1240 1241 |
if (status != 0) return status; destroy_workqueue(drv_data->workqueue); return 0; } |
138f97cd0 Blackfin SPI Driv... |
1242 |
static int __init bfin_spi_probe(struct platform_device *pdev) |
a5f6abd4f Blackfin: blackfi... |
1243 1244 1245 1246 |
{ struct device *dev = &pdev->dev; struct bfin5xx_spi_master *platform_info; struct spi_master *master; |
9c0a788b4 spi/bfin_spi: nam... |
1247 |
struct bfin_spi_master_data *drv_data; |
a32c691d7 spi: spi_bfin use... |
1248 |
struct resource *res; |
a5f6abd4f Blackfin: blackfi... |
1249 1250 1251 1252 1253 |
int status = 0; platform_info = dev->platform_data; /* Allocate master with space for drv_data */ |
2a045131d spi/bfin_spi: dro... |
1254 |
master = spi_alloc_master(dev, sizeof(*drv_data)); |
a5f6abd4f Blackfin: blackfi... |
1255 1256 1257 1258 1259 |
if (!master) { dev_err(&pdev->dev, "can not alloc spi_master "); return -ENOMEM; } |
131b17d42 spi: initial BF54... |
1260 |
|
a5f6abd4f 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 Blackfin SPI driv... |
1265 |
drv_data->pin_req = platform_info->pin_req; |
a5f6abd4f Blackfin: blackfi... |
1266 |
|
e7db06b5d 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 Blackfin: blackfi... |
1269 1270 |
master->bus_num = pdev->id; master->num_chipselect = platform_info->num_chipselect; |
138f97cd0 Blackfin SPI Driv... |
1271 1272 1273 |
master->cleanup = bfin_spi_cleanup; master->setup = bfin_spi_setup; master->transfer = bfin_spi_transfer; |
a5f6abd4f Blackfin: blackfi... |
1274 |
|
a32c691d7 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 spi/bfin_spi: use... |
1283 1284 |
drv_data->regs = ioremap(res->start, resource_size(res)); if (drv_data->regs == NULL) { |
a32c691d7 spi: spi_bfin use... |
1285 1286 1287 1288 1289 |
dev_err(dev, "Cannot map IO "); status = -ENXIO; goto out_error_ioremap; } |
f6a6d9668 spi/bfin_spi: uti... |
1290 1291 |
res = platform_get_resource(pdev, IORESOURCE_DMA, 0); if (res == NULL) { |
a32c691d7 spi: spi_bfin use... |
1292 1293 1294 |
dev_err(dev, "No DMA channel specified "); status = -ENOENT; |
f6a6d9668 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 spi: spi_bfin use... |
1305 |
} |
a5f6abd4f Blackfin: blackfi... |
1306 |
/* Initial and start queue */ |
138f97cd0 Blackfin SPI Driv... |
1307 |
status = bfin_spi_init_queue(drv_data); |
a5f6abd4f Blackfin: blackfi... |
1308 |
if (status != 0) { |
a32c691d7 spi: spi_bfin use... |
1309 1310 |
dev_err(dev, "problem initializing queue "); |
a5f6abd4f Blackfin: blackfi... |
1311 1312 |
goto out_error_queue_alloc; } |
a32c691d7 spi: spi_bfin use... |
1313 |
|
138f97cd0 Blackfin SPI Driv... |
1314 |
status = bfin_spi_start_queue(drv_data); |
a5f6abd4f Blackfin: blackfi... |
1315 |
if (status != 0) { |
a32c691d7 spi: spi_bfin use... |
1316 1317 |
dev_err(dev, "problem starting queue "); |
a5f6abd4f Blackfin: blackfi... |
1318 1319 |
goto out_error_queue_alloc; } |
f9e522cae 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 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 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 spi/bfin_spi: for... |
1331 |
|
a5f6abd4f 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 spi: spi_bfin use... |
1336 1337 |
dev_err(dev, "problem registering spi master "); |
a5f6abd4f Blackfin: blackfi... |
1338 1339 |
goto out_error_queue_alloc; } |
a32c691d7 spi: spi_bfin use... |
1340 |
|
47885ce81 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 spi: spi_bfin: ha... |
1344 |
drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
1345 |
return status; |
cc2f81a69 spi: bfin spi use... |
1346 |
out_error_queue_alloc: |
138f97cd0 Blackfin SPI Driv... |
1347 |
bfin_spi_destroy_queue(drv_data); |
f6a6d9668 spi/bfin_spi: uti... |
1348 |
out_error_free_io: |
47885ce81 spi/bfin_spi: use... |
1349 |
iounmap(drv_data->regs); |
a32c691d7 spi: spi_bfin use... |
1350 1351 |
out_error_ioremap: out_error_get_res: |
a5f6abd4f Blackfin: blackfi... |
1352 |
spi_master_put(master); |
cc2f81a69 spi: bfin spi use... |
1353 |
|
a5f6abd4f Blackfin: blackfi... |
1354 1355 1356 1357 |
return status; } /* stop hardware and remove the driver */ |
138f97cd0 Blackfin SPI Driv... |
1358 |
static int __devexit bfin_spi_remove(struct platform_device *pdev) |
a5f6abd4f Blackfin: blackfi... |
1359 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
1360 |
struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); |
a5f6abd4f Blackfin: blackfi... |
1361 1362 1363 1364 1365 1366 |
int status = 0; if (!drv_data) return 0; /* Remove the queue */ |
138f97cd0 Blackfin SPI Driv... |
1367 |
status = bfin_spi_destroy_queue(drv_data); |
a5f6abd4f 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 spi: spi_bfin: ha... |
1376 1377 |
if (dma_channel_active(drv_data->dma_channel)) free_dma(drv_data->dma_channel); |
a5f6abd4f Blackfin: blackfi... |
1378 |
} |
f6a6d9668 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 Blackfin: blackfi... |
1383 1384 |
/* Disconnect from the SPI framework */ spi_unregister_master(drv_data->master); |
003d92261 Blackfin SPI driv... |
1385 |
peripheral_free_list(drv_data->pin_req); |
cc2f81a69 spi: bfin spi use... |
1386 |
|
a5f6abd4f Blackfin: blackfi... |
1387 1388 1389 1390 1391 1392 1393 |
/* Prevent double remove */ platform_set_drvdata(pdev, NULL); return 0; } #ifdef CONFIG_PM |
138f97cd0 Blackfin SPI Driv... |
1394 |
static int bfin_spi_suspend(struct platform_device *pdev, pm_message_t state) |
a5f6abd4f Blackfin: blackfi... |
1395 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
1396 |
struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); |
a5f6abd4f Blackfin: blackfi... |
1397 |
int status = 0; |
138f97cd0 Blackfin SPI Driv... |
1398 |
status = bfin_spi_stop_queue(drv_data); |
a5f6abd4f Blackfin: blackfi... |
1399 1400 |
if (status != 0) return status; |
47885ce81 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 spi/bfin_spi: sav... |
1403 1404 1405 1406 |
/* * reset SPI_CTL and SPI_FLG registers */ |
47885ce81 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 Blackfin: blackfi... |
1409 1410 1411 |
return 0; } |
138f97cd0 Blackfin SPI Driv... |
1412 |
static int bfin_spi_resume(struct platform_device *pdev) |
a5f6abd4f Blackfin: blackfi... |
1413 |
{ |
9c0a788b4 spi/bfin_spi: nam... |
1414 |
struct bfin_spi_master_data *drv_data = platform_get_drvdata(pdev); |
a5f6abd4f Blackfin: blackfi... |
1415 |
int status = 0; |
47885ce81 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 Blackfin: blackfi... |
1418 1419 |
/* Start the queue running */ |
138f97cd0 Blackfin SPI Driv... |
1420 |
status = bfin_spi_start_queue(drv_data); |
a5f6abd4f 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 Blackfin SPI Driv... |
1430 1431 |
#define bfin_spi_suspend NULL #define bfin_spi_resume NULL |
a5f6abd4f Blackfin: blackfi... |
1432 |
#endif /* CONFIG_PM */ |
7e38c3c44 spi: fix platform... |
1433 |
MODULE_ALIAS("platform:bfin-spi"); |
138f97cd0 Blackfin SPI Driv... |
1434 |
static struct platform_driver bfin_spi_driver = { |
fc3ba9525 SPI driver hotplu... |
1435 |
.driver = { |
a32c691d7 spi: spi_bfin use... |
1436 |
.name = DRV_NAME, |
88b403693 Blackfin SPI: cle... |
1437 1438 |
.owner = THIS_MODULE, }, |
138f97cd0 Blackfin SPI Driv... |
1439 1440 1441 |
.suspend = bfin_spi_suspend, .resume = bfin_spi_resume, .remove = __devexit_p(bfin_spi_remove), |
a5f6abd4f Blackfin: blackfi... |
1442 |
}; |
138f97cd0 Blackfin SPI Driv... |
1443 |
static int __init bfin_spi_init(void) |
a5f6abd4f Blackfin: blackfi... |
1444 |
{ |
138f97cd0 Blackfin SPI Driv... |
1445 |
return platform_driver_probe(&bfin_spi_driver, bfin_spi_probe); |
a5f6abd4f Blackfin: blackfi... |
1446 |
} |
6f7c17f4f spi/bfin_spi: ini... |
1447 |
subsys_initcall(bfin_spi_init); |
a5f6abd4f Blackfin: blackfi... |
1448 |
|
138f97cd0 Blackfin SPI Driv... |
1449 |
static void __exit bfin_spi_exit(void) |
a5f6abd4f Blackfin: blackfi... |
1450 |
{ |
138f97cd0 Blackfin SPI Driv... |
1451 |
platform_driver_unregister(&bfin_spi_driver); |
a5f6abd4f Blackfin: blackfi... |
1452 |
} |
138f97cd0 Blackfin SPI Driv... |
1453 |
module_exit(bfin_spi_exit); |