1*97b909b3Skettenis /* $OpenBSD: apliic.c,v 1.5 2022/12/10 18:43:48 kettenis Exp $ */
23e05440bSpatrick /*
33e05440bSpatrick * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
43e05440bSpatrick *
53e05440bSpatrick * Permission to use, copy, modify, and distribute this software for any
63e05440bSpatrick * purpose with or without fee is hereby granted, provided that the above
73e05440bSpatrick * copyright notice and this permission notice appear in all copies.
83e05440bSpatrick *
93e05440bSpatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103e05440bSpatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113e05440bSpatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123e05440bSpatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133e05440bSpatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143e05440bSpatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153e05440bSpatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163e05440bSpatrick */
173e05440bSpatrick
183e05440bSpatrick #include <sys/param.h>
193e05440bSpatrick #include <sys/systm.h>
203e05440bSpatrick #include <sys/device.h>
210163115dSkettenis #include <sys/malloc.h>
223e05440bSpatrick
233e05440bSpatrick #include <machine/intr.h>
243e05440bSpatrick #include <machine/bus.h>
253e05440bSpatrick #include <machine/fdt.h>
263e05440bSpatrick
273e05440bSpatrick #define _I2C_PRIVATE
283e05440bSpatrick #include <dev/i2c/i2cvar.h>
293e05440bSpatrick
303e05440bSpatrick #include <dev/ofw/openfirm.h>
313e05440bSpatrick #include <dev/ofw/ofw_clock.h>
323e05440bSpatrick #include <dev/ofw/ofw_pinctrl.h>
333e05440bSpatrick #include <dev/ofw/ofw_power.h>
343e05440bSpatrick #include <dev/ofw/fdt.h>
353e05440bSpatrick
363e05440bSpatrick /* Registers. */
373e05440bSpatrick #define I2C_MTXFIFO 0x00
383e05440bSpatrick #define I2C_MTXFIFO_DATA_MASK (0xff << 0)
393e05440bSpatrick #define I2C_MTXFIFO_START (1 << 8)
403e05440bSpatrick #define I2C_MTXFIFO_STOP (1 << 9)
413e05440bSpatrick #define I2C_MTXFIFO_READ (1 << 10)
423e05440bSpatrick #define I2C_MRXFIFO 0x04
433e05440bSpatrick #define I2C_MRXFIFO_DATA_MASK (0xff << 0)
443e05440bSpatrick #define I2C_MRXFIFO_EMPTY (1 << 8)
453e05440bSpatrick #define I2C_SMSTA 0x14
463e05440bSpatrick #define I2C_SMSTA_MTN (1 << 21)
473e05440bSpatrick #define I2C_SMSTA_XEN (1 << 27)
483e05440bSpatrick #define I2C_SMSTA_XBUSY (1 << 28)
493e05440bSpatrick #define I2C_CTL 0x1c
503e05440bSpatrick #define I2C_CTL_CLK_MASK (0xff << 0)
513e05440bSpatrick #define I2C_CTL_MTR (1 << 9)
523e05440bSpatrick #define I2C_CTL_MRR (1 << 10)
533e05440bSpatrick #define I2C_CTL_EN (1 << 11)
543e05440bSpatrick #define I2C_REV 0x28
553e05440bSpatrick
563e05440bSpatrick #define HREAD4(sc, reg) \
573e05440bSpatrick (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
583e05440bSpatrick #define HWRITE4(sc, reg, val) \
593e05440bSpatrick bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
603e05440bSpatrick #define HSET4(sc, reg, bits) \
613e05440bSpatrick HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
623e05440bSpatrick #define HCLR4(sc, reg, bits) \
633e05440bSpatrick HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
643e05440bSpatrick
653e05440bSpatrick struct apliic_softc {
663e05440bSpatrick struct device sc_dev;
673e05440bSpatrick bus_space_tag_t sc_iot;
683e05440bSpatrick bus_space_handle_t sc_ioh;
693e05440bSpatrick
703e05440bSpatrick int sc_node;
713e05440bSpatrick int sc_hwrev;
723e05440bSpatrick uint32_t sc_clkdiv;
733e05440bSpatrick struct i2c_controller sc_ic;
743e05440bSpatrick };
753e05440bSpatrick
763e05440bSpatrick int apliic_match(struct device *, void *, void *);
773e05440bSpatrick void apliic_attach(struct device *, struct device *, void *);
783e05440bSpatrick
793e05440bSpatrick const struct cfattach apliic_ca = {
803e05440bSpatrick sizeof (struct apliic_softc), apliic_match, apliic_attach
813e05440bSpatrick };
823e05440bSpatrick
833e05440bSpatrick struct cfdriver apliic_cd = {
843e05440bSpatrick NULL, "apliic", DV_DULL
853e05440bSpatrick };
863e05440bSpatrick
873e05440bSpatrick int apliic_acquire_bus(void *, int);
883e05440bSpatrick void apliic_release_bus(void *, int);
893e05440bSpatrick int apliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
903e05440bSpatrick void *, size_t, int);
913e05440bSpatrick
923e05440bSpatrick void apliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
933e05440bSpatrick
943e05440bSpatrick int
apliic_match(struct device * parent,void * match,void * aux)953e05440bSpatrick apliic_match(struct device *parent, void *match, void *aux)
963e05440bSpatrick {
973e05440bSpatrick struct fdt_attach_args *faa = aux;
983e05440bSpatrick
993e05440bSpatrick return OF_is_compatible(faa->fa_node, "apple,i2c");
1003e05440bSpatrick }
1013e05440bSpatrick
1023e05440bSpatrick void
apliic_attach(struct device * parent,struct device * self,void * aux)1033e05440bSpatrick apliic_attach(struct device *parent, struct device *self, void *aux)
1043e05440bSpatrick {
1053e05440bSpatrick struct apliic_softc *sc = (struct apliic_softc *)self;
1063e05440bSpatrick struct fdt_attach_args *faa = aux;
1073e05440bSpatrick struct i2cbus_attach_args iba;
1083e05440bSpatrick uint32_t clock_speed, bus_speed;
1093e05440bSpatrick
1103e05440bSpatrick if (faa->fa_nreg < 1) {
1113e05440bSpatrick printf(": no registers\n");
1123e05440bSpatrick return;
1133e05440bSpatrick }
1143e05440bSpatrick
1153e05440bSpatrick sc->sc_iot = faa->fa_iot;
1163e05440bSpatrick sc->sc_node = faa->fa_node;
1173e05440bSpatrick
1183e05440bSpatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1193e05440bSpatrick faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1203e05440bSpatrick printf(": can't map registers\n");
1213e05440bSpatrick return;
1223e05440bSpatrick }
1233e05440bSpatrick
1243e05440bSpatrick printf("\n");
1253e05440bSpatrick
1263e05440bSpatrick pinctrl_byname(sc->sc_node, "default");
1273e05440bSpatrick power_domain_enable(sc->sc_node);
1283e05440bSpatrick clock_enable_all(sc->sc_node);
1293e05440bSpatrick
1303e05440bSpatrick clock_speed = clock_get_frequency(sc->sc_node, NULL);
1313e05440bSpatrick bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000) * 16;
1323e05440bSpatrick sc->sc_clkdiv = (clock_speed + bus_speed - 1) / bus_speed;
1333e05440bSpatrick KASSERT(sc->sc_clkdiv <= I2C_CTL_CLK_MASK);
1343e05440bSpatrick
1353e05440bSpatrick sc->sc_hwrev = HREAD4(sc, I2C_REV);
1363e05440bSpatrick
1373e05440bSpatrick HWRITE4(sc, I2C_CTL, sc->sc_clkdiv | I2C_CTL_MTR | I2C_CTL_MRR |
1383e05440bSpatrick (sc->sc_hwrev >= 6 ? I2C_CTL_EN : 0));
1393e05440bSpatrick
1403e05440bSpatrick sc->sc_ic.ic_cookie = sc;
1413e05440bSpatrick sc->sc_ic.ic_acquire_bus = apliic_acquire_bus;
1423e05440bSpatrick sc->sc_ic.ic_release_bus = apliic_release_bus;
1433e05440bSpatrick sc->sc_ic.ic_exec = apliic_exec;
1443e05440bSpatrick
1453e05440bSpatrick /* Configure its children */
1463e05440bSpatrick memset(&iba, 0, sizeof(iba));
1473e05440bSpatrick iba.iba_name = "iic";
1483e05440bSpatrick iba.iba_tag = &sc->sc_ic;
1493e05440bSpatrick iba.iba_bus_scan = apliic_bus_scan;
1503e05440bSpatrick iba.iba_bus_scan_arg = &sc->sc_node;
1513e05440bSpatrick
1523e05440bSpatrick config_found(&sc->sc_dev, &iba, iicbus_print);
1533e05440bSpatrick }
1543e05440bSpatrick
1553e05440bSpatrick int
apliic_wait(struct apliic_softc * sc)156075e123eSkettenis apliic_wait(struct apliic_softc *sc)
157075e123eSkettenis {
158075e123eSkettenis uint32_t reg;
159075e123eSkettenis int timo;
160075e123eSkettenis
161*97b909b3Skettenis for (timo = 100; timo > 0; timo--) {
162075e123eSkettenis reg = HREAD4(sc, I2C_SMSTA);
163075e123eSkettenis if (reg & I2C_SMSTA_XEN)
164075e123eSkettenis break;
165075e123eSkettenis delay(1000);
166075e123eSkettenis }
167075e123eSkettenis if (reg & I2C_SMSTA_MTN)
168075e123eSkettenis return ENXIO;
169075e123eSkettenis if (timo == 0) {
170075e123eSkettenis HWRITE4(sc, I2C_SMSTA, reg);
171075e123eSkettenis return ETIMEDOUT;
172075e123eSkettenis }
173075e123eSkettenis
174075e123eSkettenis HWRITE4(sc, I2C_SMSTA, I2C_SMSTA_XEN);
175075e123eSkettenis return 0;
176075e123eSkettenis }
177075e123eSkettenis
178075e123eSkettenis int
apliic_acquire_bus(void * cookie,int flags)1793e05440bSpatrick apliic_acquire_bus(void *cookie, int flags)
1803e05440bSpatrick {
1813e05440bSpatrick return 0;
1823e05440bSpatrick }
1833e05440bSpatrick
1843e05440bSpatrick void
apliic_release_bus(void * cookie,int flags)1853e05440bSpatrick apliic_release_bus(void *cookie, int flags)
1863e05440bSpatrick {
1873e05440bSpatrick }
1883e05440bSpatrick
1893e05440bSpatrick int
apliic_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)1903e05440bSpatrick apliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
1913e05440bSpatrick size_t cmdlen, void *buf, size_t buflen, int flags)
1923e05440bSpatrick {
1933e05440bSpatrick struct apliic_softc *sc = cookie;
1943e05440bSpatrick uint32_t reg;
195075e123eSkettenis int error, i;
1963e05440bSpatrick
1973e05440bSpatrick if (!I2C_OP_STOP_P(op))
1983e05440bSpatrick return EINVAL;
1993e05440bSpatrick
200075e123eSkettenis reg = HREAD4(sc, I2C_SMSTA);
201075e123eSkettenis HWRITE4(sc, I2C_SMSTA, reg);
2023e05440bSpatrick
2033e05440bSpatrick if (cmdlen > 0) {
2043e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
2053e05440bSpatrick for (i = 0; i < cmdlen - 1; i++)
2063e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[i]);
2073e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[cmdlen - 1] |
2083e05440bSpatrick (buflen == 0 ? I2C_MTXFIFO_STOP : 0));
2093e05440bSpatrick }
2103e05440bSpatrick
2113e05440bSpatrick if (buflen == 0)
2123e05440bSpatrick return 0;
2133e05440bSpatrick
2143e05440bSpatrick if (I2C_OP_READ_P(op)) {
2153e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1 | 1);
2163e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_READ | buflen |
2173e05440bSpatrick I2C_MTXFIFO_STOP);
218075e123eSkettenis error = apliic_wait(sc);
219075e123eSkettenis if (error)
220075e123eSkettenis return error;
2213e05440bSpatrick for (i = 0; i < buflen; i++) {
2223e05440bSpatrick reg = HREAD4(sc, I2C_MRXFIFO);
223585c4a5aSkettenis if (reg & I2C_MRXFIFO_EMPTY)
2243e05440bSpatrick return EIO;
2253e05440bSpatrick ((uint8_t *)buf)[i] = reg & I2C_MRXFIFO_DATA_MASK;
2263e05440bSpatrick }
2273e05440bSpatrick } else {
2283e05440bSpatrick if (cmdlen == 0)
2293e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
2303e05440bSpatrick for (i = 0; i < buflen - 1; i++)
2313e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[i]);
2323e05440bSpatrick HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[buflen - 1] |
2333e05440bSpatrick I2C_MTXFIFO_STOP);
234075e123eSkettenis error = apliic_wait(sc);
235075e123eSkettenis if (error)
236075e123eSkettenis return error;
2373e05440bSpatrick }
2383e05440bSpatrick
2393e05440bSpatrick return 0;
2403e05440bSpatrick }
2413e05440bSpatrick
2423e05440bSpatrick void
apliic_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * arg)2433e05440bSpatrick apliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
2443e05440bSpatrick {
2453e05440bSpatrick int iba_node = *(int *)arg;
2463e05440bSpatrick struct i2c_attach_args ia;
2470163115dSkettenis char status[32];
2480163115dSkettenis char *compat;
2493e05440bSpatrick uint32_t reg[1];
2503e05440bSpatrick int node;
2510163115dSkettenis int len;
2523e05440bSpatrick
2533e05440bSpatrick for (node = OF_child(iba_node); node; node = OF_peer(node)) {
2543e05440bSpatrick memset(status, 0, sizeof(status));
2553e05440bSpatrick if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
2563e05440bSpatrick strcmp(status, "disabled") == 0)
2573e05440bSpatrick continue;
2583e05440bSpatrick
2590163115dSkettenis memset(reg, 0, sizeof(reg));
2603e05440bSpatrick if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg))
2613e05440bSpatrick continue;
2623e05440bSpatrick
2630163115dSkettenis len = OF_getproplen(node, "compatible");
2640163115dSkettenis if (len <= 0)
2650163115dSkettenis continue;
2660163115dSkettenis
2670163115dSkettenis compat = malloc(len, M_TEMP, M_WAITOK);
2680163115dSkettenis OF_getprop(node, "compatible", compat, len);
2690163115dSkettenis
2703e05440bSpatrick memset(&ia, 0, sizeof(ia));
2713e05440bSpatrick ia.ia_tag = iba->iba_tag;
2723e05440bSpatrick ia.ia_addr = bemtoh32(®[0]);
2730163115dSkettenis ia.ia_name = compat;
2740163115dSkettenis ia.ia_namelen = len;
2753e05440bSpatrick ia.ia_cookie = &node;
2763e05440bSpatrick config_found(self, &ia, iic_print);
2770163115dSkettenis
2780163115dSkettenis free(compat, M_TEMP, len);
2793e05440bSpatrick }
2803e05440bSpatrick }
281