1*c7fb772bSthorpej /* $NetBSD: jbus-i2c.c,v 1.7 2021/08/07 16:19:05 thorpej Exp $ */
2824617f4Smacallan
3824617f4Smacallan /*
4824617f4Smacallan * Copyright (c) 2018 Michael Lorenz
5824617f4Smacallan * All rights reserved.
6824617f4Smacallan *
7824617f4Smacallan * Redistribution and use in source and binary forms, with or without
8824617f4Smacallan * modification, are permitted provided that the following conditions
9824617f4Smacallan * are met:
10824617f4Smacallan * 1. Redistributions of source code must retain the above copyright
11824617f4Smacallan * notice, this list of conditions and the following disclaimer.
12824617f4Smacallan * 2. Redistributions in binary form must reproduce the above copyright
13824617f4Smacallan * notice, this list of conditions and the following disclaimer in the
14824617f4Smacallan * documentation and/or other materials provided with the distribution.
15824617f4Smacallan *
16824617f4Smacallan * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
17824617f4Smacallan * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18824617f4Smacallan * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19824617f4Smacallan * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
20824617f4Smacallan * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21824617f4Smacallan * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22824617f4Smacallan * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23824617f4Smacallan * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
24824617f4Smacallan * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
25824617f4Smacallan * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26824617f4Smacallan * POSSIBILITY OF SUCH DAMAGE.
27824617f4Smacallan */
28824617f4Smacallan
29824617f4Smacallan #include <sys/cdefs.h>
30*c7fb772bSthorpej __KERNEL_RCSID(0, "$NetBSD: jbus-i2c.c,v 1.7 2021/08/07 16:19:05 thorpej Exp $");
31824617f4Smacallan
32824617f4Smacallan #include <sys/param.h>
33824617f4Smacallan #include <sys/device.h>
34824617f4Smacallan #include <sys/errno.h>
35824617f4Smacallan
36824617f4Smacallan #include <sys/bus.h>
37824617f4Smacallan #include <machine/autoconf.h>
38824617f4Smacallan
39824617f4Smacallan #include <dev/i2c/i2cvar.h>
40824617f4Smacallan #include <dev/i2c/i2c_bitbang.h>
41824617f4Smacallan
42824617f4Smacallan #include <machine/openfirm.h>
43824617f4Smacallan
440f10d4ceSmacallan #ifdef JBUSI2C_DEBUG
45824617f4Smacallan #define DPRINTF printf
460f10d4ceSmacallan #else
470f10d4ceSmacallan #define DPRINTF if (0) printf
480f10d4ceSmacallan #endif
49824617f4Smacallan
50824617f4Smacallan /* I2C glue */
51824617f4Smacallan static int jbusi2c_i2c_send_start(void *, int);
52824617f4Smacallan static int jbusi2c_i2c_send_stop(void *, int);
53824617f4Smacallan static int jbusi2c_i2c_initiate_xfer(void *, i2c_addr_t, int);
54824617f4Smacallan static int jbusi2c_i2c_read_byte(void *, uint8_t *, int);
55824617f4Smacallan static int jbusi2c_i2c_write_byte(void *, uint8_t, int);
56824617f4Smacallan
57824617f4Smacallan /* I2C bitbang glue */
58824617f4Smacallan static void jbusi2c_i2cbb_set_bits(void *, uint32_t);
59824617f4Smacallan static void jbusi2c_i2cbb_set_dir(void *, uint32_t);
60824617f4Smacallan static uint32_t jbusi2c_i2cbb_read(void *);
61824617f4Smacallan
62824617f4Smacallan static const struct i2c_bitbang_ops jbusi2c_i2cbb_ops = {
63824617f4Smacallan jbusi2c_i2cbb_set_bits,
64824617f4Smacallan jbusi2c_i2cbb_set_dir,
65824617f4Smacallan jbusi2c_i2cbb_read,
66824617f4Smacallan {
67824617f4Smacallan 2, /* bit 1 is data */
68824617f4Smacallan 1, /* bit 0 is clock */
69824617f4Smacallan 3, /* direction register for both out */
70824617f4Smacallan 1 /* data in, clock out */
71824617f4Smacallan }
72824617f4Smacallan };
73824617f4Smacallan
74824617f4Smacallan static int jbusi2c_match(device_t, cfdata_t, void *);
75824617f4Smacallan static void jbusi2c_attach(device_t, device_t, void *);
76824617f4Smacallan
77824617f4Smacallan struct jbusi2c_softc {
78824617f4Smacallan device_t sc_dev;
79824617f4Smacallan struct i2c_controller sc_i2c;
800f10d4ceSmacallan bus_space_tag_t sc_bustag;
810f10d4ceSmacallan bus_space_handle_t sc_regh;
82824617f4Smacallan int sc_node;
83824617f4Smacallan };
84824617f4Smacallan
85824617f4Smacallan static void jbusi2c_setup_i2c(struct jbusi2c_softc *);
86824617f4Smacallan
87824617f4Smacallan CFATTACH_DECL_NEW(jbusi2c, sizeof(struct jbusi2c_softc),
88824617f4Smacallan jbusi2c_match, jbusi2c_attach, NULL, NULL);
89824617f4Smacallan
900f10d4ceSmacallan /* schizo GPIO registers */
910f10d4ceSmacallan #define DATA 0
920f10d4ceSmacallan #define DIR 8
93824617f4Smacallan
94824617f4Smacallan int
jbusi2c_match(device_t parent,cfdata_t match,void * aux)95824617f4Smacallan jbusi2c_match(device_t parent, cfdata_t match, void *aux)
96824617f4Smacallan {
97824617f4Smacallan struct mainbus_attach_args *ma = aux;
98824617f4Smacallan char *str;
99824617f4Smacallan
100824617f4Smacallan if (strcmp(ma->ma_name, "i2c") != 0)
101824617f4Smacallan return (0);
102824617f4Smacallan
103824617f4Smacallan str = prom_getpropstring(ma->ma_node, "compatible");
104824617f4Smacallan if (strcmp(str, "jbus-i2c") == 0)
105824617f4Smacallan return (1);
106824617f4Smacallan
107824617f4Smacallan return (0);
108824617f4Smacallan }
109824617f4Smacallan
110824617f4Smacallan void
jbusi2c_attach(device_t parent,device_t self,void * aux)111824617f4Smacallan jbusi2c_attach(device_t parent, device_t self, void *aux)
112824617f4Smacallan {
113824617f4Smacallan struct jbusi2c_softc *sc = device_private(self);
114824617f4Smacallan struct mainbus_attach_args *ma = aux;
115824617f4Smacallan
1169277c3f4Smartin aprint_normal(": addr %" PRIx64 "\n", ma->ma_reg[0].ur_paddr);
117824617f4Smacallan
118824617f4Smacallan sc->sc_dev = self;
119824617f4Smacallan sc->sc_node = ma->ma_node;
1200f10d4ceSmacallan sc->sc_bustag = ma->ma_bustag;
121824617f4Smacallan
1220f10d4ceSmacallan if (bus_space_map(sc->sc_bustag, ma->ma_reg[0].ur_paddr, 16, 0,
1230f10d4ceSmacallan &sc->sc_regh)) {
1240f10d4ceSmacallan aprint_error(": failed to map registers\n");
1250f10d4ceSmacallan return;
1260f10d4ceSmacallan }
1270f10d4ceSmacallan
128824617f4Smacallan jbusi2c_setup_i2c(sc);
129824617f4Smacallan }
130824617f4Smacallan
131824617f4Smacallan
132824617f4Smacallan
133824617f4Smacallan static void
jbusi2c_setup_i2c(struct jbusi2c_softc * sc)134824617f4Smacallan jbusi2c_setup_i2c(struct jbusi2c_softc *sc)
135824617f4Smacallan {
136824617f4Smacallan struct i2cbus_attach_args iba;
137824617f4Smacallan prop_array_t cfg;
138824617f4Smacallan prop_dictionary_t dev;
139824617f4Smacallan prop_dictionary_t dict = device_properties(sc->sc_dev);
1400f10d4ceSmacallan int devs, regs[2], addr;
141824617f4Smacallan char name[64], compat[256];
142824617f4Smacallan
143601e1783Sthorpej iic_tag_init(&sc->sc_i2c);
1440f10d4ceSmacallan sc->sc_i2c.ic_cookie = sc;
145824617f4Smacallan sc->sc_i2c.ic_send_start = jbusi2c_i2c_send_start;
146824617f4Smacallan sc->sc_i2c.ic_send_stop = jbusi2c_i2c_send_stop;
147824617f4Smacallan sc->sc_i2c.ic_initiate_xfer = jbusi2c_i2c_initiate_xfer;
148824617f4Smacallan sc->sc_i2c.ic_read_byte = jbusi2c_i2c_read_byte;
149824617f4Smacallan sc->sc_i2c.ic_write_byte = jbusi2c_i2c_write_byte;
150824617f4Smacallan
151824617f4Smacallan /* round up i2c devices */
152824617f4Smacallan devs = OF_child(sc->sc_node);
153824617f4Smacallan cfg = prop_array_create();
154824617f4Smacallan prop_dictionary_set(dict, "i2c-child-devices", cfg);
155824617f4Smacallan prop_object_release(cfg);
156824617f4Smacallan while (devs != 0) {
157824617f4Smacallan if (OF_getprop(devs, "name", name, 256) <= 0)
158824617f4Smacallan goto skip;
159824617f4Smacallan memset(compat, 0, sizeof(compat));
160824617f4Smacallan if (OF_getprop(devs, "compatible",
161824617f4Smacallan compat, 255) <= 0)
162824617f4Smacallan goto skip;
163824617f4Smacallan if (OF_getprop(devs, "reg", regs, 8) <= 0)
164824617f4Smacallan goto skip;
165824617f4Smacallan if (regs[0] != 0) goto skip;
166824617f4Smacallan addr = (regs[1] & 0xff) >> 1;
167824617f4Smacallan DPRINTF("-> %s@%d,%x\n", name, regs[0], addr);
168824617f4Smacallan dev = prop_dictionary_create();
1691d33f10fSthorpej prop_dictionary_set_string(dev, "name", name);
1701d33f10fSthorpej prop_dictionary_set_data(dev, "compatible", compat,
1711d33f10fSthorpej strlen(compat)+1);
172824617f4Smacallan prop_dictionary_set_uint32(dev, "addr", addr);
173824617f4Smacallan prop_dictionary_set_uint64(dev, "cookie", devs);
174824617f4Smacallan prop_array_add(cfg, dev);
175824617f4Smacallan prop_object_release(dev);
176824617f4Smacallan skip:
177824617f4Smacallan devs = OF_peer(devs);
178824617f4Smacallan }
179824617f4Smacallan memset(&iba, 0, sizeof(iba));
180824617f4Smacallan iba.iba_tag = &sc->sc_i2c;
181*c7fb772bSthorpej config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE);
182824617f4Smacallan }
183824617f4Smacallan
1840f10d4ceSmacallan static inline void
jbusi2c_write(struct jbusi2c_softc * sc,int reg,uint64_t bits)1850f10d4ceSmacallan jbusi2c_write(struct jbusi2c_softc *sc, int reg, uint64_t bits)
1860f10d4ceSmacallan {
1870f10d4ceSmacallan bus_space_write_8(sc->sc_bustag, sc->sc_regh, reg, bits);
1880f10d4ceSmacallan }
1890f10d4ceSmacallan
1900f10d4ceSmacallan static inline uint64_t
jbusi2c_read(struct jbusi2c_softc * sc,int reg)1910f10d4ceSmacallan jbusi2c_read(struct jbusi2c_softc *sc, int reg)
1920f10d4ceSmacallan {
1930f10d4ceSmacallan return bus_space_read_8(sc->sc_bustag, sc->sc_regh, reg);
1940f10d4ceSmacallan }
1950f10d4ceSmacallan
196824617f4Smacallan /* I2C bitbanging */
197824617f4Smacallan static void
jbusi2c_i2cbb_set_bits(void * cookie,uint32_t bits)198824617f4Smacallan jbusi2c_i2cbb_set_bits(void *cookie, uint32_t bits)
199824617f4Smacallan {
2000f10d4ceSmacallan struct jbusi2c_softc *sc = cookie;
201824617f4Smacallan
2020f10d4ceSmacallan jbusi2c_write(sc, DATA, bits);
203824617f4Smacallan }
204824617f4Smacallan
205824617f4Smacallan static void
jbusi2c_i2cbb_set_dir(void * cookie,uint32_t dir)206824617f4Smacallan jbusi2c_i2cbb_set_dir(void *cookie, uint32_t dir)
207824617f4Smacallan {
2080f10d4ceSmacallan struct jbusi2c_softc *sc = cookie;
209824617f4Smacallan
2100f10d4ceSmacallan jbusi2c_write(sc, DIR, dir);
211824617f4Smacallan }
212824617f4Smacallan
213824617f4Smacallan static uint32_t
jbusi2c_i2cbb_read(void * cookie)214824617f4Smacallan jbusi2c_i2cbb_read(void *cookie)
215824617f4Smacallan {
2160f10d4ceSmacallan struct jbusi2c_softc *sc = cookie;
217824617f4Smacallan
2180f10d4ceSmacallan return jbusi2c_read(sc, DATA);
219824617f4Smacallan }
220824617f4Smacallan
221824617f4Smacallan /* higher level I2C stuff */
222824617f4Smacallan static int
jbusi2c_i2c_send_start(void * cookie,int flags)223824617f4Smacallan jbusi2c_i2c_send_start(void *cookie, int flags)
224824617f4Smacallan {
225824617f4Smacallan
226824617f4Smacallan return i2c_bitbang_send_start(cookie, flags, &jbusi2c_i2cbb_ops);
227824617f4Smacallan }
228824617f4Smacallan
229824617f4Smacallan static int
jbusi2c_i2c_send_stop(void * cookie,int flags)230824617f4Smacallan jbusi2c_i2c_send_stop(void *cookie, int flags)
231824617f4Smacallan {
232824617f4Smacallan
233824617f4Smacallan return i2c_bitbang_send_stop(cookie, flags, &jbusi2c_i2cbb_ops);
234824617f4Smacallan }
235824617f4Smacallan
236824617f4Smacallan static int
jbusi2c_i2c_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)237824617f4Smacallan jbusi2c_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
238824617f4Smacallan {
239824617f4Smacallan
240824617f4Smacallan return i2c_bitbang_initiate_xfer(cookie, addr, flags,
241824617f4Smacallan &jbusi2c_i2cbb_ops);
242824617f4Smacallan }
243824617f4Smacallan
244824617f4Smacallan static int
jbusi2c_i2c_read_byte(void * cookie,uint8_t * valp,int flags)245824617f4Smacallan jbusi2c_i2c_read_byte(void *cookie, uint8_t *valp, int flags)
246824617f4Smacallan {
247824617f4Smacallan
248824617f4Smacallan return i2c_bitbang_read_byte(cookie, valp, flags, &jbusi2c_i2cbb_ops);
249824617f4Smacallan }
250824617f4Smacallan
251824617f4Smacallan static int
jbusi2c_i2c_write_byte(void * cookie,uint8_t val,int flags)252824617f4Smacallan jbusi2c_i2c_write_byte(void *cookie, uint8_t val, int flags)
253824617f4Smacallan {
254824617f4Smacallan
255824617f4Smacallan return i2c_bitbang_write_byte(cookie, val, flags, &jbusi2c_i2cbb_ops);
256824617f4Smacallan }
257