xref: /openbsd/sys/dev/fdt/mvspi.c (revision fe922775)
1 /* $OpenBSD: mvspi.c,v 1.3 2021/10/31 15:12:00 kettenis Exp $ */
2 /*
3  * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se>
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/stdint.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_pinctrl.h>
32 #include <dev/ofw/fdt.h>
33 
34 /* registers */
35 #define SPI_CTRL			0x00
36 #define  SPI_CTRL_XFER_READY			(1 << 1)
37 #define  SPI_CTRL_CS(x)				(1 << (16 + (x)))
38 #define SPI_CFG				0x04
39 #define  SPI_CFG_BYTE_LEN			(1 << 5)
40 #define  SPI_CFG_CPHA				(1 << 6)
41 #define  SPI_CFG_CPOL				(1 << 7)
42 #define  SPI_CFG_FIFO_FLUSH			(1 << 9)
43 #define  SPI_CFG_FIFO_ENABLE			(1 << 17)
44 #define  SPI_CFG_PRESCALE_MASK			0x1f
45 #define SPI_DOUT			0x08
46 #define SPI_DIN				0x0c
47 
48 #define DEVNAME(sc)	((sc)->sc_dev.dv_xname)
49 
50 struct mvspi_softc {
51 	struct device		 sc_dev;
52 	bus_space_tag_t		 sc_iot;
53 	bus_space_handle_t	 sc_ioh;
54 	bus_size_t		 sc_ios;
55 	int			 sc_node;
56 
57 	uint32_t		 sc_pfreq;
58 
59 	struct rwlock		 sc_buslock;
60 	struct spi_controller	 sc_tag;
61 
62 	int			 sc_cs;
63 	u_int			 sc_cs_delay;
64 };
65 
66 int	 mvspi_match(struct device *, void *, void *);
67 void	 mvspi_attach(struct device *, struct device *, void *);
68 int	 mvspi_detach(struct device *, int);
69 
70 void	 mvspi_config(void *, struct spi_config *);
71 uint32_t mvspi_clkdiv(struct mvspi_softc *, uint32_t);
72 int	 mvspi_transfer(void *, char *, char *, int, int);
73 int	 mvspi_acquire_bus(void *, int);
74 void	 mvspi_release_bus(void *, int);
75 
76 void	 mvspi_set_cs(struct mvspi_softc *, int, int);
77 int	 mvspi_wait_state(struct mvspi_softc *, uint32_t, uint32_t);
78 
79 void	 mvspi_scan(struct mvspi_softc *);
80 
81 #define HREAD4(sc, reg)							\
82 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
83 #define HWRITE4(sc, reg, val)						\
84 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
85 #define HSET4(sc, reg, bits)						\
86 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
87 #define HCLR4(sc, reg, bits)						\
88 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
89 
90 const struct cfattach mvspi_ca = {
91 	sizeof(struct mvspi_softc), mvspi_match, mvspi_attach, mvspi_detach
92 };
93 
94 struct cfdriver mvspi_cd = {
95 	NULL, "mvspi", DV_DULL
96 };
97 
98 int
mvspi_match(struct device * parent,void * match,void * aux)99 mvspi_match(struct device *parent, void *match, void *aux)
100 {
101 	struct fdt_attach_args *faa = aux;
102 
103 	return OF_is_compatible(faa->fa_node, "marvell,armada-3700-spi");
104 }
105 
106 void
mvspi_attach(struct device * parent,struct device * self,void * aux)107 mvspi_attach(struct device *parent, struct device *self, void *aux)
108 {
109 	struct mvspi_softc *sc = (struct mvspi_softc *)self;
110 	struct fdt_attach_args *faa = aux;
111 	int timeout;
112 
113 	if (faa->fa_nreg < 1)
114 		return;
115 
116 	sc->sc_iot = faa->fa_iot;
117 	sc->sc_ios = faa->fa_reg[0].size;
118 	sc->sc_node = faa->fa_node;
119 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
120 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
121 		printf(": can't map registers\n");
122 		return;
123 	}
124 
125 	printf("\n");
126 
127 	pinctrl_byname(sc->sc_node, "default");
128 	clock_enable(sc->sc_node, NULL);
129 	clock_set_assigned(sc->sc_node);
130 
131 	sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL);
132 
133 	/* drain input buffer */
134 	HSET4(sc, SPI_CFG, SPI_CFG_FIFO_FLUSH);
135 	for (timeout = 1000; timeout > 0; timeout--) {
136 		if ((HREAD4(sc, SPI_CFG) & SPI_CFG_FIFO_FLUSH) == 0)
137 			break;
138 		delay(10);
139 	}
140 	if (timeout == 0) {
141 		printf("%s: timeout", sc->sc_dev.dv_xname);
142 		return;
143 	}
144 
145 	/* disable FIFO */
146 	HCLR4(sc, SPI_CFG, SPI_CFG_FIFO_ENABLE);
147 	HCLR4(sc, SPI_CFG, SPI_CFG_BYTE_LEN);
148 
149 	rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname);
150 
151 	sc->sc_tag.sc_cookie = sc;
152 	sc->sc_tag.sc_config = mvspi_config;
153 	sc->sc_tag.sc_transfer = mvspi_transfer;
154 	sc->sc_tag.sc_acquire_bus = mvspi_acquire_bus;
155 	sc->sc_tag.sc_release_bus = mvspi_release_bus;
156 
157 	mvspi_scan(sc);
158 }
159 
160 int
mvspi_detach(struct device * self,int flags)161 mvspi_detach(struct device *self, int flags)
162 {
163 	struct mvspi_softc *sc = (struct mvspi_softc *)self;
164 
165 	bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
166 	return 0;
167 }
168 
169 void
mvspi_config(void * cookie,struct spi_config * conf)170 mvspi_config(void *cookie, struct spi_config *conf)
171 {
172 	struct mvspi_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 	HCLR4(sc, SPI_CFG, SPI_CFG_PRESCALE_MASK);
184 	HSET4(sc, SPI_CFG, mvspi_clkdiv(sc, conf->sc_freq));
185 
186 	if (conf->sc_flags & SPI_CONFIG_CPHA)
187 		HSET4(sc, SPI_CFG, SPI_CFG_CPHA);
188 	if (conf->sc_flags & SPI_CONFIG_CPOL)
189 		HSET4(sc, SPI_CFG, SPI_CFG_CPOL);
190 }
191 
192 uint32_t
mvspi_clkdiv(struct mvspi_softc * sc,uint32_t freq)193 mvspi_clkdiv(struct mvspi_softc *sc, uint32_t freq)
194 {
195 	uint32_t pre;
196 
197 	pre = 0;
198 	while ((freq * pre) < sc->sc_pfreq)
199 		pre++;
200 	if (pre > 0x1f)
201 		pre = 0x1f;
202 	else if (pre > 0xf)
203 		pre = 0x10 + (pre + 1) / 2;
204 
205 	return pre;
206 }
207 
208 int
mvspi_wait_state(struct mvspi_softc * sc,uint32_t mask,uint32_t value)209 mvspi_wait_state(struct mvspi_softc *sc, uint32_t mask, uint32_t value)
210 {
211 	uint32_t state;
212 	int timeout;
213 
214 	state = HREAD4(sc, SPI_CTRL);
215 	for (timeout = 1000; timeout > 0; timeout--) {
216 		if (((state = HREAD4(sc, SPI_CTRL)) & mask) == value)
217 			return 0;
218 		delay(10);
219 	}
220 	printf("%s: timeout mask %x value %x\n", __func__, mask, value);
221 	return ETIMEDOUT;
222 }
223 
224 void
mvspi_set_cs(struct mvspi_softc * sc,int cs,int on)225 mvspi_set_cs(struct mvspi_softc *sc, int cs, int on)
226 {
227 	if (on)
228 		HSET4(sc, SPI_CTRL, SPI_CTRL_CS(cs));
229 	else
230 		HCLR4(sc, SPI_CTRL, SPI_CTRL_CS(cs));
231 }
232 
233 int
mvspi_transfer(void * cookie,char * out,char * in,int len,int flags)234 mvspi_transfer(void *cookie, char *out, char *in, int len, int flags)
235 {
236 	struct mvspi_softc *sc = cookie;
237 	int i = 0;
238 
239 	mvspi_set_cs(sc, sc->sc_cs, 1);
240 	delay(sc->sc_cs_delay);
241 
242 	while (i < len) {
243 		if (mvspi_wait_state(sc, SPI_CTRL_XFER_READY,
244 		    SPI_CTRL_XFER_READY))
245 			goto err;
246 		if (out)
247 			HWRITE4(sc, SPI_DOUT, out[i]);
248 		else
249 			HWRITE4(sc, SPI_DOUT, 0x0);
250 
251 		if (in) {
252 			if (mvspi_wait_state(sc, SPI_CTRL_XFER_READY,
253 			    SPI_CTRL_XFER_READY))
254 				goto err;
255 			in[i] = HREAD4(sc, SPI_DIN);
256 		}
257 
258 		i++;
259 	}
260 
261 	if (!ISSET(flags, SPI_KEEP_CS))
262 		mvspi_set_cs(sc, sc->sc_cs, 0);
263 	return 0;
264 
265 err:
266 	mvspi_set_cs(sc, sc->sc_cs, 0);
267 	return ETIMEDOUT;
268 }
269 
270 int
mvspi_acquire_bus(void * cookie,int flags)271 mvspi_acquire_bus(void *cookie, int flags)
272 {
273 	struct mvspi_softc *sc = cookie;
274 
275 	rw_enter(&sc->sc_buslock, RW_WRITE);
276 	return 0;
277 }
278 
279 void
mvspi_release_bus(void * cookie,int flags)280 mvspi_release_bus(void *cookie, int flags)
281 {
282 	struct mvspi_softc *sc = cookie;
283 
284 	rw_exit(&sc->sc_buslock);
285 }
286 
287 void
mvspi_scan(struct mvspi_softc * sc)288 mvspi_scan(struct mvspi_softc *sc)
289 {
290 	struct spi_attach_args sa;
291 	uint32_t reg[1];
292 	char name[32];
293 	int node;
294 
295 	for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) {
296 		memset(name, 0, sizeof(name));
297 		memset(reg, 0, sizeof(reg));
298 
299 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
300 			continue;
301 		if (name[0] == '\0')
302 			continue;
303 
304 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
305 			continue;
306 
307 		memset(&sa, 0, sizeof(sa));
308 		sa.sa_tag = &sc->sc_tag;
309 		sa.sa_name = name;
310 		sa.sa_cookie = &node;
311 
312 		config_found(&sc->sc_dev, &sa, NULL);
313 	}
314 }
315