Skip site navigation (1)Skip section navigation (2)
Date:      Sat, 10 Aug 2019 18:50:37 +0000 (UTC)
From:      Emmanuel Vadot <manu@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r350844 - in head/sys: arm/allwinner arm/allwinner/clkng conf dev/extres/clk
Message-ID:  <201908101850.x7AIobSP030442@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: manu
Date: Sat Aug 10 18:50:37 2019
New Revision: 350844
URL: https://svnweb.freebsd.org/changeset/base/350844

Log:
  allwinner: Add a new clock aw_clk_m
  
  We used the aw_clk_nm clock for clock with only one divider factor
  and used a fake multiplier factor. This cannot work properly as we
  end up writing the "fake" factor to the register (and so always set
  the LSB to 1).
  Create a new clock for those.
  The reason for not using the clk_div clock is because those clocks are
  a bit special. Since they are (almost) all related to video we also need
  to set the parent clock (the main PLL) to a frequency that they can support.
  As the main PLL have some minimal frequency that they can support we need to
  be able to set the main PLL to a multiple of the desired frequency.
  Let say you want to have a 71Mhz pixel clock (typical for a 1280x800 display)
  and the main PLL cannot go under 192Mhz, you need to set it to 3 times the
  desired frequency and set the divider to 3 on the hdmi clock.
  So this also introduce the CLK_SET_ROUND_MULTIPLE flag that allow for this kind
  of scenario.

Added:
  head/sys/arm/allwinner/clkng/aw_clk_m.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_m.h   (contents, props changed)
Modified:
  head/sys/arm/allwinner/clkng/aw_ccung.c
  head/sys/arm/allwinner/clkng/aw_ccung.h
  head/sys/arm/allwinner/clkng/aw_clk.h
  head/sys/arm/allwinner/clkng/ccu_a64.c
  head/sys/arm/allwinner/files.allwinner
  head/sys/conf/files.arm64
  head/sys/dev/extres/clk/clk.h

Modified: head/sys/arm/allwinner/clkng/aw_ccung.c
==============================================================================
--- head/sys/arm/allwinner/clkng/aw_ccung.c	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/arm/allwinner/clkng/aw_ccung.c	Sat Aug 10 18:50:37 2019	(r350844)
@@ -301,6 +301,9 @@ aw_ccung_attach(device_t dev)
 		case AW_CLK_NM:
 			aw_clk_nm_register(sc->clkdom, sc->clks[i].clk.nm);
 			break;
+		case AW_CLK_M:
+			aw_clk_m_register(sc->clkdom, sc->clks[i].clk.m);
+			break;
 		case AW_CLK_PREDIV_MUX:
 			aw_clk_prediv_mux_register(sc->clkdom,
 			    sc->clks[i].clk.prediv_mux);

Modified: head/sys/arm/allwinner/clkng/aw_ccung.h
==============================================================================
--- head/sys/arm/allwinner/clkng/aw_ccung.h	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/arm/allwinner/clkng/aw_ccung.h	Sat Aug 10 18:50:37 2019	(r350844)
@@ -31,6 +31,7 @@
 #define __CCU_NG_H__
 
 #include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_m.h>
 #include <arm/allwinner/clkng/aw_clk_nkmp.h>
 #include <arm/allwinner/clkng/aw_clk_nm.h>
 #include <arm/allwinner/clkng/aw_clk_prediv_mux.h>
@@ -48,6 +49,7 @@ enum aw_ccung_clk_type {
 	AW_CLK_NM,
 	AW_CLK_PREDIV_MUX,
 	AW_CLK_FRAC,
+	AW_CLK_M,
 };
 
 struct aw_ccung_clk {
@@ -60,6 +62,7 @@ struct aw_ccung_clk {
 		struct aw_clk_nm_def		*nm;
 		struct aw_clk_prediv_mux_def	*prediv_mux;
 		struct aw_clk_frac_def		*frac;
+		struct aw_clk_m_def		*m;
 	} clk;
 };
 

Modified: head/sys/arm/allwinner/clkng/aw_clk.h
==============================================================================
--- head/sys/arm/allwinner/clkng/aw_clk.h	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/arm/allwinner/clkng/aw_clk.h	Sat Aug 10 18:50:37 2019	(r350844)
@@ -66,6 +66,7 @@ struct aw_clk_init {
 #define	AW_CLK_SCALE_CHANGE	0x0010
 #define	AW_CLK_HAS_UPDATE	0x0040
 #define	AW_CLK_HAS_PREDIV	0x0080
+#define	AW_CLK_SET_PARENT	0x0100
 
 #define	AW_CLK_FACTOR_POWER_OF_TWO	0x0001
 #define	AW_CLK_FACTOR_ZERO_BASED	0x0002
@@ -338,6 +339,30 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, 
 		.frac.freq1 = _freq1,			\
 		.frac.mode_sel = _mode_sel,		\
 		.frac.freq_sel = _freq_sel,		\
+	}
+
+#define M_CLK(_clkname, _id, _name, _pnames,		\
+     _offset,						\
+     _mshift, _mwidth, _mvalue, _mflags,		\
+    _mux_shift, _mux_width,				\
+    _gate_shift,					\
+    _flags)						\
+	static struct aw_clk_m_def _clkname = 	{	\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = nitems(_pnames),	\
+		},					\
+		.offset = _offset,			\
+		.mux_shift = _mux_shift,		\
+		.m.shift = _mshift,			\
+		.m.width = _mwidth,			\
+		.m.value = _mvalue,			\
+		.m.flags = _mflags,			\
+		.mux_width = _mux_width,		\
+		.gate_shift = _gate_shift,		\
+		.flags = _flags,			\
 	}
 
 #define NM_CLK(_clkname, _id, _name, _pnames,		\

Added: head/sys/arm/allwinner/clkng/aw_clk_m.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_clk_m.c	Sat Aug 10 18:50:37 2019	(r350844)
@@ -0,0 +1,580 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_m.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin / m
+ * And that needs to potentially :
+ * 1) Set the parent freq
+ * 2) Support Setting the parent to a multiple
+ *
+ */
+
+struct aw_clk_m_sc {
+	uint32_t	offset;
+
+	struct aw_clk_factor	m;
+
+	uint32_t	mux_shift;
+	uint32_t	mux_mask;
+	uint32_t	gate_shift;
+
+	uint32_t	flags;
+};
+
+#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))
+
+static int
+aw_clk_m_init(struct clknode *clk, device_t dev)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val, idx;
+
+	sc = clknode_get_softc(clk);
+
+	idx = 0;
+	if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+		DEVICE_LOCK(clk);
+		READ4(clk, sc->offset, &val);
+		DEVICE_UNLOCK(clk);
+
+		idx = (val & sc->mux_mask) >> sc->mux_shift;
+	}
+
+	clknode_init_parent_idx(clk, idx);
+	return (0);
+}
+
+static int
+aw_clk_m_set_gate(struct clknode *clk, bool enable)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val;
+
+	sc = clknode_get_softc(clk);
+
+	if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+		return (0);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	if (enable)
+		val |= (1 << sc->gate_shift);
+	else
+		val &= ~(1 << sc->gate_shift);
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	return (0);
+}
+
+static int
+aw_clk_m_set_mux(struct clknode *clk, int index)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val;
+
+	sc = clknode_get_softc(clk);
+
+	if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+		return (0);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	val &= ~sc->mux_mask;
+	val |= index << sc->mux_shift;
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	return (0);
+}
+
+static uint64_t
+aw_clk_m_find_best(struct aw_clk_m_sc *sc, uint64_t fparent, uint64_t *fout,
+    uint32_t *factor_m)
+{
+	uint64_t cur, best;
+	uint32_t m, max_m, min_m;
+
+	*factor_m = 0;
+
+	max_m = aw_clk_factor_get_max(&sc->m);
+	min_m = aw_clk_factor_get_min(&sc->m);
+
+	for (m = min_m; m <= max_m; ) {
+		cur = fparent / m;
+		if (abs(*fout - cur) < abs(*fout - best)) {
+			best = cur;
+			*factor_m = m;
+		}
+		if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+			m <<= 1;
+		else
+			m++;
+	}
+
+	return (best);
+}
+
+static int
+aw_clk_m_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct aw_clk_m_sc *sc;
+	struct clknode *p_clk;
+	uint64_t cur, best;
+	uint32_t val, m, best_m;
+
+	sc = clknode_get_softc(clk);
+
+	best = cur = 0;
+
+	if ((sc->flags & AW_CLK_SET_PARENT) != 0) {
+		p_clk = clknode_get_parent(clk);
+		if (p_clk == NULL) {
+			printf("%s: Cannot get parent for clock %s\n",
+			    __func__,
+			    clknode_get_name(clk));
+			return (ENXIO);
+		}
+		clknode_set_freq(p_clk, *fout, CLK_SET_ROUND_MULTIPLE, 0);
+		clknode_get_freq(p_clk, &fparent);
+		best = aw_clk_m_find_best(sc, fparent, fout,
+		    &best_m);
+	} else {
+		best = aw_clk_m_find_best(sc, fparent, fout,
+		    &best_m);
+	}
+
+	if ((flags & CLK_SET_DRYRUN) != 0) {
+		*fout = best;
+		*stop = 1;
+		return (0);
+	}
+
+	if ((best < *fout) &&
+	  ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+		*stop = 1;
+		return (ERANGE);
+	}
+	if ((best > *fout) &&
+	  ((flags & CLK_SET_ROUND_UP) == 0)) {
+		*stop = 1;
+		return (ERANGE);
+	}
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+
+	m = aw_clk_factor_get_value(&sc->m, best_m);
+	val &= ~sc->m.mask;
+	val |= m << sc->m.shift;
+
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	*fout = best;
+	*stop = 1;
+
+	return (0);
+}
+
+static int
+aw_clk_m_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val, m;
+
+	sc = clknode_get_softc(clk);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	DEVICE_UNLOCK(clk);
+
+	m = aw_clk_get_factor(val, &sc->m);
+
+	*freq = *freq / m;
+
+	return (0);
+}
+
+static clknode_method_t aw_m_clknode_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		aw_clk_m_init),
+	CLKNODEMETHOD(clknode_set_gate,		aw_clk_m_set_gate),
+	CLKNODEMETHOD(clknode_set_mux,		aw_clk_m_set_mux),
+	CLKNODEMETHOD(clknode_recalc_freq,	aw_clk_m_recalc),
+	CLKNODEMETHOD(clknode_set_freq,		aw_clk_m_set_freq),
+	CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_m_clknode, aw_m_clknode_class, aw_m_clknode_methods,
+    sizeof(struct aw_clk_m_sc), clknode_class);
+
+int
+aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef)
+{
+	struct clknode *clk;
+	struct aw_clk_m_sc *sc;
+
+	clk = clknode_create(clkdom, &aw_m_clknode_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+
+	sc->offset = clkdef->offset;
+
+	sc->m.shift = clkdef->m.shift;
+	sc->m.width = clkdef->m.width;
+	sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+	sc->m.value = clkdef->m.value;
+	sc->m.flags = clkdef->m.flags;
+
+	sc->mux_shift = clkdef->mux_shift;
+	sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+	sc->gate_shift = clkdef->gate_shift;
+
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+
+	return (0);
+}
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm/allwinner/clkng/aw_clk.h>
+#include <arm/allwinner/clkng/aw_clk_m.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = clkin / m
+ * And that needs to potentially :
+ * 1) Set the parent freq
+ * 2) Support Setting the parent to a multiple
+ *
+ */
+
+struct aw_clk_m_sc {
+	uint32_t	offset;
+
+	struct aw_clk_factor	m;
+
+	uint32_t	mux_shift;
+	uint32_t	mux_mask;
+	uint32_t	gate_shift;
+
+	uint32_t	flags;
+};
+
+#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))
+
+static int
+aw_clk_m_init(struct clknode *clk, device_t dev)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val, idx;
+
+	sc = clknode_get_softc(clk);
+
+	idx = 0;
+	if ((sc->flags & AW_CLK_HAS_MUX) != 0) {
+		DEVICE_LOCK(clk);
+		READ4(clk, sc->offset, &val);
+		DEVICE_UNLOCK(clk);
+
+		idx = (val & sc->mux_mask) >> sc->mux_shift;
+	}
+
+	clknode_init_parent_idx(clk, idx);
+	return (0);
+}
+
+static int
+aw_clk_m_set_gate(struct clknode *clk, bool enable)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val;
+
+	sc = clknode_get_softc(clk);
+
+	if ((sc->flags & AW_CLK_HAS_GATE) == 0)
+		return (0);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	if (enable)
+		val |= (1 << sc->gate_shift);
+	else
+		val &= ~(1 << sc->gate_shift);
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	return (0);
+}
+
+static int
+aw_clk_m_set_mux(struct clknode *clk, int index)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val;
+
+	sc = clknode_get_softc(clk);
+
+	if ((sc->flags & AW_CLK_HAS_MUX) == 0)
+		return (0);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	val &= ~sc->mux_mask;
+	val |= index << sc->mux_shift;
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	return (0);
+}
+
+static uint64_t
+aw_clk_m_find_best(struct aw_clk_m_sc *sc, uint64_t fparent, uint64_t *fout,
+    uint32_t *factor_m)
+{
+	uint64_t cur, best;
+	uint32_t m, max_m, min_m;
+
+	*factor_m = 0;
+
+	max_m = aw_clk_factor_get_max(&sc->m);
+	min_m = aw_clk_factor_get_min(&sc->m);
+
+	for (m = min_m; m <= max_m; ) {
+		cur = fparent / m;
+		if (abs(*fout - cur) < abs(*fout - best)) {
+			best = cur;
+			*factor_m = m;
+		}
+		if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+			m <<= 1;
+		else
+			m++;
+	}
+
+	return (best);
+}
+
+static int
+aw_clk_m_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct aw_clk_m_sc *sc;
+	struct clknode *p_clk;
+	uint64_t cur, best;
+	uint32_t val, m, best_m;
+
+	sc = clknode_get_softc(clk);
+
+	best = cur = 0;
+
+	if ((sc->flags & AW_CLK_SET_PARENT) != 0) {
+		p_clk = clknode_get_parent(clk);
+		if (p_clk == NULL) {
+			printf("%s: Cannot get parent for clock %s\n",
+			    __func__,
+			    clknode_get_name(clk));
+			return (ENXIO);
+		}
+		clknode_set_freq(p_clk, *fout, CLK_SET_ROUND_MULTIPLE, 0);
+		clknode_get_freq(p_clk, &fparent);
+		best = aw_clk_m_find_best(sc, fparent, fout,
+		    &best_m);
+	} else {
+		best = aw_clk_m_find_best(sc, fparent, fout,
+		    &best_m);
+	}
+
+	if ((flags & CLK_SET_DRYRUN) != 0) {
+		*fout = best;
+		*stop = 1;
+		return (0);
+	}
+
+	if ((best < *fout) &&
+	  ((flags & CLK_SET_ROUND_DOWN) == 0)) {
+		*stop = 1;
+		return (ERANGE);
+	}
+	if ((best > *fout) &&
+	  ((flags & CLK_SET_ROUND_UP) == 0)) {
+		*stop = 1;
+		return (ERANGE);
+	}
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+
+	m = aw_clk_factor_get_value(&sc->m, best_m);
+	val &= ~sc->m.mask;
+	val |= m << sc->m.shift;
+
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	*fout = best;
+	*stop = 1;
+
+	return (0);
+}
+
+static int
+aw_clk_m_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct aw_clk_m_sc *sc;
+	uint32_t val, m;
+
+	sc = clknode_get_softc(clk);
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	DEVICE_UNLOCK(clk);
+
+	m = aw_clk_get_factor(val, &sc->m);
+
+	*freq = *freq / m;
+
+	return (0);
+}
+
+static clknode_method_t aw_m_clknode_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		aw_clk_m_init),
+	CLKNODEMETHOD(clknode_set_gate,		aw_clk_m_set_gate),
+	CLKNODEMETHOD(clknode_set_mux,		aw_clk_m_set_mux),
+	CLKNODEMETHOD(clknode_recalc_freq,	aw_clk_m_recalc),
+	CLKNODEMETHOD(clknode_set_freq,		aw_clk_m_set_freq),
+	CLKNODEMETHOD_END
+};
+
+DEFINE_CLASS_1(aw_m_clknode, aw_m_clknode_class, aw_m_clknode_methods,
+    sizeof(struct aw_clk_m_sc), clknode_class);
+
+int
+aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef)
+{
+	struct clknode *clk;
+	struct aw_clk_m_sc *sc;
+
+	clk = clknode_create(clkdom, &aw_m_clknode_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+
+	sc->offset = clkdef->offset;
+
+	sc->m.shift = clkdef->m.shift;
+	sc->m.width = clkdef->m.width;
+	sc->m.mask = ((1 << sc->m.width) - 1) << sc->m.shift;
+	sc->m.value = clkdef->m.value;
+	sc->m.flags = clkdef->m.flags;
+
+	sc->mux_shift = clkdef->mux_shift;
+	sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
+
+	sc->gate_shift = clkdef->gate_shift;
+
+	sc->flags = clkdef->flags;
+
+	clknode_register(clkdom, clk);
+
+	return (0);
+}

Added: head/sys/arm/allwinner/clkng/aw_clk_m.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_clk_m.h	Sat Aug 10 18:50:37 2019	(r350844)
@@ -0,0 +1,96 @@
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * 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$
+ */
+
+#ifndef	__AW_CLK_M_H__
+#define __AW_CLK_M_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_m_def {
+	struct clknode_init_def clkdef;
+	uint32_t		offset;
+
+	struct aw_clk_factor	m;
+
+	uint32_t		mux_shift;
+	uint32_t		mux_width;
+	uint32_t		gate_shift;
+
+	uint32_t		flags;
+};
+
+int	aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef);
+
+#endif /* __AW_CLK_M_H__ */
+/*-
+ * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org>
+ *
+ * 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$
+ */
+
+#ifndef	__AW_CLK_M_H__
+#define __AW_CLK_M_H__
+
+#include <dev/extres/clk/clk.h>
+
+struct aw_clk_m_def {
+	struct clknode_init_def clkdef;
+	uint32_t		offset;
+
+	struct aw_clk_factor	m;
+
+	uint32_t		mux_shift;
+	uint32_t		mux_width;
+	uint32_t		gate_shift;
+
+	uint32_t		flags;
+};
+
+int	aw_clk_m_register(struct clkdom *clkdom, struct aw_clk_m_def *clkdef);
+
+#endif /* __AW_CLK_M_H__ */

Modified: head/sys/arm/allwinner/clkng/ccu_a64.c
==============================================================================
--- head/sys/arm/allwinner/clkng/ccu_a64.c	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/arm/allwinner/clkng/ccu_a64.c	Sat Aug 10 18:50:37 2019	(r350844)
@@ -607,10 +607,9 @@ MUX_CLK(i2s2mux_clk,
     0xb8, 16, 2);				/* offset, mux shift, mux width */
 
 static const char *spdif_parents[] = {"pll_audio"};
-NM_CLK(spdif_clk,
+M_CLK(spdif_clk,
     CLK_SPDIF, "spdif", spdif_parents,		/* id, name, parents */
     0xC0,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake); */
     0, 4, 0, 0,					/* m factor */
     0, 0,					/* mux */
     31,						/* gate */
@@ -620,20 +619,18 @@ NM_CLK(spdif_clk,
 
 /* DRAM needs update bit */
 static const char *dram_parents[] = {"pll_ddr0", "pll_ddr1"};
-NM_CLK(dram_clk,
+M_CLK(dram_clk,
     CLK_DRAM, "dram", dram_parents,		/* id, name, parents */
     0xF4,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 2, 0, 0,					/* m factor */
     20, 2,					/* mux */
     0,						/* gate */
     AW_CLK_HAS_MUX);				/* flags */
 
 static const char *de_parents[] = {"pll_periph0_2x", "pll_de"};
-NM_CLK(de_clk,
+M_CLK(de_clk,
     CLK_DE, "de", de_parents,			/* id, name, parents */
     0x104,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 4, 0, 0,					/* m factor */
     24, 2,					/* mux */
     31,						/* gate */
@@ -641,81 +638,74 @@ NM_CLK(de_clk,
 
 /* TCON0/1 Needs mux table */
 static const char *tcon1_parents[] = {"pll_video0", "pll_video0", "pll_video1"};
-NM_CLK(tcon1_clk,
-  CLK_TCON1, "tcon1", tcon1_parents,
-  0x11C,
-  0, 0, 1, AW_CLK_FACTOR_FIXED,
-  0, 4, 0, 0,
-  24, 2,
-  31,
-  AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);
+M_CLK(tcon1_clk,
+    CLK_TCON1, "tcon1", tcon1_parents,	/* id, name, parents */
+    0x11C,				/* offset */
+    0, 5, 0, 0,				/* m factor */
+    24, 2,				/* mux */
+    31,					/* gate */
+    AW_CLK_HAS_MUX | AW_CLK_HAS_GATE |
+    AW_CLK_SET_PARENT);			/* flags */
 
 static const char *deinterlace_parents[] = {"pll_periph0", "pll_periph1"};
-NM_CLK(deinterlace_clk,
+M_CLK(deinterlace_clk,
     CLK_DEINTERLACE, "deinterlace", deinterlace_parents,	/* id, name, parents */
     0x124,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 4, 0, 0,					/* m factor */
     24, 2,					/* mux */
     31,						/* gate */
     AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);		/* flags */
 
 static const char *csi_sclk_parents[] = {"pll_periph0", "pll_periph1"};
-NM_CLK(csi_sclk_clk,
+M_CLK(csi_sclk_clk,
     CLK_CSI_SCLK, "csi-sclk", csi_sclk_parents,	/* id, name, parents */
     0x134,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     16, 4, 0, 0,				/* m factor */
     24, 2,					/* mux */
     31,						/* gate */
     AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);		/* flags */
 
 static const char *csi_mclk_parents[] = {"osc24M", "pll_video0", "pll_periph1"};
-NM_CLK(csi_mclk_clk,
+M_CLK(csi_mclk_clk,
     CLK_CSI_MCLK, "csi-mclk", csi_mclk_parents,	/* id, name, parents */
     0x134,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 4, 0, 0,					/* m factor */
     8, 2,					/* mux */
     15,						/* gate */
     AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);		/* flags */
 
 static const char *ve_parents[] = {"pll_ve"};
-NM_CLK(ve_clk,
+M_CLK(ve_clk,
     CLK_VE, "ve", ve_parents,			/* id, name, parents */
     0x13C,					/* offset */
-    16, 3, 0, 0,				/* n factor */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* m factor (fake) */
+    16, 3, 0, 0,				/* m factor */
     0, 0,					/* mux */
     31,						/* gate */
     AW_CLK_HAS_GATE);				/* flags */
 
 static const char *hdmi_parents[] = {"pll_video0"};
-NM_CLK(hdmi_clk,
+M_CLK(hdmi_clk,
     CLK_HDMI, "hdmi", hdmi_parents,		/* id, name, parents */
     0x150,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 4, 0, 0,					/* m factor */
     24, 2,					/* mux */
     31,						/* gate */
-    AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);		/* flags */
+    AW_CLK_HAS_MUX | AW_CLK_HAS_GATE | AW_CLK_SET_PARENT);		/* flags */
 
 static const char *mbus_parents[] = {"osc24M", "pll_periph0_2x", "pll_ddr0"};
-NM_CLK(mbus_clk,
+M_CLK(mbus_clk,
     CLK_MBUS, "mbus", mbus_parents,		/* id, name, parents */
     0x15C,					/* offset */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* n factor (fake) */
     0, 3, 0, 0,					/* m factor */
     24, 2,					/* mux */
     31,						/* gate */
     AW_CLK_HAS_MUX | AW_CLK_HAS_GATE);		/* flags */
 
 static const char *gpu_parents[] = {"pll_gpu"};
-NM_CLK(gpu_clk,
+M_CLK(gpu_clk,
     CLK_GPU, "gpu", gpu_parents,		/* id, name, parents */
     0x1A0,					/* offset */
-    0, 2, 0, 0,					/* n factor */
-    0, 0, 1, AW_CLK_FACTOR_FIXED,		/* m factor (fake) */
+    0, 2, 0, 0,					/* m factor */
     0, 0,					/* mux */
     31,						/* gate */
     AW_CLK_HAS_GATE);				/* flags */
@@ -744,17 +734,17 @@ static struct aw_ccung_clk a64_ccu_clks[] = {
 	{ .type = AW_CLK_NM, .clk.nm = &ce_clk},
 	{ .type = AW_CLK_NM, .clk.nm = &spi0_clk},
 	{ .type = AW_CLK_NM, .clk.nm = &spi1_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &spdif_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &dram_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &de_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &tcon1_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &deinterlace_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &csi_sclk_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &csi_mclk_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &ve_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &hdmi_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &mbus_clk},
-	{ .type = AW_CLK_NM, .clk.nm = &gpu_clk},
+	{ .type = AW_CLK_M, .clk.m = &spdif_clk},
+	{ .type = AW_CLK_M, .clk.m = &dram_clk},
+	{ .type = AW_CLK_M, .clk.m = &de_clk},
+	{ .type = AW_CLK_M, .clk.m = &tcon1_clk},
+	{ .type = AW_CLK_M, .clk.m = &deinterlace_clk},
+	{ .type = AW_CLK_M, .clk.m = &csi_sclk_clk},
+	{ .type = AW_CLK_M, .clk.m = &csi_mclk_clk},
+	{ .type = AW_CLK_M, .clk.m = &ve_clk},
+	{ .type = AW_CLK_M, .clk.m = &hdmi_clk},
+	{ .type = AW_CLK_M, .clk.m = &mbus_clk},
+	{ .type = AW_CLK_M, .clk.m = &gpu_clk},
 	{ .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb1_clk},
 	{ .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ahb2_clk},
 	{ .type = AW_CLK_MUX, .clk.mux = &cpux_clk},

Modified: head/sys/arm/allwinner/files.allwinner
==============================================================================
--- head/sys/arm/allwinner/files.allwinner	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/arm/allwinner/files.allwinner	Sat Aug 10 18:50:37 2019	(r350844)
@@ -36,6 +36,7 @@ arm/allwinner/aw_gmacclk.c		standard
 
 arm/allwinner/clkng/aw_ccung.c		standard
 arm/allwinner/clkng/aw_clk_frac.c	standard
+arm/allwinner/clkng/aw_clk_m.c		standard
 arm/allwinner/clkng/aw_clk_nkmp.c	standard
 arm/allwinner/clkng/aw_clk_nm.c		standard
 arm/allwinner/clkng/aw_clk_prediv_mux.c	standard

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/conf/files.arm64	Sat Aug 10 18:50:37 2019	(r350844)
@@ -50,6 +50,7 @@ arm/allwinner/if_awg.c		optional	awg ext_resources sys
 # Allwinner clock driver
 arm/allwinner/clkng/aw_ccung.c		optional	aw_ccu fdt
 arm/allwinner/clkng/aw_clk_frac.c	optional	aw_ccu fdt
+arm/allwinner/clkng/aw_clk_m.c		optional	aw_ccu fdt
 arm/allwinner/clkng/aw_clk_nkmp.c	optional	aw_ccu fdt
 arm/allwinner/clkng/aw_clk_nm.c		optional	aw_ccu fdt
 arm/allwinner/clkng/aw_clk_prediv_mux.c	optional	aw_ccu fdt

Modified: head/sys/dev/extres/clk/clk.h
==============================================================================
--- head/sys/dev/extres/clk/clk.h	Sat Aug 10 18:22:22 2019	(r350843)
+++ head/sys/dev/extres/clk/clk.h	Sat Aug 10 18:50:37 2019	(r350844)
@@ -48,6 +48,7 @@
 #define	CLK_SET_ROUND_EXACT	0
 #define	CLK_SET_ROUND_UP	0x00000001
 #define	CLK_SET_ROUND_DOWN	0x00000002
+#define	CLK_SET_ROUND_MULTIPLE	0x00000004
 #define	CLK_SET_ROUND_ANY	(CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)
 
 #define	CLK_SET_USER_MASK	0x0000FFFF



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201908101850.x7AIobSP030442>