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