diff --git a/sys/arm64/rockchip/clk/rk_clk_composite.c b/sys/arm64/rockchip/clk/rk_clk_composite.c index 17d258e02985..b6d1621a0eac 100644 --- a/sys/arm64/rockchip/clk/rk_clk_composite.c +++ b/sys/arm64/rockchip/clk/rk_clk_composite.c @@ -1,370 +1,370 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include "clkdev_if.h" #include "syscon_if.h" struct rk_clk_composite_sc { uint32_t muxdiv_offset; uint32_t mux_shift; uint32_t mux_width; uint32_t mux_mask; uint32_t div_shift; uint32_t div_width; uint32_t div_mask; uint32_t gate_offset; uint32_t gate_shift; uint32_t flags; struct syscon *grf; }; #define WRITE4(_clk, off, val) \ rk_clk_composite_write_4(_clk, off, val) #define READ4(_clk, off, val) \ rk_clk_composite_read_4(_clk, off, val) #define DEVICE_LOCK(_clk) \ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) #define RK_CLK_COMPOSITE_MASK_SHIFT 16 #if 0 #define dprintf(format, arg...) \ printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) #else #define dprintf(format, arg...) #endif static void rk_clk_composite_read_4(struct clknode *clk, bus_addr_t addr, uint32_t *val) { struct rk_clk_composite_sc *sc; sc = clknode_get_softc(clk); if (sc->grf) *val = SYSCON_READ_4(sc->grf, addr); else CLKDEV_READ_4(clknode_get_device(clk), addr, val); } static void rk_clk_composite_write_4(struct clknode *clk, bus_addr_t addr, uint32_t val) { struct rk_clk_composite_sc *sc; sc = clknode_get_softc(clk); if (sc->grf) SYSCON_WRITE_4(sc->grf, addr, val | (0xffff << 16)); else CLKDEV_WRITE_4(clknode_get_device(clk), addr, val); } static struct syscon * rk_clk_composite_get_grf(struct clknode *clk) { device_t dev; phandle_t node; struct syscon *grf; grf = NULL; dev = clknode_get_device(clk); node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &grf) != 0) { return (NULL); } return (grf); } static int rk_clk_composite_init(struct clknode *clk, device_t dev) { struct rk_clk_composite_sc *sc; uint32_t val, idx; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_COMPOSITE_GRF) != 0) { sc->grf = rk_clk_composite_get_grf(clk); if (sc->grf == NULL) panic("clock %s has GRF flag set but no syscon is available", clknode_get_name(clk)); } idx = 0; if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) != 0) { DEVICE_LOCK(clk); READ4(clk, sc->muxdiv_offset, &val); DEVICE_UNLOCK(clk); idx = (val & sc->mux_mask) >> sc->mux_shift; } clknode_init_parent_idx(clk, idx); return (0); } static int rk_clk_composite_set_gate(struct clknode *clk, bool enable) { struct rk_clk_composite_sc *sc; uint32_t val = 0; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_COMPOSITE_HAVE_GATE) == 0) return (0); dprintf("%sabling gate\n", enable ? "En" : "Dis"); if (!enable) val |= 1 << sc->gate_shift; dprintf("sc->gate_shift: %x\n", sc->gate_shift); val |= (1 << sc->gate_shift) << RK_CLK_COMPOSITE_MASK_SHIFT; dprintf("Write: gate_offset=%x, val=%x\n", sc->gate_offset, val); DEVICE_LOCK(clk); WRITE4(clk, sc->gate_offset, val); DEVICE_UNLOCK(clk); return (0); } static int rk_clk_composite_set_mux(struct clknode *clk, int index) { struct rk_clk_composite_sc *sc; uint32_t val = 0; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_COMPOSITE_HAVE_MUX) == 0) return (0); dprintf("Set mux to %d\n", index); DEVICE_LOCK(clk); val |= (index << sc->mux_shift); val |= sc->mux_mask << RK_CLK_COMPOSITE_MASK_SHIFT; dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val); WRITE4(clk, sc->muxdiv_offset, val); DEVICE_UNLOCK(clk); return (0); } static int rk_clk_composite_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_composite_sc *sc; uint32_t reg, div; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->muxdiv_offset, ®); dprintf("Read: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, reg); DEVICE_UNLOCK(clk); div = ((reg & sc->div_mask) >> sc->div_shift); if (sc->flags & RK_CLK_COMPOSITE_DIV_EXP) div = 1 << div; else div += 1; dprintf("parent_freq=%ju, div=%u\n", *freq, div); *freq = *freq / div; dprintf("Final freq=%ju\n", *freq); return (0); } static uint32_t rk_clk_composite_find_best(struct rk_clk_composite_sc *sc, uint64_t fparent, uint64_t freq, uint32_t *reg) { uint64_t best, cur; uint32_t best_div, best_div_reg; uint32_t div, div_reg; best = 0; best_div = 0; best_div_reg = 0; for (div_reg = 0; div_reg <= ((sc->div_mask >> sc->div_shift) + 1); div_reg++) { if (sc->flags == RK_CLK_COMPOSITE_DIV_EXP) div = 1 << div_reg; else div = div_reg + 1; cur = fparent / div; if ((freq - cur) < (freq - best)) { best = cur; best_div = div; best_div_reg = div_reg; break; } } - *reg = div_reg; + *reg = best_div_reg; return (best_div); } static int rk_clk_composite_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_composite_sc *sc; struct clknode *p_clk; const char **p_names; uint64_t best, cur; uint32_t div, div_reg, best_div, best_div_reg, val; int p_idx, best_parent; sc = clknode_get_softc(clk); dprintf("Finding best parent/div for target freq of %ju\n", *fout); p_names = clknode_get_parent_names(clk); for (best_div = 0, best = 0, p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) { p_clk = clknode_find_by_name(p_names[p_idx]); clknode_get_freq(p_clk, &fparent); dprintf("Testing with parent %s (%d) at freq %ju\n", clknode_get_name(p_clk), p_idx, fparent); div = rk_clk_composite_find_best(sc, fparent, *fout, &div_reg); cur = fparent / div; if ((*fout - cur) < (*fout - best)) { best = cur; best_div = div; best_div_reg = div_reg; best_parent = p_idx; dprintf("Best parent so far %s (%d) with best freq at " "%ju\n", clknode_get_name(p_clk), p_idx, best); } } *stop = 1; if (best_div == 0) return (ERANGE); if ((best < *fout) && ((flags & CLK_SET_ROUND_DOWN) == 0)) return (ERANGE); if ((best > *fout) && ((flags & CLK_SET_ROUND_UP) == 0)) { return (ERANGE); } if ((flags & CLK_SET_DRYRUN) != 0) { *fout = best; return (0); } p_idx = clknode_get_parent_idx(clk); if (p_idx != best_parent) { dprintf("Switching parent index from %d to %d\n", p_idx, best_parent); clknode_set_parent_by_idx(clk, best_parent); } dprintf("Setting divider to %d (reg: %d)\n", best_div, best_div_reg); dprintf(" div_mask: 0x%X, div_shift: %d\n", sc->div_mask, sc->div_shift); DEVICE_LOCK(clk); val = best_div_reg << sc->div_shift; val |= sc->div_mask << RK_CLK_COMPOSITE_MASK_SHIFT; dprintf("Write: muxdiv_offset=%x, val=%x\n", sc->muxdiv_offset, val); WRITE4(clk, sc->muxdiv_offset, val); DEVICE_UNLOCK(clk); *fout = best; return (0); } static clknode_method_t rk_clk_composite_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk_clk_composite_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_composite_set_gate), CLKNODEMETHOD(clknode_set_mux, rk_clk_composite_set_mux), CLKNODEMETHOD(clknode_recalc_freq, rk_clk_composite_recalc), CLKNODEMETHOD(clknode_set_freq, rk_clk_composite_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk_clk_composite_clknode, rk_clk_composite_clknode_class, rk_clk_composite_clknode_methods, sizeof(struct rk_clk_composite_sc), clknode_class); int rk_clk_composite_register(struct clkdom *clkdom, struct rk_clk_composite_def *clkdef) { struct clknode *clk; struct rk_clk_composite_sc *sc; clk = clknode_create(clkdom, &rk_clk_composite_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->muxdiv_offset = clkdef->muxdiv_offset; sc->mux_shift = clkdef->mux_shift; sc->mux_width = clkdef->mux_width; sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift; sc->div_shift = clkdef->div_shift; sc->div_width = clkdef->div_width; sc->div_mask = ((1 << clkdef->div_width) - 1) << sc->div_shift; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->flags = clkdef->flags; clknode_register(clkdom, clk); return (0); } diff --git a/sys/arm64/rockchip/clk/rk_clk_pll.c b/sys/arm64/rockchip/clk/rk_clk_pll.c index 99026c546727..4327735f401d 100644 --- a/sys/arm64/rockchip/clk/rk_clk_pll.c +++ b/sys/arm64/rockchip/clk/rk_clk_pll.c @@ -1,786 +1,779 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include "clkdev_if.h" struct rk_clk_pll_sc { uint32_t base_offset; uint32_t gate_offset; uint32_t gate_shift; uint32_t mode_reg; uint32_t mode_shift; uint32_t flags; struct rk_clk_pll_rate *rates; struct rk_clk_pll_rate *frac_rates; }; #define WRITE4(_clk, off, val) \ CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) #define READ4(_clk, off, val) \ CLKDEV_READ_4(clknode_get_device(_clk), off, val) #define DEVICE_LOCK(_clk) \ CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) #define DEVICE_UNLOCK(_clk) \ CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) #define RK_CLK_PLL_MASK_SHIFT 16 #if 0 #define dprintf(format, arg...) \ printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) #else #define dprintf(format, arg...) #endif static int rk_clk_pll_set_gate(struct clknode *clk, bool enable) { struct rk_clk_pll_sc *sc; uint32_t val = 0; sc = clknode_get_softc(clk); if ((sc->flags & RK_CLK_PLL_HAVE_GATE) == 0) return (0); dprintf("%sabling gate\n", enable ? "En" : "Dis"); if (!enable) val |= 1 << sc->gate_shift; dprintf("sc->gate_shift: %x\n", sc->gate_shift); val |= (1 << sc->gate_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Write: gate_offset=%x, val=%x\n", sc->gate_offset, val); DEVICE_LOCK(clk); WRITE4(clk, sc->gate_offset, val); DEVICE_UNLOCK(clk); return (0); } /* CON0 */ #define RK3066_CLK_PLL_REFDIV_SHIFT 8 #define RK3066_CLK_PLL_REFDIV_MASK 0x3F00 #define RK3066_CLK_PLL_POSTDIV_SHIFT 0 #define RK3066_CLK_PLL_POSTDIV_MASK 0x000F /* CON1 */ #define RK3066_CLK_PLL_LOCK_MASK (1U << 31) #define RK3066_CLK_PLL_FBDIV_SHIFT 0 #define RK3066_CLK_PLL_FBDIV_MASK 0x0FFF /* CON2 */ /* CON3 */ #define RK3066_CLK_PLL_RESET (1 << 5) #define RK3066_CLK_PLL_TEST (1 << 4) #define RK3066_CLK_PLL_ENSAT (1 << 3) #define RK3066_CLK_PLL_FASTEN (1 << 2) #define RK3066_CLK_PLL_POWER_DOWN (1 << 1) #define RK3066_CLK_PLL_BYPASS (1 << 0) #define RK3066_CLK_PLL_MODE_SLOW 0 #define RK3066_CLK_PLL_MODE_NORMAL 1 #define RK3066_CLK_PLL_MODE_DEEP_SLOW 2 #define RK3066_CLK_PLL_MODE_MASK 0x3 static int rk3066_clk_pll_init(struct clknode *clk, device_t dev) { struct rk_clk_pll_sc *sc; uint32_t reg; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->mode_reg, ®); DEVICE_UNLOCK(clk); reg = (reg >> sc->mode_shift) & RK3066_CLK_PLL_MODE_MASK; clknode_init_parent_idx(clk, reg); return (0); } static int rk3066_clk_pll_set_mux(struct clknode *clk, int idx) { uint32_t reg; struct rk_clk_pll_sc *sc; sc = clknode_get_softc(clk); reg = (idx & RK3066_CLK_PLL_MODE_MASK) << sc->mode_shift; reg |= (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; DEVICE_LOCK(clk); WRITE4(clk, sc->mode_reg, reg); DEVICE_UNLOCK(clk); return(0); } static int rk3066_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; uint64_t rate; uint32_t refdiv, fbdiv, postdiv; uint32_t raw0, raw1, raw2, reg; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &raw0); READ4(clk, sc->base_offset + 4, &raw1); READ4(clk, sc->base_offset + 8, &raw2); READ4(clk, sc->mode_reg, ®); DEVICE_UNLOCK(clk); reg = (reg >> sc->mode_shift) & RK3066_CLK_PLL_MODE_MASK; if (reg != RK3066_CLK_PLL_MODE_NORMAL) return (0); if (!(raw1 & RK3066_CLK_PLL_LOCK_MASK)) { *freq = 0; return (0); } /* TODO MUX */ refdiv = (raw0 & RK3066_CLK_PLL_REFDIV_MASK) >> RK3066_CLK_PLL_REFDIV_SHIFT; refdiv += 1; postdiv = (raw0 & RK3066_CLK_PLL_POSTDIV_MASK) >> RK3066_CLK_PLL_POSTDIV_SHIFT; postdiv += 1; fbdiv = (raw1 & RK3066_CLK_PLL_FBDIV_MASK) >> RK3066_CLK_PLL_FBDIV_SHIFT; fbdiv += 1; rate = *freq * fbdiv; rate /= refdiv; *freq = rate / postdiv; return (0); } static int rk3066_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_pll_rate *rates; struct rk_clk_pll_sc *sc; uint32_t reg; int rv, timeout; sc = clknode_get_softc(clk); if (sc->rates == NULL) return (EINVAL); for (rates = sc->rates; rates->freq; rates++) { if (rates->freq == *fout) break; } if (rates->freq == 0) { *stop = 1; return (EINVAL); } DEVICE_LOCK(clk); /* Setting to slow mode during frequency change */ reg = (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); /* Reset PLL */ WRITE4(clk, sc->base_offset + 12, RK3066_CLK_PLL_RESET | RK3066_CLK_PLL_RESET << RK_CLK_PLL_MASK_SHIFT); /* Setting postdiv and refdiv */ reg = 0; reg |= RK3066_CLK_PLL_POSTDIV_MASK << 16; reg |= (rates->postdiv1 - 1) << RK3066_CLK_PLL_POSTDIV_SHIFT; reg |= RK3066_CLK_PLL_REFDIV_MASK << 16; reg |= (rates->refdiv - 1)<< RK3066_CLK_PLL_REFDIV_SHIFT; dprintf("Set PLL_CON0 to %x\n", reg); WRITE4(clk, sc->base_offset, reg); /* Setting fbdiv (no write mask)*/ READ4(clk, sc->base_offset + 4, ®); reg &= ~RK3066_CLK_PLL_FBDIV_MASK; reg |= RK3066_CLK_PLL_FBDIV_MASK << 16; reg = (rates->fbdiv - 1) << RK3066_CLK_PLL_FBDIV_SHIFT; dprintf("Set PLL_CON1 to %x\n", reg); WRITE4(clk, sc->base_offset + 0x4, reg); /* PLL loop bandwidth adjust */ reg = rates->bwadj - 1; dprintf("Set PLL_CON2 to %x (%x)\n", reg, rates->bwadj); WRITE4(clk, sc->base_offset + 0x8, reg); /* Clear reset */ WRITE4(clk, sc->base_offset + 12, RK3066_CLK_PLL_RESET << RK_CLK_PLL_MASK_SHIFT); DELAY(100000); /* Reading lock */ for (timeout = 1000; timeout >= 0; timeout--) { READ4(clk, sc->base_offset + 0x4, ®); if ((reg & RK3066_CLK_PLL_LOCK_MASK) != 0) break; DELAY(1); } rv = 0; if (timeout < 0) { device_printf(clknode_get_device(clk), "%s - Timedout while waiting for lock.\n", clknode_get_name(clk)); dprintf("PLL_CON1: %x\n", reg); rv = ETIMEDOUT; } /* Set back to normal mode */ reg = (RK3066_CLK_PLL_MODE_NORMAL << sc->mode_shift); reg |= (RK3066_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); DEVICE_UNLOCK(clk); *stop = 1; rv = clknode_set_parent_by_idx(clk, 1); return (rv); } static clknode_method_t rk3066_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk3066_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk3066_clk_pll_recalc), CLKNODEMETHOD(clknode_set_freq, rk3066_clk_pll_set_freq), CLKNODEMETHOD(clknode_set_mux, rk3066_clk_pll_set_mux), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk3066_clk_pll_clknode, rk3066_clk_pll_clknode_class, rk3066_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk3066_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk3066_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->mode_reg = clkdef->mode_reg; sc->mode_shift = clkdef->mode_shift; sc->flags = clkdef->flags; sc->rates = clkdef->rates; sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } #define RK3328_CLK_PLL_FBDIV_OFFSET 0 #define RK3328_CLK_PLL_FBDIV_SHIFT 0 #define RK3328_CLK_PLL_FBDIV_MASK 0xFFF #define RK3328_CLK_PLL_POSTDIV1_OFFSET 0 #define RK3328_CLK_PLL_POSTDIV1_SHIFT 12 #define RK3328_CLK_PLL_POSTDIV1_MASK 0x7000 #define RK3328_CLK_PLL_DSMPD_OFFSET 4 #define RK3328_CLK_PLL_DSMPD_SHIFT 12 #define RK3328_CLK_PLL_DSMPD_MASK 0x1000 #define RK3328_CLK_PLL_REFDIV_OFFSET 4 #define RK3328_CLK_PLL_REFDIV_SHIFT 0 #define RK3328_CLK_PLL_REFDIV_MASK 0x3F #define RK3328_CLK_PLL_POSTDIV2_OFFSET 4 #define RK3328_CLK_PLL_POSTDIV2_SHIFT 6 #define RK3328_CLK_PLL_POSTDIV2_MASK 0x1C0 #define RK3328_CLK_PLL_FRAC_OFFSET 8 #define RK3328_CLK_PLL_FRAC_SHIFT 0 #define RK3328_CLK_PLL_FRAC_MASK 0xFFFFFF #define RK3328_CLK_PLL_LOCK_MASK 0x400 #define RK3328_CLK_PLL_MODE_SLOW 0 #define RK3328_CLK_PLL_MODE_NORMAL 1 #define RK3328_CLK_PLL_MODE_MASK 0x1 static int rk3328_clk_pll_init(struct clknode *clk, device_t dev) { - struct rk_clk_pll_sc *sc; - - sc = clknode_get_softc(clk); - clknode_init_parent_idx(clk, 0); return (0); } static int rk3328_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; uint64_t rate; uint32_t dsmpd, refdiv, fbdiv; uint32_t postdiv1, postdiv2, frac; uint32_t raw1, raw2, raw3; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &raw1); READ4(clk, sc->base_offset + 4, &raw2); READ4(clk, sc->base_offset + 8, &raw3); fbdiv = (raw1 & RK3328_CLK_PLL_FBDIV_MASK) >> RK3328_CLK_PLL_FBDIV_SHIFT; postdiv1 = (raw1 & RK3328_CLK_PLL_POSTDIV1_MASK) >> RK3328_CLK_PLL_POSTDIV1_SHIFT; dsmpd = (raw2 & RK3328_CLK_PLL_DSMPD_MASK) >> RK3328_CLK_PLL_DSMPD_SHIFT; refdiv = (raw2 & RK3328_CLK_PLL_REFDIV_MASK) >> RK3328_CLK_PLL_REFDIV_SHIFT; postdiv2 = (raw2 & RK3328_CLK_PLL_POSTDIV2_MASK) >> RK3328_CLK_PLL_POSTDIV2_SHIFT; frac = (raw3 & RK3328_CLK_PLL_FRAC_MASK) >> RK3328_CLK_PLL_FRAC_SHIFT; DEVICE_UNLOCK(clk); rate = *freq * fbdiv / refdiv; if (dsmpd == 0) { /* Fractional mode */ uint64_t frac_rate; frac_rate = *freq * frac / refdiv; rate += frac_rate >> 24; } *freq = rate / postdiv1 / postdiv2; if (*freq % 2) *freq = *freq + 1; return (0); } static int rk3328_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_pll_rate *rates; struct rk_clk_pll_sc *sc; uint32_t reg; int timeout; sc = clknode_get_softc(clk); if (sc->rates) rates = sc->rates; else if (sc->frac_rates) rates = sc->frac_rates; else return (EINVAL); for (; rates->freq; rates++) { if (rates->freq == *fout) break; } if (rates->freq == 0) { *stop = 1; return (EINVAL); } DEVICE_LOCK(clk); /* Setting to slow mode during frequency change */ reg = (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); /* Setting postdiv1 and fbdiv */ reg = (rates->postdiv1 << RK3328_CLK_PLL_POSTDIV1_SHIFT) | (rates->fbdiv << RK3328_CLK_PLL_FBDIV_SHIFT); reg |= (RK3328_CLK_PLL_POSTDIV1_MASK | RK3328_CLK_PLL_FBDIV_MASK) << 16; dprintf("Set PLL_CON0 to %x\n", reg); WRITE4(clk, sc->base_offset, reg); /* Setting dsmpd, postdiv2 and refdiv */ reg = (rates->dsmpd << RK3328_CLK_PLL_DSMPD_SHIFT) | (rates->postdiv2 << RK3328_CLK_PLL_POSTDIV2_SHIFT) | (rates->refdiv << RK3328_CLK_PLL_REFDIV_SHIFT); reg |= (RK3328_CLK_PLL_DSMPD_MASK | RK3328_CLK_PLL_POSTDIV2_MASK | RK3328_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_CON1 to %x\n", reg); WRITE4(clk, sc->base_offset + 0x4, reg); /* Setting frac */ READ4(clk, sc->base_offset + 0x8, ®); reg &= ~RK3328_CLK_PLL_FRAC_MASK; reg |= rates->frac << RK3328_CLK_PLL_FRAC_SHIFT; dprintf("Set PLL_CON2 to %x\n", reg); WRITE4(clk, sc->base_offset + 0x8, reg); /* Reading lock */ for (timeout = 1000; timeout; timeout--) { READ4(clk, sc->base_offset + 0x4, ®); if ((reg & RK3328_CLK_PLL_LOCK_MASK) == 0) break; DELAY(1); } /* Set back to normal mode */ reg = (RK3328_CLK_PLL_MODE_NORMAL << sc->mode_shift); reg |= (RK3328_CLK_PLL_MODE_MASK << sc->mode_shift) << RK_CLK_PLL_MASK_SHIFT; dprintf("Set PLL_MODEREG to %x\n", reg); WRITE4(clk, sc->mode_reg, reg); DEVICE_UNLOCK(clk); *stop = 1; return (0); } static clknode_method_t rk3328_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk3328_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk3328_clk_pll_recalc), CLKNODEMETHOD(clknode_set_freq, rk3328_clk_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk3328_clk_pll_clknode, rk3328_clk_pll_clknode_class, rk3328_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk3328_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk3328_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->mode_reg = clkdef->mode_reg; sc->mode_shift = clkdef->mode_shift; sc->flags = clkdef->flags; sc->rates = clkdef->rates; sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } #define RK3399_CLK_PLL_FBDIV_OFFSET 0 #define RK3399_CLK_PLL_FBDIV_SHIFT 0 #define RK3399_CLK_PLL_FBDIV_MASK 0xFFF #define RK3399_CLK_PLL_POSTDIV2_OFFSET 4 #define RK3399_CLK_PLL_POSTDIV2_SHIFT 12 #define RK3399_CLK_PLL_POSTDIV2_MASK 0x7000 #define RK3399_CLK_PLL_POSTDIV1_OFFSET 4 #define RK3399_CLK_PLL_POSTDIV1_SHIFT 8 #define RK3399_CLK_PLL_POSTDIV1_MASK 0x700 #define RK3399_CLK_PLL_REFDIV_OFFSET 4 #define RK3399_CLK_PLL_REFDIV_SHIFT 0 #define RK3399_CLK_PLL_REFDIV_MASK 0x3F #define RK3399_CLK_PLL_FRAC_OFFSET 8 #define RK3399_CLK_PLL_FRAC_SHIFT 0 #define RK3399_CLK_PLL_FRAC_MASK 0xFFFFFF #define RK3399_CLK_PLL_DSMPD_OFFSET 0xC #define RK3399_CLK_PLL_DSMPD_SHIFT 3 #define RK3399_CLK_PLL_DSMPD_MASK 0x8 #define RK3399_CLK_PLL_LOCK_OFFSET 8 #define RK3399_CLK_PLL_LOCK_MASK 0x400 #define RK3399_CLK_PLL_MODE_OFFSET 0xC #define RK3399_CLK_PLL_MODE_MASK 0x300 #define RK3399_CLK_PLL_MODE_SLOW 0 #define RK3399_CLK_PLL_MODE_NORMAL 1 #define RK3399_CLK_PLL_MODE_DEEPSLOW 2 #define RK3399_CLK_PLL_MODE_SHIFT 8 #define RK3399_CLK_PLL_WRITE_MASK 0xFFFF0000 static int rk3399_clk_pll_init(struct clknode *clk, device_t dev) { - struct rk_clk_pll_sc *sc; - - sc = clknode_get_softc(clk); clknode_init_parent_idx(clk, 0); return (0); } static int rk3399_clk_pll_recalc(struct clknode *clk, uint64_t *freq) { struct rk_clk_pll_sc *sc; uint32_t dsmpd, refdiv, fbdiv; uint32_t postdiv1, postdiv2, fracdiv; uint32_t con1, con2, con3, con4; uint64_t foutvco; uint32_t mode; sc = clknode_get_softc(clk); DEVICE_LOCK(clk); READ4(clk, sc->base_offset, &con1); READ4(clk, sc->base_offset + 4, &con2); READ4(clk, sc->base_offset + 8, &con3); READ4(clk, sc->base_offset + 0xC, &con4); DEVICE_UNLOCK(clk); /* * if we are in slow mode the output freq * is the parent one, the 24Mhz external oscillator * if we are in deep mode the output freq is 32.768khz */ mode = (con4 & RK3399_CLK_PLL_MODE_MASK) >> RK3399_CLK_PLL_MODE_SHIFT; if (mode == RK3399_CLK_PLL_MODE_SLOW) { dprintf("pll in slow mode, con4=%x\n", con4); return (0); } else if (mode == RK3399_CLK_PLL_MODE_DEEPSLOW) { dprintf("pll in deep slow, con4=%x\n", con4); *freq = 32768; return (0); } dprintf("con0: %x\n", con1); dprintf("con1: %x\n", con2); dprintf("con2: %x\n", con3); dprintf("con3: %x\n", con4); fbdiv = (con1 & RK3399_CLK_PLL_FBDIV_MASK) >> RK3399_CLK_PLL_FBDIV_SHIFT; postdiv1 = (con2 & RK3399_CLK_PLL_POSTDIV1_MASK) >> RK3399_CLK_PLL_POSTDIV1_SHIFT; postdiv2 = (con2 & RK3399_CLK_PLL_POSTDIV2_MASK) >> RK3399_CLK_PLL_POSTDIV2_SHIFT; refdiv = (con2 & RK3399_CLK_PLL_REFDIV_MASK) >> RK3399_CLK_PLL_REFDIV_SHIFT; fracdiv = (con3 & RK3399_CLK_PLL_FRAC_MASK) >> RK3399_CLK_PLL_FRAC_SHIFT; fracdiv >>= 24; dsmpd = (con4 & RK3399_CLK_PLL_DSMPD_MASK) >> RK3399_CLK_PLL_DSMPD_SHIFT; dprintf("fbdiv: %d\n", fbdiv); dprintf("postdiv1: %d\n", postdiv1); dprintf("postdiv2: %d\n", postdiv2); dprintf("refdiv: %d\n", refdiv); dprintf("fracdiv: %d\n", fracdiv); dprintf("dsmpd: %d\n", dsmpd); dprintf("parent freq=%ju\n", *freq); if (dsmpd == 0) { /* Fractional mode */ foutvco = *freq / refdiv * (fbdiv + fracdiv); } else { /* Integer mode */ foutvco = *freq / refdiv * fbdiv; } dprintf("foutvco: %ju\n", foutvco); *freq = foutvco / postdiv1 / postdiv2; dprintf("freq: %ju\n", *freq); return (0); } static int rk3399_clk_pll_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout, int flags, int *stop) { struct rk_clk_pll_rate *rates; struct rk_clk_pll_sc *sc; uint32_t reg; int timeout; sc = clknode_get_softc(clk); if (sc->rates) rates = sc->rates; else if (sc->frac_rates) rates = sc->frac_rates; else return (EINVAL); for (; rates->freq; rates++) { if (rates->freq == *fout) break; } if (rates->freq == 0) { *stop = 1; return (EINVAL); } DEVICE_LOCK(clk); /* Set to slow mode during frequency change */ reg = RK3399_CLK_PLL_MODE_SLOW << RK3399_CLK_PLL_MODE_SHIFT; reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); /* Setting fbdiv */ reg = rates->fbdiv << RK3399_CLK_PLL_FBDIV_SHIFT; reg |= RK3399_CLK_PLL_FBDIV_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset, reg); /* Setting postdiv1, postdiv2 and refdiv */ reg = rates->postdiv1 << RK3399_CLK_PLL_POSTDIV1_SHIFT; reg |= rates->postdiv2 << RK3399_CLK_PLL_POSTDIV2_SHIFT; reg |= rates->refdiv << RK3399_CLK_PLL_REFDIV_SHIFT; reg |= (RK3399_CLK_PLL_POSTDIV1_MASK | RK3399_CLK_PLL_POSTDIV2_MASK | RK3399_CLK_PLL_REFDIV_MASK) << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0x4, reg); /* Setting frac */ READ4(clk, sc->base_offset + 0x8, ®); reg &= ~RK3399_CLK_PLL_FRAC_MASK; reg |= rates->frac << RK3399_CLK_PLL_FRAC_SHIFT; WRITE4(clk, sc->base_offset + 0x8, reg | RK3399_CLK_PLL_WRITE_MASK); /* Set dsmpd */ reg = rates->dsmpd << RK3399_CLK_PLL_DSMPD_SHIFT; reg |= RK3399_CLK_PLL_DSMPD_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); /* Reading lock */ for (timeout = 1000; timeout; timeout--) { READ4(clk, sc->base_offset + RK3399_CLK_PLL_LOCK_OFFSET, ®); if ((reg & RK3399_CLK_PLL_LOCK_MASK) == 0) break; DELAY(1); } /* Set back to normal mode */ reg = RK3399_CLK_PLL_MODE_NORMAL << RK3399_CLK_PLL_MODE_SHIFT; reg |= RK3399_CLK_PLL_MODE_MASK << RK_CLK_PLL_MASK_SHIFT; WRITE4(clk, sc->base_offset + 0xC, reg); DEVICE_UNLOCK(clk); *stop = 1; return (0); } static clknode_method_t rk3399_clk_pll_clknode_methods[] = { /* Device interface */ CLKNODEMETHOD(clknode_init, rk3399_clk_pll_init), CLKNODEMETHOD(clknode_set_gate, rk_clk_pll_set_gate), CLKNODEMETHOD(clknode_recalc_freq, rk3399_clk_pll_recalc), CLKNODEMETHOD(clknode_set_freq, rk3399_clk_pll_set_freq), CLKNODEMETHOD_END }; DEFINE_CLASS_1(rk3399_clk_pll_clknode, rk3399_clk_pll_clknode_class, rk3399_clk_pll_clknode_methods, sizeof(struct rk_clk_pll_sc), clknode_class); int rk3399_clk_pll_register(struct clkdom *clkdom, struct rk_clk_pll_def *clkdef) { struct clknode *clk; struct rk_clk_pll_sc *sc; clk = clknode_create(clkdom, &rk3399_clk_pll_clknode_class, &clkdef->clkdef); if (clk == NULL) return (1); sc = clknode_get_softc(clk); sc->base_offset = clkdef->base_offset; sc->gate_offset = clkdef->gate_offset; sc->gate_shift = clkdef->gate_shift; sc->flags = clkdef->flags; sc->rates = clkdef->rates; sc->frac_rates = clkdef->frac_rates; clknode_register(clkdom, clk); return (0); } diff --git a/sys/arm64/rockchip/rk_i2s.c b/sys/arm64/rockchip/rk_i2s.c index 52f80789a795..450c2c6e2b77 100644 --- a/sys/arm64/rockchip/rk_i2s.c +++ b/sys/arm64/rockchip/rk_i2s.c @@ -1,657 +1,658 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2019 Oleksandr Tymoshenko * * 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. * * $FreeBSD$ */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "syscon_if.h" #include "opt_snd.h" #include #include #include "audio_dai_if.h" #define AUDIO_BUFFER_SIZE 48000 * 4 #define I2S_TXCR 0x0000 #define I2S_CSR_2 (0 << 15) #define I2S_CSR_4 (1 << 15) #define I2S_CSR_6 (2 << 15) #define I2S_CSR_8 (3 << 15) #define I2S_TXCR_IBM_NORMAL (0 << 9) #define I2S_TXCR_IBM_LJ (1 << 9) #define I2S_TXCR_IBM_RJ (2 << 9) #define I2S_TXCR_PBM_NODELAY (0 << 7) #define I2S_TXCR_PBM_1 (1 << 7) #define I2S_TXCR_PBM_2 (2 << 7) #define I2S_TXCR_PBM_3 (3 << 7) #define I2S_TXCR_TFS_I2S (0 << 5) #define I2S_TXCR_TFS_PCM (1 << 5) #define I2S_TXCR_VDW_16 (0xf << 0) #define I2S_RXCR 0x0004 #define I2S_RXCR_IBM_NORMAL (0 << 9) #define I2S_RXCR_IBM_LJ (1 << 9) #define I2S_RXCR_IBM_RJ (2 << 9) #define I2S_RXCR_PBM_NODELAY (0 << 7) #define I2S_RXCR_PBM_1 (1 << 7) #define I2S_RXCR_PBM_2 (2 << 7) #define I2S_RXCR_PBM_3 (3 << 7) #define I2S_RXCR_TFS_I2S (0 << 5) #define I2S_RXCR_TFS_PCM (1 << 5) #define I2S_RXCR_VDW_16 (0xf << 0) #define I2S_CKR 0x0008 #define I2S_CKR_MSS_MASK (1 << 27) #define I2S_CKR_MSS_MASTER (0 << 27) #define I2S_CKR_MSS_SLAVE (1 << 27) #define I2S_CKR_CKP (1 << 26) #define I2S_CKR_MDIV(n) (((n) - 1) << 16) #define I2S_CKR_MDIV_MASK (0xff << 16) #define I2S_CKR_RSD(n) (((n) - 1) << 8) #define I2S_CKR_RSD_MASK (0xff << 8) #define I2S_CKR_TSD(n) (((n) - 1) << 0) #define I2S_CKR_TSD_MASK (0xff << 0) #define I2S_TXFIFOLR 0x000c #define TXFIFO0LR_MASK 0x3f #define I2S_DMACR 0x0010 #define I2S_DMACR_RDE_ENABLE (1 << 24) #define I2S_DMACR_RDL(n) ((n) << 16) #define I2S_DMACR_TDE_ENABLE (1 << 8) #define I2S_DMACR_TDL(n) ((n) << 0) #define I2S_INTCR 0x0014 #define I2S_INTCR_RFT(n) (((n) - 1) << 20) #define I2S_INTCR_TFT(n) (((n) - 1) << 4) #define I2S_INTCR_RXFIE (1 << 16) #define I2S_INTCR_TXUIC (1 << 2) #define I2S_INTCR_TXEIE (1 << 0) #define I2S_INTSR 0x0018 #define I2S_INTSR_RXFI (1 << 16) #define I2S_INTSR_TXUI (1 << 1) #define I2S_INTSR_TXEI (1 << 0) #define I2S_XFER 0x001c #define I2S_XFER_RXS_START (1 << 1) #define I2S_XFER_TXS_START (1 << 0) #define I2S_CLR 0x0020 #define I2S_CLR_RXC (1 << 1) #define I2S_CLR_TXC (1 << 0) #define I2S_TXDR 0x0024 #define I2S_RXDR 0x0028 #define I2S_RXFIFOLR 0x002c #define RXFIFO0LR_MASK 0x3f /* syscon */ #define GRF_SOC_CON8 0xe220 #define I2S_IO_DIRECTION_MASK 7 #define I2S_IO_DIRECTION_SHIFT 11 #define I2S_IO_8CH_OUT_2CH_IN 0 #define I2S_IO_6CH_OUT_4CH_IN 4 #define I2S_IO_4CH_OUT_6CH_IN 6 #define I2S_IO_2CH_OUT_8CH_IN 7 #define DIV_ROUND_CLOSEST(n,d) (((n) + (d) / 2) / (d)) #define RK_I2S_SAMPLING_RATE 48000 #define FIFO_SIZE 32 static struct ofw_compat_data compat_data[] = { { "rockchip,rk3066-i2s", 1 }, { "rockchip,rk3399-i2s", 1 }, { NULL, 0 } }; static struct resource_spec rk_i2s_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE | RF_SHAREABLE }, { -1, 0 } }; struct rk_i2s_softc { device_t dev; struct resource *res[2]; struct mtx mtx; clk_t clk; clk_t hclk; void * intrhand; struct syscon *grf; /* pointers to playback/capture buffers */ uint32_t play_ptr; uint32_t rec_ptr; }; #define RK_I2S_LOCK(sc) mtx_lock(&(sc)->mtx) #define RK_I2S_UNLOCK(sc) mtx_unlock(&(sc)->mtx) #define RK_I2S_READ_4(sc, reg) bus_read_4((sc)->res[0], (reg)) #define RK_I2S_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], (reg), (val)) static int rk_i2s_probe(device_t dev); static int rk_i2s_attach(device_t dev); static int rk_i2s_detach(device_t dev); static uint32_t sc_fmt[] = { SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps rk_i2s_caps = {RK_I2S_SAMPLING_RATE, RK_I2S_SAMPLING_RATE, sc_fmt, 0}; static int rk_i2s_init(struct rk_i2s_softc *sc) { uint32_t val; int error; clk_set_freq(sc->clk, RK_I2S_SAMPLING_RATE * 256, CLK_SET_ROUND_DOWN); error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "cannot enable i2s_clk clock\n"); return (ENXIO); } val = I2S_INTCR_TFT(FIFO_SIZE/2); val |= I2S_INTCR_RFT(FIFO_SIZE/2); RK_I2S_WRITE_4(sc, I2S_INTCR, val); if (sc->grf && ofw_bus_is_compatible(sc->dev, "rockchip,rk3399-i2s")) { val = (I2S_IO_2CH_OUT_8CH_IN << I2S_IO_DIRECTION_SHIFT); val |= (I2S_IO_DIRECTION_MASK << I2S_IO_DIRECTION_SHIFT) << 16; SYSCON_WRITE_4(sc->grf, GRF_SOC_CON8, val); #if 0 // HACK: enable IO domain val = (1 << 1); val |= (1 << 1) << 16; SYSCON_WRITE_4(sc->grf, 0xe640, val); #endif } RK_I2S_WRITE_4(sc, I2S_XFER, 0); return (0); } static int rk_i2s_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Rockchip I2S"); return (BUS_PROBE_DEFAULT); } static int rk_i2s_attach(device_t dev) { struct rk_i2s_softc *sc; int error; phandle_t node; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, rk_i2s_spec, sc->res) != 0) { device_printf(dev, "cannot allocate resources for device\n"); error = ENXIO; goto fail; } error = clk_get_by_ofw_name(dev, 0, "i2s_hclk", &sc->hclk); if (error != 0) { device_printf(dev, "cannot get i2s_hclk clock\n"); goto fail; } error = clk_get_by_ofw_name(dev, 0, "i2s_clk", &sc->clk); if (error != 0) { device_printf(dev, "cannot get i2s_clk clock\n"); goto fail; } /* Activate the module clock. */ error = clk_enable(sc->hclk); if (error != 0) { device_printf(dev, "cannot enable i2s_hclk clock\n"); goto fail; } node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } rk_i2s_init(sc); OF_device_register_xref(OF_xref_from_node(node), dev); return (0); fail: rk_i2s_detach(dev); return (error); } static int rk_i2s_detach(device_t dev) { struct rk_i2s_softc *i2s; i2s = device_get_softc(dev); if (i2s->hclk != NULL) clk_release(i2s->hclk); if (i2s->clk) clk_release(i2s->clk); if (i2s->intrhand != NULL) bus_teardown_intr(i2s->dev, i2s->res[1], i2s->intrhand); bus_release_resources(dev, rk_i2s_spec, i2s->res); mtx_destroy(&i2s->mtx); return (0); } static int rk_i2s_dai_init(device_t dev, uint32_t format) { uint32_t val, txcr, rxcr; struct rk_i2s_softc *sc; int fmt, pol, clk; sc = device_get_softc(dev); fmt = AUDIO_DAI_FORMAT_FORMAT(format); pol = AUDIO_DAI_FORMAT_POLARITY(format); clk = AUDIO_DAI_FORMAT_CLOCK(format); /* Set format */ val = RK_I2S_READ_4(sc, I2S_CKR); val &= ~(I2S_CKR_MSS_MASK); switch (clk) { case AUDIO_DAI_CLOCK_CBM_CFM: val |= I2S_CKR_MSS_MASTER; break; case AUDIO_DAI_CLOCK_CBS_CFS: val |= I2S_CKR_MSS_SLAVE; break; default: return (EINVAL); } switch (pol) { case AUDIO_DAI_POLARITY_IB_NF: val |= I2S_CKR_CKP; break; case AUDIO_DAI_POLARITY_NB_NF: val &= ~I2S_CKR_CKP; break; default: return (EINVAL); } RK_I2S_WRITE_4(sc, I2S_CKR, val); txcr = I2S_TXCR_VDW_16 | I2S_CSR_2; rxcr = I2S_RXCR_VDW_16 | I2S_CSR_2; switch (fmt) { case AUDIO_DAI_FORMAT_I2S: txcr |= I2S_TXCR_IBM_NORMAL; rxcr |= I2S_RXCR_IBM_NORMAL; break; case AUDIO_DAI_FORMAT_LJ: txcr |= I2S_TXCR_IBM_LJ; rxcr |= I2S_RXCR_IBM_LJ; break; case AUDIO_DAI_FORMAT_RJ: txcr |= I2S_TXCR_IBM_RJ; rxcr |= I2S_RXCR_IBM_RJ; break; case AUDIO_DAI_FORMAT_DSPA: txcr |= I2S_TXCR_TFS_PCM; rxcr |= I2S_RXCR_TFS_PCM; txcr |= I2S_TXCR_PBM_1; rxcr |= I2S_RXCR_PBM_1; break; case AUDIO_DAI_FORMAT_DSPB: txcr |= I2S_TXCR_TFS_PCM; rxcr |= I2S_RXCR_TFS_PCM; txcr |= I2S_TXCR_PBM_2; rxcr |= I2S_RXCR_PBM_2; break; default: return EINVAL; } RK_I2S_WRITE_4(sc, I2S_TXCR, txcr); RK_I2S_WRITE_4(sc, I2S_RXCR, rxcr); RK_I2S_WRITE_4(sc, I2S_XFER, 0); return (0); } static int rk_i2s_dai_intr(device_t dev, struct snd_dbuf *play_buf, struct snd_dbuf *rec_buf) { struct rk_i2s_softc *sc; uint32_t status; uint32_t level; uint32_t val = 0x00; int ret = 0; sc = device_get_softc(dev); RK_I2S_LOCK(sc); status = RK_I2S_READ_4(sc, I2S_INTSR); if (status & I2S_INTSR_TXEI) { level = RK_I2S_READ_4(sc, I2S_TXFIFOLR) & TXFIFO0LR_MASK; uint8_t *samples; uint32_t count, size, readyptr, written; count = sndbuf_getready(play_buf); + if (count > FIFO_SIZE - 1) + count = FIFO_SIZE - 1; size = sndbuf_getsize(play_buf); readyptr = sndbuf_getreadyptr(play_buf); - /* FIXME: check actual count size */ samples = (uint8_t*)sndbuf_getbuf(play_buf); written = 0; - for (; level < FIFO_SIZE - 1; level++) { + for (; level < count; level++) { val = (samples[readyptr++ % size] << 0); val |= (samples[readyptr++ % size] << 8); val |= (samples[readyptr++ % size] << 16); val |= (samples[readyptr++ % size] << 24); written += 4; RK_I2S_WRITE_4(sc, I2S_TXDR, val); } sc->play_ptr += written; sc->play_ptr %= size; ret |= AUDIO_DAI_PLAY_INTR; } if (status & I2S_INTSR_RXFI) { level = RK_I2S_READ_4(sc, I2S_RXFIFOLR) & RXFIFO0LR_MASK; uint8_t *samples; uint32_t count, size, freeptr, recorded; count = sndbuf_getfree(rec_buf); size = sndbuf_getsize(rec_buf); freeptr = sndbuf_getfreeptr(rec_buf); samples = (uint8_t*)sndbuf_getbuf(rec_buf); recorded = 0; if (level > count / 4) level = count / 4; for (; level > 0; level--) { val = RK_I2S_READ_4(sc, I2S_RXDR); samples[freeptr++ % size] = val & 0xff; samples[freeptr++ % size] = (val >> 8) & 0xff; samples[freeptr++ % size] = (val >> 16) & 0xff; samples[freeptr++ % size] = (val >> 24) & 0xff; recorded += 4; } sc->rec_ptr += recorded; sc->rec_ptr %= size; ret |= AUDIO_DAI_REC_INTR; } RK_I2S_UNLOCK(sc); return (ret); } static struct pcmchan_caps * rk_i2s_dai_get_caps(device_t dev) { return (&rk_i2s_caps); } static int rk_i2s_dai_trigger(device_t dev, int go, int pcm_dir) { struct rk_i2s_softc *sc = device_get_softc(dev); uint32_t val; uint32_t clear_bit; if ((pcm_dir != PCMDIR_PLAY) && (pcm_dir != PCMDIR_REC)) return (EINVAL); switch (go) { case PCMTRIG_START: val = RK_I2S_READ_4(sc, I2S_INTCR); if (pcm_dir == PCMDIR_PLAY) val |= I2S_INTCR_TXEIE; else if (pcm_dir == PCMDIR_REC) val |= I2S_INTCR_RXFIE; RK_I2S_WRITE_4(sc, I2S_INTCR, val); val = I2S_XFER_TXS_START | I2S_XFER_RXS_START; RK_I2S_WRITE_4(sc, I2S_XFER, val); break; case PCMTRIG_STOP: case PCMTRIG_ABORT: val = RK_I2S_READ_4(sc, I2S_INTCR); if (pcm_dir == PCMDIR_PLAY) val &= ~I2S_INTCR_TXEIE; else if (pcm_dir == PCMDIR_REC) val &= ~I2S_INTCR_RXFIE; RK_I2S_WRITE_4(sc, I2S_INTCR, val); /* * If there is no other activity going on, stop transfers */ if ((val & (I2S_INTCR_TXEIE | I2S_INTCR_RXFIE)) == 0) { RK_I2S_WRITE_4(sc, I2S_XFER, 0); if (pcm_dir == PCMDIR_PLAY) clear_bit = I2S_CLR_TXC; else if (pcm_dir == PCMDIR_REC) clear_bit = I2S_CLR_RXC; else return (EINVAL); val = RK_I2S_READ_4(sc, I2S_CLR); val |= clear_bit; RK_I2S_WRITE_4(sc, I2S_CLR, val); while ((RK_I2S_READ_4(sc, I2S_CLR) & clear_bit) != 0) DELAY(1); } RK_I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) sc->play_ptr = 0; else sc->rec_ptr = 0; RK_I2S_UNLOCK(sc); break; } return (0); } static uint32_t rk_i2s_dai_get_ptr(device_t dev, int pcm_dir) { struct rk_i2s_softc *sc; uint32_t ptr; sc = device_get_softc(dev); RK_I2S_LOCK(sc); if (pcm_dir == PCMDIR_PLAY) ptr = sc->play_ptr; else ptr = sc->rec_ptr; RK_I2S_UNLOCK(sc); return ptr; } static int rk_i2s_dai_setup_intr(device_t dev, driver_intr_t intr_handler, void *intr_arg) { struct rk_i2s_softc *sc = device_get_softc(dev); if (bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, NULL, intr_handler, intr_arg, &sc->intrhand)) { device_printf(dev, "cannot setup interrupt handler\n"); return (ENXIO); } return (0); } static uint32_t rk_i2s_dai_set_chanformat(device_t dev, uint32_t format) { return (0); } static int rk_i2s_dai_set_sysclk(device_t dev, unsigned int rate, int dai_dir) { struct rk_i2s_softc *sc; int error; sc = device_get_softc(dev); error = clk_disable(sc->clk); if (error != 0) { device_printf(sc->dev, "could not disable i2s_clk clock\n"); return (error); } error = clk_set_freq(sc->clk, rate, CLK_SET_ROUND_DOWN); if (error != 0) device_printf(sc->dev, "could not set i2s_clk freq\n"); error = clk_enable(sc->clk); if (error != 0) { device_printf(sc->dev, "could not enable i2s_clk clock\n"); return (error); } return (0); } static uint32_t rk_i2s_dai_set_chanspeed(device_t dev, uint32_t speed) { struct rk_i2s_softc *sc; int error; uint32_t val; uint32_t bus_clock_div, lr_clock_div; uint64_t bus_clk_freq; uint64_t clk_freq; sc = device_get_softc(dev); /* Set format */ val = RK_I2S_READ_4(sc, I2S_CKR); if ((val & I2S_CKR_MSS_SLAVE) == 0) { error = clk_get_freq(sc->clk, &clk_freq); if (error != 0) { device_printf(sc->dev, "failed to get clk frequency: err=%d\n", error); return (error); } bus_clk_freq = 2 * 32 * speed; bus_clock_div = DIV_ROUND_CLOSEST(clk_freq, bus_clk_freq); lr_clock_div = bus_clk_freq / speed; val &= ~(I2S_CKR_MDIV_MASK | I2S_CKR_RSD_MASK | I2S_CKR_TSD_MASK); val |= I2S_CKR_MDIV(bus_clock_div); val |= I2S_CKR_RSD(lr_clock_div); val |= I2S_CKR_TSD(lr_clock_div); RK_I2S_WRITE_4(sc, I2S_CKR, val); } return (speed); } static device_method_t rk_i2s_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_i2s_probe), DEVMETHOD(device_attach, rk_i2s_attach), DEVMETHOD(device_detach, rk_i2s_detach), DEVMETHOD(audio_dai_init, rk_i2s_dai_init), DEVMETHOD(audio_dai_setup_intr, rk_i2s_dai_setup_intr), DEVMETHOD(audio_dai_set_sysclk, rk_i2s_dai_set_sysclk), DEVMETHOD(audio_dai_set_chanspeed, rk_i2s_dai_set_chanspeed), DEVMETHOD(audio_dai_set_chanformat, rk_i2s_dai_set_chanformat), DEVMETHOD(audio_dai_intr, rk_i2s_dai_intr), DEVMETHOD(audio_dai_get_caps, rk_i2s_dai_get_caps), DEVMETHOD(audio_dai_trigger, rk_i2s_dai_trigger), DEVMETHOD(audio_dai_get_ptr, rk_i2s_dai_get_ptr), DEVMETHOD_END }; static driver_t rk_i2s_driver = { "i2s", rk_i2s_methods, sizeof(struct rk_i2s_softc), }; static devclass_t rk_i2s_devclass; DRIVER_MODULE(rk_i2s, simplebus, rk_i2s_driver, rk_i2s_devclass, 0, 0); SIMPLEBUS_PNP_INFO(compat_data); diff --git a/sys/arm64/rockchip/rk_pinctrl.c b/sys/arm64/rockchip/rk_pinctrl.c index 24ad7798eef0..21ab984aca5a 100644 --- a/sys/arm64/rockchip/rk_pinctrl.c +++ b/sys/arm64/rockchip/rk_pinctrl.c @@ -1,1348 +1,1347 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2018 Emmanuel Vadot * * 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. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "syscon_if.h" #include "fdt_pinctrl_if.h" struct rk_pinctrl_pin_drive { uint32_t bank; uint32_t subbank; uint32_t offset; uint32_t value; uint32_t ma; }; struct rk_pinctrl_bank { uint32_t bank; uint32_t subbank; uint32_t offset; uint32_t nbits; }; struct rk_pinctrl_pin_fixup { uint32_t bank; uint32_t subbank; uint32_t pin; uint32_t reg; uint32_t bit; uint32_t mask; }; struct rk_pinctrl_gpio { uint32_t bank; char *gpio_name; device_t gpio_dev; }; struct rk_pinctrl_softc; struct rk_pinctrl_conf { struct rk_pinctrl_bank *iomux_conf; uint32_t iomux_nbanks; struct rk_pinctrl_pin_fixup *pin_fixup; uint32_t npin_fixup; struct rk_pinctrl_pin_drive *pin_drive; uint32_t npin_drive; struct rk_pinctrl_gpio *gpio_bank; uint32_t ngpio_bank; uint32_t (*get_pd_offset)(struct rk_pinctrl_softc *, uint32_t); struct syscon *(*get_syscon)(struct rk_pinctrl_softc *, uint32_t); int (*parse_bias)(phandle_t, int); int (*resolv_bias_value)(int, int); int (*get_bias_value)(int, int); }; struct rk_pinctrl_softc { struct simplebus_softc simplebus_sc; device_t dev; struct syscon *grf; struct syscon *pmu; struct rk_pinctrl_conf *conf; struct mtx mtx; }; #define RK_PINCTRL_LOCK(_sc) mtx_lock_spin(&(_sc)->mtx) #define RK_PINCTRL_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->mtx) #define RK_PINCTRL_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) #define RK_IOMUX(_bank, _subbank, _offset, _nbits) \ { \ .bank = _bank, \ .subbank = _subbank, \ .offset = _offset, \ .nbits = _nbits, \ } #define RK_PINFIX(_bank, _pin, _reg, _bit, _mask) \ { \ .bank = _bank, \ .pin = _pin, \ .reg = _reg, \ .bit = _bit, \ .mask = _mask, \ } #define RK_PINDRIVE(_bank, _subbank, _offset, _value, _ma) \ { \ .bank = _bank, \ .subbank = _subbank, \ .offset = _offset, \ .value = _value, \ .ma = _ma, \ } #define RK_GPIO(_bank, _name) \ { \ .bank = _bank, \ .gpio_name = _name, \ } static struct rk_pinctrl_gpio rk3288_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), RK_GPIO(4, "gpio4"), RK_GPIO(5, "gpio5"), RK_GPIO(6, "gpio6"), RK_GPIO(7, "gpio7"), RK_GPIO(8, "gpio8"), }; static struct rk_pinctrl_bank rk3288_iomux_bank[] = { /* bank sub offs nbits */ /* PMU */ RK_IOMUX(0, 0, 0x0084, 2), RK_IOMUX(0, 1, 0x0088, 2), RK_IOMUX(0, 2, 0x008C, 2), /* GFR */ RK_IOMUX(1, 3, 0x000C, 2), RK_IOMUX(2, 0, 0x0010, 2), RK_IOMUX(2, 1, 0x0014, 2), RK_IOMUX(2, 2, 0x0018, 2), RK_IOMUX(2, 3, 0x001C, 2), RK_IOMUX(3, 0, 0x0020, 2), RK_IOMUX(3, 1, 0x0024, 2), RK_IOMUX(3, 2, 0x0028, 2), RK_IOMUX(3, 3, 0x002C, 4), RK_IOMUX(4, 0, 0x0034, 4), RK_IOMUX(4, 1, 0x003C, 4), RK_IOMUX(4, 2, 0x0044, 2), RK_IOMUX(4, 3, 0x0048, 2), /* 5,0 - Empty */ RK_IOMUX(5, 1, 0x0050, 2), RK_IOMUX(5, 2, 0x0054, 2), /* 5,3 - Empty */ RK_IOMUX(6, 0, 0x005C, 2), RK_IOMUX(6, 1, 0x0060, 2), RK_IOMUX(6, 2, 0x0064, 2), /* 6,3 - Empty */ RK_IOMUX(7, 0, 0x006C, 2), RK_IOMUX(7, 1, 0x0070, 2), RK_IOMUX(7, 2, 0x0074, 4), /* 7,3 - Empty */ RK_IOMUX(8, 0, 0x0080, 2), RK_IOMUX(8, 1, 0x0084, 2), /* 8,2 - Empty */ /* 8,3 - Empty */ }; static struct rk_pinctrl_pin_fixup rk3288_pin_fixup[] = { }; static struct rk_pinctrl_pin_drive rk3288_pin_drive[] = { /* bank sub offs val ma */ /* GPIO0A (PMU)*/ RK_PINDRIVE(0, 0, 0x070, 0, 2), RK_PINDRIVE(0, 0, 0x070, 1, 4), RK_PINDRIVE(0, 0, 0x070, 2, 8), RK_PINDRIVE(0, 0, 0x070, 3, 12), /* GPIO0B (PMU)*/ RK_PINDRIVE(0, 1, 0x074, 0, 2), RK_PINDRIVE(0, 1, 0x074, 1, 4), RK_PINDRIVE(0, 1, 0x074, 2, 8), RK_PINDRIVE(0, 1, 0x074, 3, 12), /* GPIO0C (PMU)*/ RK_PINDRIVE(0, 2, 0x078, 0, 2), RK_PINDRIVE(0, 2, 0x078, 1, 4), RK_PINDRIVE(0, 2, 0x078, 2, 8), RK_PINDRIVE(0, 2, 0x078, 3, 12), /* GPIO1D */ RK_PINDRIVE(1, 3, 0x1CC, 0, 2), RK_PINDRIVE(1, 3, 0x1CC, 1, 4), RK_PINDRIVE(1, 3, 0x1CC, 2, 8), RK_PINDRIVE(1, 3, 0x1CC, 3, 12), /* GPIO2A */ RK_PINDRIVE(2, 0, 0x1D0, 0, 2), RK_PINDRIVE(2, 0, 0x1D0, 1, 4), RK_PINDRIVE(2, 0, 0x1D0, 2, 8), RK_PINDRIVE(2, 0, 0x1D0, 3, 12), /* GPIO2B */ RK_PINDRIVE(2, 1, 0x1D4, 0, 2), RK_PINDRIVE(2, 1, 0x1D4, 1, 4), RK_PINDRIVE(2, 1, 0x1D4, 2, 8), RK_PINDRIVE(2, 1, 0x1D4, 3, 12), /* GPIO2C */ RK_PINDRIVE(2, 2, 0x1D8, 0, 2), RK_PINDRIVE(2, 2, 0x1D8, 1, 4), RK_PINDRIVE(2, 2, 0x1D8, 2, 8), RK_PINDRIVE(2, 2, 0x1D8, 3, 12), /* GPIO2D */ RK_PINDRIVE(2, 3, 0x1DC, 0, 2), RK_PINDRIVE(2, 3, 0x1DC, 1, 4), RK_PINDRIVE(2, 3, 0x1DC, 2, 8), RK_PINDRIVE(2, 3, 0x1DC, 3, 12), /* GPIO3A */ RK_PINDRIVE(3, 0, 0x1E0, 0, 2), RK_PINDRIVE(3, 0, 0x1E0, 1, 4), RK_PINDRIVE(3, 0, 0x1E0, 2, 8), RK_PINDRIVE(3, 0, 0x1E0, 3, 12), /* GPIO3B */ RK_PINDRIVE(3, 1, 0x1E4, 0, 2), RK_PINDRIVE(3, 1, 0x1E4, 1, 4), RK_PINDRIVE(3, 1, 0x1E4, 2, 8), RK_PINDRIVE(3, 1, 0x1E4, 3, 12), /* GPIO3C */ RK_PINDRIVE(3, 2, 0x1E8, 0, 2), RK_PINDRIVE(3, 2, 0x1E8, 1, 4), RK_PINDRIVE(3, 2, 0x1E8, 2, 8), RK_PINDRIVE(3, 2, 0x1E8, 3, 12), /* GPIO3D */ RK_PINDRIVE(3, 3, 0x1EC, 0, 2), RK_PINDRIVE(3, 3, 0x1EC, 1, 4), RK_PINDRIVE(3, 3, 0x1EC, 2, 8), RK_PINDRIVE(3, 3, 0x1EC, 3, 12), /* GPIO4A */ RK_PINDRIVE(4, 0, 0x1F0, 0, 2), RK_PINDRIVE(4, 0, 0x1F0, 1, 4), RK_PINDRIVE(4, 0, 0x1F0, 2, 8), RK_PINDRIVE(4, 0, 0x1F0, 3, 12), /* GPIO4B */ RK_PINDRIVE(4, 1, 0x1F4, 0, 2), RK_PINDRIVE(4, 1, 0x1F4, 1, 4), RK_PINDRIVE(4, 1, 0x1F4, 2, 8), RK_PINDRIVE(4, 1, 0x1F4, 3, 12), /* GPIO4C */ RK_PINDRIVE(4, 2, 0x1F8, 0, 2), RK_PINDRIVE(4, 2, 0x1F8, 1, 4), RK_PINDRIVE(4, 2, 0x1F8, 2, 8), RK_PINDRIVE(4, 2, 0x1F8, 3, 12), /* GPIO4D */ RK_PINDRIVE(4, 3, 0x1FC, 0, 2), RK_PINDRIVE(4, 3, 0x1FC, 1, 4), RK_PINDRIVE(4, 3, 0x1FC, 2, 8), RK_PINDRIVE(4, 3, 0x1FC, 3, 12), /* GPIO5B */ RK_PINDRIVE(5, 1, 0x204, 0, 2), RK_PINDRIVE(5, 1, 0x204, 1, 4), RK_PINDRIVE(5, 1, 0x204, 2, 8), RK_PINDRIVE(5, 1, 0x204, 3, 12), /* GPIO5C */ RK_PINDRIVE(5, 2, 0x208, 0, 2), RK_PINDRIVE(5, 2, 0x208, 1, 4), RK_PINDRIVE(5, 2, 0x208, 2, 8), RK_PINDRIVE(5, 2, 0x208, 3, 12), /* GPIO6A */ RK_PINDRIVE(6, 0, 0x210, 0, 2), RK_PINDRIVE(6, 0, 0x210, 1, 4), RK_PINDRIVE(6, 0, 0x210, 2, 8), RK_PINDRIVE(6, 0, 0x210, 3, 12), /* GPIO6B */ RK_PINDRIVE(6, 1, 0x214, 0, 2), RK_PINDRIVE(6, 1, 0x214, 1, 4), RK_PINDRIVE(6, 1, 0x214, 2, 8), RK_PINDRIVE(6, 1, 0x214, 3, 12), /* GPIO6C */ RK_PINDRIVE(6, 2, 0x218, 0, 2), RK_PINDRIVE(6, 2, 0x218, 1, 4), RK_PINDRIVE(6, 2, 0x218, 2, 8), RK_PINDRIVE(6, 2, 0x218, 3, 12), /* GPIO7A */ RK_PINDRIVE(7, 0, 0x220, 0, 2), RK_PINDRIVE(7, 0, 0x220, 1, 4), RK_PINDRIVE(7, 0, 0x220, 2, 8), RK_PINDRIVE(7, 0, 0x220, 3, 12), /* GPIO7B */ RK_PINDRIVE(7, 1, 0x224, 0, 2), RK_PINDRIVE(7, 1, 0x224, 1, 4), RK_PINDRIVE(7, 1, 0x224, 2, 8), RK_PINDRIVE(7, 1, 0x224, 3, 12), /* GPIO7C */ RK_PINDRIVE(7, 2, 0x228, 0, 2), RK_PINDRIVE(7, 2, 0x228, 1, 4), RK_PINDRIVE(7, 2, 0x228, 2, 8), RK_PINDRIVE(7, 2, 0x228, 3, 12), /* GPIO8A */ RK_PINDRIVE(8, 0, 0x230, 0, 2), RK_PINDRIVE(8, 0, 0x230, 1, 4), RK_PINDRIVE(8, 0, 0x230, 2, 8), RK_PINDRIVE(8, 0, 0x230, 3, 12), /* GPIO8B */ RK_PINDRIVE(8, 1, 0x234, 0, 2), RK_PINDRIVE(8, 1, 0x234, 1, 4), RK_PINDRIVE(8, 1, 0x234, 2, 8), RK_PINDRIVE(8, 1, 0x234, 3, 12), }; static uint32_t rk3288_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank == 0) return (0x064); /* PMU */ return (0x130); } static struct syscon * rk3288_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank == 0) return (sc->pmu); return (sc->grf); } static int rk3288_parse_bias(phandle_t node, int bank) { if (OF_hasprop(node, "bias-disable")) return (0); if (OF_hasprop(node, "bias-pull-up")) return (1); if (OF_hasprop(node, "bias-pull-down")) return (2); return (-1); } static int rk3288_resolv_bias_value(int bank, int bias) { int rv = 0; if (bias == 1) rv = GPIO_PIN_PULLUP; else if (bias == 2) rv = GPIO_PIN_PULLDOWN; return (rv); } static int rk3288_get_bias_value(int bank, int bias) { int rv = 0; if (bias & GPIO_PIN_PULLUP) rv = 1; else if (bias & GPIO_PIN_PULLDOWN) rv = 2; return (rv); } struct rk_pinctrl_conf rk3288_conf = { .iomux_conf = rk3288_iomux_bank, .iomux_nbanks = nitems(rk3288_iomux_bank), .pin_fixup = rk3288_pin_fixup, .npin_fixup = nitems(rk3288_pin_fixup), .pin_drive = rk3288_pin_drive, .npin_drive = nitems(rk3288_pin_drive), .gpio_bank = rk3288_gpio_bank, .ngpio_bank = nitems(rk3288_gpio_bank), .get_pd_offset = rk3288_get_pd_offset, .get_syscon = rk3288_get_syscon, .parse_bias = rk3288_parse_bias, .resolv_bias_value = rk3288_resolv_bias_value, .get_bias_value = rk3288_get_bias_value, }; static struct rk_pinctrl_gpio rk3328_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), }; static struct rk_pinctrl_bank rk3328_iomux_bank[] = { /* bank sub offs nbits */ RK_IOMUX(0, 0, 0x0000, 2), RK_IOMUX(0, 1, 0x0004, 2), RK_IOMUX(0, 2, 0x0008, 2), RK_IOMUX(0, 3, 0x000C, 2), RK_IOMUX(1, 0, 0x0010, 2), RK_IOMUX(1, 1, 0x0014, 2), RK_IOMUX(1, 2, 0x0018, 2), RK_IOMUX(1, 3, 0x001C, 2), RK_IOMUX(2, 0, 0x0020, 2), RK_IOMUX(2, 1, 0x0024, 3), RK_IOMUX(2, 2, 0x002c, 3), RK_IOMUX(2, 3, 0x0034, 2), RK_IOMUX(3, 0, 0x0038, 3), RK_IOMUX(3, 1, 0x0040, 3), RK_IOMUX(3, 2, 0x0048, 2), RK_IOMUX(3, 3, 0x004c, 2), }; static struct rk_pinctrl_pin_fixup rk3328_pin_fixup[] = { /* bank pin reg bit mask */ RK_PINFIX(2, 12, 0x24, 8, 0x300), RK_PINFIX(2, 15, 0x28, 0, 0x7), RK_PINFIX(2, 23, 0x30, 14, 0x6000), }; static struct rk_pinctrl_pin_drive rk3328_pin_drive[] = { /* bank sub offs val ma */ RK_PINDRIVE(0, 0, 0x200, 0, 2), RK_PINDRIVE(0, 0, 0x200, 1, 4), RK_PINDRIVE(0, 0, 0x200, 2, 8), RK_PINDRIVE(0, 0, 0x200, 3, 12), RK_PINDRIVE(0, 1, 0x204, 0, 2), RK_PINDRIVE(0, 1, 0x204, 1, 4), RK_PINDRIVE(0, 1, 0x204, 2, 8), RK_PINDRIVE(0, 1, 0x204, 3, 12), RK_PINDRIVE(0, 2, 0x208, 0, 2), RK_PINDRIVE(0, 2, 0x208, 1, 4), RK_PINDRIVE(0, 2, 0x208, 2, 8), RK_PINDRIVE(0, 2, 0x208, 3, 12), RK_PINDRIVE(0, 3, 0x20C, 0, 2), RK_PINDRIVE(0, 3, 0x20C, 1, 4), RK_PINDRIVE(0, 3, 0x20C, 2, 8), RK_PINDRIVE(0, 3, 0x20C, 3, 12), RK_PINDRIVE(1, 0, 0x210, 0, 2), RK_PINDRIVE(1, 0, 0x210, 1, 4), RK_PINDRIVE(1, 0, 0x210, 2, 8), RK_PINDRIVE(1, 0, 0x210, 3, 12), RK_PINDRIVE(1, 1, 0x214, 0, 2), RK_PINDRIVE(1, 1, 0x214, 1, 4), RK_PINDRIVE(1, 1, 0x214, 2, 8), RK_PINDRIVE(1, 1, 0x214, 3, 12), RK_PINDRIVE(1, 2, 0x218, 0, 2), RK_PINDRIVE(1, 2, 0x218, 1, 4), RK_PINDRIVE(1, 2, 0x218, 2, 8), RK_PINDRIVE(1, 2, 0x218, 3, 12), RK_PINDRIVE(1, 3, 0x21C, 0, 2), RK_PINDRIVE(1, 3, 0x21C, 1, 4), RK_PINDRIVE(1, 3, 0x21C, 2, 8), RK_PINDRIVE(1, 3, 0x21C, 3, 12), RK_PINDRIVE(2, 0, 0x220, 0, 2), RK_PINDRIVE(2, 0, 0x220, 1, 4), RK_PINDRIVE(2, 0, 0x220, 2, 8), RK_PINDRIVE(2, 0, 0x220, 3, 12), RK_PINDRIVE(2, 1, 0x224, 0, 2), RK_PINDRIVE(2, 1, 0x224, 1, 4), RK_PINDRIVE(2, 1, 0x224, 2, 8), RK_PINDRIVE(2, 1, 0x224, 3, 12), RK_PINDRIVE(2, 2, 0x228, 0, 2), RK_PINDRIVE(2, 2, 0x228, 1, 4), RK_PINDRIVE(2, 2, 0x228, 2, 8), RK_PINDRIVE(2, 2, 0x228, 3, 12), RK_PINDRIVE(2, 3, 0x22C, 0, 2), RK_PINDRIVE(2, 3, 0x22C, 1, 4), RK_PINDRIVE(2, 3, 0x22C, 2, 8), RK_PINDRIVE(2, 3, 0x22C, 3, 12), RK_PINDRIVE(3, 0, 0x230, 0, 2), RK_PINDRIVE(3, 0, 0x230, 1, 4), RK_PINDRIVE(3, 0, 0x230, 2, 8), RK_PINDRIVE(3, 0, 0x230, 3, 12), RK_PINDRIVE(3, 1, 0x234, 0, 2), RK_PINDRIVE(3, 1, 0x234, 1, 4), RK_PINDRIVE(3, 1, 0x234, 2, 8), RK_PINDRIVE(3, 1, 0x234, 3, 12), RK_PINDRIVE(3, 2, 0x238, 0, 2), RK_PINDRIVE(3, 2, 0x238, 1, 4), RK_PINDRIVE(3, 2, 0x238, 2, 8), RK_PINDRIVE(3, 2, 0x238, 3, 12), RK_PINDRIVE(3, 3, 0x23C, 0, 2), RK_PINDRIVE(3, 3, 0x23C, 1, 4), RK_PINDRIVE(3, 3, 0x23C, 2, 8), RK_PINDRIVE(3, 3, 0x23C, 3, 12), }; static uint32_t rk3328_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { return (0x100); } static struct syscon * rk3328_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { return (sc->grf); } struct rk_pinctrl_conf rk3328_conf = { .iomux_conf = rk3328_iomux_bank, .iomux_nbanks = nitems(rk3328_iomux_bank), .pin_fixup = rk3328_pin_fixup, .npin_fixup = nitems(rk3328_pin_fixup), .pin_drive = rk3328_pin_drive, .npin_drive = nitems(rk3328_pin_drive), .gpio_bank = rk3328_gpio_bank, .ngpio_bank = nitems(rk3328_gpio_bank), .get_pd_offset = rk3328_get_pd_offset, .get_syscon = rk3328_get_syscon, .parse_bias = rk3288_parse_bias, .resolv_bias_value = rk3288_resolv_bias_value, .get_bias_value = rk3288_get_bias_value, }; static struct rk_pinctrl_gpio rk3399_gpio_bank[] = { RK_GPIO(0, "gpio0"), RK_GPIO(1, "gpio1"), RK_GPIO(2, "gpio2"), RK_GPIO(3, "gpio3"), RK_GPIO(4, "gpio4"), }; static struct rk_pinctrl_bank rk3399_iomux_bank[] = { /* bank sub offs nbits */ RK_IOMUX(0, 0, 0x0000, 2), RK_IOMUX(0, 1, 0x0004, 2), RK_IOMUX(0, 2, 0x0008, 2), RK_IOMUX(0, 3, 0x000C, 2), RK_IOMUX(1, 0, 0x0010, 2), RK_IOMUX(1, 1, 0x0014, 2), RK_IOMUX(1, 2, 0x0018, 2), RK_IOMUX(1, 3, 0x001C, 2), RK_IOMUX(2, 0, 0xE000, 2), RK_IOMUX(2, 1, 0xE004, 2), RK_IOMUX(2, 2, 0xE008, 2), RK_IOMUX(2, 3, 0xE00C, 2), RK_IOMUX(3, 0, 0xE010, 2), RK_IOMUX(3, 1, 0xE014, 2), RK_IOMUX(3, 2, 0xE018, 2), RK_IOMUX(3, 3, 0xE01C, 2), RK_IOMUX(4, 0, 0xE020, 2), RK_IOMUX(4, 1, 0xE024, 2), RK_IOMUX(4, 2, 0xE028, 2), RK_IOMUX(4, 3, 0xE02C, 2), }; static struct rk_pinctrl_pin_fixup rk3399_pin_fixup[] = {}; static struct rk_pinctrl_pin_drive rk3399_pin_drive[] = { /* bank sub offs val ma */ /* GPIO0A */ RK_PINDRIVE(0, 0, 0x80, 0, 5), RK_PINDRIVE(0, 0, 0x80, 1, 10), RK_PINDRIVE(0, 0, 0x80, 2, 15), RK_PINDRIVE(0, 0, 0x80, 3, 20), /* GPIOB */ RK_PINDRIVE(0, 1, 0x88, 0, 5), RK_PINDRIVE(0, 1, 0x88, 1, 10), RK_PINDRIVE(0, 1, 0x88, 2, 15), RK_PINDRIVE(0, 1, 0x88, 3, 20), /* GPIO1A */ RK_PINDRIVE(1, 0, 0xA0, 0, 3), RK_PINDRIVE(1, 0, 0xA0, 1, 6), RK_PINDRIVE(1, 0, 0xA0, 2, 9), RK_PINDRIVE(1, 0, 0xA0, 3, 12), /* GPIO1B */ RK_PINDRIVE(1, 1, 0xA8, 0, 3), RK_PINDRIVE(1, 1, 0xA8, 1, 6), RK_PINDRIVE(1, 1, 0xA8, 2, 9), RK_PINDRIVE(1, 1, 0xA8, 3, 12), /* GPIO1C */ RK_PINDRIVE(1, 2, 0xB0, 0, 3), RK_PINDRIVE(1, 2, 0xB0, 1, 6), RK_PINDRIVE(1, 2, 0xB0, 2, 9), RK_PINDRIVE(1, 2, 0xB0, 3, 12), /* GPIO1D */ RK_PINDRIVE(1, 3, 0xB8, 0, 3), RK_PINDRIVE(1, 3, 0xB8, 1, 6), RK_PINDRIVE(1, 3, 0xB8, 2, 9), RK_PINDRIVE(1, 3, 0xB8, 3, 12), }; static uint32_t rk3399_get_pd_offset(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank < 2) return (0x40); return (0xE040); } static struct syscon * rk3399_get_syscon(struct rk_pinctrl_softc *sc, uint32_t bank) { if (bank < 2) return (sc->pmu); return (sc->grf); } static int rk3399_parse_bias(phandle_t node, int bank) { int pullup, pulldown; if (OF_hasprop(node, "bias-disable")) return (0); switch (bank) { case 0: case 2: pullup = 3; pulldown = 1; break; case 1: case 3: case 4: pullup = 1; pulldown = 2; break; } if (OF_hasprop(node, "bias-pull-up")) return (pullup); if (OF_hasprop(node, "bias-pull-down")) return (pulldown); return (-1); } static int rk3399_resolv_bias_value(int bank, int bias) { int rv = 0; switch (bank) { case 0: case 2: if (bias == 3) rv = GPIO_PIN_PULLUP; else if (bias == 1) rv = GPIO_PIN_PULLDOWN; break; case 1: case 3: case 4: if (bias == 1) rv = GPIO_PIN_PULLUP; else if (bias == 2) rv = GPIO_PIN_PULLDOWN; break; } return (rv); } static int rk3399_get_bias_value(int bank, int bias) { int rv = 0; switch (bank) { case 0: case 2: if (bias & GPIO_PIN_PULLUP) rv = 3; else if (bias & GPIO_PIN_PULLDOWN) rv = 1; break; case 1: case 3: case 4: if (bias & GPIO_PIN_PULLUP) rv = 1; else if (bias & GPIO_PIN_PULLDOWN) rv = 2; break; } return (rv); } struct rk_pinctrl_conf rk3399_conf = { .iomux_conf = rk3399_iomux_bank, .iomux_nbanks = nitems(rk3399_iomux_bank), .pin_fixup = rk3399_pin_fixup, .npin_fixup = nitems(rk3399_pin_fixup), .pin_drive = rk3399_pin_drive, .npin_drive = nitems(rk3399_pin_drive), .gpio_bank = rk3399_gpio_bank, .ngpio_bank = nitems(rk3399_gpio_bank), .get_pd_offset = rk3399_get_pd_offset, .get_syscon = rk3399_get_syscon, .parse_bias = rk3399_parse_bias, .resolv_bias_value = rk3399_resolv_bias_value, .get_bias_value = rk3399_get_bias_value, }; static struct ofw_compat_data compat_data[] = { {"rockchip,rk3288-pinctrl", (uintptr_t)&rk3288_conf}, {"rockchip,rk3328-pinctrl", (uintptr_t)&rk3328_conf}, {"rockchip,rk3399-pinctrl", (uintptr_t)&rk3399_conf}, {NULL, 0} }; static int rk_pinctrl_parse_drive(struct rk_pinctrl_softc *sc, phandle_t node, uint32_t bank, uint32_t subbank, uint32_t *drive, uint32_t *offset) { uint32_t value; int i; if (OF_getencprop(node, "drive-strength", &value, sizeof(value)) != 0) return (-1); /* Map to the correct drive value */ for (i = 0; i < sc->conf->npin_drive; i++) { if (sc->conf->pin_drive[i].bank != bank && sc->conf->pin_drive[i].subbank != subbank) continue; if (sc->conf->pin_drive[i].ma == value) { *drive = sc->conf->pin_drive[i].value; return (0); } } return (-1); } static void rk_pinctrl_get_fixup(struct rk_pinctrl_softc *sc, uint32_t bank, uint32_t pin, uint32_t *reg, uint32_t *mask, uint32_t *bit) { int i; for (i = 0; i < sc->conf->npin_fixup; i++) if (sc->conf->pin_fixup[i].bank == bank && sc->conf->pin_fixup[i].pin == pin) { *reg = sc->conf->pin_fixup[i].reg; *mask = sc->conf->pin_fixup[i].mask; *bit = sc->conf->pin_fixup[i].bit; return; } } static int rk_pinctrl_handle_io(struct rk_pinctrl_softc *sc, phandle_t node, uint32_t bank, uint32_t pin) { bool have_cfg, have_direction, have_value; uint32_t direction_value, pin_value; struct rk_pinctrl_gpio *gpio; int i, rv; have_cfg = false; have_direction = false; have_value = false; /* Get (subset of) GPIO pin properties. */ if (OF_hasprop(node, "output-disable")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_INPUT; } if (OF_hasprop(node, "output-enable")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; } if (OF_hasprop(node, "output-low")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; have_value = true; pin_value = 0; } if (OF_hasprop(node, "output-high")) { have_cfg = true; have_direction = true; direction_value = GPIO_PIN_OUTPUT; have_value = true; pin_value = 1; } if (!have_cfg) return (0); /* Find gpio */ gpio = NULL; for(i = 0; i < sc->conf->ngpio_bank; i++) { if (bank == sc->conf->gpio_bank[i].bank) { gpio = sc->conf->gpio_bank + i; break; } } if (gpio == NULL) { device_printf(sc->dev, "Cannot find GPIO bank %d\n", bank); return (ENXIO); } if (gpio->gpio_dev == NULL) { device_printf(sc->dev, "No GPIO subdevice found for bank %d\n", bank); return (ENXIO); } rv = 0; if (have_value) { rv = GPIO_PIN_SET(gpio->gpio_dev, pin, pin_value); if (rv != 0) { device_printf(sc->dev, "Cannot set GPIO value: %d\n", rv); return (rv); } } if (have_direction) { rv = GPIO_PIN_SETFLAGS(gpio->gpio_dev, pin, direction_value); if (rv != 0) { device_printf(sc->dev, "Cannot set GPIO direction: %d\n", rv); return (rv); } } return (0); } static void rk_pinctrl_configure_pin(struct rk_pinctrl_softc *sc, uint32_t *pindata) { phandle_t pin_conf; struct syscon *syscon; uint32_t bank, subbank, pin, function, bias; uint32_t bit, mask, reg, drive; int i, rv; bank = pindata[0]; pin = pindata[1]; function = pindata[2]; pin_conf = OF_node_from_xref(pindata[3]); subbank = pin / 8; for (i = 0; i < sc->conf->iomux_nbanks; i++) if (sc->conf->iomux_conf[i].bank == bank && sc->conf->iomux_conf[i].subbank == subbank) break; if (i == sc->conf->iomux_nbanks) { device_printf(sc->dev, "Unknown pin %d in bank %d\n", pin, bank); return; } /* Find syscon */ syscon = sc->conf->get_syscon(sc, bank); /* Setup GPIO properties first */ rv = rk_pinctrl_handle_io(sc, pin_conf, bank, pin); /* Then pin pull-up/down */ bias = sc->conf->parse_bias(pin_conf, bank); if (bias >= 0) { reg = sc->conf->get_pd_offset(sc, bank); reg += bank * 0x10 + ((pin / 8) * 0x4); bit = (pin % 8) * 2; mask = (0x3 << bit); SYSCON_MODIFY_4(syscon, reg, mask, bias << bit | (mask << 16)); } /* Then drive strength */ rv = rk_pinctrl_parse_drive(sc, pin_conf, bank, subbank, &drive, ®); if (rv == 0) { bit = (pin % 8) * 2; mask = (0x3 << bit); SYSCON_MODIFY_4(syscon, reg, mask, drive << bit | (mask << 16)); } /* Finally set the pin function */ reg = sc->conf->iomux_conf[i].offset; switch (sc->conf->iomux_conf[i].nbits) { case 4: if ((pin % 8) >= 4) reg += 0x4; bit = (pin % 4) * 4; mask = (0xF << bit); break; case 3: if ((pin % 8) >= 5) reg += 4; bit = (pin % 8 % 5) * 3; mask = (0x7 << bit); break; case 2: bit = (pin % 8) * 2; mask = (0x3 << bit); break; default: device_printf(sc->dev, "Unknown pin stride width %d in bank %d\n", sc->conf->iomux_conf[i].nbits, bank); return; } rk_pinctrl_get_fixup(sc, bank, pin, ®, &mask, &bit); /* * NOTE: not all syscon registers uses hi-word write mask, thus * register modify method should be used. * XXXX We should not pass write mask to syscon register * without hi-word write mask. */ SYSCON_MODIFY_4(syscon, reg, mask, function << bit | (mask << 16)); } static int rk_pinctrl_configure_pins(device_t dev, phandle_t cfgxref) { struct rk_pinctrl_softc *sc; phandle_t node; uint32_t *pins; int i, npins; sc = device_get_softc(dev); node = OF_node_from_xref(cfgxref); npins = OF_getencprop_alloc_multi(node, "rockchip,pins", sizeof(*pins), (void **)&pins); if (npins <= 0) return (ENOENT); for (i = 0; i != npins; i += 4) rk_pinctrl_configure_pin(sc, pins + i); return (0); } static int rk_pinctrl_is_gpio_locked(struct rk_pinctrl_softc *sc, struct syscon *syscon, int bank, uint32_t pin, bool *is_gpio) { uint32_t subbank, bit, mask, reg; uint32_t pinfunc; int i; RK_PINCTRL_LOCK_ASSERT(sc); subbank = pin / 8; *is_gpio = false; for (i = 0; i < sc->conf->iomux_nbanks; i++) if (sc->conf->iomux_conf[i].bank == bank && sc->conf->iomux_conf[i].subbank == subbank) break; if (i == sc->conf->iomux_nbanks) { device_printf(sc->dev, "Unknown pin %d in bank %d\n", pin, bank); return (EINVAL); } syscon = sc->conf->get_syscon(sc, bank); /* Parse pin function */ reg = sc->conf->iomux_conf[i].offset; switch (sc->conf->iomux_conf[i].nbits) { case 4: if ((pin % 8) >= 4) reg += 0x4; bit = (pin % 4) * 4; mask = (0xF << bit); break; case 3: if ((pin % 8) >= 5) reg += 4; bit = (pin % 8 % 5) * 3; mask = (0x7 << bit); break; case 2: bit = (pin % 8) * 2; mask = (0x3 << bit); break; default: device_printf(sc->dev, "Unknown pin stride width %d in bank %d\n", sc->conf->iomux_conf[i].nbits, bank); return (EINVAL); } rk_pinctrl_get_fixup(sc, bank, pin, ®, &mask, &bit); reg = SYSCON_READ_4(syscon, reg); pinfunc = (reg & mask) >> bit; /* Test if the pin is in gpio mode */ if (pinfunc == 0) *is_gpio = true; return (0); } static int rk_pinctrl_get_bank(struct rk_pinctrl_softc *sc, device_t gpio, int *bank) { int i; for (i = 0; i < sc->conf->ngpio_bank; i++) { if (sc->conf->gpio_bank[i].gpio_dev == gpio) break; } if (i == sc->conf->ngpio_bank) return (EINVAL); *bank = i; return (0); } static int rk_pinctrl_is_gpio(device_t pinctrl, device_t gpio, uint32_t pin, bool *is_gpio) { struct rk_pinctrl_softc *sc; struct syscon *syscon; int bank; int rv; sc = device_get_softc(pinctrl); RK_PINCTRL_LOCK(sc); rv = rk_pinctrl_get_bank(sc, gpio, &bank); if (rv != 0) goto done; syscon = sc->conf->get_syscon(sc, bank); rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, is_gpio); done: RK_PINCTRL_UNLOCK(sc); return (rv); } static int rk_pinctrl_get_flags(device_t pinctrl, device_t gpio, uint32_t pin, uint32_t *flags) { struct rk_pinctrl_softc *sc; struct syscon *syscon; - uint32_t reg, mask, bit; + uint32_t reg, bit; uint32_t bias; int bank; int rv = 0; bool is_gpio; sc = device_get_softc(pinctrl); RK_PINCTRL_LOCK(sc); rv = rk_pinctrl_get_bank(sc, gpio, &bank); if (rv != 0) goto done; syscon = sc->conf->get_syscon(sc, bank); rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio); if (rv != 0) goto done; if (!is_gpio) { rv = EINVAL; goto done; } /* Get the pullup/pulldown configuration */ reg = sc->conf->get_pd_offset(sc, bank); reg += bank * 0x10 + ((pin / 8) * 0x4); bit = (pin % 8) * 2; - mask = (0x3 << bit) << 16; reg = SYSCON_READ_4(syscon, reg); reg = (reg >> bit) & 0x3; bias = sc->conf->resolv_bias_value(bank, reg); *flags = bias; done: RK_PINCTRL_UNLOCK(sc); return (rv); } static int rk_pinctrl_set_flags(device_t pinctrl, device_t gpio, uint32_t pin, uint32_t flags) { struct rk_pinctrl_softc *sc; struct syscon *syscon; uint32_t bit, mask, reg; uint32_t bias; int bank; int rv = 0; bool is_gpio; sc = device_get_softc(pinctrl); RK_PINCTRL_LOCK(sc); rv = rk_pinctrl_get_bank(sc, gpio, &bank); if (rv != 0) goto done; syscon = sc->conf->get_syscon(sc, bank); rv = rk_pinctrl_is_gpio_locked(sc, syscon, bank, pin, &is_gpio); if (rv != 0) goto done; if (!is_gpio) { rv = EINVAL; goto done; } /* Get the pullup/pulldown configuration */ reg = sc->conf->get_pd_offset(sc, bank); reg += bank * 0x10 + ((pin / 8) * 0x4); bit = (pin % 8) * 2; mask = (0x3 << bit); bias = sc->conf->get_bias_value(bank, flags); SYSCON_MODIFY_4(syscon, reg, mask, bias << bit | (mask << 16)); done: RK_PINCTRL_UNLOCK(sc); return (rv); } static int rk_pinctrl_register_gpio(struct rk_pinctrl_softc *sc, char *gpio_name, device_t gpio_dev) { int i; for(i = 0; i < sc->conf->ngpio_bank; i++) { if (strcmp(gpio_name, sc->conf->gpio_bank[i].gpio_name) != 0) continue; sc->conf->gpio_bank[i].gpio_dev = gpio_dev; return(0); } return (ENXIO); } static int rk_pinctrl_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) return (ENXIO); device_set_desc(dev, "RockChip Pinctrl controller"); return (BUS_PROBE_DEFAULT); } static int rk_pinctrl_attach(device_t dev) { struct rk_pinctrl_softc *sc; phandle_t node; device_t cdev; char *gpio_name, *eptr; int rv; sc = device_get_softc(dev); sc->dev = dev; node = ofw_bus_get_node(dev); if (OF_hasprop(node, "rockchip,grf") && syscon_get_by_ofw_property(dev, node, "rockchip,grf", &sc->grf) != 0) { device_printf(dev, "cannot get grf driver handle\n"); return (ENXIO); } /* RK3399,RK3288 has banks in PMU. RK3328 does not have a PMU. */ if (ofw_bus_node_is_compatible(node, "rockchip,rk3399-pinctrl") || ofw_bus_node_is_compatible(node, "rockchip,rk3288-pinctrl")) { if (OF_hasprop(node, "rockchip,pmu") && syscon_get_by_ofw_property(dev, node, "rockchip,pmu", &sc->pmu) != 0) { device_printf(dev, "cannot get pmu driver handle\n"); return (ENXIO); } } mtx_init(&sc->mtx, "rk pinctrl", "pinctrl", MTX_SPIN); sc->conf = (struct rk_pinctrl_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; fdt_pinctrl_register(dev, "rockchip,pins"); simplebus_init(dev, node); bus_generic_probe(dev); /* Attach child devices */ for (node = OF_child(node); node > 0; node = OF_peer(node)) { if (!ofw_bus_node_is_compatible(node, "rockchip,gpio-bank")) continue; rv = OF_getprop_alloc(node, "name", (void **)&gpio_name); if (rv <= 0) { device_printf(sc->dev, "Cannot GPIO subdevice name.\n"); continue; } cdev = simplebus_add_device(dev, node, 0, NULL, -1, NULL); if (cdev == NULL) { device_printf(dev, " Cannot add GPIO subdevice: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } rv = device_probe_and_attach(cdev); if (rv != 0) { device_printf(sc->dev, "Cannot attach GPIO subdevice: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } /* Grep device name from name property */ eptr = gpio_name; strsep(&eptr, "@"); if (gpio_name == eptr) { device_printf(sc->dev, "Unrecognized format of GPIO subdevice name: %s\n", gpio_name); OF_prop_free(gpio_name); continue; } rv = rk_pinctrl_register_gpio(sc, gpio_name, cdev); if (rv != 0) { device_printf(sc->dev, "Cannot register GPIO subdevice %s: %d\n", gpio_name, rv); OF_prop_free(gpio_name); continue; } OF_prop_free(gpio_name); } fdt_pinctrl_configure_tree(dev); return (bus_generic_attach(dev)); } static int rk_pinctrl_detach(device_t dev) { return (EBUSY); } static device_method_t rk_pinctrl_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk_pinctrl_probe), DEVMETHOD(device_attach, rk_pinctrl_attach), DEVMETHOD(device_detach, rk_pinctrl_detach), /* fdt_pinctrl interface */ DEVMETHOD(fdt_pinctrl_configure, rk_pinctrl_configure_pins), DEVMETHOD(fdt_pinctrl_is_gpio, rk_pinctrl_is_gpio), DEVMETHOD(fdt_pinctrl_get_flags, rk_pinctrl_get_flags), DEVMETHOD(fdt_pinctrl_set_flags, rk_pinctrl_set_flags), DEVMETHOD_END }; static devclass_t rk_pinctrl_devclass; DEFINE_CLASS_1(rk_pinctrl, rk_pinctrl_driver, rk_pinctrl_methods, sizeof(struct rk_pinctrl_softc), simplebus_driver); EARLY_DRIVER_MODULE(rk_pinctrl, simplebus, rk_pinctrl_driver, rk_pinctrl_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE); MODULE_VERSION(rk_pinctrl, 1);