Module Name: src Committed By: jmcneill Date: Sat Nov 5 17:31:38 UTC 2022
Modified Files: src/sys/conf: files src/sys/dev/fdt: files.fdt Added Files: src/sys/dev/fdt: cdnsiic_fdt.c src/sys/dev/ic: cdnsiic.c cdnsiicvar.h Log Message: Add driver for Cadence I2C controller. To generate a diff of this commit: cvs rdiff -u -r1.1302 -r1.1303 src/sys/conf/files cvs rdiff -u -r0 -r1.1 src/sys/dev/fdt/cdnsiic_fdt.c cvs rdiff -u -r1.65 -r1.66 src/sys/dev/fdt/files.fdt cvs rdiff -u -r0 -r1.1 src/sys/dev/ic/cdnsiic.c src/sys/dev/ic/cdnsiicvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/conf/files diff -u src/sys/conf/files:1.1302 src/sys/conf/files:1.1303 --- src/sys/conf/files:1.1302 Fri Oct 28 07:16:34 2022 +++ src/sys/conf/files Sat Nov 5 17:31:38 2022 @@ -1,4 +1,4 @@ -# $NetBSD: files,v 1.1302 2022/10/28 07:16:34 skrll Exp $ +# $NetBSD: files,v 1.1303 2022/11/05 17:31:38 jmcneill Exp $ # @(#)files.newconf 7.5 (Berkeley) 5/10/93 version 20171118 @@ -1159,6 +1159,11 @@ define dwiic device dwiic: dwiic, i2cbus file dev/ic/dwiic.c dwiic +# Cadence I2C controller +define cdnsiic +device cdnsiic: i2cbus +file dev/ic/cdnsiic.c cdnsiic + # ACPI power management timer (hardware access, independent of ACPI) # define acpipmtimer Index: src/sys/dev/fdt/files.fdt diff -u src/sys/dev/fdt/files.fdt:1.65 src/sys/dev/fdt/files.fdt:1.66 --- src/sys/dev/fdt/files.fdt:1.65 Wed Jul 20 10:01:11 2022 +++ src/sys/dev/fdt/files.fdt Sat Nov 5 17:31:37 2022 @@ -1,4 +1,4 @@ -# $NetBSD: files.fdt,v 1.65 2022/07/20 10:01:11 riastradh Exp $ +# $NetBSD: files.fdt,v 1.66 2022/11/05 17:31:37 jmcneill Exp $ include "external/bsd/libfdt/conf/files.libfdt" @@ -185,6 +185,10 @@ file dev/fdt/ahcisata_fdt.c ahcisata_f attach dwiic at fdt with dwiic_fdt file dev/fdt/dwiic_fdt.c dwiic_fdt +# Cadence I2C +attach cdnsiic at fdt with cdnsiic_fdt +file dev/fdt/cdnsiic_fdt.c cdnsiic_fdt + # AMD Cryptographic Coprocessor attach amdccp at fdt with amdccp_fdt file dev/fdt/amdccp_fdt.c amdccp_fdt Added files: Index: src/sys/dev/fdt/cdnsiic_fdt.c diff -u /dev/null src/sys/dev/fdt/cdnsiic_fdt.c:1.1 --- /dev/null Sat Nov 5 17:31:38 2022 +++ src/sys/dev/fdt/cdnsiic_fdt.c Sat Nov 5 17:31:37 2022 @@ -0,0 +1,103 @@ +/* $NetBSD: cdnsiic_fdt.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $ */ + +/*- + * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(0, "$NetBSD: cdnsiic_fdt.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/kmem.h> + +#include <dev/i2c/i2cvar.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/ic/cdnsiicvar.h> + +static const struct device_compatible_entry compat_data[] = { + { .compat = "cdns,i2c-r1p10" }, + { .compat = "cdns,i2c-r1p14" }, + DEVICE_COMPAT_EOL +}; + +static int +cdnsiic_fdt_match(device_t parent, cfdata_t cf, void *aux) +{ + struct fdt_attach_args * const faa = aux; + + return of_compatible_match(faa->faa_phandle, compat_data); +} + +static void +cdnsiic_fdt_attach(device_t parent, device_t self, void *aux) +{ + struct cdnsiic_softc * const sc = device_private(self); + struct fdt_attach_args * const faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr; + bus_size_t size; + int error; + + if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + sc->sc_pclk = fdtbus_clock_get_index(phandle, 0); + if (sc->sc_pclk == NULL || clk_enable(sc->sc_pclk) != 0) { + aprint_error(": couldn't enable pclk\n"); + return; + } + + if (of_getprop_uint32(phandle, "clock-frequency", &sc->sc_bus_freq)) + sc->sc_bus_freq = 100000; + + sc->sc_dev = self; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + error = cdnsiic_attach(sc); + if (error != 0) { + aprint_error_dev(self, "init failed, error = %d\n", error); + return; + } + + fdtbus_register_i2c_controller(&sc->sc_ic, phandle); + + fdtbus_attach_i2cbus(self, phandle, &sc->sc_ic, iicbus_print); +} + +CFATTACH_DECL_NEW(cdnsiic_fdt, sizeof(struct cdnsiic_softc), + cdnsiic_fdt_match, cdnsiic_fdt_attach, NULL, NULL); Index: src/sys/dev/ic/cdnsiic.c diff -u /dev/null src/sys/dev/ic/cdnsiic.c:1.1 --- /dev/null Sat Nov 5 17:31:38 2022 +++ src/sys/dev/ic/cdnsiic.c Sat Nov 5 17:31:37 2022 @@ -0,0 +1,297 @@ +/* $NetBSD: cdnsiic.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $ */ + +/*- + * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * Cadence I2C controller + */ + +#include <sys/cdefs.h> + +__KERNEL_RCSID(0, "$NetBSD: cdnsiic.c,v 1.1 2022/11/05 17:31:37 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/device.h> +#include <sys/intr.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/kmem.h> + +#include <dev/clk/clk.h> +#include <dev/i2c/i2cvar.h> + +#include <dev/ic/cdnsiicvar.h> + +/* From Zynq-7000 SoC Technical Reference Manual, "Supports 16-byte FIFO" */ +#define FIFO_DEPTH 16 + +/* Poll timeout, in microseconds. */ +#define POLL_TIMEOUT 10000 + +#define CR_REG 0x00 +#define CR_DIV_A __BITS(15,14) +#define CR_DIV_B __BITS(13,8) +#define CR_CLR_FIFO __BIT(6) +#define CR_HOLD __BIT(4) +#define CR_ACKEN __BIT(3) +#define CR_NEA __BIT(2) +#define CR_MS __BIT(1) +#define CR_RD_WR __BIT(0) +#define SR_REG 0x04 +#define SR_TXDV __BIT(6) +#define SR_RXDV __BIT(5) +#define ADDR_REG 0x08 +#define DATA_REG 0x0c +#define ISR_REG 0x10 +#define ISR_ARB_LOST __BIT(9) +#define ISR_RX_UNF __BIT(7) +#define ISR_TX_OVR __BIT(6) +#define ISR_RX_OVR __BIT(5) +#define ISR_SLV_RDY __BIT(4) +#define ISR_TO __BIT(3) +#define ISR_NACK __BIT(2) +#define ISR_DATA __BIT(1) +#define ISR_COMP __BIT(0) +#define ISR_ERROR_MASK (ISR_ARB_LOST | ISR_TX_OVR | ISR_RX_OVR | ISR_NACK) +#define TRANS_SIZE_REG 0x14 +#define SLV_PAUSE_REG 0x18 +#define TIME_OUT_REG 0x1c +#define IMR_REG 0x20 +#define IER_REG 0x24 +#define IDR_REG 0x28 + +#define RD4(sc, reg) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg)) +#define WR4(sc, reg, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) + +static int +cdnsiic_init(struct cdnsiic_softc *sc) +{ + int diva, divb; + int diff, calc_bus_freq; + int best_diva, best_divb, best_diff; + u_int pclk; + + /* + * SCL frequency is calculated by the following formula: + * + * SCL Divisor = 22 * (divisor_a + 1) * (divisor_b + 1) + * SCL = PCLK / SCLK Divisor + */ + + pclk = clk_get_rate(sc->sc_pclk); + best_diff = sc->sc_bus_freq; + best_diva = best_divb = 0; + + for (diva = 0; diva <= 0x3; diva++) { + divb = howmany(pclk, 22 * sc->sc_bus_freq * (diva + 1)) - 1; + if (divb < 0 || divb > 0x3f) { + continue; + } + calc_bus_freq = pclk / (22 * (diva + 1) * (divb + 1)); + diff = sc->sc_bus_freq - calc_bus_freq; + if (diff < best_diff) { + best_diff = diff; + best_diva = diva; + best_divb = divb; + } + } + if (best_diff == sc->sc_bus_freq) { + return ENXIO; + } + + WR4(sc, CR_REG, + __SHIFTIN(best_diva, CR_DIV_A) | + __SHIFTIN(best_divb, CR_DIV_B) | + CR_CLR_FIFO | + CR_ACKEN | + CR_NEA | + CR_MS); + WR4(sc, TIME_OUT_REG, 0xff); + + return 0; +} + +static int +cdnsiic_poll_fifo(struct cdnsiic_softc *sc, uint32_t sr_mask, uint32_t sr_maskval) +{ + uint32_t sr_val, isr_val; + int retry = POLL_TIMEOUT; + + while (--retry > 0) { + sr_val = RD4(sc, SR_REG); + isr_val = RD4(sc, ISR_REG); + if ((isr_val & ISR_ERROR_MASK) != 0) { + return EIO; + } + if ((sr_val & sr_mask) == sr_maskval) { + return 0; + } + delay(1); + } + + return ETIMEDOUT; +} + +static int +cdnsiic_poll_transfer_complete(struct cdnsiic_softc *sc) +{ + uint32_t val; + int retry = POLL_TIMEOUT; + + while (--retry > 0) { + val = RD4(sc, ISR_REG); + if ((val & ISR_COMP) != 0) { + return 0; + } + delay(1); + } + + return ETIMEDOUT; +} + +static int +cdnsiic_write(struct cdnsiic_softc *sc, i2c_addr_t addr, + const uint8_t *data, size_t datalen, bool send_stop) +{ + uint32_t val; + u_int xferlen, fifo_space, n; + bool write_addr = true; + int error; + + if (datalen == 0 || datalen > 256) { + return EINVAL; + } + + val = RD4(sc, CR_REG); + val |= CR_CLR_FIFO; + val &= ~CR_RD_WR; + WR4(sc, CR_REG, val); + WR4(sc, ISR_REG, RD4(sc, ISR_REG)); + + while (datalen > 0) { + fifo_space = FIFO_DEPTH - RD4(sc, TRANS_SIZE_REG); + xferlen = uimin(datalen, fifo_space); + for (n = 0; n < xferlen; n++, data++) { + WR4(sc, DATA_REG, *data); + } + if (write_addr) { + WR4(sc, ADDR_REG, addr); + write_addr = false; + } + datalen -= xferlen; + error = cdnsiic_poll_fifo(sc, SR_TXDV, 0); + if (error != 0) { + return error; + } + } + + return cdnsiic_poll_transfer_complete(sc); +} + +static int +cdnsiic_read(struct cdnsiic_softc *sc, i2c_addr_t addr, + uint8_t *data, size_t datalen) +{ + uint32_t val; + int error; + + if (datalen == 0 || datalen > 255) { + return EINVAL; + } + + val = RD4(sc, CR_REG); + val |= CR_CLR_FIFO | CR_RD_WR; + WR4(sc, CR_REG, val); + WR4(sc, ISR_REG, RD4(sc, ISR_REG)); + WR4(sc, TRANS_SIZE_REG, datalen); + WR4(sc, ADDR_REG, addr); + + while (datalen > 0) { + error = cdnsiic_poll_fifo(sc, SR_RXDV, SR_RXDV); + if (error != 0) { + return error; + } + *data = RD4(sc, DATA_REG) & 0xff; + data++; + datalen--; + } + + return cdnsiic_poll_transfer_complete(sc); +} + +static int +cdnsiic_exec(void *priv, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, void *buf, size_t buflen, int flags) +{ + struct cdnsiic_softc * const sc = priv; + uint32_t val; + int error; + + val = RD4(sc, CR_REG); + WR4(sc, CR_REG, val | CR_HOLD); + + if (cmdlen > 0) { + error = cdnsiic_write(sc, addr, cmdbuf, cmdlen, false); + if (error != 0) { + goto done; + } + } + if (I2C_OP_READ_P(op)) { + error = cdnsiic_read(sc, addr, buf, buflen); + } else { + error = cdnsiic_write(sc, addr, buf, buflen, true); + } + +done: + val = RD4(sc, CR_REG); + WR4(sc, CR_REG, val & ~CR_HOLD); + + return error; +} + +int +cdnsiic_attach(struct cdnsiic_softc *sc) +{ + int error; + + aprint_naive("\n"); + aprint_normal(": Cadence I2C (%u Hz)\n", sc->sc_bus_freq); + + error = cdnsiic_init(sc); + if (error != 0) { + return error; + } + + iic_tag_init(&sc->sc_ic); + sc->sc_ic.ic_cookie = sc; + sc->sc_ic.ic_exec = cdnsiic_exec; + + return 0; +} Index: src/sys/dev/ic/cdnsiicvar.h diff -u /dev/null src/sys/dev/ic/cdnsiicvar.h:1.1 --- /dev/null Sat Nov 5 17:31:38 2022 +++ src/sys/dev/ic/cdnsiicvar.h Sat Nov 5 17:31:37 2022 @@ -0,0 +1,50 @@ +/* $NetBSD: cdnsiicvar.h,v 1.1 2022/11/05 17:31:37 jmcneill Exp $ */ + +/*- + * Copyright (c) 2022 Jared McNeill <jmcne...@invisible.ca> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Cadence I2C + */ + +#ifndef _CDNSIICVAR_H +#define _CDNSIICVAR_H + +#include <dev/i2c/i2cvar.h> + +struct cdnsiic_softc { + device_t sc_dev; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + struct clk *sc_pclk; + u_int sc_bus_freq; + + struct i2c_controller sc_ic; +}; + +int cdnsiic_attach(struct cdnsiic_softc *); + +#endif /* !_CDNSIICVAR_H */