Module Name: src Committed By: jmcneill Date: Mon Oct 28 21:16:47 UTC 2019
Modified Files: src/sys/arch/arm/ti: files.ti Added Files: src/sys/arch/arm/ti: ti_cpufreq.c ti_div_clock.c ti_dpll_clock.c ti_mux_clock.c Log Message: Add AM335x DVFS support. To generate a diff of this commit: cvs rdiff -u -r1.11 -r1.12 src/sys/arch/arm/ti/files.ti cvs rdiff -u -r0 -r1.1 src/sys/arch/arm/ti/ti_cpufreq.c \ src/sys/arch/arm/ti/ti_div_clock.c src/sys/arch/arm/ti/ti_dpll_clock.c \ src/sys/arch/arm/ti/ti_mux_clock.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/arch/arm/ti/files.ti diff -u src/sys/arch/arm/ti/files.ti:1.11 src/sys/arch/arm/ti/files.ti:1.12 --- src/sys/arch/arm/ti/files.ti:1.11 Sun Oct 27 19:11:07 2019 +++ src/sys/arch/arm/ti/files.ti Mon Oct 28 21:16:47 2019 @@ -1,7 +1,8 @@ -# $NetBSD: files.ti,v 1.11 2019/10/27 19:11:07 jmcneill Exp $ +# $NetBSD: files.ti,v 1.12 2019/10/28 21:16:47 jmcneill Exp $ # file arch/arm/ti/ti_platform.c soc_ti +file arch/arm/ti/ti_cpufreq.c soc_ti # Interrupt controller device omapintc: pic, pic_splfuncs @@ -18,10 +19,23 @@ define ti_prcm file arch/arm/ti/ti_prcm.c ti_prcm # PRCM (AM3xxx) -device am3prcm: ti_prcm +device am3prcm { } : fdt, ti_prcm attach am3prcm at fdt with am3_prcm file arch/arm/ti/am3_prcm.c am3_prcm +# Clocks +device timuxclk +attach timuxclk at fdt with ti_mux_clock +file arch/arm/ti/ti_mux_clock.c ti_mux_clock + +device tidivclk +attach tidivclk at fdt with ti_div_clock +file arch/arm/ti/ti_div_clock.c ti_div_clock + +device tidpllclk +attach tidpllclk at fdt with ti_dpll_clock +file arch/arm/ti/ti_dpll_clock.c ti_dpll_clock + # UART attach com at fdt with ti_com: ti_prcm file arch/arm/ti/ti_com.c ti_com needs-flag Added files: Index: src/sys/arch/arm/ti/ti_cpufreq.c diff -u /dev/null src/sys/arch/arm/ti/ti_cpufreq.c:1.1 --- /dev/null Mon Oct 28 21:16:47 2019 +++ src/sys/arch/arm/ti/ti_cpufreq.c Mon Oct 28 21:16:47 2019 @@ -0,0 +1,113 @@ +/* $NetBSD: ti_cpufreq.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */ + +/*- + * Copyright (c) 2019 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. + */ + +#include "opt_soc.h" + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ti_cpufreq.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/bus.h> + +#include <dev/fdt/fdtvar.h> +#include <dev/fdt/syscon.h> + +static bool ti_opp_probed = false; +static bool (*ti_opp_supportedfn)(const int, const int); +static struct syscon *ti_opp_syscon; + +#ifdef SOC_TI_AM335X + +#define AM33XX_REV_OFFSET 0x0600 +#define AM33XX_REV_MASK 0xf0000000 +#define AM33XX_EFUSE_OFFSET 0x07fc +#define AM33XX_EFUSE_MASK 0x00001fff +#define AM33XX_EFUSE_DEFAULT 0x1e2f + +static const char * const am33xx_compatible[] = { "ti,am33xx", NULL }; + +static bool +am33xx_opp_supported(const int opp_table, const int opp_node) +{ + const u_int *supported_hw; + uint32_t efuse, rev; + int len; + + syscon_lock(ti_opp_syscon); + rev = __SHIFTOUT(syscon_read_4(ti_opp_syscon, AM33XX_REV_OFFSET), AM33XX_REV_MASK); + efuse = __SHIFTOUT(syscon_read_4(ti_opp_syscon, AM33XX_EFUSE_OFFSET), AM33XX_EFUSE_MASK); + syscon_unlock(ti_opp_syscon); + + if (efuse == 0) + efuse = AM33XX_EFUSE_DEFAULT; + efuse = ~efuse; + + supported_hw = fdtbus_get_prop(opp_node, "opp-supported-hw", &len); + if (len != 8) + return false; + + if ((rev & be32toh(supported_hw[0])) == 0) + return false; + + if ((efuse & be32toh(supported_hw[1])) == 0) + return false; + + return true; +} +#endif + +static void +ti_opp_probe(const int opp_table) +{ + if (ti_opp_probed) + return; + ti_opp_probed = true; + + ti_opp_syscon = fdtbus_syscon_acquire(opp_table, "syscon"); + +#ifdef SOC_TI_AM335X + if (ti_opp_syscon && of_match_compatible(OF_finddevice("/"), am33xx_compatible)) + ti_opp_supportedfn = am33xx_opp_supported; +#endif +} + +static bool +ti_opp_supported(const int opp_table, const int opp_node) +{ + ti_opp_probe(opp_table); + + if (!of_hasprop(opp_node, "opp-supported-hw")) + return true; + + return ti_opp_supportedfn ? ti_opp_supportedfn(opp_table, opp_node) : false; +} + +FDT_OPP(ti, "operating-points-v2-ti-cpu", ti_opp_supported); Index: src/sys/arch/arm/ti/ti_div_clock.c diff -u /dev/null src/sys/arch/arm/ti/ti_div_clock.c:1.1 --- /dev/null Mon Oct 28 21:16:47 2019 +++ src/sys/arch/arm/ti/ti_div_clock.c Mon Oct 28 21:16:47 2019 @@ -0,0 +1,202 @@ +/* $NetBSD: ti_div_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */ + +/*- + * Copyright (c) 2019 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. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ti_div_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <dev/fdt/fdtvar.h> + +static const char * const compatible[] = { + "ti,divider-clock", + NULL +}; + +static int ti_div_clock_match(device_t, cfdata_t, void *); +static void ti_div_clock_attach(device_t, device_t, void *); + +static struct clk *ti_div_clock_decode(device_t, int, const void *, size_t); + +static const struct fdtbus_clock_controller_func ti_div_clock_fdt_funcs = { + .decode = ti_div_clock_decode +}; + +static struct clk *ti_div_clock_get(void *, const char *); +static void ti_div_clock_put(void *, struct clk *); +static u_int ti_div_clock_get_rate(void *, struct clk *); +static struct clk *ti_div_clock_get_parent(void *, struct clk *); + +static const struct clk_funcs ti_div_clock_clk_funcs = { + .get = ti_div_clock_get, + .put = ti_div_clock_put, + .get_rate = ti_div_clock_get_rate, + .get_parent = ti_div_clock_get_parent, +}; + +struct ti_div_clock_softc { + device_t sc_dev; + int sc_phandle; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + + struct clk_domain sc_clkdom; + struct clk sc_clk; +}; + +#define RD4(sc) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, 0) +#define WR4(sc, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, 0, (val)) + +CFATTACH_DECL_NEW(ti_div_clock, sizeof(struct ti_div_clock_softc), + ti_div_clock_match, ti_div_clock_attach, NULL, NULL); + +static int +ti_div_clock_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +ti_div_clock_attach(device_t parent, device_t self, void *aux) +{ + struct ti_div_clock_softc * const sc = device_private(self); + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr, base_addr; + + const int prcm_phandle = OF_parent(OF_parent(phandle)); + if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0 || + fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, base_addr + addr, 4, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &ti_div_clock_clk_funcs; + sc->sc_clkdom.priv = sc; + + sc->sc_clk.domain = &sc->sc_clkdom; + sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name); + clk_attach(&sc->sc_clk); + + aprint_naive("\n"); + aprint_normal(": TI divider clock (%s)\n", sc->sc_clk.name); + + fdtbus_register_clock_controller(self, phandle, &ti_div_clock_fdt_funcs); +} + +static struct clk * +ti_div_clock_decode(device_t dev, int cc_phandle, const void *data, + size_t len) +{ + struct ti_div_clock_softc * const sc = device_private(dev); + + return &sc->sc_clk; +} + +static struct clk * +ti_div_clock_get(void *priv, const char *name) +{ + struct ti_div_clock_softc * const sc = priv; + + return &sc->sc_clk; +} + +static void +ti_div_clock_put(void *priv, struct clk *clk) +{ +} + +static u_int +ti_div_clock_get_rate(void *priv, struct clk *clk) +{ + struct ti_div_clock_softc * const sc = priv; + struct clk *clk_parent = clk_get_parent(clk); + uint64_t parent_rate; + uint32_t val, max_val, mask; + u_int div, max_div, bit_shift; + + if (clk_parent == NULL) + return 0; + + if (of_hasprop(sc->sc_phandle, "ti,index-power-of-two")) + return EINVAL; /* not supported yet */ + + const int start_index = of_hasprop(sc->sc_phandle, "ti,index-starts-at-one") ? 1 : 0; + + if (of_getprop_uint32(sc->sc_phandle, "ti,bit-shift", &bit_shift) != 0) + bit_shift = 0; + + if (of_getprop_uint32(sc->sc_phandle, "ti,max-div", &max_div) == 0) { + max_val = start_index + max_div; + while (!powerof2(max_val)) + max_val++; + mask = (max_val - 1) << bit_shift; + } else { + /* + * The bindings allow for this, but it is not yet supported + * by this driver. + */ + return EINVAL; + } + + val = RD4(sc); + div = __SHIFTOUT(val, mask) + (start_index ? 0 : 1); + if (div == 0) + return EINVAL; + + parent_rate = clk_get_rate(clk_parent); + + return (u_int)(parent_rate / div); +} + +static struct clk * +ti_div_clock_get_parent(void *priv, struct clk *clk) +{ + struct ti_div_clock_softc * const sc = priv; + + return fdtbus_clock_get_index(sc->sc_phandle, 0); +} Index: src/sys/arch/arm/ti/ti_dpll_clock.c diff -u /dev/null src/sys/arch/arm/ti/ti_dpll_clock.c:1.1 --- /dev/null Mon Oct 28 21:16:48 2019 +++ src/sys/arch/arm/ti/ti_dpll_clock.c Mon Oct 28 21:16:47 2019 @@ -0,0 +1,252 @@ +/* $NetBSD: ti_dpll_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */ + +/*- + * Copyright (c) 2019 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. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ti_dpll_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <dev/fdt/fdtvar.h> + +/* CM_IDLEST_DPLL_MPU */ +#define ST_MN_BYPASS __BIT(8) +#define ST_DPLL_CLK __BIT(0) + +/* CM_CLKSEL_DPLL_MPU */ +#define DPLL_BYP_CLKSEL __BIT(23) +#define DPLL_MULT __BITS(18,8) +#define DPLL_DIV __BITS(6,0) + +/* CM_CLKMODE_DPLL_MPU */ +#define DPLL_EN __BITS(2,0) +#define DPLL_EN_NM_BYPASS 4 +#define DPLL_EN_LOCK 7 + +static const char * const compatible[] = { + "ti,am3-dpll-clock", + NULL +}; + +static int ti_dpll_clock_match(device_t, cfdata_t, void *); +static void ti_dpll_clock_attach(device_t, device_t, void *); + +static struct clk *ti_dpll_clock_decode(device_t, int, const void *, size_t); + +static const struct fdtbus_clock_controller_func ti_dpll_clock_fdt_funcs = { + .decode = ti_dpll_clock_decode +}; + +static struct clk *ti_dpll_clock_get(void *, const char *); +static void ti_dpll_clock_put(void *, struct clk *); +static int ti_dpll_clock_set_rate(void *, struct clk *, u_int); +static u_int ti_dpll_clock_get_rate(void *, struct clk *); +static struct clk *ti_dpll_clock_get_parent(void *, struct clk *); + +static const struct clk_funcs ti_dpll_clock_clk_funcs = { + .get = ti_dpll_clock_get, + .put = ti_dpll_clock_put, + .set_rate = ti_dpll_clock_set_rate, + .get_rate = ti_dpll_clock_get_rate, + .get_parent = ti_dpll_clock_get_parent, +}; + +enum { + REG_CONTROL, + REG_IDLEST, + REG_MULT_DIV1, + NREG +}; + +struct ti_dpll_clock_softc { + device_t sc_dev; + int sc_phandle; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh[NREG]; + + struct clk_domain sc_clkdom; + struct clk sc_clk; +}; + +#define RD4(sc, space) \ + bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0) +#define WR4(sc, space, val) \ + bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh[(space)], 0, (val)) + +CFATTACH_DECL_NEW(ti_dpll_clock, sizeof(struct ti_dpll_clock_softc), + ti_dpll_clock_match, ti_dpll_clock_attach, NULL, NULL); + +static int +ti_dpll_clock_match(device_t parent, cfdata_t cf, void *aux) +{ + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +ti_dpll_clock_attach(device_t parent, device_t self, void *aux) +{ + struct ti_dpll_clock_softc * const sc = device_private(self); + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr[NREG], base_addr; + u_int n; + + const int prcm_phandle = OF_parent(OF_parent(phandle)); + if (fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) { + aprint_error(": couldn't get prcm registers\n"); + return; + } + + for (n = 0; n < NREG; n++) { + if (fdtbus_get_reg(phandle, n, &addr[n], NULL) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + } + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_bst = faa->faa_bst; + for (n = 0; n < NREG; n++) { + if (bus_space_map(sc->sc_bst, base_addr + addr[n], 4, 0, &sc->sc_bsh[n]) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + } + + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &ti_dpll_clock_clk_funcs; + sc->sc_clkdom.priv = sc; + + sc->sc_clk.domain = &sc->sc_clkdom; + sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name); + clk_attach(&sc->sc_clk); + + aprint_naive("\n"); + aprint_normal(": TI DPLL clock (%s)\n", sc->sc_clk.name); + + fdtbus_register_clock_controller(self, phandle, &ti_dpll_clock_fdt_funcs); +} + +static struct clk * +ti_dpll_clock_decode(device_t dev, int cc_phandle, const void *data, + size_t len) +{ + struct ti_dpll_clock_softc * const sc = device_private(dev); + + return &sc->sc_clk; +} + +static struct clk * +ti_dpll_clock_get(void *priv, const char *name) +{ + struct ti_dpll_clock_softc * const sc = priv; + + return &sc->sc_clk; +} + +static void +ti_dpll_clock_put(void *priv, struct clk *clk) +{ +} + +static u_int +ti_dpll_clock_get_rate(void *priv, struct clk *clk) +{ + struct ti_dpll_clock_softc * const sc = priv; + struct clk *clk_parent = clk_get_parent(clk); + uint32_t val; + uint64_t parent_rate; + + if (clk_parent == NULL) + return 0; + + val = RD4(sc, REG_MULT_DIV1); + const u_int mult = __SHIFTOUT(val, DPLL_MULT); + const u_int div = __SHIFTOUT(val, DPLL_DIV) + 1; + + parent_rate = clk_get_rate(clk_parent); + + return (u_int)((mult * parent_rate) / div); +} + +static int +ti_dpll_clock_set_rate(void *priv, struct clk *clk, u_int rate) +{ + struct ti_dpll_clock_softc * const sc = priv; + struct clk *clk_parent = clk_get_parent(clk); + uint64_t parent_rate; + uint32_t control, mult_div1; + + if (clk_parent == NULL) + return ENXIO; + + parent_rate = clk_get_rate(clk_parent); + + const u_int div = (parent_rate / 1000000) - 1; + const u_int mult = rate / (parent_rate / (div + 1)); + if (mult < 2 || mult > 2047) + return EINVAL; + + control = RD4(sc, REG_CONTROL); + control &= ~DPLL_EN; + control |= __SHIFTIN(DPLL_EN_LOCK, DPLL_EN); + WR4(sc, REG_CONTROL, control); + + while ((RD4(sc, REG_IDLEST) & DPLL_EN_NM_BYPASS) != 0) + ; + + mult_div1 = __SHIFTIN(mult, DPLL_MULT); + mult_div1 |= __SHIFTIN(div, DPLL_DIV); + WR4(sc, REG_MULT_DIV1, mult_div1); + + control &= ~DPLL_EN; + control |= __SHIFTIN(DPLL_EN_LOCK, DPLL_EN); + WR4(sc, REG_CONTROL, control); + + while ((RD4(sc, REG_IDLEST) & ST_DPLL_CLK) != 0) + ; + + return 0; +} + +static struct clk * +ti_dpll_clock_get_parent(void *priv, struct clk *clk) +{ + struct ti_dpll_clock_softc * const sc = priv; + + /* XXX assume ref clk */ + return fdtbus_clock_get_index(sc->sc_phandle, 0); +} Index: src/sys/arch/arm/ti/ti_mux_clock.c diff -u /dev/null src/sys/arch/arm/ti/ti_mux_clock.c:1.1 --- /dev/null Mon Oct 28 21:16:48 2019 +++ src/sys/arch/arm/ti/ti_mux_clock.c Mon Oct 28 21:16:47 2019 @@ -0,0 +1,211 @@ +/* $NetBSD: ti_mux_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $ */ + +/*- + * Copyright (c) 2019 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. + */ + +#include <sys/cdefs.h> +__KERNEL_RCSID(0, "$NetBSD: ti_mux_clock.c,v 1.1 2019/10/28 21:16:47 jmcneill Exp $"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kmem.h> +#include <sys/bus.h> + +#include <dev/clk/clk_backend.h> + +#include <dev/fdt/fdtvar.h> + +static int ti_mux_clock_match(device_t, cfdata_t, void *); +static void ti_mux_clock_attach(device_t, device_t, void *); + +static struct clk *ti_mux_clock_decode(device_t, int, const void *, size_t); + +static const struct fdtbus_clock_controller_func ti_mux_clock_fdt_funcs = { + .decode = ti_mux_clock_decode +}; + +static struct clk *ti_mux_clock_get(void *, const char *); +static void ti_mux_clock_put(void *, struct clk *); +static u_int ti_mux_clock_get_rate(void *, struct clk *); +static struct clk *ti_mux_clock_get_parent(void *, struct clk *); +static int ti_mux_clock_set_parent(void *, struct clk *, struct clk *); + +static const struct clk_funcs ti_mux_clock_clk_funcs = { + .get = ti_mux_clock_get, + .put = ti_mux_clock_put, + .get_rate = ti_mux_clock_get_rate, + .get_parent = ti_mux_clock_get_parent, + .set_parent = ti_mux_clock_set_parent, +}; + +struct ti_mux_clock_softc { + device_t sc_dev; + int sc_phandle; + bus_space_tag_t sc_bst; + bus_space_handle_t sc_bsh; + + struct clk_domain sc_clkdom; + struct clk sc_clk; + u_int sc_nparent; + + uint32_t sc_mask; + u_int sc_start_index; +}; + +#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)) + +CFATTACH_DECL_NEW(ti_mux_clock, sizeof(struct ti_mux_clock_softc), + ti_mux_clock_match, ti_mux_clock_attach, NULL, NULL); + +static int +ti_mux_clock_match(device_t parent, cfdata_t cf, void *aux) +{ + const char * const compatible[] = { "ti,mux-clock", NULL }; + const struct fdt_attach_args *faa = aux; + + return of_match_compatible(faa->faa_phandle, compatible); +} + +static void +ti_mux_clock_attach(device_t parent, device_t self, void *aux) +{ + struct ti_mux_clock_softc * const sc = device_private(self); + const struct fdt_attach_args *faa = aux; + const int phandle = faa->faa_phandle; + bus_addr_t addr, base_addr; + u_int shift; + + const int prcm_phandle = OF_parent(OF_parent(phandle)); + if (fdtbus_get_reg(phandle, 0, &addr, NULL) != 0 || + fdtbus_get_reg(prcm_phandle, 0, &base_addr, NULL) != 0) { + aprint_error(": couldn't get registers\n"); + return; + } + + sc->sc_dev = self; + sc->sc_phandle = phandle; + sc->sc_bst = faa->faa_bst; + if (bus_space_map(sc->sc_bst, base_addr + addr, 4, 0, &sc->sc_bsh) != 0) { + aprint_error(": couldn't map registers\n"); + return; + } + + sc->sc_nparent = fdtbus_clock_count(phandle, "clocks"); + sc->sc_start_index = of_hasprop(phandle, "ti,index-starts-at-one") ? 1 : 0; + sc->sc_nparent += sc->sc_start_index; + while (!powerof2(sc->sc_nparent)) + sc->sc_nparent++; + + if (of_getprop_uint32(phandle, "ti,bit-shift", &shift) != 0) + shift = 0; + + sc->sc_mask = (sc->sc_nparent - 1) << shift; + + sc->sc_clkdom.name = device_xname(self); + sc->sc_clkdom.funcs = &ti_mux_clock_clk_funcs; + sc->sc_clkdom.priv = sc; + + sc->sc_clk.domain = &sc->sc_clkdom; + sc->sc_clk.name = kmem_asprintf("%s", faa->faa_name); + clk_attach(&sc->sc_clk); + + aprint_naive("\n"); + aprint_normal(": TI mux clock (%s)\n", sc->sc_clk.name); + + fdtbus_register_clock_controller(self, phandle, &ti_mux_clock_fdt_funcs); +} + +static struct clk * +ti_mux_clock_decode(device_t dev, int cc_phandle, const void *data, + size_t len) +{ + struct ti_mux_clock_softc * const sc = device_private(dev); + + return &sc->sc_clk; +} + +static struct clk * +ti_mux_clock_get(void *priv, const char *name) +{ + struct ti_mux_clock_softc * const sc = priv; + + return &sc->sc_clk; +} + +static void +ti_mux_clock_put(void *priv, struct clk *clk) +{ +} + +static u_int +ti_mux_clock_get_rate(void *priv, struct clk *clk) +{ + struct clk *clk_parent = clk_get_parent(clk); + + if (clk_parent == NULL) + return 0; + + return clk_get_rate(clk_parent); +} + +static struct clk * +ti_mux_clock_get_parent(void *priv, struct clk *clk) +{ + struct ti_mux_clock_softc * const sc = priv; + uint32_t val; + u_int sel; + + val = RD4(sc, 0); + sel = __SHIFTOUT(val, sc->sc_mask); + + return fdtbus_clock_get_index(sc->sc_phandle, sel - sc->sc_start_index); +} + +static int +ti_mux_clock_set_parent(void *priv, struct clk *clk, struct clk *parent_clk) +{ + struct ti_mux_clock_softc * const sc = priv; + uint32_t val; + u_int sel; + + for (sel = sc->sc_start_index; sel < sc->sc_nparent; sel++) { + if (fdtbus_clock_get_index(sc->sc_phandle, sel - sc->sc_start_index) == parent_clk) + break; + } + if (sel == sc->sc_nparent) + return EINVAL; + + val = RD4(sc, 0); + val &= ~sc->sc_mask; + val |= __SHIFTIN(sel, sc->sc_mask); + WR4(sc, 0, val); + + return 0; +}