From 3e05440b539a0a18b50cf3a9d6be1fffae6f7c43 Mon Sep 17 00:00:00 2001 From: patrick Date: Fri, 24 Dec 2021 00:01:39 +0000 Subject: [PATCH] Add apliic(4), a driver for the I2C controller found on various Apple SoCs. ok kettenis@ --- sys/arch/arm64/conf/GENERIC | 4 +- sys/arch/arm64/conf/RAMDISK | 4 +- sys/arch/arm64/conf/files.arm64 | 6 +- sys/arch/arm64/dev/apliic.c | 260 ++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+), 3 deletions(-) create mode 100644 sys/arch/arm64/dev/apliic.c diff --git a/sys/arch/arm64/conf/GENERIC b/sys/arch/arm64/conf/GENERIC index 3ec9f09c362..19fd6e03c4f 100644 --- a/sys/arch/arm64/conf/GENERIC +++ b/sys/arch/arm64/conf/GENERIC @@ -1,4 +1,4 @@ -# $OpenBSD: GENERIC,v 1.218 2021/12/19 13:07:36 kettenis Exp $ +# $OpenBSD: GENERIC,v 1.219 2021/12/24 00:01:39 patrick Exp $ # # GENERIC machine description file # @@ -141,6 +141,8 @@ gpioleds* at fdt? apldart* at fdt? early 1 apldog* at fdt? early 1 apldwusb* at fdt? +apliic* at fdt? +iic* at apliic? aplintc* at fdt? early 1 aplmbox* at fdt? aplpcie* at fdt? diff --git a/sys/arch/arm64/conf/RAMDISK b/sys/arch/arm64/conf/RAMDISK index 38b8e431b0d..b505e37b399 100644 --- a/sys/arch/arm64/conf/RAMDISK +++ b/sys/arch/arm64/conf/RAMDISK @@ -1,4 +1,4 @@ -# $OpenBSD: RAMDISK,v 1.164 2021/12/19 13:07:36 kettenis Exp $ +# $OpenBSD: RAMDISK,v 1.165 2021/12/24 00:01:39 patrick Exp $ machine arm64 maxusers 4 @@ -105,6 +105,8 @@ wsdisplay* at simplefb? apldart* at fdt? early 1 apldog* at fdt? early 1 apldwusb* at fdt? +apliic* at fdt? +iic* at apliic? aplintc* at fdt? early 1 aplmbox* at fdt? aplpcie* at fdt? diff --git a/sys/arch/arm64/conf/files.arm64 b/sys/arch/arm64/conf/files.arm64 index 8e1572f1d22..574cedea6ca 100644 --- a/sys/arch/arm64/conf/files.arm64 +++ b/sys/arch/arm64/conf/files.arm64 @@ -1,4 +1,4 @@ -# $OpenBSD: files.arm64,v 1.49 2021/12/19 23:47:24 kettenis Exp $ +# $OpenBSD: files.arm64,v 1.50 2021/12/24 00:01:39 patrick Exp $ maxpartitions 16 maxusers 2 8 128 @@ -151,6 +151,10 @@ device apldwusb: fdt attach apldwusb at fdt file arch/arm64/dev/apldwusb.c apldwusb +device apliic: i2cbus +attach apliic at fdt +file arch/arm64/dev/apliic.c apliic + device aplintc attach aplintc at fdt file arch/arm64/dev/aplintc.c aplintc diff --git a/sys/arch/arm64/dev/apliic.c b/sys/arch/arm64/dev/apliic.c new file mode 100644 index 00000000000..a6c106f0d80 --- /dev/null +++ b/sys/arch/arm64/dev/apliic.c @@ -0,0 +1,260 @@ +/* $OpenBSD: apliic.c,v 1.1 2021/12/24 00:01:39 patrick Exp $ */ +/* + * Copyright (c) 2021 Patrick Wildt + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include +#include +#include + +#define _I2C_PRIVATE +#include + +#include +#include +#include +#include +#include + +/* Registers. */ +#define I2C_MTXFIFO 0x00 +#define I2C_MTXFIFO_DATA_MASK (0xff << 0) +#define I2C_MTXFIFO_START (1 << 8) +#define I2C_MTXFIFO_STOP (1 << 9) +#define I2C_MTXFIFO_READ (1 << 10) +#define I2C_MRXFIFO 0x04 +#define I2C_MRXFIFO_DATA_MASK (0xff << 0) +#define I2C_MRXFIFO_EMPTY (1 << 8) +#define I2C_SMSTA 0x14 +#define I2C_SMSTA_MTN (1 << 21) +#define I2C_SMSTA_XEN (1 << 27) +#define I2C_SMSTA_XBUSY (1 << 28) +#define I2C_CTL 0x1c +#define I2C_CTL_CLK_MASK (0xff << 0) +#define I2C_CTL_MTR (1 << 9) +#define I2C_CTL_MRR (1 << 10) +#define I2C_CTL_EN (1 << 11) +#define I2C_REV 0x28 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct apliic_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + + int sc_node; + int sc_hwrev; + uint32_t sc_clkdiv; + struct i2c_controller sc_ic; +}; + +int apliic_match(struct device *, void *, void *); +void apliic_attach(struct device *, struct device *, void *); + +const struct cfattach apliic_ca = { + sizeof (struct apliic_softc), apliic_match, apliic_attach +}; + +struct cfdriver apliic_cd = { + NULL, "apliic", DV_DULL +}; + +int apliic_acquire_bus(void *, int); +void apliic_release_bus(void *, int); +int apliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, + void *, size_t, int); + +void apliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *); + +int +apliic_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + return OF_is_compatible(faa->fa_node, "apple,i2c"); +} + +void +apliic_attach(struct device *parent, struct device *self, void *aux) +{ + struct apliic_softc *sc = (struct apliic_softc *)self; + struct fdt_attach_args *faa = aux; + struct i2cbus_attach_args iba; + uint32_t clock_speed, bus_speed; + + if (faa->fa_nreg < 1) { + printf(": no registers\n"); + return; + } + + sc->sc_iot = faa->fa_iot; + sc->sc_node = faa->fa_node; + + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, + faa->fa_reg[0].size, 0, &sc->sc_ioh)) { + printf(": can't map registers\n"); + return; + } + + printf("\n"); + + pinctrl_byname(sc->sc_node, "default"); + power_domain_enable(sc->sc_node); + clock_enable_all(sc->sc_node); + + clock_speed = clock_get_frequency(sc->sc_node, NULL); + bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000) * 16; + sc->sc_clkdiv = (clock_speed + bus_speed - 1) / bus_speed; + KASSERT(sc->sc_clkdiv <= I2C_CTL_CLK_MASK); + + sc->sc_hwrev = HREAD4(sc, I2C_REV); + + HWRITE4(sc, I2C_CTL, sc->sc_clkdiv | I2C_CTL_MTR | I2C_CTL_MRR | + (sc->sc_hwrev >= 6 ? I2C_CTL_EN : 0)); + + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_acquire_bus = apliic_acquire_bus; + sc->sc_ic.ic_release_bus = apliic_release_bus; + sc->sc_ic.ic_exec = apliic_exec; + + /* Configure its children */ + memset(&iba, 0, sizeof(iba)); + iba.iba_name = "iic"; + iba.iba_tag = &sc->sc_ic; + iba.iba_bus_scan = apliic_bus_scan; + iba.iba_bus_scan_arg = &sc->sc_node; + + config_found(&sc->sc_dev, &iba, iicbus_print); +} + +int +apliic_acquire_bus(void *cookie, int flags) +{ + return 0; +} + +void +apliic_release_bus(void *cookie, int flags) +{ +} + +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) +{ + struct apliic_softc *sc = cookie; + uint32_t reg; + int i, j; + + if (!I2C_OP_STOP_P(op)) + return EINVAL; + + HSET4(sc, I2C_CTL, I2C_CTL_MTR | I2C_CTL_MRR); + HWRITE4(sc, I2C_SMSTA, 0xffffffff); + + if (cmdlen > 0) { + HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1); + for (i = 0; i < cmdlen - 1; i++) + HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[i]); + HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[cmdlen - 1] | + (buflen == 0 ? I2C_MTXFIFO_STOP : 0)); + } + + if (buflen == 0) + return 0; + + if (I2C_OP_READ_P(op)) { + HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1 | 1); + HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_READ | buflen | + I2C_MTXFIFO_STOP); + for (i = 1000; i > 0; i--) { + reg = HREAD4(sc, I2C_SMSTA); + if (reg & I2C_SMSTA_XEN) + break; + delay(1000); + } + if (reg & I2C_SMSTA_MTN) + return ENXIO; + if (i == 0) + return ETIMEDOUT; + HWRITE4(sc, I2C_SMSTA, I2C_SMSTA_XEN); + for (i = 0; i < buflen; i++) { + for (j = 10000; j > 0; j--) { + reg = HREAD4(sc, I2C_MRXFIFO); + if (!(reg & I2C_MRXFIFO_EMPTY)) + break; + } + if (j == 0) + return EIO; + ((uint8_t *)buf)[i] = reg & I2C_MRXFIFO_DATA_MASK; + } + } else { + if (cmdlen == 0) + HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1); + for (i = 0; i < buflen - 1; i++) + HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[i]); + HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[buflen - 1] | + I2C_MTXFIFO_STOP); + } + + return 0; +} + +void +apliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) +{ + int iba_node = *(int *)arg; + struct i2c_attach_args ia; + char name[32], status[32]; + uint32_t reg[1]; + int node; + + for (node = OF_child(iba_node); node; node = OF_peer(node)) { + memset(name, 0, sizeof(name)); + memset(status, 0, sizeof(status)); + memset(reg, 0, sizeof(reg)); + + if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) + continue; + if (name[0] == '\0') + continue; + + if (OF_getprop(node, "status", status, sizeof(status)) > 0 && + strcmp(status, "disabled") == 0) + continue; + + if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) + continue; + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = iba->iba_tag; + ia.ia_addr = bemtoh32(®[0]); + ia.ia_name = name; + ia.ia_cookie = &node; + config_found(self, &ia, iic_print); + } +} -- 2.20.1