1 // SPDX-License-Identifier: GPL-2.0-only
5 * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
7 * Based on spi_s3c24xx.c, which is:
8 * Copyright (c) 2006 Ben Dooks
9 * Copyright (c) 2006 Simtec Electronics
10 * Ben Dooks <ben@simtec.co.uk>
13 #include <linux/interrupt.h>
14 #include <linux/errno.h>
15 #include <linux/module.h>
16 #include <linux/platform_device.h>
17 #include <linux/spi/spi.h>
21 #define DRV_NAME "spi_altera"
23 #define ALTERA_SPI_RXDATA 0
24 #define ALTERA_SPI_TXDATA 4
25 #define ALTERA_SPI_STATUS 8
26 #define ALTERA_SPI_CONTROL 12
27 #define ALTERA_SPI_SLAVE_SEL 20
29 #define ALTERA_SPI_STATUS_ROE_MSK 0x8
30 #define ALTERA_SPI_STATUS_TOE_MSK 0x10
31 #define ALTERA_SPI_STATUS_TMT_MSK 0x20
32 #define ALTERA_SPI_STATUS_TRDY_MSK 0x40
33 #define ALTERA_SPI_STATUS_RRDY_MSK 0x80
34 #define ALTERA_SPI_STATUS_E_MSK 0x100
36 #define ALTERA_SPI_CONTROL_IROE_MSK 0x8
37 #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
38 #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
39 #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
40 #define ALTERA_SPI_CONTROL_IE_MSK 0x100
41 #define ALTERA_SPI_CONTROL_SSO_MSK 0x400
52 const unsigned char *tx
;
56 static inline struct altera_spi
*altera_spi_to_hw(struct spi_device
*sdev
)
58 return spi_master_get_devdata(sdev
->master
);
61 static void altera_spi_set_cs(struct spi_device
*spi
, bool is_high
)
63 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
66 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
67 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
68 writel(0, hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
70 writel(BIT(spi
->chip_select
), hw
->base
+ ALTERA_SPI_SLAVE_SEL
);
71 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
72 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
76 static void altera_spi_tx_word(struct altera_spi
*hw
)
81 switch (hw
->bytes_per_word
) {
83 txd
= hw
->tx
[hw
->count
];
86 txd
= (hw
->tx
[hw
->count
* 2]
87 | (hw
->tx
[hw
->count
* 2 + 1] << 8));
92 writel(txd
, hw
->base
+ ALTERA_SPI_TXDATA
);
95 static void altera_spi_rx_word(struct altera_spi
*hw
)
99 rxd
= readl(hw
->base
+ ALTERA_SPI_RXDATA
);
101 switch (hw
->bytes_per_word
) {
103 hw
->rx
[hw
->count
] = rxd
;
106 hw
->rx
[hw
->count
* 2] = rxd
;
107 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
115 static int altera_spi_txrx(struct spi_master
*master
,
116 struct spi_device
*spi
, struct spi_transfer
*t
)
118 struct altera_spi
*hw
= spi_master_get_devdata(master
);
123 hw
->bytes_per_word
= DIV_ROUND_UP(t
->bits_per_word
, 8);
124 hw
->len
= t
->len
/ hw
->bytes_per_word
;
127 /* enable receive interrupt */
128 hw
->imr
|= ALTERA_SPI_CONTROL_IRRDY_MSK
;
129 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
131 /* send the first byte */
132 altera_spi_tx_word(hw
);
134 while (hw
->count
< hw
->len
) {
135 altera_spi_tx_word(hw
);
137 while (!(readl(hw
->base
+ ALTERA_SPI_STATUS
) &
138 ALTERA_SPI_STATUS_RRDY_MSK
))
141 altera_spi_rx_word(hw
);
143 spi_finalize_current_transfer(master
);
149 static irqreturn_t
altera_spi_irq(int irq
, void *dev
)
151 struct spi_master
*master
= dev
;
152 struct altera_spi
*hw
= spi_master_get_devdata(master
);
154 altera_spi_rx_word(hw
);
156 if (hw
->count
< hw
->len
) {
157 altera_spi_tx_word(hw
);
159 /* disable receive interrupt */
160 hw
->imr
&= ~ALTERA_SPI_CONTROL_IRRDY_MSK
;
161 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
163 spi_finalize_current_transfer(master
);
169 static int altera_spi_probe(struct platform_device
*pdev
)
171 struct altera_spi
*hw
;
172 struct spi_master
*master
;
175 master
= spi_alloc_master(&pdev
->dev
, sizeof(struct altera_spi
));
179 /* setup the master state. */
180 master
->bus_num
= pdev
->id
;
181 master
->num_chipselect
= 16;
182 master
->mode_bits
= SPI_CS_HIGH
;
183 master
->bits_per_word_mask
= SPI_BPW_RANGE_MASK(1, 16);
184 master
->dev
.of_node
= pdev
->dev
.of_node
;
185 master
->transfer_one
= altera_spi_txrx
;
186 master
->set_cs
= altera_spi_set_cs
;
188 hw
= spi_master_get_devdata(master
);
190 /* find and map our resources */
191 hw
->base
= devm_platform_ioremap_resource(pdev
, 0);
192 if (IS_ERR(hw
->base
)) {
193 err
= PTR_ERR(hw
->base
);
196 /* program defaults into the registers */
197 hw
->imr
= 0; /* disable spi interrupts */
198 writel(hw
->imr
, hw
->base
+ ALTERA_SPI_CONTROL
);
199 writel(0, hw
->base
+ ALTERA_SPI_STATUS
); /* clear status reg */
200 if (readl(hw
->base
+ ALTERA_SPI_STATUS
) & ALTERA_SPI_STATUS_RRDY_MSK
)
201 readl(hw
->base
+ ALTERA_SPI_RXDATA
); /* flush rxdata */
202 /* irq is optional */
203 hw
->irq
= platform_get_irq(pdev
, 0);
205 err
= devm_request_irq(&pdev
->dev
, hw
->irq
, altera_spi_irq
, 0,
211 err
= devm_spi_register_master(&pdev
->dev
, master
);
214 dev_info(&pdev
->dev
, "base %p, irq %d\n", hw
->base
, hw
->irq
);
218 spi_master_put(master
);
223 static const struct of_device_id altera_spi_match
[] = {
224 { .compatible
= "ALTR,spi-1.0", },
225 { .compatible
= "altr,spi-1.0", },
228 MODULE_DEVICE_TABLE(of
, altera_spi_match
);
229 #endif /* CONFIG_OF */
231 static struct platform_driver altera_spi_driver
= {
232 .probe
= altera_spi_probe
,
236 .of_match_table
= of_match_ptr(altera_spi_match
),
239 module_platform_driver(altera_spi_driver
);
241 MODULE_DESCRIPTION("Altera SPI driver");
242 MODULE_AUTHOR("Thomas Chou <thomas@wytron.com.tw>");
243 MODULE_LICENSE("GPL");
244 MODULE_ALIAS("platform:" DRV_NAME
);