jbus-i2c.c revision 1.2
1/* $NetBSD: jbus-i2c.c,v 1.2 2018/10/26 01:57:59 macallan Exp $ */ 2 3/* 4 * Copyright (c) 2018 Michael Lorenz 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 20 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 24 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 25 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29#include <sys/cdefs.h> 30__KERNEL_RCSID(0, "$NetBSD: jbus-i2c.c,v 1.2 2018/10/26 01:57:59 macallan Exp $"); 31 32#include <sys/param.h> 33#include <sys/device.h> 34#include <sys/errno.h> 35 36#include <sys/bus.h> 37#include <machine/autoconf.h> 38 39#include <dev/i2c/i2cvar.h> 40#include <dev/i2c/i2c_bitbang.h> 41 42#include <machine/openfirm.h> 43 44#ifdef JBUSI2C_DEBUG 45#define DPRINTF printf 46#else 47#define DPRINTF if (0) printf 48#endif 49 50/* I2C glue */ 51static int jbusi2c_i2c_acquire_bus(void *, int); 52static void jbusi2c_i2c_release_bus(void *, int); 53static int jbusi2c_i2c_send_start(void *, int); 54static int jbusi2c_i2c_send_stop(void *, int); 55static int jbusi2c_i2c_initiate_xfer(void *, i2c_addr_t, int); 56static int jbusi2c_i2c_read_byte(void *, uint8_t *, int); 57static int jbusi2c_i2c_write_byte(void *, uint8_t, int); 58 59/* I2C bitbang glue */ 60static void jbusi2c_i2cbb_set_bits(void *, uint32_t); 61static void jbusi2c_i2cbb_set_dir(void *, uint32_t); 62static uint32_t jbusi2c_i2cbb_read(void *); 63 64static const struct i2c_bitbang_ops jbusi2c_i2cbb_ops = { 65 jbusi2c_i2cbb_set_bits, 66 jbusi2c_i2cbb_set_dir, 67 jbusi2c_i2cbb_read, 68 { 69 2, /* bit 1 is data */ 70 1, /* bit 0 is clock */ 71 3, /* direction register for both out */ 72 1 /* data in, clock out */ 73 } 74}; 75 76static int jbusi2c_match(device_t, cfdata_t, void *); 77static void jbusi2c_attach(device_t, device_t, void *); 78 79struct jbusi2c_softc { 80 device_t sc_dev; 81 struct i2c_controller sc_i2c; 82 kmutex_t sc_i2c_lock; 83 bus_space_tag_t sc_bustag; 84 bus_space_handle_t sc_regh; 85 int sc_node; 86}; 87 88static void jbusi2c_setup_i2c(struct jbusi2c_softc *); 89 90CFATTACH_DECL_NEW(jbusi2c, sizeof(struct jbusi2c_softc), 91 jbusi2c_match, jbusi2c_attach, NULL, NULL); 92 93/* schizo GPIO registers */ 94#define DATA 0 95#define DIR 8 96 97int 98jbusi2c_match(device_t parent, cfdata_t match, void *aux) 99{ 100 struct mainbus_attach_args *ma = aux; 101 char *str; 102 103 if (strcmp(ma->ma_name, "i2c") != 0) 104 return (0); 105 106 str = prom_getpropstring(ma->ma_node, "compatible"); 107 if (strcmp(str, "jbus-i2c") == 0) 108 return (1); 109 110 return (0); 111} 112 113void 114jbusi2c_attach(device_t parent, device_t self, void *aux) 115{ 116 struct jbusi2c_softc *sc = device_private(self); 117 struct mainbus_attach_args *ma = aux; 118 119 aprint_normal(": addr %lx\n", ma->ma_reg[0].ur_paddr); 120 121 sc->sc_dev = self; 122 sc->sc_node = ma->ma_node; 123 sc->sc_bustag = ma->ma_bustag; 124 125 if (bus_space_map(sc->sc_bustag, ma->ma_reg[0].ur_paddr, 16, 0, 126 &sc->sc_regh)) { 127 aprint_error(": failed to map registers\n"); 128 return; 129 } 130 131 jbusi2c_setup_i2c(sc); 132} 133 134 135 136static void 137jbusi2c_setup_i2c(struct jbusi2c_softc *sc) 138{ 139 struct i2cbus_attach_args iba; 140 prop_array_t cfg; 141 prop_dictionary_t dev; 142 prop_data_t data; 143 prop_dictionary_t dict = device_properties(sc->sc_dev); 144 int devs, regs[2], addr; 145 char name[64], compat[256]; 146 147 sc->sc_i2c.ic_cookie = sc; 148 sc->sc_i2c.ic_acquire_bus = jbusi2c_i2c_acquire_bus; 149 sc->sc_i2c.ic_release_bus = jbusi2c_i2c_release_bus; 150 sc->sc_i2c.ic_send_start = jbusi2c_i2c_send_start; 151 sc->sc_i2c.ic_send_stop = jbusi2c_i2c_send_stop; 152 sc->sc_i2c.ic_initiate_xfer = jbusi2c_i2c_initiate_xfer; 153 sc->sc_i2c.ic_read_byte = jbusi2c_i2c_read_byte; 154 sc->sc_i2c.ic_write_byte = jbusi2c_i2c_write_byte; 155 sc->sc_i2c.ic_exec = NULL; 156 157 /* round up i2c devices */ 158 devs = OF_child(sc->sc_node); 159 cfg = prop_array_create(); 160 prop_dictionary_set(dict, "i2c-child-devices", cfg); 161 prop_object_release(cfg); 162 while (devs != 0) { 163 if (OF_getprop(devs, "name", name, 256) <= 0) 164 goto skip; 165 memset(compat, 0, sizeof(compat)); 166 if (OF_getprop(devs, "compatible", 167 compat, 255) <= 0) 168 goto skip; 169 if (OF_getprop(devs, "reg", regs, 8) <= 0) 170 goto skip; 171 if (regs[0] != 0) goto skip; 172 addr = (regs[1] & 0xff) >> 1; 173 DPRINTF("-> %s@%d,%x\n", name, regs[0], addr); 174 dev = prop_dictionary_create(); 175 prop_dictionary_set_cstring(dev, "name", name); 176 data = prop_data_create_data(compat, strlen(compat)+1); 177 prop_dictionary_set(dev, "compatible", data); 178 prop_object_release(data); 179 prop_dictionary_set_uint32(dev, "addr", addr); 180 prop_dictionary_set_uint64(dev, "cookie", devs); 181 prop_array_add(cfg, dev); 182 prop_object_release(dev); 183 skip: 184 devs = OF_peer(devs); 185 } 186 memset(&iba, 0, sizeof(iba)); 187 iba.iba_tag = &sc->sc_i2c; 188 mutex_init(&sc->sc_i2c_lock, MUTEX_DEFAULT, IPL_NONE); 189 config_found_ia(sc->sc_dev, "i2cbus", &iba, 190 iicbus_print); 191} 192 193static inline void 194jbusi2c_write(struct jbusi2c_softc *sc, int reg, uint64_t bits) 195{ 196 bus_space_write_8(sc->sc_bustag, sc->sc_regh, reg, bits); 197} 198 199static inline uint64_t 200jbusi2c_read(struct jbusi2c_softc *sc, int reg) 201{ 202 return bus_space_read_8(sc->sc_bustag, sc->sc_regh, reg); 203} 204 205/* I2C bitbanging */ 206static void 207jbusi2c_i2cbb_set_bits(void *cookie, uint32_t bits) 208{ 209 struct jbusi2c_softc *sc = cookie; 210 211 jbusi2c_write(sc, DATA, bits); 212} 213 214static void 215jbusi2c_i2cbb_set_dir(void *cookie, uint32_t dir) 216{ 217 struct jbusi2c_softc *sc = cookie; 218 219 jbusi2c_write(sc, DIR, dir); 220} 221 222static uint32_t 223jbusi2c_i2cbb_read(void *cookie) 224{ 225 struct jbusi2c_softc *sc = cookie; 226 227 return jbusi2c_read(sc, DATA); 228} 229 230/* higher level I2C stuff */ 231static int 232jbusi2c_i2c_acquire_bus(void *cookie, int flags) 233{ 234 struct jbusi2c_softc *sc = cookie; 235 236 mutex_enter(&sc->sc_i2c_lock); 237 return 0; 238} 239 240static void 241jbusi2c_i2c_release_bus(void *cookie, int flags) 242{ 243 struct jbusi2c_softc *sc = cookie; 244 245 mutex_exit(&sc->sc_i2c_lock); 246} 247 248static int 249jbusi2c_i2c_send_start(void *cookie, int flags) 250{ 251 252 return i2c_bitbang_send_start(cookie, flags, &jbusi2c_i2cbb_ops); 253} 254 255static int 256jbusi2c_i2c_send_stop(void *cookie, int flags) 257{ 258 259 return i2c_bitbang_send_stop(cookie, flags, &jbusi2c_i2cbb_ops); 260} 261 262static int 263jbusi2c_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags) 264{ 265 266 return i2c_bitbang_initiate_xfer(cookie, addr, flags, 267 &jbusi2c_i2cbb_ops); 268} 269 270static int 271jbusi2c_i2c_read_byte(void *cookie, uint8_t *valp, int flags) 272{ 273 274 return i2c_bitbang_read_byte(cookie, valp, flags, &jbusi2c_i2cbb_ops); 275} 276 277static int 278jbusi2c_i2c_write_byte(void *cookie, uint8_t val, int flags) 279{ 280 281 return i2c_bitbang_write_byte(cookie, val, flags, &jbusi2c_i2cbb_ops); 282} 283