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/errno.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/spi/altera.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_TARGET_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
43 static int altr_spi_writel(struct altera_spi
*hw
, unsigned int reg
,
48 ret
= regmap_write(hw
->regmap
, hw
->regoff
+ reg
, val
);
50 dev_err(hw
->dev
, "fail to write reg 0x%x val 0x%x: %d\n",
56 static int altr_spi_readl(struct altera_spi
*hw
, unsigned int reg
,
61 ret
= regmap_read(hw
->regmap
, hw
->regoff
+ reg
, val
);
63 dev_err(hw
->dev
, "fail to read reg 0x%x: %d\n", reg
, ret
);
68 static inline struct altera_spi
*altera_spi_to_hw(struct spi_device
*sdev
)
70 return spi_controller_get_devdata(sdev
->controller
);
73 static void altera_spi_set_cs(struct spi_device
*spi
, bool is_high
)
75 struct altera_spi
*hw
= altera_spi_to_hw(spi
);
78 hw
->imr
&= ~ALTERA_SPI_CONTROL_SSO_MSK
;
79 altr_spi_writel(hw
, ALTERA_SPI_CONTROL
, hw
->imr
);
80 altr_spi_writel(hw
, ALTERA_SPI_TARGET_SEL
, 0);
82 altr_spi_writel(hw
, ALTERA_SPI_TARGET_SEL
,
83 BIT(spi_get_chipselect(spi
, 0)));
84 hw
->imr
|= ALTERA_SPI_CONTROL_SSO_MSK
;
85 altr_spi_writel(hw
, ALTERA_SPI_CONTROL
, hw
->imr
);
89 static void altera_spi_tx_word(struct altera_spi
*hw
)
94 switch (hw
->bytes_per_word
) {
96 txd
= hw
->tx
[hw
->count
];
99 txd
= (hw
->tx
[hw
->count
* 2]
100 | (hw
->tx
[hw
->count
* 2 + 1] << 8));
103 txd
= (hw
->tx
[hw
->count
* 4]
104 | (hw
->tx
[hw
->count
* 4 + 1] << 8)
105 | (hw
->tx
[hw
->count
* 4 + 2] << 16)
106 | (hw
->tx
[hw
->count
* 4 + 3] << 24));
112 altr_spi_writel(hw
, ALTERA_SPI_TXDATA
, txd
);
115 static void altera_spi_rx_word(struct altera_spi
*hw
)
119 altr_spi_readl(hw
, ALTERA_SPI_RXDATA
, &rxd
);
121 switch (hw
->bytes_per_word
) {
123 hw
->rx
[hw
->count
] = rxd
;
126 hw
->rx
[hw
->count
* 2] = rxd
;
127 hw
->rx
[hw
->count
* 2 + 1] = rxd
>> 8;
130 hw
->rx
[hw
->count
* 4] = rxd
;
131 hw
->rx
[hw
->count
* 4 + 1] = rxd
>> 8;
132 hw
->rx
[hw
->count
* 4 + 2] = rxd
>> 16;
133 hw
->rx
[hw
->count
* 4 + 3] = rxd
>> 24;
142 static int altera_spi_txrx(struct spi_controller
*host
,
143 struct spi_device
*spi
, struct spi_transfer
*t
)
145 struct altera_spi
*hw
= spi_controller_get_devdata(host
);
151 hw
->bytes_per_word
= DIV_ROUND_UP(t
->bits_per_word
, 8);
152 hw
->len
= t
->len
/ hw
->bytes_per_word
;
155 /* enable receive interrupt */
156 hw
->imr
|= ALTERA_SPI_CONTROL_IRRDY_MSK
;
157 altr_spi_writel(hw
, ALTERA_SPI_CONTROL
, hw
->imr
);
159 /* send the first byte */
160 altera_spi_tx_word(hw
);
165 while (hw
->count
< hw
->len
) {
166 altera_spi_tx_word(hw
);
169 altr_spi_readl(hw
, ALTERA_SPI_STATUS
, &val
);
170 if (val
& ALTERA_SPI_STATUS_RRDY_MSK
)
176 altera_spi_rx_word(hw
);
178 spi_finalize_current_transfer(host
);
183 irqreturn_t
altera_spi_irq(int irq
, void *dev
)
185 struct spi_controller
*host
= dev
;
186 struct altera_spi
*hw
= spi_controller_get_devdata(host
);
188 altera_spi_rx_word(hw
);
190 if (hw
->count
< hw
->len
) {
191 altera_spi_tx_word(hw
);
193 /* disable receive interrupt */
194 hw
->imr
&= ~ALTERA_SPI_CONTROL_IRRDY_MSK
;
195 altr_spi_writel(hw
, ALTERA_SPI_CONTROL
, hw
->imr
);
197 spi_finalize_current_transfer(host
);
202 EXPORT_SYMBOL_GPL(altera_spi_irq
);
204 void altera_spi_init_host(struct spi_controller
*host
)
206 struct altera_spi
*hw
= spi_controller_get_devdata(host
);
209 host
->transfer_one
= altera_spi_txrx
;
210 host
->set_cs
= altera_spi_set_cs
;
212 /* program defaults into the registers */
213 hw
->imr
= 0; /* disable spi interrupts */
214 altr_spi_writel(hw
, ALTERA_SPI_CONTROL
, hw
->imr
);
215 altr_spi_writel(hw
, ALTERA_SPI_STATUS
, 0); /* clear status reg */
216 altr_spi_readl(hw
, ALTERA_SPI_STATUS
, &val
);
217 if (val
& ALTERA_SPI_STATUS_RRDY_MSK
)
218 altr_spi_readl(hw
, ALTERA_SPI_RXDATA
, &val
); /* flush rxdata */
220 EXPORT_SYMBOL_GPL(altera_spi_init_host
);
222 MODULE_DESCRIPTION("Altera SPI Controller driver core");
223 MODULE_LICENSE("GPL");