1b0c3d935SMatthew Gerlach // SPDX-License-Identifier: GPL-2.0-only
2b0c3d935SMatthew Gerlach /*
3b0c3d935SMatthew Gerlach * Altera SPI driver
4b0c3d935SMatthew Gerlach *
5b0c3d935SMatthew Gerlach * Copyright (C) 2008 Thomas Chou <thomas@wytron.com.tw>
6b0c3d935SMatthew Gerlach *
7b0c3d935SMatthew Gerlach * Based on spi_s3c24xx.c, which is:
8b0c3d935SMatthew Gerlach * Copyright (c) 2006 Ben Dooks
9b0c3d935SMatthew Gerlach * Copyright (c) 2006 Simtec Electronics
10b0c3d935SMatthew Gerlach * Ben Dooks <ben@simtec.co.uk>
11b0c3d935SMatthew Gerlach */
12b0c3d935SMatthew Gerlach
13b0c3d935SMatthew Gerlach #include <linux/errno.h>
14b0c3d935SMatthew Gerlach #include <linux/module.h>
15b0c3d935SMatthew Gerlach #include <linux/platform_device.h>
16b0c3d935SMatthew Gerlach #include <linux/spi/altera.h>
17b0c3d935SMatthew Gerlach #include <linux/spi/spi.h>
18b0c3d935SMatthew Gerlach #include <linux/io.h>
19b0c3d935SMatthew Gerlach #include <linux/of.h>
20b0c3d935SMatthew Gerlach
21b0c3d935SMatthew Gerlach #define DRV_NAME "spi_altera"
22b0c3d935SMatthew Gerlach
23b0c3d935SMatthew Gerlach #define ALTERA_SPI_RXDATA 0
24b0c3d935SMatthew Gerlach #define ALTERA_SPI_TXDATA 4
25b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS 8
26b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL 12
2726c48aeaSYang Yingliang #define ALTERA_SPI_TARGET_SEL 20
28b0c3d935SMatthew Gerlach
29b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_ROE_MSK 0x8
30b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_TOE_MSK 0x10
31b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_TMT_MSK 0x20
32b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_TRDY_MSK 0x40
33b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_RRDY_MSK 0x80
34b0c3d935SMatthew Gerlach #define ALTERA_SPI_STATUS_E_MSK 0x100
35b0c3d935SMatthew Gerlach
36b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_IROE_MSK 0x8
37b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_ITOE_MSK 0x10
38b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_ITRDY_MSK 0x40
39b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_IRRDY_MSK 0x80
40b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_IE_MSK 0x100
41b0c3d935SMatthew Gerlach #define ALTERA_SPI_CONTROL_SSO_MSK 0x400
42b0c3d935SMatthew Gerlach
altr_spi_writel(struct altera_spi * hw,unsigned int reg,unsigned int val)43b0c3d935SMatthew Gerlach static int altr_spi_writel(struct altera_spi *hw, unsigned int reg,
44b0c3d935SMatthew Gerlach unsigned int val)
45b0c3d935SMatthew Gerlach {
46b0c3d935SMatthew Gerlach int ret;
47b0c3d935SMatthew Gerlach
48b0c3d935SMatthew Gerlach ret = regmap_write(hw->regmap, hw->regoff + reg, val);
49b0c3d935SMatthew Gerlach if (ret)
50b0c3d935SMatthew Gerlach dev_err(hw->dev, "fail to write reg 0x%x val 0x%x: %d\n",
51b0c3d935SMatthew Gerlach reg, val, ret);
52b0c3d935SMatthew Gerlach
53b0c3d935SMatthew Gerlach return ret;
54b0c3d935SMatthew Gerlach }
55b0c3d935SMatthew Gerlach
altr_spi_readl(struct altera_spi * hw,unsigned int reg,unsigned int * val)56b0c3d935SMatthew Gerlach static int altr_spi_readl(struct altera_spi *hw, unsigned int reg,
57b0c3d935SMatthew Gerlach unsigned int *val)
58b0c3d935SMatthew Gerlach {
59b0c3d935SMatthew Gerlach int ret;
60b0c3d935SMatthew Gerlach
61b0c3d935SMatthew Gerlach ret = regmap_read(hw->regmap, hw->regoff + reg, val);
62b0c3d935SMatthew Gerlach if (ret)
63b0c3d935SMatthew Gerlach dev_err(hw->dev, "fail to read reg 0x%x: %d\n", reg, ret);
64b0c3d935SMatthew Gerlach
65b0c3d935SMatthew Gerlach return ret;
66b0c3d935SMatthew Gerlach }
67b0c3d935SMatthew Gerlach
altera_spi_to_hw(struct spi_device * sdev)68b0c3d935SMatthew Gerlach static inline struct altera_spi *altera_spi_to_hw(struct spi_device *sdev)
69b0c3d935SMatthew Gerlach {
7026c48aeaSYang Yingliang return spi_controller_get_devdata(sdev->controller);
71b0c3d935SMatthew Gerlach }
72b0c3d935SMatthew Gerlach
altera_spi_set_cs(struct spi_device * spi,bool is_high)73b0c3d935SMatthew Gerlach static void altera_spi_set_cs(struct spi_device *spi, bool is_high)
74b0c3d935SMatthew Gerlach {
75b0c3d935SMatthew Gerlach struct altera_spi *hw = altera_spi_to_hw(spi);
76b0c3d935SMatthew Gerlach
77b0c3d935SMatthew Gerlach if (is_high) {
78b0c3d935SMatthew Gerlach hw->imr &= ~ALTERA_SPI_CONTROL_SSO_MSK;
79b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
8026c48aeaSYang Yingliang altr_spi_writel(hw, ALTERA_SPI_TARGET_SEL, 0);
81b0c3d935SMatthew Gerlach } else {
8226c48aeaSYang Yingliang altr_spi_writel(hw, ALTERA_SPI_TARGET_SEL,
83*9e264f3fSAmit Kumar Mahapatra via Alsa-devel BIT(spi_get_chipselect(spi, 0)));
84b0c3d935SMatthew Gerlach hw->imr |= ALTERA_SPI_CONTROL_SSO_MSK;
85b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
86b0c3d935SMatthew Gerlach }
87b0c3d935SMatthew Gerlach }
88b0c3d935SMatthew Gerlach
altera_spi_tx_word(struct altera_spi * hw)89b0c3d935SMatthew Gerlach static void altera_spi_tx_word(struct altera_spi *hw)
90b0c3d935SMatthew Gerlach {
91b0c3d935SMatthew Gerlach unsigned int txd = 0;
92b0c3d935SMatthew Gerlach
93b0c3d935SMatthew Gerlach if (hw->tx) {
94b0c3d935SMatthew Gerlach switch (hw->bytes_per_word) {
95b0c3d935SMatthew Gerlach case 1:
96b0c3d935SMatthew Gerlach txd = hw->tx[hw->count];
97b0c3d935SMatthew Gerlach break;
98b0c3d935SMatthew Gerlach case 2:
99b0c3d935SMatthew Gerlach txd = (hw->tx[hw->count * 2]
100b0c3d935SMatthew Gerlach | (hw->tx[hw->count * 2 + 1] << 8));
101b0c3d935SMatthew Gerlach break;
102b0c3d935SMatthew Gerlach case 4:
103b0c3d935SMatthew Gerlach txd = (hw->tx[hw->count * 4]
104b0c3d935SMatthew Gerlach | (hw->tx[hw->count * 4 + 1] << 8)
105b0c3d935SMatthew Gerlach | (hw->tx[hw->count * 4 + 2] << 16)
106b0c3d935SMatthew Gerlach | (hw->tx[hw->count * 4 + 3] << 24));
107b0c3d935SMatthew Gerlach break;
108b0c3d935SMatthew Gerlach
109b0c3d935SMatthew Gerlach }
110b0c3d935SMatthew Gerlach }
111b0c3d935SMatthew Gerlach
112b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_TXDATA, txd);
113b0c3d935SMatthew Gerlach }
114b0c3d935SMatthew Gerlach
altera_spi_rx_word(struct altera_spi * hw)115b0c3d935SMatthew Gerlach static void altera_spi_rx_word(struct altera_spi *hw)
116b0c3d935SMatthew Gerlach {
117b0c3d935SMatthew Gerlach unsigned int rxd;
118b0c3d935SMatthew Gerlach
119b0c3d935SMatthew Gerlach altr_spi_readl(hw, ALTERA_SPI_RXDATA, &rxd);
120b0c3d935SMatthew Gerlach if (hw->rx) {
121b0c3d935SMatthew Gerlach switch (hw->bytes_per_word) {
122b0c3d935SMatthew Gerlach case 1:
123b0c3d935SMatthew Gerlach hw->rx[hw->count] = rxd;
124b0c3d935SMatthew Gerlach break;
125b0c3d935SMatthew Gerlach case 2:
126b0c3d935SMatthew Gerlach hw->rx[hw->count * 2] = rxd;
127b0c3d935SMatthew Gerlach hw->rx[hw->count * 2 + 1] = rxd >> 8;
128b0c3d935SMatthew Gerlach break;
129b0c3d935SMatthew Gerlach case 4:
130b0c3d935SMatthew Gerlach hw->rx[hw->count * 4] = rxd;
131b0c3d935SMatthew Gerlach hw->rx[hw->count * 4 + 1] = rxd >> 8;
132b0c3d935SMatthew Gerlach hw->rx[hw->count * 4 + 2] = rxd >> 16;
133b0c3d935SMatthew Gerlach hw->rx[hw->count * 4 + 3] = rxd >> 24;
134b0c3d935SMatthew Gerlach break;
135b0c3d935SMatthew Gerlach
136b0c3d935SMatthew Gerlach }
137b0c3d935SMatthew Gerlach }
138b0c3d935SMatthew Gerlach
139b0c3d935SMatthew Gerlach hw->count++;
140b0c3d935SMatthew Gerlach }
141b0c3d935SMatthew Gerlach
altera_spi_txrx(struct spi_controller * host,struct spi_device * spi,struct spi_transfer * t)14226c48aeaSYang Yingliang static int altera_spi_txrx(struct spi_controller *host,
143b0c3d935SMatthew Gerlach struct spi_device *spi, struct spi_transfer *t)
144b0c3d935SMatthew Gerlach {
14526c48aeaSYang Yingliang struct altera_spi *hw = spi_controller_get_devdata(host);
146b0c3d935SMatthew Gerlach u32 val;
147b0c3d935SMatthew Gerlach
148b0c3d935SMatthew Gerlach hw->tx = t->tx_buf;
149b0c3d935SMatthew Gerlach hw->rx = t->rx_buf;
150b0c3d935SMatthew Gerlach hw->count = 0;
151b0c3d935SMatthew Gerlach hw->bytes_per_word = DIV_ROUND_UP(t->bits_per_word, 8);
152b0c3d935SMatthew Gerlach hw->len = t->len / hw->bytes_per_word;
153b0c3d935SMatthew Gerlach
154b0c3d935SMatthew Gerlach if (hw->irq >= 0) {
155b0c3d935SMatthew Gerlach /* enable receive interrupt */
156b0c3d935SMatthew Gerlach hw->imr |= ALTERA_SPI_CONTROL_IRRDY_MSK;
157b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
158b0c3d935SMatthew Gerlach
159b0c3d935SMatthew Gerlach /* send the first byte */
160b0c3d935SMatthew Gerlach altera_spi_tx_word(hw);
161b0c3d935SMatthew Gerlach
162b0c3d935SMatthew Gerlach return 1;
163b0c3d935SMatthew Gerlach }
164b0c3d935SMatthew Gerlach
165b0c3d935SMatthew Gerlach while (hw->count < hw->len) {
166b0c3d935SMatthew Gerlach altera_spi_tx_word(hw);
167b0c3d935SMatthew Gerlach
168b0c3d935SMatthew Gerlach for (;;) {
169b0c3d935SMatthew Gerlach altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
170b0c3d935SMatthew Gerlach if (val & ALTERA_SPI_STATUS_RRDY_MSK)
171b0c3d935SMatthew Gerlach break;
172b0c3d935SMatthew Gerlach
173b0c3d935SMatthew Gerlach cpu_relax();
174b0c3d935SMatthew Gerlach }
175b0c3d935SMatthew Gerlach
176b0c3d935SMatthew Gerlach altera_spi_rx_word(hw);
177b0c3d935SMatthew Gerlach }
17826c48aeaSYang Yingliang spi_finalize_current_transfer(host);
179b0c3d935SMatthew Gerlach
180b0c3d935SMatthew Gerlach return 0;
181b0c3d935SMatthew Gerlach }
182b0c3d935SMatthew Gerlach
altera_spi_irq(int irq,void * dev)183b0c3d935SMatthew Gerlach irqreturn_t altera_spi_irq(int irq, void *dev)
184b0c3d935SMatthew Gerlach {
18526c48aeaSYang Yingliang struct spi_controller *host = dev;
18626c48aeaSYang Yingliang struct altera_spi *hw = spi_controller_get_devdata(host);
187b0c3d935SMatthew Gerlach
188b0c3d935SMatthew Gerlach altera_spi_rx_word(hw);
189b0c3d935SMatthew Gerlach
190b0c3d935SMatthew Gerlach if (hw->count < hw->len) {
191b0c3d935SMatthew Gerlach altera_spi_tx_word(hw);
192b0c3d935SMatthew Gerlach } else {
193b0c3d935SMatthew Gerlach /* disable receive interrupt */
194b0c3d935SMatthew Gerlach hw->imr &= ~ALTERA_SPI_CONTROL_IRRDY_MSK;
195b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
196b0c3d935SMatthew Gerlach
19726c48aeaSYang Yingliang spi_finalize_current_transfer(host);
198b0c3d935SMatthew Gerlach }
199b0c3d935SMatthew Gerlach
200b0c3d935SMatthew Gerlach return IRQ_HANDLED;
201b0c3d935SMatthew Gerlach }
202b0c3d935SMatthew Gerlach EXPORT_SYMBOL_GPL(altera_spi_irq);
203b0c3d935SMatthew Gerlach
altera_spi_init_host(struct spi_controller * host)20426c48aeaSYang Yingliang void altera_spi_init_host(struct spi_controller *host)
205b0c3d935SMatthew Gerlach {
20626c48aeaSYang Yingliang struct altera_spi *hw = spi_controller_get_devdata(host);
207b0c3d935SMatthew Gerlach u32 val;
208b0c3d935SMatthew Gerlach
20926c48aeaSYang Yingliang host->transfer_one = altera_spi_txrx;
21026c48aeaSYang Yingliang host->set_cs = altera_spi_set_cs;
211b0c3d935SMatthew Gerlach
212b0c3d935SMatthew Gerlach /* program defaults into the registers */
213b0c3d935SMatthew Gerlach hw->imr = 0; /* disable spi interrupts */
214b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_CONTROL, hw->imr);
215b0c3d935SMatthew Gerlach altr_spi_writel(hw, ALTERA_SPI_STATUS, 0); /* clear status reg */
216b0c3d935SMatthew Gerlach altr_spi_readl(hw, ALTERA_SPI_STATUS, &val);
217b0c3d935SMatthew Gerlach if (val & ALTERA_SPI_STATUS_RRDY_MSK)
218b0c3d935SMatthew Gerlach altr_spi_readl(hw, ALTERA_SPI_RXDATA, &val); /* flush rxdata */
219b0c3d935SMatthew Gerlach }
22026c48aeaSYang Yingliang EXPORT_SYMBOL_GPL(altera_spi_init_host);
221b0c3d935SMatthew Gerlach
222b0c3d935SMatthew Gerlach MODULE_LICENSE("GPL");
223