xref: /openbmc/linux/drivers/spi/spi-altera-core.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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