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;
+}

Reply via email to