Index: sys/arm64/rockchip/rk3399_efuse.c =================================================================== --- sys/arm64/rockchip/rk3399_efuse.c +++ sys/arm64/rockchip/rk3399_efuse.c @@ -0,0 +1,259 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause-FreeBSD + * + * Copyright (c) 2020 BusyTech + * + * 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 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 AUTHOR 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. + */ + +/* + * RK3399 non-secure efuse driver (read + write mode). + * Based on RK3399 TRM part 1, please read it for further details. + * + * Write mode has very specific hardware requirements: VQPS must be between 1.8 + * and 1.98V to allow programming bits in efuse. RockPro64 has this line powered + * correctly, not sure about other boards. + * + * Writing bits to efuse array is irreversible! You have been warned. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "nvmem_if.h" + +/* Registers & bits in registers. */ +#define EFUSE_CTRL 0x0000 +#define EFUSE_CTRL_CSB (1 << 0) +#define EFUSE_CTRL_STROBE (1 << 1) +#define EFUSE_CTRL_LOAD (1 << 2) +#define EFUSE_CTRL_PGENB (1 << 3) +#define EFUSE_CTRL_PS (1 << 4) +#define EFUSE_CTRL_PD (1 << 5) +#define EFUSE_CTRL_MR (1 << 6) +#define EFUSE_CTRL_RSB (1 << 7) +#define EFUSE_CTRL_RWL (1 << 8) +#define EFUSE_CTRL_STROBE_SFT_SEL (1 << 9) +#define EFUSE_CTRL_ADDR_SHIFT 16 +#define EFUSE_CTRL_ADDR_MASK (0x3ff << EFUSE_CTRL_ADDR_SHIFT) +#define EFUSE_DOUT 0x0004 +#define EFUSE_RF 0x0008 +#define EFUSE_JTAG_PASS 0x0010 +#define EFUSE_STROBE_FINISH_CTRL 0x0014 + +#define EFUSE_NUM 32 +#define EFUSE_SIZE 4 /* EFUSE DOUT size in bytes. */ +#define EFUSE_TOTAL_SIZE EFUSE_NUM * EFUSE_SIZE +#define EFUSE_WRITE_DELAY 13 + +#define READ4(sc, reg) bus_read_4((sc)->sc_mem_res, (reg)) +#define WRITE4(sc, reg, val) bus_write_4((sc)->sc_mem_res, (reg), (val)) + +struct rk3399_efuse_softc { + device_t sc_dev; + struct resource *sc_mem_res; + struct mtx sc_lock; + clk_t sc_clk; + int sc_mem_rid; +}; + +static devclass_t rk3399_efuse_devclass; + +static int rk3399_efuse_probe(device_t); +static int rk3399_efuse_attach(device_t); + +static int rk3399_efuse_read(device_t, uint32_t, uint32_t, uint8_t *); +static int rk3399_efuse_write(device_t, uint32_t, uint32_t, uint8_t *); + +static int +rk3399_efuse_read(device_t dev, uint32_t offset, uint32_t size, uint8_t *buffer) +{ + struct rk3399_efuse_softc *sc = device_get_softc(dev); + uint32_t buf[EFUSE_NUM]; + uint32_t val; + uint32_t ii; + + if (sc == NULL) + return (ENXIO); + + if (offset + size > EFUSE_TOTAL_SIZE || size == 0) + return (ENOMEM); + + mtx_lock(&sc->sc_lock); + WRITE4(sc, EFUSE_CTRL, EFUSE_CTRL_LOAD | EFUSE_CTRL_PGENB | + EFUSE_CTRL_STROBE_SFT_SEL | EFUSE_CTRL_RSB); + DELAY(1); + for (ii = 0; ii < EFUSE_NUM; ii++) { + val = READ4(sc, EFUSE_CTRL) | EFUSE_CTRL_STROBE | + (ii << EFUSE_CTRL_ADDR_SHIFT); + WRITE4(sc, EFUSE_CTRL, val); + DELAY(1); + + buf[ii] = READ4(sc, EFUSE_DOUT); + } + WRITE4(sc, EFUSE_CTRL, EFUSE_CTRL_CSB | EFUSE_CTRL_PD); + mtx_unlock(&sc->sc_lock); + + memcpy(buffer, (const uint8_t *)buf + offset, size); + return (0); +} + +static int +rk3399_efuse_write(device_t dev, uint32_t offset, uint32_t size, uint8_t * + buffer) +{ + /* + * Algorithm: + * 1) Enable program mode and wait 1 µs. + * 2) Locate 1-bits (and their addresses for A9~A0). + * 3) For any 1-bit, do following: + * set address & STROBE for 13 µs (12 is minimum, add 1) + * 4) Disable program mode. + */ + struct rk3399_efuse_softc *sc = device_get_softc(dev); + uint32_t buf[EFUSE_NUM]; + uint32_t val; + int ii, jj; + + if (sc == NULL) + return (ENXIO); + + if (offset + size > EFUSE_TOTAL_SIZE) + return (ENOMEM); + + memset(buf, '\0', sizeof(buf)); + memcpy((uint8_t *)buf + offset, buffer, size); + + mtx_lock(&sc->sc_lock); + WRITE4(sc, EFUSE_CTRL, EFUSE_CTRL_PS | EFUSE_CTRL_RSB | + EFUSE_CTRL_STROBE_SFT_SEL); + DELAY(1); + for (ii = 0; ii < EFUSE_NUM; ii++) { + for (jj = 0; jj < EFUSE_SIZE * 8; jj++) { + if ((buf[ii] & (1U << jj)) != 0) { + /* Compute bit number (0 ... 1023). */ + const uint32_t addr = ii + jj * EFUSE_SIZE * 8; + KASSERT(addr < 1024, ("Invalid bit number")); + + val = addr << EFUSE_CTRL_ADDR_SHIFT; + + WRITE4(sc, EFUSE_CTRL, EFUSE_CTRL_STROBE | val); + DELAY(EFUSE_WRITE_DELAY); + } + } + } + WRITE4(sc, EFUSE_CTRL, EFUSE_CTRL_CSB | EFUSE_CTRL_PD); + mtx_unlock(&sc->sc_lock); + + return (0); +} + +static int +rk3399_efuse_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "rockchip,rk3399-efuse")) + return (ENXIO); + + device_set_desc(dev, "Rockchip RK3399 fuse (non-secure)"); + return (BUS_PROBE_DEFAULT); +} + +static int +rk3399_efuse_attach(device_t dev) +{ + struct rk3399_efuse_softc *sc; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + sc->sc_mem_rid = 0; + sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem_res == NULL) { + device_printf(dev, "cannot allocate memory resource\n"); + goto err_alloc_mem; + } + + if (clk_get_by_ofw_index(dev, 0, 0, &sc->sc_clk) != 0) { + device_printf(dev, "cannot find clock\n"); + goto err_no_clock; + } + + if (clk_enable(sc->sc_clk) != 0) { + device_printf(dev, "cannot enable clock\n"); + goto err_clock_disabled; + } + + mtx_init(&sc->sc_lock, device_get_nameunit(dev), "rk3399_efuse", + MTX_DEF); + return (bus_generic_attach(dev)); + +err_clock_disabled: + clk_release(sc->sc_clk); +err_no_clock: + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem_res); +err_alloc_mem: + return (ENXIO); +} + +static device_method_t rk3399_efuse_methods[] = { + DEVMETHOD(device_probe, rk3399_efuse_probe), + DEVMETHOD(device_attach, rk3399_efuse_attach), + + DEVMETHOD(nvmem_read, rk3399_efuse_read), + DEVMETHOD(nvmem_write, rk3399_efuse_write), + + DEVMETHOD_END +}; + +static driver_t rk3399_efuse_driver = { + "rk3399_efuse", + rk3399_efuse_methods, + sizeof(struct rk3399_efuse_softc), +}; + +DRIVER_MODULE(rk3399_efuse, simplebus, rk3399_efuse_driver, + rk3399_efuse_devclass, NULL, NULL); +MODULE_VERSION(rk3399_efuse, 1);