1/*-
2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca>
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
77static inline int
78tstohz(const struct timespec *tsp)
79{
80	struct timeval tv;
81
82	TIMESPEC_TO_TIMEVAL(&tv, tsp);
83	return (tvtohz(&tv));
84}
85
86static struct ofw_compat_data compat_data[] = {
87	{ "ingenic,jz4780-i2c",		1 },
88	{ NULL,				0 }
89};
90
91static struct resource_spec jzsmb_spec[] = {
92	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
93	{ -1, 0 }
94};
95
96struct 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
115static phandle_t
116jzsmb_get_node(device_t bus, device_t dev)
117{
118	return (ofw_bus_get_node(bus));
119}
120
121static int
122jzsmb_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
139static int
140jzsmb_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
214static int
215jzsmb_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
229static int
230jzsmb_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
276static int
277jzsmb_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
323static int
324jzsmb_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
355done:
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
372static int
373jzsmb_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
386static int
387jzsmb_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
434fail:
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
442static 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
469static driver_t jzsmb_driver = {
470	"iichb",
471	jzsmb_methods,
472	sizeof(struct jzsmb_softc),
473};
474
475static devclass_t jzsmb_devclass;
476
477EARLY_DRIVER_MODULE(iicbus, jzsmb, iicbus_driver, iicbus_devclass, 0, 0,
478    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
479EARLY_DRIVER_MODULE(jzsmb, simplebus, jzsmb_driver, jzsmb_devclass, 0, 0,
480    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
481MODULE_VERSION(jzsmb, 1);
482