xref: /openbsd-src/sys/arch/arm64/dev/aplspi.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: aplspi.c,v 1.5 2023/07/23 11:17:50 kettenis Exp $	*/
2 /*
3  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  */
17 
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/kernel.h>
21 #include <sys/device.h>
22 #include <sys/malloc.h>
23 #include <sys/mutex.h>
24 
25 #include <machine/bus.h>
26 #include <machine/fdt.h>
27 
28 #include <dev/spi/spivar.h>
29 #include <dev/ofw/openfirm.h>
30 #include <dev/ofw/ofw_clock.h>
31 #include <dev/ofw/ofw_gpio.h>
32 #include <dev/ofw/ofw_pinctrl.h>
33 #include <dev/ofw/ofw_power.h>
34 #include <dev/ofw/fdt.h>
35 
36 #define SPI_CLKCFG		0x00
37 #define  SPI_CLKCFG_EN		0xd
38 #define SPI_CONFIG		0x04
39 #define  SPI_CONFIG_EN		(1 << 18)
40 #define  SPI_CONFIG_PIOEN	(1 << 5)
41 #define SPI_STATUS		0x08
42 #define SPI_PIN			0x0c
43 #define  SPI_PIN_CS		(1 << 1)
44 #define SPI_TXDATA		0x10
45 #define SPI_RXDATA		0x20
46 #define SPI_CLKDIV		0x30
47 #define  SPI_CLKDIV_MIN		2
48 #define  SPI_CLKDIV_MAX		2047
49 #define SPI_RXCNT		0x34
50 #define SPI_CLKIDLE		0x38
51 #define SPI_TXCNT		0x4c
52 #define SPI_AVAIL		0x10c
53 #define  SPI_AVAIL_TX(avail)	((avail >> 8) & 0xff)
54 #define  SPI_AVAIL_RX(avail)	((avail >> 24) & 0xff)
55 #define SPI_SHIFTCFG		0x150
56 #define  SPI_SHIFTCFG_OVERRIDE_CS	(1 << 24)
57 #define SPI_PINCFG		0x154
58 #define  SPI_PINCFG_KEEP_CS	(1 << 1)
59 #define  SPI_PINCFG_CS_IDLE_VAL	(1 << 9)
60 
61 #define SPI_FIFO_SIZE		16
62 
63 #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
64 
65 struct aplspi_softc {
66 	struct device		sc_dev;
67 	bus_space_tag_t		sc_iot;
68 	bus_space_handle_t	sc_ioh;
69 	int			sc_node;
70 
71 	uint32_t		sc_pfreq;
72 
73 	struct spi_controller	sc_tag;
74 	struct mutex		sc_mtx;
75 
76 	int			sc_cs;
77 	uint32_t		*sc_csgpio;
78 	int			sc_csgpiolen;
79 	u_int			sc_cs_delay;
80 };
81 
82 int	 aplspi_match(struct device *, void *, void *);
83 void	 aplspi_attach(struct device *, struct device *, void *);
84 
85 void	 aplspi_config(void *, struct spi_config *);
86 uint32_t aplspi_clkdiv(struct aplspi_softc *, uint32_t);
87 int	 aplspi_transfer(void *, char *, char *, int, int);
88 int	 aplspi_acquire_bus(void *, int);
89 void	 aplspi_release_bus(void *, int);
90 
91 void	 aplspi_set_cs(struct aplspi_softc *, int, int);
92 int	 aplspi_wait_state(struct aplspi_softc *, uint32_t, uint32_t);
93 
94 void	 aplspi_scan(struct aplspi_softc *);
95 
96 #define HREAD4(sc, reg)							\
97 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
98 #define HWRITE4(sc, reg, val)						\
99 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
100 #define HSET4(sc, reg, bits)						\
101 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
102 #define HCLR4(sc, reg, bits)						\
103 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
104 
105 const struct cfattach aplspi_ca = {
106 	sizeof(struct aplspi_softc), aplspi_match, aplspi_attach
107 };
108 
109 struct cfdriver aplspi_cd = {
110 	NULL, "aplspi", DV_DULL
111 };
112 
113 int
114 aplspi_match(struct device *parent, void *match, void *aux)
115 {
116 	struct fdt_attach_args *faa = aux;
117 
118 	return OF_is_compatible(faa->fa_node, "apple,spi");
119 }
120 
121 void
122 aplspi_attach(struct device *parent, struct device *self, void *aux)
123 {
124 	struct aplspi_softc *sc = (struct aplspi_softc *)self;
125 	struct fdt_attach_args *faa = aux;
126 
127 	if (faa->fa_nreg < 1)
128 		return;
129 
130 	sc->sc_iot = faa->fa_iot;
131 	sc->sc_node = faa->fa_node;
132 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
133 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
134 		printf(": can't map registers\n");
135 		return;
136 	}
137 
138 	sc->sc_csgpiolen = OF_getproplen(faa->fa_node, "cs-gpios");
139 	if (sc->sc_csgpiolen > 0) {
140 		sc->sc_csgpio = malloc(sc->sc_csgpiolen, M_DEVBUF, M_WAITOK);
141 		OF_getpropintarray(faa->fa_node, "cs-gpios",
142 		    sc->sc_csgpio, sc->sc_csgpiolen);
143 		gpio_controller_config_pin(sc->sc_csgpio, GPIO_CONFIG_OUTPUT);
144 		gpio_controller_set_pin(sc->sc_csgpio, 0);
145 	}
146 
147 	printf("\n");
148 
149 	sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL);
150 
151 	power_domain_enable(sc->sc_node);
152 	pinctrl_byname(sc->sc_node, "default");
153 
154 	/* Configure CS# pin for manual control. */
155 	HWRITE4(sc, SPI_PIN, SPI_PIN_CS);
156 	HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS);
157 	HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL);
158 	HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS);
159 
160 	sc->sc_tag.sc_cookie = sc;
161 	sc->sc_tag.sc_config = aplspi_config;
162 	sc->sc_tag.sc_transfer = aplspi_transfer;
163 	sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus;
164 	sc->sc_tag.sc_release_bus = aplspi_release_bus;
165 
166 	mtx_init(&sc->sc_mtx, IPL_TTY);
167 
168 	aplspi_scan(sc);
169 }
170 
171 void
172 aplspi_config(void *cookie, struct spi_config *conf)
173 {
174 	struct aplspi_softc *sc = cookie;
175 	int cs;
176 
177 	cs = conf->sc_cs;
178 	if (cs > 4) {
179 		printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs);
180 		return;
181 	}
182 	sc->sc_cs = cs;
183 	sc->sc_cs_delay = conf->sc_cs_delay;
184 
185 	HWRITE4(sc, SPI_CLKCFG, 0);
186 
187 	HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq));
188 	HWRITE4(sc, SPI_CLKIDLE, 0);
189 
190 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
191 	HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN);
192 	HREAD4(sc, SPI_CONFIG);
193 }
194 
195 uint32_t
196 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq)
197 {
198 	uint32_t div = 0;
199 
200 	while ((freq * div) < sc->sc_pfreq)
201 		div++;
202 	if (div < SPI_CLKDIV_MIN)
203 		div = SPI_CLKDIV_MIN;
204 	if (div > SPI_CLKDIV_MAX)
205 		div = SPI_CLKDIV_MAX;
206 
207 	return div << 1;
208 }
209 
210 void
211 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on)
212 {
213 	if (cs == 0) {
214 		if (sc->sc_csgpio)
215 			gpio_controller_set_pin(sc->sc_csgpio, on);
216 		else
217 			HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS);
218 	}
219 }
220 
221 int
222 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags)
223 {
224 	struct aplspi_softc *sc = cookie;
225 	uint32_t avail, data, status;
226 	int rsplen;
227 	int count;
228 
229 	aplspi_set_cs(sc, sc->sc_cs, 1);
230 	delay(sc->sc_cs_delay);
231 
232 	HWRITE4(sc, SPI_TXCNT, len);
233 	HWRITE4(sc, SPI_RXCNT, len);
234 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN);
235 
236 	rsplen = len;
237 	while (len > 0 || rsplen > 0) {
238 		avail = HREAD4(sc, SPI_AVAIL);
239 		count = SPI_AVAIL_RX(avail);
240 		while (rsplen > 0 && count > 0) {
241 			data = HREAD4(sc, SPI_RXDATA);
242 			if (in)
243 				*in++ = data;
244 			rsplen--;
245 
246 			avail = HREAD4(sc, SPI_AVAIL);
247 			count = SPI_AVAIL_RX(avail);
248 		}
249 
250 		count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail);
251 		while (len > 0 && count > 0) {
252 			data = out ? *out++ : 0;
253 			HWRITE4(sc, SPI_TXDATA, data);
254 			len--;
255 			count--;
256 		}
257 	}
258 
259 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
260 	status = HREAD4(sc, SPI_STATUS);
261 	HWRITE4(sc, SPI_STATUS, status);
262 
263 	if (!ISSET(flags, SPI_KEEP_CS))
264 		aplspi_set_cs(sc, sc->sc_cs, 0);
265 
266 	return 0;
267 }
268 
269 int
270 aplspi_acquire_bus(void *cookie, int flags)
271 {
272 	struct aplspi_softc *sc = cookie;
273 
274 	mtx_enter(&sc->sc_mtx);
275 	return 0;
276 }
277 
278 void
279 aplspi_release_bus(void *cookie, int flags)
280 {
281 	struct aplspi_softc *sc = cookie;
282 
283 	mtx_leave(&sc->sc_mtx);
284 }
285 
286 void
287 aplspi_scan(struct aplspi_softc *sc)
288 {
289 	struct spi_attach_args sa;
290 	uint32_t reg[1];
291 	char name[32];
292 	int node;
293 
294 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
295 		memset(name, 0, sizeof(name));
296 		memset(reg, 0, sizeof(reg));
297 
298 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
299 			continue;
300 		if (name[0] == '\0')
301 			continue;
302 
303 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
304 			continue;
305 
306 		memset(&sa, 0, sizeof(sa));
307 		sa.sa_tag = &sc->sc_tag;
308 		sa.sa_name = name;
309 		sa.sa_cookie = &node;
310 
311 		config_found(&sc->sc_dev, &sa, NULL);
312 	}
313 }
314