> Date: Mon, 14 Feb 2022 17:50:55 +0000 > From: Visa Hankala <v...@hankala.org> > > This patch adds a basic driver for the PolarFire SoC MSS I2C controller. > > OK?
Small not below, Otherwise ok kettenis@ > 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. */ /* Let a few bus clock cycles 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; > +} > >