xref: /openbsd-src/sys/arch/arm64/dev/aplspi.c (revision 0f9e9ec23bb2b65cc62a3d17df12827a45dae80c)
1 /*	$OpenBSD: aplspi.c,v 1.6 2024/05/13 01:15:50 jsg 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 
93 void	 aplspi_scan(struct aplspi_softc *);
94 
95 #define HREAD4(sc, reg)							\
96 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
97 #define HWRITE4(sc, reg, val)						\
98 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
99 #define HSET4(sc, reg, bits)						\
100 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
101 #define HCLR4(sc, reg, bits)						\
102 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
103 
104 const struct cfattach aplspi_ca = {
105 	sizeof(struct aplspi_softc), aplspi_match, aplspi_attach
106 };
107 
108 struct cfdriver aplspi_cd = {
109 	NULL, "aplspi", DV_DULL
110 };
111 
112 int
aplspi_match(struct device * parent,void * match,void * aux)113 aplspi_match(struct device *parent, void *match, void *aux)
114 {
115 	struct fdt_attach_args *faa = aux;
116 
117 	return OF_is_compatible(faa->fa_node, "apple,spi");
118 }
119 
120 void
aplspi_attach(struct device * parent,struct device * self,void * aux)121 aplspi_attach(struct device *parent, struct device *self, void *aux)
122 {
123 	struct aplspi_softc *sc = (struct aplspi_softc *)self;
124 	struct fdt_attach_args *faa = aux;
125 
126 	if (faa->fa_nreg < 1)
127 		return;
128 
129 	sc->sc_iot = faa->fa_iot;
130 	sc->sc_node = faa->fa_node;
131 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
132 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
133 		printf(": can't map registers\n");
134 		return;
135 	}
136 
137 	sc->sc_csgpiolen = OF_getproplen(faa->fa_node, "cs-gpios");
138 	if (sc->sc_csgpiolen > 0) {
139 		sc->sc_csgpio = malloc(sc->sc_csgpiolen, M_DEVBUF, M_WAITOK);
140 		OF_getpropintarray(faa->fa_node, "cs-gpios",
141 		    sc->sc_csgpio, sc->sc_csgpiolen);
142 		gpio_controller_config_pin(sc->sc_csgpio, GPIO_CONFIG_OUTPUT);
143 		gpio_controller_set_pin(sc->sc_csgpio, 0);
144 	}
145 
146 	printf("\n");
147 
148 	sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL);
149 
150 	power_domain_enable(sc->sc_node);
151 	pinctrl_byname(sc->sc_node, "default");
152 
153 	/* Configure CS# pin for manual control. */
154 	HWRITE4(sc, SPI_PIN, SPI_PIN_CS);
155 	HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS);
156 	HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL);
157 	HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS);
158 
159 	sc->sc_tag.sc_cookie = sc;
160 	sc->sc_tag.sc_config = aplspi_config;
161 	sc->sc_tag.sc_transfer = aplspi_transfer;
162 	sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus;
163 	sc->sc_tag.sc_release_bus = aplspi_release_bus;
164 
165 	mtx_init(&sc->sc_mtx, IPL_TTY);
166 
167 	aplspi_scan(sc);
168 }
169 
170 void
aplspi_config(void * cookie,struct spi_config * conf)171 aplspi_config(void *cookie, struct spi_config *conf)
172 {
173 	struct aplspi_softc *sc = cookie;
174 	int cs;
175 
176 	cs = conf->sc_cs;
177 	if (cs > 4) {
178 		printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs);
179 		return;
180 	}
181 	sc->sc_cs = cs;
182 	sc->sc_cs_delay = conf->sc_cs_delay;
183 
184 	HWRITE4(sc, SPI_CLKCFG, 0);
185 
186 	HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq));
187 	HWRITE4(sc, SPI_CLKIDLE, 0);
188 
189 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
190 	HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN);
191 	HREAD4(sc, SPI_CONFIG);
192 }
193 
194 uint32_t
aplspi_clkdiv(struct aplspi_softc * sc,uint32_t freq)195 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq)
196 {
197 	uint32_t div = 0;
198 
199 	while ((freq * div) < sc->sc_pfreq)
200 		div++;
201 	if (div < SPI_CLKDIV_MIN)
202 		div = SPI_CLKDIV_MIN;
203 	if (div > SPI_CLKDIV_MAX)
204 		div = SPI_CLKDIV_MAX;
205 
206 	return div << 1;
207 }
208 
209 void
aplspi_set_cs(struct aplspi_softc * sc,int cs,int on)210 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on)
211 {
212 	if (cs == 0) {
213 		if (sc->sc_csgpio)
214 			gpio_controller_set_pin(sc->sc_csgpio, on);
215 		else
216 			HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS);
217 	}
218 }
219 
220 int
aplspi_transfer(void * cookie,char * out,char * in,int len,int flags)221 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags)
222 {
223 	struct aplspi_softc *sc = cookie;
224 	uint32_t avail, data, status;
225 	int rsplen;
226 	int count;
227 
228 	aplspi_set_cs(sc, sc->sc_cs, 1);
229 	delay(sc->sc_cs_delay);
230 
231 	HWRITE4(sc, SPI_TXCNT, len);
232 	HWRITE4(sc, SPI_RXCNT, len);
233 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN);
234 
235 	rsplen = len;
236 	while (len > 0 || rsplen > 0) {
237 		avail = HREAD4(sc, SPI_AVAIL);
238 		count = SPI_AVAIL_RX(avail);
239 		while (rsplen > 0 && count > 0) {
240 			data = HREAD4(sc, SPI_RXDATA);
241 			if (in)
242 				*in++ = data;
243 			rsplen--;
244 
245 			avail = HREAD4(sc, SPI_AVAIL);
246 			count = SPI_AVAIL_RX(avail);
247 		}
248 
249 		count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail);
250 		while (len > 0 && count > 0) {
251 			data = out ? *out++ : 0;
252 			HWRITE4(sc, SPI_TXDATA, data);
253 			len--;
254 			count--;
255 		}
256 	}
257 
258 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
259 	status = HREAD4(sc, SPI_STATUS);
260 	HWRITE4(sc, SPI_STATUS, status);
261 
262 	if (!ISSET(flags, SPI_KEEP_CS))
263 		aplspi_set_cs(sc, sc->sc_cs, 0);
264 
265 	return 0;
266 }
267 
268 int
aplspi_acquire_bus(void * cookie,int flags)269 aplspi_acquire_bus(void *cookie, int flags)
270 {
271 	struct aplspi_softc *sc = cookie;
272 
273 	mtx_enter(&sc->sc_mtx);
274 	return 0;
275 }
276 
277 void
aplspi_release_bus(void * cookie,int flags)278 aplspi_release_bus(void *cookie, int flags)
279 {
280 	struct aplspi_softc *sc = cookie;
281 
282 	mtx_leave(&sc->sc_mtx);
283 }
284 
285 void
aplspi_scan(struct aplspi_softc * sc)286 aplspi_scan(struct aplspi_softc *sc)
287 {
288 	struct spi_attach_args sa;
289 	uint32_t reg[1];
290 	char name[32];
291 	int node;
292 
293 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
294 		memset(name, 0, sizeof(name));
295 		memset(reg, 0, sizeof(reg));
296 
297 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
298 			continue;
299 		if (name[0] == '\0')
300 			continue;
301 
302 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
303 			continue;
304 
305 		memset(&sa, 0, sizeof(sa));
306 		sa.sa_tag = &sc->sc_tag;
307 		sa.sa_name = name;
308 		sa.sa_cookie = &node;
309 
310 		config_found(&sc->sc_dev, &sa, NULL);
311 	}
312 }
313