xref: /openbsd-src/sys/arch/arm64/dev/aplspi.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: aplspi.c,v 1.3 2021/12/11 20:04:37 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/fdt.h>
34 
35 #define SPI_CLKCFG		0x00
36 #define  SPI_CLKCFG_EN		0xd
37 #define SPI_CONFIG		0x04
38 #define  SPI_CONFIG_EN		(1 << 18)
39 #define  SPI_CONFIG_PIOEN	(1 << 5)
40 #define SPI_STATUS		0x08
41 #define SPI_PIN			0x0c
42 #define  SPI_PIN_CS		(1 << 1)
43 #define SPI_TXDATA		0x10
44 #define SPI_RXDATA		0x20
45 #define SPI_CLKDIV		0x30
46 #define  SPI_CLKDIV_MIN		2
47 #define  SPI_CLKDIV_MAX		2047
48 #define SPI_RXCNT		0x34
49 #define SPI_CLKIDLE		0x38
50 #define SPI_TXCNT		0x4c
51 #define SPI_AVAIL		0x10c
52 #define  SPI_AVAIL_TX(avail)	((avail >> 8) & 0xff)
53 #define  SPI_AVAIL_RX(avail)	((avail >> 24) & 0xff)
54 #define SPI_SHIFTCFG		0x150
55 #define  SPI_SHIFTCFG_OVERRIDE_CS	(1 << 24)
56 #define SPI_PINCFG		0x154
57 #define  SPI_PINCFG_KEEP_CS	(1 << 1)
58 #define  SPI_PINCFG_CS_IDLE_VAL	(1 << 9)
59 
60 #define SPI_FIFO_SIZE		16
61 
62 #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
63 
64 struct aplspi_softc {
65 	struct device		sc_dev;
66 	bus_space_tag_t		sc_iot;
67 	bus_space_handle_t	sc_ioh;
68 	int			sc_node;
69 
70 	uint32_t		sc_pfreq;
71 
72 	struct spi_controller	sc_tag;
73 	struct mutex		sc_mtx;
74 
75 	int			sc_cs;
76 	uint32_t		*sc_csgpio;
77 	int			sc_csgpiolen;
78 	u_int			sc_cs_delay;
79 };
80 
81 int	 aplspi_match(struct device *, void *, void *);
82 void	 aplspi_attach(struct device *, struct device *, void *);
83 
84 void	 aplspi_config(void *, struct spi_config *);
85 uint32_t aplspi_clkdiv(struct aplspi_softc *, uint32_t);
86 int	 aplspi_transfer(void *, char *, char *, int, int);
87 int	 aplspi_acquire_bus(void *, int);
88 void	 aplspi_release_bus(void *, int);
89 
90 void	 aplspi_set_cs(struct aplspi_softc *, int, int);
91 int	 aplspi_wait_state(struct aplspi_softc *, uint32_t, uint32_t);
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 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
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
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 	pinctrl_byname(sc->sc_node, "default");
151 
152 	/* Configure CS# pin for manual control. */
153 	HWRITE4(sc, SPI_PIN, SPI_PIN_CS);
154 	HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS);
155 	HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL);
156 	HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS);
157 
158 	sc->sc_tag.sc_cookie = sc;
159 	sc->sc_tag.sc_config = aplspi_config;
160 	sc->sc_tag.sc_transfer = aplspi_transfer;
161 	sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus;
162 	sc->sc_tag.sc_release_bus = aplspi_release_bus;
163 
164 	mtx_init(&sc->sc_mtx, IPL_TTY);
165 
166 	aplspi_scan(sc);
167 }
168 
169 void
170 aplspi_config(void *cookie, struct spi_config *conf)
171 {
172 	struct aplspi_softc *sc = cookie;
173 	int cs;
174 
175 	cs = conf->sc_cs;
176 	if (cs > 4) {
177 		printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs);
178 		return;
179 	}
180 	sc->sc_cs = cs;
181 	sc->sc_cs_delay = conf->sc_cs_delay;
182 
183 	HWRITE4(sc, SPI_CLKCFG, 0);
184 
185 	HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq));
186 	HWRITE4(sc, SPI_CLKIDLE, 0);
187 
188 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
189 	HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN);
190 	HREAD4(sc, SPI_CONFIG);
191 }
192 
193 uint32_t
194 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq)
195 {
196 	uint32_t div = 0;
197 
198 	while ((freq * div) < sc->sc_pfreq)
199 		div++;
200 	if (div < SPI_CLKDIV_MIN)
201 		div = SPI_CLKDIV_MIN;
202 	if (div > SPI_CLKDIV_MAX)
203 		div = SPI_CLKDIV_MAX;
204 
205 	return div << 1;
206 }
207 
208 void
209 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on)
210 {
211 	if (cs == 0) {
212 		if (sc->sc_csgpio)
213 			gpio_controller_set_pin(sc->sc_csgpio, on);
214 		else
215 			HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS);
216 	}
217 }
218 
219 int
220 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags)
221 {
222 	struct aplspi_softc *sc = cookie;
223 	uint32_t avail, data, status;
224 	int rsplen;
225 	int count;
226 
227 	aplspi_set_cs(sc, sc->sc_cs, 1);
228 	delay(sc->sc_cs_delay);
229 
230 	HWRITE4(sc, SPI_TXCNT, len);
231 	HWRITE4(sc, SPI_RXCNT, len);
232 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN);
233 
234 	rsplen = len;
235 	while (len > 0 || rsplen > 0) {
236 		avail = HREAD4(sc, SPI_AVAIL);
237 		count = SPI_AVAIL_RX(avail);
238 		while (rsplen > 0 && count > 0) {
239 			data = HREAD4(sc, SPI_RXDATA);
240 			if (in)
241 				*in++ = data;
242 			rsplen--;
243 
244 			avail = HREAD4(sc, SPI_AVAIL);
245 			count = SPI_AVAIL_RX(avail);
246 		}
247 
248 		count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail);
249 		while (len > 0 && count > 0) {
250 			data = out ? *out++ : 0;
251 			HWRITE4(sc, SPI_TXDATA, data);
252 			len--;
253 			count--;
254 		}
255 	}
256 
257 	HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN);
258 	status = HREAD4(sc, SPI_STATUS);
259 	HWRITE4(sc, SPI_STATUS, status);
260 
261 	if (!ISSET(flags, SPI_KEEP_CS))
262 		aplspi_set_cs(sc, sc->sc_cs, 0);
263 
264 	return 0;
265 }
266 
267 int
268 aplspi_acquire_bus(void *cookie, int flags)
269 {
270 	struct aplspi_softc *sc = cookie;
271 
272 	mtx_enter(&sc->sc_mtx);
273 	return 0;
274 }
275 
276 void
277 aplspi_release_bus(void *cookie, int flags)
278 {
279 	struct aplspi_softc *sc = cookie;
280 
281 	mtx_leave(&sc->sc_mtx);
282 }
283 
284 void
285 aplspi_scan(struct aplspi_softc *sc)
286 {
287 	struct spi_attach_args sa;
288 	uint32_t reg[1];
289 	char name[32];
290 	int node;
291 
292 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
293 		memset(name, 0, sizeof(name));
294 		memset(reg, 0, sizeof(reg));
295 
296 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
297 			continue;
298 		if (name[0] == '\0')
299 			continue;
300 
301 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
302 			continue;
303 
304 		memset(&sa, 0, sizeof(sa));
305 		sa.sa_tag = &sc->sc_tag;
306 		sa.sa_name = name;
307 		sa.sa_cookie = &node;
308 
309 		config_found(&sc->sc_dev, &sa, NULL);
310 	}
311 }
312