]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/mips/ingenic/jz4780_smb.c
Update ELF Tool Chain to upstream r3769
[FreeBSD/FreeBSD.git] / sys / mips / ingenic / jz4780_smb.c
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
77 static inline int
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
116 jzsmb_get_node(device_t bus, device_t dev)
117 {
118         return (ofw_bus_get_node(bus));
119 }
120
121 static int
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
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
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
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
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
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
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
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);