4 * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
6 * Based on spi_s3c24xx.c, which is:
7 * Copyright (c) 2006 Ben Dooks
8 * Copyright (c) 2006 Simtec Electronics
9 * Ben Dooks <ben@simtec.co.uk>
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License version 2 as
13 * published by the Free Software Foundation.
16 #include <linux/init.h>
17 #include <linux/interrupt.h>
18 #include <linux/errno.h>
19 #include <linux/module.h>
20 #include <linux/platform_device.h>
21 #include <linux/spi/spi.h>
22 #include <linux/spi/spi_bitbang.h>
26 #define DRV_NAME "spi_altera"
28 #define ALTERA_SPI_RXDATA 0
29 #define ALTERA_SPI_TXDATA 4
30 #define ALTERA_SPI_STATUS 8
31 #define ALTERA_SPI_CONTROL 12
32 #define ALTERA_SPI_SLAVE_SEL 20
34 #define ALTERA_SPI_STATUS_ROE_MSK 0x8
35 #define ALTERA_SPI_STATUS_TOE_MSK 0x10
36 #define ALTERA_SPI_STATUS_TMT_MSK 0x20
37 #define ALTERA_SPI_STATUS_TRDY_MSK 0x40
38 #define ALTERA_SPI_STATUS_RRDY_MSK 0x80
39 #define ALTERA_SPI_STATUS_E_MSK 0x100
41 #define ALTERA_SPI_CONTROL_IROE_MSK 0x8
42 #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
43 #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
44 #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
45 #define ALTERA_SPI_CONTROL_IE_MSK 0x100
46 #define ALTERA_SPI_CONTROL_SSO_MSK 0x400
49 /* bitbang has to be first */
50 struct spi_bitbang bitbang
;
51 struct completion done
;
61 const unsigned char *tx
;
65 static inline struct altera_spi
*altera_spi_to_hw(struct spi_device
*sdev
)
67 return spi_master_get_devdata(sdev
->master
);
70 static void altera_spi_chipsel(struct spi_device
*spi
, int value
)
72 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
74 if (spi
->mode
& SPI_CS_HIGH
) {
76 case BITBANG_CS_INACTIVE
:
77 writel(1 << spi
->chip_select
,
78 hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
79 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
80 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
83 case BITBANG_CS_ACTIVE
:
84 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
85 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
86 writel(0, hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
91 case BITBANG_CS_INACTIVE
:
92 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
93 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
96 case BITBANG_CS_ACTIVE
:
97 writel(1 << spi
->chip_select
,
98 hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
99 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
100 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
106 static inline unsigned int hw_txbyte(struct altera_spi
*hw
, int count
)
109 switch (hw
->bytes_per_word
) {
111 return hw
->tx
[count
];
113 return (hw
->tx
[count
* 2]
114 | (hw
->tx
[count
* 2 + 1] << 8));
120 static int altera_spi_txrx(struct spi_device
*spi
, struct spi_transfer
*t
)
122 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
127 hw
->bytes_per_word
= DIV_ROUND_UP(t
->bits_per_word
, 8);
128 hw
->len
= t
->len
/ hw
->bytes_per_word
;
131 /* enable receive interrupt */
132 hw
->imr
|= ALTERA_SPI_CONTROL_IRRDY_MSK
;
133 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
135 /* send the first byte */
136 writel(hw_txbyte(hw
, 0), hw
->base
+ ALTERA_SPI_TXDATA
);
138 wait_for_completion(&hw
->done
);
139 /* disable receive interrupt */
140 hw
->imr
&= ~ALTERA_SPI_CONTROL_IRRDY_MSK
;
141 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
143 while (hw
->count
< hw
->len
) {
146 writel(hw_txbyte(hw
, hw
->count
),
147 hw
->base
+ ALTERA_SPI_TXDATA
);
149 while (!(readl(hw
->base
+ ALTERA_SPI_STATUS
) &
150 ALTERA_SPI_STATUS_RRDY_MSK
))
153 rxd
= readl(hw
->base
+ ALTERA_SPI_RXDATA
);
155 switch (hw
->bytes_per_word
) {
157 hw
->rx
[hw
->count
] = rxd
;
160 hw
->rx
[hw
->count
* 2] = rxd
;
161 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
170 return hw
->count
* hw
->bytes_per_word
;
173 static irqreturn_t
altera_spi_irq(int irq
, void *dev
)
175 struct altera_spi
*hw
= dev
;
178 rxd
= readl(hw
->base
+ ALTERA_SPI_RXDATA
);
180 switch (hw
->bytes_per_word
) {
182 hw
->rx
[hw
->count
] = rxd
;
185 hw
->rx
[hw
->count
* 2] = rxd
;
186 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
193 if (hw
->count
< hw
->len
)
194 writel(hw_txbyte(hw
, hw
->count
), hw
->base
+ ALTERA_SPI_TXDATA
);
201 static int altera_spi_probe(struct platform_device
*pdev
)
203 struct altera_spi_platform_data
*platp
= dev_get_platdata(&pdev
->dev
);
204 struct altera_spi
*hw
;
205 struct spi_master
*master
;
206 struct resource
*res
;
209 master
= spi_alloc_master(&pdev
->dev
, sizeof(struct altera_spi
));
213 /* setup the master state. */
214 master
->bus_num
= pdev
->id
;
215 master
->num_chipselect
= 16;
216 master
->mode_bits
= SPI_CS_HIGH
;
218 hw
= spi_master_get_devdata(master
);
219 platform_set_drvdata(pdev
, hw
);
221 /* setup the state for the bitbang driver */
222 hw
->bitbang
.master
= master
;
223 hw
->bitbang
.chipselect
= altera_spi_chipsel
;
224 hw
->bitbang
.txrx_bufs
= altera_spi_txrx
;
226 /* find and map our resources */
227 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
228 hw
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
229 if (IS_ERR(hw
->base
)) {
230 err
= PTR_ERR(hw
->base
);
233 /* program defaults into the registers */
234 hw
->imr
= 0; /* disable spi interrupts */
235 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
236 writel(0, hw
->base
+ ALTERA_SPI_STATUS
); /* clear status reg */
237 if (readl(hw
->base
+ ALTERA_SPI_STATUS
) & ALTERA_SPI_STATUS_RRDY_MSK
)
238 readl(hw
->base
+ ALTERA_SPI_RXDATA
); /* flush rxdata */
239 /* irq is optional */
240 hw
->irq
= platform_get_irq(pdev
, 0);
242 init_completion(&hw
->done
);
243 err
= devm_request_irq(&pdev
->dev
, hw
->irq
, altera_spi_irq
, 0,
248 /* find platform data */
250 hw
->bitbang
.master
->dev
.of_node
= pdev
->dev
.of_node
;
252 /* register our spi controller */
253 err
= spi_bitbang_start(&hw
->bitbang
);
256 dev_info(&pdev
->dev
, "base %p, irq %d\n", hw
->base
, hw
->irq
);
260 spi_master_put(master
);
264 static int altera_spi_remove(struct platform_device
*dev
)
266 struct altera_spi
*hw
= platform_get_drvdata(dev
);
267 struct spi_master
*master
= hw
->bitbang
.master
;
269 spi_bitbang_stop(&hw
->bitbang
);
270 spi_master_put(master
);
275 static const struct of_device_id altera_spi_match
[] = {
276 { .compatible
= "ALTR,spi-1.0", },
277 { .compatible
= "altr,spi-1.0", },
280 MODULE_DEVICE_TABLE(of
, altera_spi_match
);
281 #endif /* CONFIG_OF */
283 static struct platform_driver altera_spi_driver
= {
284 .probe
= altera_spi_probe
,
285 .remove
= altera_spi_remove
,
288 .owner
= THIS_MODULE
,
290 .of_match_table
= of_match_ptr(altera_spi_match
),
293 module_platform_driver(altera_spi_driver
);
295 MODULE_DESCRIPTION("Altera SPI driver");
296 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
297 MODULE_LICENSE("GPL");
298 MODULE_ALIAS("platform:" DRV_NAME
);