This patch adds a basic driver for the PolarFire SoC MSS I2C controller. OK?
Index: share/man/man4/iic.4 =================================================================== RCS file: src/share/man/man4/iic.4,v retrieving revision 1.122 diff -u -p -r1.122 iic.4 --- share/man/man4/iic.4 24 Dec 2021 07:05:55 -0000 1.122 +++ share/man/man4/iic.4 14 Feb 2022 17:40:51 -0000 @@ -48,6 +48,7 @@ .Cd "iic* at mviic? # arm64" .Cd "iic* at rkiic? # arm64 armv7" .Cd "iic* at sxitwi? # arm64 armv7" +.Cd "iic* at mpfiic? # riscv64" .Cd "iic* at ociic? # riscv64" .Sh DESCRIPTION .Tn I2C @@ -113,6 +114,8 @@ Intel ICH SMBus controller Freescale i.MX I2C controller .It Xr kiic 4 Apple Kauai I2C controller +.It Xr mpfiic 4 +Microchip PolarFire SoC MSS I2C controller .It Xr mviic 4 Marvell Armada 3700 I2C Controller .It Xr nviic 4 Index: share/man/man4/man4.riscv64/Makefile =================================================================== RCS file: src/share/man/man4/man4.riscv64/Makefile,v retrieving revision 1.4 diff -u -p -r1.4 Makefile --- share/man/man4/man4.riscv64/Makefile 5 Jan 2022 03:32:43 -0000 1.4 +++ share/man/man4/man4.riscv64/Makefile 14 Feb 2022 17:40:52 -0000 @@ -1,6 +1,6 @@ # $OpenBSD: Makefile,v 1.4 2022/01/05 03:32:43 visa Exp $ -MAN= intro.4 mpfclock.4 plic.4 sfcc.4 sfclock.4 sfuart.4 +MAN= intro.4 mpfclock.4 mpfiic.4 plic.4 sfcc.4 sfclock.4 sfuart.4 MANSUBDIR=riscv64 Index: share/man/man4/man4.riscv64/mpfiic.4 =================================================================== RCS file: share/man/man4/man4.riscv64/mpfiic.4 diff -N share/man/man4/man4.riscv64/mpfiic.4 --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ share/man/man4/man4.riscv64/mpfiic.4 14 Feb 2022 17:40:53 -0000 @@ -0,0 +1,40 @@ +.\" $OpenBSD$ +.\" +.\" Copyright (c) 2022 Visa Hankala +.\" +.\" 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. +.\" +.Dd $Mdocdate$ +.Dt MPFIIC 4 riscv64 +.Os +.Sh NAME +.Nm mpfiic +.Nd Microchip PolarFire SoC MSS I2C controller +.Sh SYNOPSIS +.Cd "mpfiic* at fdt?" +.Cd "iic* at mpfiic?" +.Sh DESCRIPTION +The +.Nm +driver supports the Microchip PolarFire SoC MSS I2C controller +for use with the +.Xr iic 4 +framework. +.Sh SEE ALSO +.Xr iic 4 , +.Xr intro 4 +.Sh HISTORY +The +.Nm +driver first appeared in +.Ox 7.1 . Index: sys/arch/riscv64/conf/GENERIC =================================================================== RCS file: src/sys/arch/riscv64/conf/GENERIC,v retrieving revision 1.33 diff -u -p -r1.33 GENERIC --- sys/arch/riscv64/conf/GENERIC 18 Jan 2022 07:53:39 -0000 1.33 +++ sys/arch/riscv64/conf/GENERIC 14 Feb 2022 17:40:53 -0000 @@ -48,6 +48,8 @@ com* at fdt? cdsdhc* at fdt? sdmmc* at cdsdhc? mpfclock* at fdt? early 1 +mpfiic* at fdt? +iic* at mpfiic? # SiFive SoCs sfclock* at fdt? early 1 # PRCI Index: sys/arch/riscv64/conf/RAMDISK =================================================================== RCS file: src/sys/arch/riscv64/conf/RAMDISK,v retrieving revision 1.29 diff -u -p -r1.29 RAMDISK --- sys/arch/riscv64/conf/RAMDISK 18 Jan 2022 07:53:39 -0000 1.29 +++ sys/arch/riscv64/conf/RAMDISK 14 Feb 2022 17:40:53 -0000 @@ -39,6 +39,8 @@ com* at fdt? cdsdhc* at fdt? sdmmc* at cdsdhc? mpfclock* at fdt? early 1 +mpfiic* at fdt? +iic* at mpfiic? # SiFive SoCs sfclock* at fdt? early 1 # PRCI Index: sys/arch/riscv64/conf/files.riscv64 =================================================================== RCS file: src/sys/arch/riscv64/conf/files.riscv64,v retrieving revision 1.18 diff -u -p -r1.18 files.riscv64 --- sys/arch/riscv64/conf/files.riscv64 5 Jan 2022 03:32:44 -0000 1.18 +++ sys/arch/riscv64/conf/files.riscv64 14 Feb 2022 17:40:53 -0000 @@ -89,6 +89,11 @@ device mpfclock attach mpfclock at fdt file arch/riscv64/dev/mpfclock.c mpfclock +# PolarFire SoC MSS I2C controller +device mpfiic: i2cbus +attach mpfiic at fdt +file arch/riscv64/dev/mpfiic.c mpfiic + # L2 cache controller device sfcc attach sfcc at fdt Index: sys/arch/riscv64/dev/mpfiic.c =================================================================== RCS file: sys/arch/riscv64/dev/mpfiic.c diff -N sys/arch/riscv64/dev/mpfiic.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ sys/arch/riscv64/dev/mpfiic.c 14 Feb 2022 17:40:53 -0000 @@ -0,0 +1,401 @@ +/* $OpenBSD$ */ + +/* + * Copyright (c) 2022 Visa Hankala + * + * 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. + */ + +/* + * Driver for PolarFire SoC MSS I2C controller. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> + +#include <machine/bus.h> +#include <machine/fdt.h> + +#define _I2C_PRIVATE +#include <dev/i2c/i2cvar.h> + +#include <dev/ofw/fdt.h> +#include <dev/ofw/openfirm.h> +#include <dev/ofw/ofw_clock.h> +#include <dev/ofw/ofw_misc.h> + +#define I2C_CTRL 0x0000 +#define I2C_CTRL_CR2 (1 << 7) +#define I2C_CTRL_ENS1 (1 << 6) +#define I2C_CTRL_STA (1 << 5) +#define I2C_CTRL_STO (1 << 4) +#define I2C_CTRL_SI (1 << 3) +#define I2C_CTRL_AA (1 << 2) +#define I2C_CTRL_CR1 (1 << 1) +#define I2C_CTRL_CR0 (1 << 0) +#define I2C_STATUS 0x0004 +#define I2C_DATA 0x0008 +#define I2C_SLAVE0ADR 0x000c +#define I2C_SMBUS 0x0010 +#define I2C_FREQ 0x0014 +#define I2C_GLITCHREG 0x0018 +#define I2C_SLAVE1ADR 0x001c + +#define I2C_STATUS_START 0x08 +#define I2C_STATUS_RESTART 0x10 +#define I2C_STATUS_SLAW_ACK 0x18 +#define I2C_STATUS_DATAW_ACK 0x28 +#define I2C_STATUS_LOSTARB 0x38 +#define I2C_STATUS_SLAR_ACK 0x40 +#define I2C_STATUS_DATAR_ACK 0x50 +#define I2C_STATUS_DATAR_NACK 0x58 +#define I2C_STATUS_IDLE 0xf8 + +struct mpfiic_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + int sc_node; + + struct i2c_bus sc_i2c_bus; + struct i2c_controller sc_i2c_tag; + struct rwlock sc_i2c_lock; + + uint32_t sc_bus_freq; /* in Hz */ + uint8_t sc_ctrl; + uint8_t sc_start_sent; +}; + +#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)) + +int mpfiic_match(struct device *, void *, void*); +void mpfiic_attach(struct device *, struct device *, void *); + +int mpfiic_i2c_acquire_bus(void *, int); +void mpfiic_i2c_release_bus(void *, int); +int mpfiic_i2c_send_start(void *, int); +int mpfiic_i2c_send_stop(void *, int); +int mpfiic_i2c_initiate_xfer(void *, i2c_addr_t, int); +int mpfiic_i2c_read_byte(void *, uint8_t *, int); +int mpfiic_i2c_write_byte(void *, uint8_t, int); +void mpfiic_i2c_scan(struct device *, struct i2cbus_attach_args *, void *); + +int mpfiic_wait(struct mpfiic_softc *, uint8_t); + +const struct cfattach mpfiic_ca = { + sizeof(struct mpfiic_softc), mpfiic_match, mpfiic_attach +}; + +struct cfdriver mpfiic_cd = { + NULL, "mpfiic", DV_DULL +}; + +static struct { + uint32_t div; + uint32_t cr; +} mpfiic_clk_divs[] = { +#ifdef notused + /* BCLK */ + { 8, I2C_CTRL_CR2 | I2C_CTRL_CR1 | I2C_CTRL_CR0 }, +#endif + /* PCLK */ + { 60, I2C_CTRL_CR2 | I2C_CTRL_CR1 }, + { 120, I2C_CTRL_CR2 | I2C_CTRL_CR0 }, + { 160, I2C_CTRL_CR1 | I2C_CTRL_CR0 }, + { 192, I2C_CTRL_CR1 }, + { 224, I2C_CTRL_CR0 }, + { 256, 0 }, + { 960, I2C_CTRL_CR2 }, +}; + +int +mpfiic_match(struct device *parent, void *match, void *aux) +{ + struct fdt_attach_args *faa = aux; + + if (faa->fa_nreg < 1) + return 0; + return OF_is_compatible(faa->fa_node, "microchip,mpfs-i2c"); +} + +void +mpfiic_attach(struct device *parent, struct device *self, void *aux) +{ + struct i2cbus_attach_args iba; + struct fdt_attach_args *faa = aux; + struct mpfiic_softc *sc = (struct mpfiic_softc *)self; + uint32_t i, bus_freq, clock_freq; + + sc->sc_node = faa->fa_node; + sc->sc_iot = faa->fa_iot; + + clock_freq = clock_get_frequency(sc->sc_node, NULL); + bus_freq = OF_getpropint(sc->sc_node, "clock-frequency", 100000); + + /* Determine clock divider, assumes PCLK. */ + for (i = 0; i < nitems(mpfiic_clk_divs) - 1; i++) { + if (clock_freq / mpfiic_clk_divs[i].div <= bus_freq) + break; + } + sc->sc_bus_freq = clock_freq / mpfiic_clk_divs[i].div; + sc->sc_ctrl = mpfiic_clk_divs[i].cr | I2C_CTRL_ENS1; + + if (sc->sc_bus_freq == 0) { + printf(": invalid bus frequency\n"); + return; + } + + if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, + 0, &sc->sc_ioh) != 0) { + printf(": can't map registers\n"); + return; + } + + clock_enable_all(sc->sc_node); + + /* Initialize the device. */ + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl); + HWRITE4(sc, I2C_CTRL, 0); + + /* Disable slave address comparison. */ + HWRITE4(sc, I2C_SLAVE0ADR, 0); + HWRITE4(sc, I2C_SLAVE1ADR, 0); + + /* Disable SMBus logic, operate in standard I2C mode. */ + HWRITE4(sc, I2C_SMBUS, 0); + + printf("\n"); + + rw_init(&sc->sc_i2c_lock, "iiclk"); + sc->sc_i2c_tag.ic_cookie = sc; + sc->sc_i2c_tag.ic_acquire_bus = mpfiic_i2c_acquire_bus; + sc->sc_i2c_tag.ic_release_bus = mpfiic_i2c_release_bus; + sc->sc_i2c_tag.ic_send_start = mpfiic_i2c_send_start; + sc->sc_i2c_tag.ic_send_stop = mpfiic_i2c_send_stop; + sc->sc_i2c_tag.ic_initiate_xfer = mpfiic_i2c_initiate_xfer; + sc->sc_i2c_tag.ic_read_byte = mpfiic_i2c_read_byte; + sc->sc_i2c_tag.ic_write_byte = mpfiic_i2c_write_byte; + + memset(&iba, 0, sizeof(iba)); + iba.iba_name = "iic"; + iba.iba_tag = &sc->sc_i2c_tag; + iba.iba_bus_scan = mpfiic_i2c_scan; + iba.iba_bus_scan_arg = &sc->sc_node; + config_found(self, &iba, iicbus_print); + + sc->sc_i2c_bus.ib_node = faa->fa_node; + sc->sc_i2c_bus.ib_ic = &sc->sc_i2c_tag; + i2c_register(&sc->sc_i2c_bus); +} + +int +mpfiic_i2c_acquire_bus(void *arg, int flags) +{ + struct mpfiic_softc *sc = arg; + + if (cold || (flags & I2C_F_POLL)) + return 0; + + return rw_enter(&sc->sc_i2c_lock, RW_WRITE | RW_INTR); +} + +void +mpfiic_i2c_release_bus(void *arg, int flags) +{ + struct mpfiic_softc *sc = arg; + + if (cold || (flags & I2C_F_POLL)) + return; + + rw_exit(&sc->sc_i2c_lock); +} + +int +mpfiic_i2c_send_start(void *cookie, int flags) +{ + struct mpfiic_softc *sc = cookie; + int error; + uint8_t nstatus; + + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl | I2C_CTRL_STA); + + if (sc->sc_start_sent) + nstatus = I2C_STATUS_RESTART; + else + nstatus = I2C_STATUS_START; + error = mpfiic_wait(sc, nstatus); + if (error != 0) + return error; + + sc->sc_start_sent = 1; + + return 0; +} + +int +mpfiic_i2c_send_stop(void *cookie, int flags) +{ + struct mpfiic_softc *sc = cookie; + + sc->sc_start_sent = 0; + + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl | I2C_CTRL_STO); + + /* Let a few bus clock cycles to pass. */ + delay(4 * 1000000 / sc->sc_bus_freq); + + /* Disable the device. This resets the state machine. */ + HWRITE4(sc, I2C_CTRL, 0); + + return 0; +} + +int +mpfiic_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags) +{ + struct mpfiic_softc *sc = cookie; + int error; + uint8_t mode, nstatus; + + if (addr >= 0x80) + return EINVAL; + + error = mpfiic_i2c_send_start(sc, flags); + if (error != 0) + return error; + + if (flags & I2C_F_READ) { + mode = 0x01; + nstatus = I2C_STATUS_SLAR_ACK; + } else { + mode = 0x00; + nstatus = I2C_STATUS_SLAW_ACK; + } + + HWRITE4(sc, I2C_DATA, (addr << 1) | mode); + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl); + + return mpfiic_wait(sc, nstatus); +} + +int +mpfiic_i2c_read_byte(void *cookie, uint8_t *datap, int flags) +{ + struct mpfiic_softc *sc = cookie; + int error; + uint8_t ack = 0, nstatus; + + if ((flags & I2C_F_LAST) == 0) + ack = I2C_CTRL_AA; + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl | ack); + + if (flags & I2C_F_LAST) + nstatus = I2C_STATUS_DATAR_NACK; + else + nstatus = I2C_STATUS_DATAR_ACK; + error = mpfiic_wait(sc, nstatus); + if (error != 0) + return error; + + *datap = HREAD4(sc, I2C_DATA); + + if (flags & I2C_F_STOP) + error = mpfiic_i2c_send_stop(sc, flags); + + return error; +} + +int +mpfiic_i2c_write_byte(void *cookie, uint8_t data, int flags) +{ + struct mpfiic_softc *sc = cookie; + int error; + + HWRITE4(sc, I2C_DATA, data); + HWRITE4(sc, I2C_CTRL, sc->sc_ctrl); + + error = mpfiic_wait(sc, I2C_STATUS_DATAW_ACK); + if (error != 0) + return error; + + if (flags & I2C_F_STOP) + error = mpfiic_i2c_send_stop(sc, flags); + + return error; +} + +void +mpfiic_i2c_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) +{ + struct i2c_attach_args ia; + char status[32]; + char *compat; + uint32_t reg[1]; + int iba_node = *(int *)arg; + int len, node; + + for (node = OF_child(iba_node); node != 0; node = OF_peer(node)) { + memset(status, 0, sizeof(status)); + if (OF_getprop(node, "status", status, sizeof(status)) > 0 && + strcmp(status, "disabled") == 0) + continue; + + memset(reg, 0, sizeof(reg)); + if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) + continue; + + len = OF_getproplen(node, "compatible"); + if (len <= 0) + continue; + + compat = malloc(len, M_TEMP, M_WAITOK); + OF_getprop(node, "compatible", compat, len); + + memset(&ia, 0, sizeof(ia)); + ia.ia_tag = iba->iba_tag; + ia.ia_addr = bemtoh32(®[0]); + ia.ia_name = compat; + ia.ia_namelen = len; + ia.ia_cookie = &node; + config_found(self, &ia, iic_print); + + free(compat, M_TEMP, len); + } +} + +int +mpfiic_wait(struct mpfiic_softc *sc, uint8_t nstatus) +{ + int timeout; + uint8_t ctrl, status; + + for (timeout = 100000; timeout > 0; timeout--) { + ctrl = HREAD4(sc, I2C_CTRL); + if (ctrl & I2C_CTRL_SI) + break; + delay(1); + } + if (timeout == 0) + return ETIMEDOUT; + + status = HREAD4(sc, I2C_STATUS); + if (status != nstatus) + return EIO; + + return 0; +}