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/interrupt.h>
17 #include <linux/errno.h>
18 #include <linux/module.h>
19 #include <linux/platform_device.h>
20 #include <linux/spi/spi.h>
21 #include <linux/spi/spi_bitbang.h>
25 #define DRV_NAME "spi_altera"
27 #define ALTERA_SPI_RXDATA 0
28 #define ALTERA_SPI_TXDATA 4
29 #define ALTERA_SPI_STATUS 8
30 #define ALTERA_SPI_CONTROL 12
31 #define ALTERA_SPI_SLAVE_SEL 20
33 #define ALTERA_SPI_STATUS_ROE_MSK 0x8
34 #define ALTERA_SPI_STATUS_TOE_MSK 0x10
35 #define ALTERA_SPI_STATUS_TMT_MSK 0x20
36 #define ALTERA_SPI_STATUS_TRDY_MSK 0x40
37 #define ALTERA_SPI_STATUS_RRDY_MSK 0x80
38 #define ALTERA_SPI_STATUS_E_MSK 0x100
40 #define ALTERA_SPI_CONTROL_IROE_MSK 0x8
41 #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
42 #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
43 #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
44 #define ALTERA_SPI_CONTROL_IE_MSK 0x100
45 #define ALTERA_SPI_CONTROL_SSO_MSK 0x400
48 /* bitbang has to be first */
49 struct spi_bitbang bitbang
;
50 struct completion done
;
60 const unsigned char *tx
;
64 static inline struct altera_spi
*altera_spi_to_hw(struct spi_device
*sdev
)
66 return spi_master_get_devdata(sdev
->master
);
69 static void altera_spi_chipsel(struct spi_device
*spi
, int value
)
71 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
73 if (spi
->mode
& SPI_CS_HIGH
) {
75 case BITBANG_CS_INACTIVE
:
76 writel(1 << spi
->chip_select
,
77 hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
78 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
79 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
82 case BITBANG_CS_ACTIVE
:
83 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
84 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
85 writel(0, hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
90 case BITBANG_CS_INACTIVE
:
91 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
92 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
95 case BITBANG_CS_ACTIVE
:
96 writel(1 << spi
->chip_select
,
97 hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
98 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
99 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
105 static inline unsigned int hw_txbyte(struct altera_spi
*hw
, int count
)
108 switch (hw
->bytes_per_word
) {
110 return hw
->tx
[count
];
112 return (hw
->tx
[count
* 2]
113 | (hw
->tx
[count
* 2 + 1] << 8));
119 static int altera_spi_txrx(struct spi_device
*spi
, struct spi_transfer
*t
)
121 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
126 hw
->bytes_per_word
= DIV_ROUND_UP(t
->bits_per_word
, 8);
127 hw
->len
= t
->len
/ hw
->bytes_per_word
;
130 /* enable receive interrupt */
131 hw
->imr
|= ALTERA_SPI_CONTROL_IRRDY_MSK
;
132 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
134 /* send the first byte */
135 writel(hw_txbyte(hw
, 0), hw
->base
+ ALTERA_SPI_TXDATA
);
137 wait_for_completion(&hw
->done
);
138 /* disable receive interrupt */
139 hw
->imr
&= ~ALTERA_SPI_CONTROL_IRRDY_MSK
;
140 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
142 while (hw
->count
< hw
->len
) {
145 writel(hw_txbyte(hw
, hw
->count
),
146 hw
->base
+ ALTERA_SPI_TXDATA
);
148 while (!(readl(hw
->base
+ ALTERA_SPI_STATUS
) &
149 ALTERA_SPI_STATUS_RRDY_MSK
))
152 rxd
= readl(hw
->base
+ ALTERA_SPI_RXDATA
);
154 switch (hw
->bytes_per_word
) {
156 hw
->rx
[hw
->count
] = rxd
;
159 hw
->rx
[hw
->count
* 2] = rxd
;
160 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
169 return hw
->count
* hw
->bytes_per_word
;
172 static irqreturn_t
altera_spi_irq(int irq
, void *dev
)
174 struct altera_spi
*hw
= dev
;
177 rxd
= readl(hw
->base
+ ALTERA_SPI_RXDATA
);
179 switch (hw
->bytes_per_word
) {
181 hw
->rx
[hw
->count
] = rxd
;
184 hw
->rx
[hw
->count
* 2] = rxd
;
185 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
192 if (hw
->count
< hw
->len
)
193 writel(hw_txbyte(hw
, hw
->count
), hw
->base
+ ALTERA_SPI_TXDATA
);
200 static int altera_spi_probe(struct platform_device
*pdev
)
202 struct altera_spi
*hw
;
203 struct spi_master
*master
;
204 struct resource
*res
;
207 master
= spi_alloc_master(&pdev
->dev
, sizeof(struct altera_spi
));
211 /* setup the master state. */
212 master
->bus_num
= pdev
->id
;
213 master
->num_chipselect
= 16;
214 master
->mode_bits
= SPI_CS_HIGH
;
215 master
->bits_per_word_mask
= SPI_BPW_RANGE_MASK(1, 16);
216 master
->dev
.of_node
= pdev
->dev
.of_node
;
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,
249 /* register our spi controller */
250 err
= spi_bitbang_start(&hw
->bitbang
);
253 dev_info(&pdev
->dev
, "base %p, irq %d\n", hw
->base
, hw
->irq
);
257 spi_master_put(master
);
261 static int altera_spi_remove(struct platform_device
*dev
)
263 struct altera_spi
*hw
= platform_get_drvdata(dev
);
264 struct spi_master
*master
= hw
->bitbang
.master
;
266 spi_bitbang_stop(&hw
->bitbang
);
267 spi_master_put(master
);
272 static const struct of_device_id altera_spi_match
[] = {
273 { .compatible
= "ALTR,spi-1.0", },
274 { .compatible
= "altr,spi-1.0", },
277 MODULE_DEVICE_TABLE(of
, altera_spi_match
);
278 #endif /* CONFIG_OF */
280 static struct platform_driver altera_spi_driver
= {
281 .probe
= altera_spi_probe
,
282 .remove
= altera_spi_remove
,
286 .of_match_table
= of_match_ptr(altera_spi_match
),
289 module_platform_driver(altera_spi_driver
);
291 MODULE_DESCRIPTION("Altera SPI driver");
292 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
293 MODULE_LICENSE("GPL");
294 MODULE_ALIAS("platform:" DRV_NAME
);