xref: /f-stack/freebsd/mips/ingenic/jz4780_smb.c (revision 22ce4aff)
1 /*-
2  * Copyright (c) 2016 Jared McNeill <[email protected]>
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
17  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
18  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
20  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 
28 /*
29  * Ingenic JZ4780 SMB Controller
30  */
31 
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/rman.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41 #include <sys/lock.h>
42 #include <sys/mutex.h>
43 #include <sys/time.h>
44 #include <machine/bus.h>
45 
46 #include <dev/ofw/ofw_bus.h>
47 #include <dev/ofw/ofw_bus_subr.h>
48 
49 #include <dev/iicbus/iiconf.h>
50 #include <dev/iicbus/iicbus.h>
51 
52 #include <dev/extres/clk/clk.h>
53 
54 #include <mips/ingenic/jz4780_smb.h>
55 
56 #include "iicbus_if.h"
57 
58 #define	JZSMB_TIMEOUT			((300UL * hz) / 1000)
59 
60 #define	JZSMB_SPEED_STANDARD		100000
61 #define	JZSMB_SETUP_TIME_STANDARD	300
62 #define	JZSMB_HOLD_TIME_STANDARD	400
63 #define	JZSMB_PERIOD_MIN_STANDARD	4000
64 #define	JZSMB_PERIOD_MAX_STANDARD	4700
65 
66 #define	JZSMB_SPEED_FAST		400000
67 #define	JZSMB_SETUP_TIME_FAST		450
68 #define	JZSMB_HOLD_TIME_FAST		450
69 #define	JZSMB_PERIOD_MIN_FAST		600
70 #define	JZSMB_PERIOD_MAX_FAST		1300
71 
72 #define	JZSMB_HCNT_BASE			8
73 #define	JZSMB_HCNT_MIN			6
74 #define	JZSMB_LCNT_BASE			1
75 #define	JZSMB_LCNT_MIN			8
76 
77 static inline int
tstohz(const struct timespec * tsp)78 tstohz(const struct timespec *tsp)
79 {
80 	struct timeval tv;
81 
82 	TIMESPEC_TO_TIMEVAL(&tv, tsp);
83 	return (tvtohz(&tv));
84 }
85 
86 static struct ofw_compat_data compat_data[] = {
87 	{ "ingenic,jz4780-i2c",		1 },
88 	{ NULL,				0 }
89 };
90 
91 static struct resource_spec jzsmb_spec[] = {
92 	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
93 	{ -1, 0 }
94 };
95 
96 struct jzsmb_softc {
97 	struct resource	*res;
98 	struct mtx	mtx;
99 	clk_t		clk;
100 	device_t	iicbus;
101 	int		busy;
102 	uint32_t	i2c_freq;
103 	uint64_t	bus_freq;
104 	uint32_t	status;
105 
106 	struct iic_msg	*msg;
107 };
108 
109 #define	SMB_LOCK(sc)			mtx_lock(&(sc)->mtx)
110 #define	SMB_UNLOCK(sc)			mtx_unlock(&(sc)->mtx)
111 #define	SMB_ASSERT_LOCKED(sc)		mtx_assert(&(sc)->mtx, MA_OWNED)
112 #define	SMB_READ(sc, reg)		bus_read_2((sc)->res, (reg))
113 #define	SMB_WRITE(sc, reg, val)		bus_write_2((sc)->res, (reg), (val))
114 
115 static phandle_t
jzsmb_get_node(device_t bus,device_t dev)116 jzsmb_get_node(device_t bus, device_t dev)
117 {
118 	return (ofw_bus_get_node(bus));
119 }
120 
121 static int
jzsmb_enable(struct jzsmb_softc * sc,int enable)122 jzsmb_enable(struct jzsmb_softc *sc, int enable)
123 {
124 	SMB_ASSERT_LOCKED(sc);
125 
126 	if (enable) {
127 		SMB_WRITE(sc, SMBENB, SMBENB_SMBENB);
128 		while ((SMB_READ(sc, SMBENBST) & SMBENBST_SMBEN) == 0)
129 			;
130 	} else {
131 		SMB_WRITE(sc, SMBENB, 0);
132 		while ((SMB_READ(sc, SMBENBST) & SMBENBST_SMBEN) != 0)
133 			;
134 	}
135 
136 	return (0);
137 }
138 
139 static int
jzsmb_reset_locked(device_t dev,u_char addr)140 jzsmb_reset_locked(device_t dev, u_char addr)
141 {
142 	struct jzsmb_softc *sc;
143 	uint16_t con;
144 	uint32_t period;
145 	int hcnt, lcnt, setup_time, hold_time;
146 
147 	sc = device_get_softc(dev);
148 
149 	SMB_ASSERT_LOCKED(sc);
150 
151 	/* Setup master mode operation */
152 
153 	/* Disable SMB */
154 	jzsmb_enable(sc, 0);
155 
156 	/* Disable interrupts */
157 	SMB_WRITE(sc, SMBINTM, 0);
158 
159 	/* Set supported speed mode and expected SCL frequency */
160 	period = sc->bus_freq / sc->i2c_freq;
161 	con = SMBCON_REST | SMBCON_SLVDIS | SMBCON_MD;
162 	switch (sc->i2c_freq) {
163 	case JZSMB_SPEED_STANDARD:
164 		con |= SMBCON_SPD_STANDARD;
165 		setup_time = JZSMB_SETUP_TIME_STANDARD;
166 		hold_time = JZSMB_HOLD_TIME_STANDARD;
167 		hcnt = (period * JZSMB_PERIOD_MIN_STANDARD) /
168 		    (JZSMB_PERIOD_MAX_STANDARD + JZSMB_PERIOD_MIN_STANDARD);
169 		lcnt = period - hcnt;
170 		hcnt = MAX(hcnt - JZSMB_HCNT_BASE, JZSMB_HCNT_MIN);
171 		lcnt = MAX(lcnt - JZSMB_LCNT_BASE, JZSMB_LCNT_MIN);
172 		SMB_WRITE(sc, SMBCON, con);
173 		SMB_WRITE(sc, SMBSHCNT, hcnt);
174 		SMB_WRITE(sc, SMBSLCNT, lcnt);
175 		break;
176 	case JZSMB_SPEED_FAST:
177 		con |= SMBCON_SPD_FAST;
178 		setup_time = JZSMB_SETUP_TIME_FAST;
179 		hold_time = JZSMB_HOLD_TIME_FAST;
180 		hcnt = (period * JZSMB_PERIOD_MIN_FAST) /
181 		    (JZSMB_PERIOD_MAX_FAST + JZSMB_PERIOD_MIN_FAST);
182 		lcnt = period - hcnt;
183 		hcnt = MAX(hcnt - JZSMB_HCNT_BASE, JZSMB_HCNT_MIN);
184 		lcnt = MAX(lcnt - JZSMB_LCNT_BASE, JZSMB_LCNT_MIN);
185 		SMB_WRITE(sc, SMBCON, con);
186 		SMB_WRITE(sc, SMBFHCNT, hcnt);
187 		SMB_WRITE(sc, SMBFLCNT, lcnt);
188 		break;
189 	default:
190 		return (EINVAL);
191 	}
192 
193 	setup_time = ((setup_time * sc->bus_freq / 1000) / 1000000) + 1;
194 	setup_time = MIN(1, MAX(255, setup_time));
195 	SMB_WRITE(sc, SMBSDASU, setup_time);
196 
197 	hold_time = ((hold_time * sc->bus_freq / 1000) / 1000000) - 1;
198 	hold_time = MAX(255, hold_time);
199 	if (hold_time >= 0)
200 		SMB_WRITE(sc, SMBSDAHD, hold_time | SMBSDAHD_HDENB);
201 	else
202 		SMB_WRITE(sc, SMBSDAHD, 0);
203 
204 	SMB_WRITE(sc, SMBTAR, addr >> 1);
205 
206 	if (addr != 0) {
207 		/* Enable SMB */
208 		jzsmb_enable(sc, 1);
209 	}
210 
211 	return (0);
212 }
213 
214 static int
jzsmb_reset(device_t dev,u_char speed,u_char addr,u_char * oldaddr)215 jzsmb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
216 {
217 	struct jzsmb_softc *sc;
218 	int error;
219 
220 	sc = device_get_softc(dev);
221 
222 	SMB_LOCK(sc);
223 	error = jzsmb_reset_locked(dev, addr);
224 	SMB_UNLOCK(sc);
225 
226 	return (error);
227 }
228 
229 static int
jzsmb_transfer_read(device_t dev,struct iic_msg * msg)230 jzsmb_transfer_read(device_t dev, struct iic_msg *msg)
231 {
232 	struct jzsmb_softc *sc;
233 	struct timespec start, diff;
234 	uint16_t con, resid;
235 	int timeo;
236 
237 	sc = device_get_softc(dev);
238 	timeo = JZSMB_TIMEOUT * msg->len;
239 
240 	SMB_ASSERT_LOCKED(sc);
241 
242 	con = SMB_READ(sc, SMBCON);
243 	con |= SMBCON_STPHLD;
244 	SMB_WRITE(sc, SMBCON, con);
245 
246 	getnanouptime(&start);
247 	for (resid = msg->len; resid > 0; resid--) {
248 		for (int i = 0; i < min(resid, 8); i++)
249 			SMB_WRITE(sc, SMBDC, SMBDC_CMD);
250 		for (;;) {
251 			getnanouptime(&diff);
252 			timespecsub(&diff, &start, &diff);
253 			if ((SMB_READ(sc, SMBST) & SMBST_RFNE) != 0) {
254 				msg->buf[msg->len - resid] =
255 				    SMB_READ(sc, SMBDC) & SMBDC_DAT;
256 				break;
257 			} else
258 				DELAY(1000);
259 
260 			if (tstohz(&diff) >= timeo) {
261 				device_printf(dev,
262 				    "read timeout (status=0x%02x)\n",
263 				    SMB_READ(sc, SMBST));
264 				return (EIO);
265 			}
266 		}
267 	}
268 
269 	con = SMB_READ(sc, SMBCON);
270 	con &= ~SMBCON_STPHLD;
271 	SMB_WRITE(sc, SMBCON, con);
272 
273 	return (0);
274 }
275 
276 static int
jzsmb_transfer_write(device_t dev,struct iic_msg * msg,int stop_hold)277 jzsmb_transfer_write(device_t dev, struct iic_msg *msg, int stop_hold)
278 {
279 	struct jzsmb_softc *sc;
280 	struct timespec start, diff;
281 	uint16_t con, resid;
282 	int timeo;
283 
284 	sc = device_get_softc(dev);
285 	timeo = JZSMB_TIMEOUT * msg->len;
286 
287 	SMB_ASSERT_LOCKED(sc);
288 
289 	con = SMB_READ(sc, SMBCON);
290 	con |= SMBCON_STPHLD;
291 	SMB_WRITE(sc, SMBCON, con);
292 
293 	getnanouptime(&start);
294 	for (resid = msg->len; resid > 0; resid--) {
295 		for (;;) {
296 			getnanouptime(&diff);
297 			timespecsub(&diff, &start, &diff);
298 			if ((SMB_READ(sc, SMBST) & SMBST_TFNF) != 0) {
299 				SMB_WRITE(sc, SMBDC,
300 				    msg->buf[msg->len - resid]);
301 				break;
302 			} else
303 				DELAY((1000 * hz) / JZSMB_TIMEOUT);
304 
305 			if (tstohz(&diff) >= timeo) {
306 				device_printf(dev,
307 				    "write timeout (status=0x%02x)\n",
308 				    SMB_READ(sc, SMBST));
309 				return (EIO);
310 			}
311 		}
312 	}
313 
314 	if (!stop_hold) {
315 		con = SMB_READ(sc, SMBCON);
316 		con &= ~SMBCON_STPHLD;
317 		SMB_WRITE(sc, SMBCON, con);
318 	}
319 
320 	return (0);
321 }
322 
323 static int
jzsmb_transfer(device_t dev,struct iic_msg * msgs,uint32_t nmsgs)324 jzsmb_transfer(device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
325 {
326 	struct jzsmb_softc *sc;
327 	uint32_t n;
328 	uint16_t con;
329 	int error;
330 
331 	sc = device_get_softc(dev);
332 
333 	SMB_LOCK(sc);
334 	while (sc->busy)
335 		mtx_sleep(sc, &sc->mtx, 0, "i2cbuswait", 0);
336 	sc->busy = 1;
337 	sc->status = 0;
338 
339 	for (n = 0; n < nmsgs; n++) {
340 		/* Set target address */
341 		if (n == 0 || msgs[n].slave != msgs[n - 1].slave)
342 			jzsmb_reset_locked(dev, msgs[n].slave);
343 
344 		/* Set read or write */
345 		if ((msgs[n].flags & IIC_M_RD) != 0)
346 			error = jzsmb_transfer_read(dev, &msgs[n]);
347 		else
348 			error = jzsmb_transfer_write(dev, &msgs[n],
349 			    n < nmsgs - 1);
350 
351 		if (error != 0)
352 			goto done;
353 	}
354 
355 done:
356 	/* Send stop if necessary */
357 	con = SMB_READ(sc, SMBCON);
358 	con &= ~SMBCON_STPHLD;
359 	SMB_WRITE(sc, SMBCON, con);
360 
361 	/* Disable SMB */
362 	jzsmb_enable(sc, 0);
363 
364 	sc->msg = NULL;
365 	sc->busy = 0;
366 	wakeup(sc);
367 	SMB_UNLOCK(sc);
368 
369 	return (error);
370 }
371 
372 static int
jzsmb_probe(device_t dev)373 jzsmb_probe(device_t dev)
374 {
375 	if (!ofw_bus_status_okay(dev))
376 		return (ENXIO);
377 
378 	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
379 		return (ENXIO);
380 
381 	device_set_desc(dev, "Ingenic JZ4780 SMB Controller");
382 
383 	return (BUS_PROBE_DEFAULT);
384 }
385 
386 static int
jzsmb_attach(device_t dev)387 jzsmb_attach(device_t dev)
388 {
389 	struct jzsmb_softc *sc;
390 	phandle_t node;
391 	int error;
392 
393 	sc = device_get_softc(dev);
394 	node = ofw_bus_get_node(dev);
395 	mtx_init(&sc->mtx, device_get_nameunit(dev), "jzsmb", MTX_DEF);
396 
397 	error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
398 	if (error != 0) {
399 		device_printf(dev, "cannot get clock\n");
400 		goto fail;
401 	}
402 	error = clk_enable(sc->clk);
403 	if (error != 0) {
404 		device_printf(dev, "cannot enable clock\n");
405 		goto fail;
406 	}
407 	error = clk_get_freq(sc->clk, &sc->bus_freq);
408 	if (error != 0 || sc->bus_freq == 0) {
409 		device_printf(dev, "cannot get bus frequency\n");
410 		return (error);
411 	}
412 
413 	if (bus_alloc_resources(dev, jzsmb_spec, &sc->res) != 0) {
414 		device_printf(dev, "cannot allocate resources for device\n");
415 		error = ENXIO;
416 		goto fail;
417 	}
418 
419 	if (OF_getencprop(node, "clock-frequency", &sc->i2c_freq,
420 	    sizeof(sc->i2c_freq)) != 0 || sc->i2c_freq == 0)
421 		sc->i2c_freq = 100000;	/* Default to standard mode */
422 
423 	sc->iicbus = device_add_child(dev, "iicbus", -1);
424 	if (sc->iicbus == NULL) {
425 		device_printf(dev, "cannot add iicbus child device\n");
426 		error = ENXIO;
427 		goto fail;
428 	}
429 
430 	bus_generic_attach(dev);
431 
432 	return (0);
433 
434 fail:
435 	bus_release_resources(dev, jzsmb_spec, &sc->res);
436 	if (sc->clk != NULL)
437 		clk_release(sc->clk);
438 	mtx_destroy(&sc->mtx);
439 	return (error);
440 }
441 
442 static device_method_t jzsmb_methods[] = {
443 	/* Device interface */
444 	DEVMETHOD(device_probe,		jzsmb_probe),
445 	DEVMETHOD(device_attach,	jzsmb_attach),
446 
447 	/* Bus interface */
448 	DEVMETHOD(bus_setup_intr,	bus_generic_setup_intr),
449 	DEVMETHOD(bus_teardown_intr,	bus_generic_teardown_intr),
450 	DEVMETHOD(bus_alloc_resource,	bus_generic_alloc_resource),
451 	DEVMETHOD(bus_release_resource,	bus_generic_release_resource),
452 	DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
453 	DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
454 	DEVMETHOD(bus_adjust_resource,	bus_generic_adjust_resource),
455 	DEVMETHOD(bus_set_resource,	bus_generic_rl_set_resource),
456 	DEVMETHOD(bus_get_resource,	bus_generic_rl_get_resource),
457 
458 	/* OFW methods */
459 	DEVMETHOD(ofw_bus_get_node,	jzsmb_get_node),
460 
461 	/* iicbus interface */
462 	DEVMETHOD(iicbus_callback,	iicbus_null_callback),
463 	DEVMETHOD(iicbus_reset,		jzsmb_reset),
464 	DEVMETHOD(iicbus_transfer,	jzsmb_transfer),
465 
466 	DEVMETHOD_END
467 };
468 
469 static driver_t jzsmb_driver = {
470 	"iichb",
471 	jzsmb_methods,
472 	sizeof(struct jzsmb_softc),
473 };
474 
475 static devclass_t jzsmb_devclass;
476 
477 EARLY_DRIVER_MODULE(iicbus, jzsmb, iicbus_driver, iicbus_devclass, 0, 0,
478     BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
479 EARLY_DRIVER_MODULE(jzsmb, simplebus, jzsmb_driver, jzsmb_devclass, 0, 0,
480     BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
481 MODULE_VERSION(jzsmb, 1);
482