Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 28 Nov 2019 17:30:17 +0000 (UTC)
From:      Emmanuel Vadot <manu@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-12@freebsd.org
Subject:   svn commit: r355174 - in stable/12/sys: arm/allwinner arm/allwinner/a20 arm/allwinner/clkng conf dev/extres/clk
Message-ID:  <201911281730.xASHUH1x056531@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: manu
Date: Thu Nov 28 17:30:16 2019
New Revision: 355174
URL: https://svnweb.freebsd.org/changeset/base/355174

Log:
  MFC r350842, r350844-r350846, r351099, r352848, r352859
  
  r350842:
  allwinner: Rework the BUS_PASS on drivers
  
  - Put all clock and control unit driver in BUS_PASS_RESOURCE except
    for the DE2 CCU as it needs the main CCU to be available.
  - Use BUS_PASS_CPU for a20_cpu_cfg as it makes more sense.
  - For aw_syscon use SCHEDULER pass as we need it early for drivers
    that attach in BUS_PASS_SUPPORTDEV
  - For the rest we can use BUS_PASS_SUPPORTDEV
  
  r350844:
  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.
  
  r350845:
  Remove some duplicate code that end up in r350844
  
  r350846:
  allwinner: Add support to min/max in aw_clk_frac
  
  The Fractionals clock in Allwinner device have some min/max frequencies
  that they can do.
  Add support for it.
  
  r351099:
  arm: allwinner: Set aw_ccu to BUS_PASS_BUS
  
  In r350842 I've switched the bus pass to resource so it matches the other
  clock drivers but this cannot work as this drivers is meant to match
  the dts node '/clocks' and if we don't do it at this pass simplebus is
  catching this node and we cannot attach.
  This solve booting on Allwinner boards that are still using /clocks (A20 SoC)
  
  r352848:
  arm64: allwinner: a64: Add PLL_MIPI
  
  PLL_MIPI is the last important PLL that we missed.
  Add support for it.
  Since it's one of the possible parent for TCON0 also add this clock
  now that we can.
  While here add some info about what video related clocks should be
  enabled at boot and with what frequency.
  
  r352859:
  arm: allwinner: Add pll_mipi to the files

Added:
  stable/12/sys/arm/allwinner/clkng/aw_clk_m.c
     - copied unchanged from r350846, head/sys/arm/allwinner/clkng/aw_clk_m.c
  stable/12/sys/arm/allwinner/clkng/aw_clk_m.h
     - copied unchanged from r350846, head/sys/arm/allwinner/clkng/aw_clk_m.h
  stable/12/sys/arm/allwinner/clkng/aw_clk_mipi.c
     - copied unchanged from r352848, head/sys/arm/allwinner/clkng/aw_clk_mipi.c
  stable/12/sys/arm/allwinner/clkng/aw_clk_mipi.h
     - copied unchanged from r352848, head/sys/arm/allwinner/clkng/aw_clk_mipi.h
Modified:
  stable/12/sys/arm/allwinner/a10_sramc.c
  stable/12/sys/arm/allwinner/a20/a20_cpu_cfg.c
  stable/12/sys/arm/allwinner/aw_gmacclk.c
  stable/12/sys/arm/allwinner/aw_reset.c
  stable/12/sys/arm/allwinner/aw_rsb.c
  stable/12/sys/arm/allwinner/aw_rtc.c
  stable/12/sys/arm/allwinner/aw_sid.c
  stable/12/sys/arm/allwinner/aw_syscon.c
  stable/12/sys/arm/allwinner/clkng/aw_ccung.c
  stable/12/sys/arm/allwinner/clkng/aw_ccung.h
  stable/12/sys/arm/allwinner/clkng/aw_clk.h
  stable/12/sys/arm/allwinner/clkng/aw_clk_frac.c
  stable/12/sys/arm/allwinner/clkng/aw_clk_frac.h
  stable/12/sys/arm/allwinner/clkng/ccu_a10.c
  stable/12/sys/arm/allwinner/clkng/ccu_a13.c
  stable/12/sys/arm/allwinner/clkng/ccu_a31.c
  stable/12/sys/arm/allwinner/clkng/ccu_a64.c
  stable/12/sys/arm/allwinner/clkng/ccu_a83t.c
  stable/12/sys/arm/allwinner/clkng/ccu_de2.c
  stable/12/sys/arm/allwinner/clkng/ccu_h3.c
  stable/12/sys/arm/allwinner/clkng/ccu_sun8i_r.c
  stable/12/sys/arm/allwinner/files.allwinner
  stable/12/sys/conf/files.arm64
  stable/12/sys/dev/extres/clk/clk.h
Directory Properties:
  stable/12/   (props changed)

Modified: stable/12/sys/arm/allwinner/a10_sramc.c
==============================================================================
--- stable/12/sys/arm/allwinner/a10_sramc.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/a10_sramc.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -116,7 +116,7 @@ static driver_t a10_sramc_driver = {
 static devclass_t a10_sramc_devclass;
 
 EARLY_DRIVER_MODULE(a10_sramc, simplebus, a10_sramc_driver, a10_sramc_devclass,
-    0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_EARLY);
+    0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
 
 int
 a10_map_to_emac(void)

Modified: stable/12/sys/arm/allwinner/a20/a20_cpu_cfg.c
==============================================================================
--- stable/12/sys/arm/allwinner/a20/a20_cpu_cfg.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/a20/a20_cpu_cfg.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -119,7 +119,7 @@ static driver_t a20_cpu_cfg_driver = {
 static devclass_t a20_cpu_cfg_devclass;
 
 EARLY_DRIVER_MODULE(a20_cpu_cfg, simplebus, a20_cpu_cfg_driver, a20_cpu_cfg_devclass, 0, 0,
-    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+    BUS_PASS_CPU + BUS_PASS_ORDER_FIRST);
 
 uint64_t
 a20_read_counter64(void)

Modified: stable/12/sys/arm/allwinner/aw_gmacclk.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_gmacclk.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_gmacclk.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -277,4 +277,4 @@ static driver_t aw_gmacclk_driver = {
 static devclass_t aw_gmacclk_devclass;
 
 EARLY_DRIVER_MODULE(aw_gmacclk, simplebus, aw_gmacclk_driver,
-    aw_gmacclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+    aw_gmacclk_devclass, 0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);

Modified: stable/12/sys/arm/allwinner/aw_reset.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_reset.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_reset.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -160,5 +160,5 @@ static driver_t aw_reset_driver = {
 static devclass_t aw_reset_devclass;
 
 EARLY_DRIVER_MODULE(aw_reset, simplebus, aw_reset_driver, aw_reset_devclass,
-    0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+    0, 0, BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
 MODULE_VERSION(aw_reset, 1);

Modified: stable/12/sys/arm/allwinner/aw_rsb.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_rsb.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_rsb.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -492,9 +492,9 @@ static driver_t rsb_driver = {
 static devclass_t rsb_devclass;
 
 EARLY_DRIVER_MODULE(iicbus, rsb, iicbus_driver, iicbus_devclass, 0, 0,
-    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+    BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
 EARLY_DRIVER_MODULE(rsb, simplebus, rsb_driver, rsb_devclass, 0, 0,
-    BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE);
+    BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
 MODULE_VERSION(rsb, 1);
 MODULE_DEPEND(rsb, iicbus, 1, 1, 1);
 SIMPLEBUS_PNP_INFO(compat_data);

Modified: stable/12/sys/arm/allwinner/aw_rtc.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_rtc.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_rtc.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -185,7 +185,7 @@ static driver_t aw_rtc_driver = {
 static devclass_t aw_rtc_devclass;
 
 EARLY_DRIVER_MODULE(aw_rtc, simplebus, aw_rtc_driver, aw_rtc_devclass, 0, 0,
-    BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+    BUS_PASS_RESOURCE + BUS_PASS_ORDER_FIRST);
 MODULE_VERSION(aw_rtc, 1);
 SIMPLEBUS_PNP_INFO(compat_data);
 

Modified: stable/12/sys/arm/allwinner/aw_sid.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_sid.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_sid.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -412,6 +412,6 @@ static driver_t aw_sid_driver = {
 static devclass_t aw_sid_devclass;
 
 EARLY_DRIVER_MODULE(aw_sid, simplebus, aw_sid_driver, aw_sid_devclass, 0, 0,
-    BUS_PASS_RESOURCE + BUS_PASS_ORDER_FIRST);
+    BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_FIRST);
 MODULE_VERSION(aw_sid, 1);
 SIMPLEBUS_PNP_INFO(compat_data);

Modified: stable/12/sys/arm/allwinner/aw_syscon.c
==============================================================================
--- stable/12/sys/arm/allwinner/aw_syscon.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/aw_syscon.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -82,5 +82,5 @@ DEFINE_CLASS_1(aw_syscon, aw_syscon_driver, aw_syscon_
 static devclass_t aw_syscon_devclass;
 /* aw_syscon needs to attach prior to if_awg */
 EARLY_DRIVER_MODULE(aw_syscon, simplebus, aw_syscon_driver, aw_syscon_devclass,
-    0, 0, BUS_PASS_SUPPORTDEV + BUS_PASS_ORDER_MIDDLE);
+    0, 0, BUS_PASS_SCHEDULER + BUS_PASS_ORDER_LAST);
 MODULE_VERSION(aw_syscon, 1);

Modified: stable/12/sys/arm/allwinner/clkng/aw_ccung.c
==============================================================================
--- stable/12/sys/arm/allwinner/clkng/aw_ccung.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/clkng/aw_ccung.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -299,6 +299,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);
@@ -306,6 +309,8 @@ aw_ccung_attach(device_t dev)
 		case AW_CLK_FRAC:
 			aw_clk_frac_register(sc->clkdom, sc->clks[i].clk.frac);
 			break;
+		case AW_CLK_MIPI:
+			aw_clk_mipi_register(sc->clkdom, sc->clks[i].clk.mipi);
 		}
 	}
 

Modified: stable/12/sys/arm/allwinner/clkng/aw_ccung.h
==============================================================================
--- stable/12/sys/arm/allwinner/clkng/aw_ccung.h	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/clkng/aw_ccung.h	Thu Nov 28 17:30:16 2019	(r355174)
@@ -31,6 +31,8 @@
 #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_mipi.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 +50,8 @@ enum aw_ccung_clk_type {
 	AW_CLK_NM,
 	AW_CLK_PREDIV_MUX,
 	AW_CLK_FRAC,
+	AW_CLK_M,
+	AW_CLK_MIPI,
 };
 
 struct aw_ccung_clk {
@@ -60,6 +64,8 @@ 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;
+		struct aw_clk_mipi_def		*mipi;
 	} clk;
 };
 

Modified: stable/12/sys/arm/allwinner/clkng/aw_clk.h
==============================================================================
--- stable/12/sys/arm/allwinner/clkng/aw_clk.h	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk.h	Thu Nov 28 17:30:16 2019	(r355174)
@@ -66,12 +66,15 @@ 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
 #define	AW_CLK_FACTOR_HAS_COND		0x0004
 #define	AW_CLK_FACTOR_FIXED		0x0008
 #define	AW_CLK_FACTOR_ZERO_IS_ONE	0x0010
+#define	AW_CLK_FACTOR_MIN_VALUE		0x0020
+#define	AW_CLK_FACTOR_MAX_VALUE		0x0040
 
 struct aw_clk_factor {
 	uint32_t	shift;		/* Shift bits for the factor */
@@ -84,6 +87,9 @@ struct aw_clk_factor {
 	uint32_t	cond_width;
 	uint32_t	cond_value;
 
+	uint32_t	min_value;
+	uint32_t	max_value;
+
 	uint32_t	flags;		/* Flags */
 };
 
@@ -146,6 +152,8 @@ aw_clk_factor_get_min(struct aw_clk_factor *factor)
 		min = factor->value;
 	else if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
 		min = 0;
+	else if (factor->flags & AW_CLK_FACTOR_MIN_VALUE)
+		min = factor->min_value;
 	else
 		min = 1;
 
@@ -165,7 +173,9 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, 
 	else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO) {
 		for (val = 0; raw != 1; val++)
 			raw >>= 1;
-	} else
+	} else if (factor->flags & AW_CLK_FACTOR_MAX_VALUE)
+		val = factor->max_value;
+	else
 		val = raw - 1;
 
 	return (val);
@@ -313,13 +323,15 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, 
      _nshift, _nwidth, _nvalue, _nflags,		\
      _mshift, _mwidth, _mvalue, _mflags,		\
      _gate_shift, _lock_shift,_lock_retries,		\
-    _flags, _freq0, _freq1, _mode_sel, _freq_sel)	\
+    _flags, _freq0, _freq1, _mode_sel, _freq_sel,	\
+    _min_freq, _max_freq)				\
 	static struct aw_clk_frac_def _clkname = {	\
 		.clkdef = {				\
 			.id = _id,			\
 			.name = _name,			\
 			.parent_names = _pnames,	\
 			.parent_cnt = nitems(_pnames),	\
+			.flags = CLK_NODE_GLITCH_FREE,	\
 		},					\
 		.offset = _offset,			\
 		.n.shift = _nshift,			\
@@ -338,8 +350,34 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, 
 		.frac.freq1 = _freq1,			\
 		.frac.mode_sel = _mode_sel,		\
 		.frac.freq_sel = _freq_sel,		\
+		.min_freq = _min_freq,			\
+		.max_freq = _max_freq,			\
 	}
 
+#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,		\
      _offset,						\
      _nshift, _nwidth, _nvalue, _nflags,		\
@@ -427,6 +465,32 @@ aw_clk_factor_get_value(struct aw_clk_factor *factor, 
 		.prediv.cond_mask = _prediv_cond_mask,		\
 		.prediv.cond_value = _prediv_cond_value,	\
 	}
+
+#define MIPI_CLK(_clkname, _id, _name, _pnames,			\
+	_offset,						\
+	_kshift, _kwidth, _kflags, _kmin,			\
+	_mshift, _mwidth,				\
+	_nshift, _nwidth,				\
+	_gate_shift, _lock_shift)				\
+	static struct aw_clk_mipi_def _clkname = {		\
+		.clkdef = {					\
+			.id = _id,				\
+			.name = _name,				\
+			.parent_names = _pnames,		\
+			.parent_cnt = nitems(_pnames)		\
+		},						\
+		.offset = _offset,				\
+		.k.shift = _kshift,				\
+		.k.width = _kwidth,				\
+		.k.flags = _kflags,				\
+		.k.min_value = _kmin,				\
+		.m.shift = _mshift,				\
+		.m.width = _mwidth,				\
+		.n.shift = _nshift,				\
+		.n.width = _nwidth,				\
+		.gate_shift = _gate_shift,			\
+		.lock_shift = _lock_shift,			\
+		}
 
 #define MUX_CLK(_clkname, _id, _name, _pnames,		\
   _offset,  _shift,  _width)				\

Modified: stable/12/sys/arm/allwinner/clkng/aw_clk_frac.c
==============================================================================
--- stable/12/sys/arm/allwinner/clkng/aw_clk_frac.c	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk_frac.c	Thu Nov 28 17:30:16 2019	(r355174)
@@ -39,6 +39,9 @@ __FBSDID("$FreeBSD$");
 
 #include "clkdev_if.h"
 
+/* #define	dprintf(format, arg...)	printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
+#define	dprintf(format, arg...)
+
 /*
  * clknode for clocks matching the formula :
  *
@@ -54,6 +57,9 @@ struct aw_clk_frac_sc {
 	struct aw_clk_factor	n;
 	struct aw_clk_frac	frac;
 
+	uint64_t		min_freq;
+	uint64_t		max_freq;
+
 	uint32_t	mux_shift;
 	uint32_t	mux_mask;
 	uint32_t	gate_shift;
@@ -89,6 +95,7 @@ aw_clk_frac_init(struct clknode *clk, device_t dev)
 		idx = (val & sc->mux_mask) >> sc->mux_shift;
 	}
 
+	dprintf("init parent idx %d\n", idx);
 	clknode_init_parent_idx(clk, idx);
 	return (0);
 }
@@ -104,6 +111,7 @@ aw_clk_frac_set_gate(struct clknode *clk, bool enable)
 	if ((sc->flags & AW_CLK_HAS_GATE) == 0)
 		return (0);
 
+	dprintf("%sabling gate\n", enable ? "En" : "Dis");
 	DEVICE_LOCK(clk);
 	READ4(clk, sc->offset, &val);
 	if (enable)
@@ -127,6 +135,7 @@ aw_clk_frac_set_mux(struct clknode *clk, int index)
 	if ((sc->flags & AW_CLK_HAS_MUX) == 0)
 		return (0);
 
+	dprintf("Set mux to %d\n", index);
 	DEVICE_LOCK(clk);
 	READ4(clk, sc->offset, &val);
 	val &= ~sc->mux_mask;
@@ -138,7 +147,7 @@ aw_clk_frac_set_mux(struct clknode *clk, int index)
 }
 
 static uint64_t
-aw_clk_frac_find_best(struct aw_clk_frac_sc *sc, uint64_t fparent, uint64_t *fout,
+aw_clk_frac_find_best(struct aw_clk_frac_sc *sc, uint64_t fparent, uint64_t fout,
     uint32_t *factor_n, uint32_t *factor_m)
 {
 	uint64_t cur, best;
@@ -150,18 +159,27 @@ aw_clk_frac_find_best(struct aw_clk_frac_sc *sc, uint6
 	max_m = aw_clk_factor_get_max(&sc->m);
 	max_n = aw_clk_factor_get_max(&sc->n);
 	min_m = aw_clk_factor_get_min(&sc->m);
-	min_n = aw_clk_factor_get_min(&sc->n);
+	min_n = sc->min_freq / fparent;
 
 	for (n = min_n; n <= max_n; n++) {
 		for (m = min_m; m <= max_m; m++) {
 			cur = fparent * n / m;
-			if ((*fout - cur) < (*fout - best)) {
+			if (cur < sc->min_freq) {
+				continue;
+			}
+			if (cur > sc->max_freq) {
+				continue;
+			}
+			if (cur == fout) {
+				*factor_n = n;
+				*factor_m = m;
+				return (cur);
+			}
+			if (abs((fout - cur)) < abs((fout - best))) {
 				best = cur;
 				*factor_n = n;
 				*factor_m = m;
 			}
-			if (best == *fout)
-				return (best);
 		}
 	}
 
@@ -175,32 +193,73 @@ aw_clk_frac_set_freq(struct clknode *clk, uint64_t fpa
 	struct aw_clk_frac_sc *sc;
 	uint64_t cur, best, best_frac;
 	uint32_t val, m, n, best_m, best_n;
-	int retry;
+	int retry, multiple, max_mult, best_mult;
 
 	sc = clknode_get_softc(clk);
 
 	best = best_frac = cur = 0;
+	best_mult = 0;
+	max_mult = 1;
 
-	if (*fout == sc->frac.freq0)
-		best = best_frac = sc->frac.freq0;
-	else if (*fout == sc->frac.freq1)
-		best = best_frac = sc->frac.freq1;
-	else
-		best = aw_clk_frac_find_best(sc, fparent, fout,
-		    &best_n, &best_m);
+	dprintf("Trying to find freq %ju with parent %ju\n", *fout, fparent);
+	if ((flags & CLK_SET_ROUND_MULTIPLE) != 0)
+		max_mult = 10;
+	for (multiple = 1; multiple <= max_mult; multiple++) {
+		/* First test the fractional frequencies */
+		dprintf("Testing with multiple %d\n", multiple);
+		if (*fout * multiple == sc->frac.freq0) {
+			best = best_frac = sc->frac.freq0;
+			best_mult = multiple;
+			dprintf("Found with using frac.freq0 and multiple %d\n", multiple);
+			break;
+		}
+		else if (*fout * multiple == sc->frac.freq1) {
+			best = best_frac = sc->frac.freq1;
+			best_mult = multiple;
+			dprintf("Found with using frac.freq1 and multiple %d\n", multiple);
+			break;
+		}
+		else {
+			cur = aw_clk_frac_find_best(sc, fparent, *fout * multiple,
+			  &n, &m);
+			dprintf("Got %ju with n=%d, m=%d\n", cur, n, m);
+			if (cur == (*fout * multiple)) {
+				best = cur;
+				best_mult = multiple;
+				best_n = n;
+				best_m = m;
+				dprintf("This is the one: n=%d m=%d mult=%d\n", best_n, best_m, best_mult);
+				break;
+			}
+			if (abs(((*fout * multiple) - cur)) < abs(((*fout * multiple) - best))) {
+				best = cur;
+				best_mult = multiple;
+				best_n = n;
+				best_m = m;
+				dprintf("This is the best for now: n=%d m=%d mult=%d\n", best_n, best_m, best_mult);
+			}
+		}
+	}
 
+	if (best < sc->min_freq ||
+	    best > sc->max_freq) {
+		printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
+		    __func__, best, clknode_get_name(clk),
+		    sc->min_freq, sc->max_freq);
+		return (ERANGE);
+	}
 	if ((flags & CLK_SET_DRYRUN) != 0) {
 		*fout = best;
 		*stop = 1;
 		return (0);
 	}
 
-	if ((best < *fout) &&
+	if ((best < (*fout * best_mult)) &&
 	  ((flags & CLK_SET_ROUND_DOWN) == 0)) {
 		*stop = 1;
 		return (ERANGE);
 	}
-	if ((best > *fout) &&
+	if ((best > *fout * best_mult) &&
 	  ((flags & CLK_SET_ROUND_UP) == 0)) {
 		*stop = 1;
 		return (ERANGE);
@@ -271,7 +330,6 @@ aw_clk_frac_recalc(struct clknode *clk, uint64_t *freq
 	} else {
 		m = aw_clk_get_factor(val, &sc->m);
 		n = aw_clk_get_factor(val, &sc->n);
-
 		*freq = *freq * n / m;
 	}
 
@@ -321,6 +379,9 @@ aw_clk_frac_register(struct clkdom *clkdom, struct aw_
 	sc->frac.freq1 = clkdef->frac.freq1;
 	sc->frac.mode_sel = 1 << clkdef->frac.mode_sel;
 	sc->frac.freq_sel = 1 << clkdef->frac.freq_sel;
+
+	sc->min_freq = clkdef->min_freq;
+	sc->max_freq = clkdef->max_freq;
 
 	sc->mux_shift = clkdef->mux_shift;
 	sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;

Modified: stable/12/sys/arm/allwinner/clkng/aw_clk_frac.h
==============================================================================
--- stable/12/sys/arm/allwinner/clkng/aw_clk_frac.h	Thu Nov 28 17:01:31 2019	(r355173)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk_frac.h	Thu Nov 28 17:30:16 2019	(r355174)
@@ -38,6 +38,9 @@ struct aw_clk_frac_def {
 	struct aw_clk_factor	n;
 	struct aw_clk_frac	frac;
 
+	uint64_t		min_freq;
+	uint64_t		max_freq;
+
 	uint32_t		mux_shift;
 	uint32_t		mux_width;
 	uint32_t		gate_shift;

Copied: stable/12/sys/arm/allwinner/clkng/aw_clk_m.c (from r350846, head/sys/arm/allwinner/clkng/aw_clk_m.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk_m.c	Thu Nov 28 17:30:16 2019	(r355174, copy of r350846, head/sys/arm/allwinner/clkng/aw_clk_m.c)
@@ -0,0 +1,290 @@
+/*-
+ * 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);
+}

Copied: stable/12/sys/arm/allwinner/clkng/aw_clk_m.h (from r350846, head/sys/arm/allwinner/clkng/aw_clk_m.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk_m.h	Thu Nov 28 17:30:16 2019	(r355174, copy of r350846, head/sys/arm/allwinner/clkng/aw_clk_m.h)
@@ -0,0 +1,48 @@
+/*-
+ * 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__ */

Copied: stable/12/sys/arm/allwinner/clkng/aw_clk_mipi.c (from r352848, head/sys/arm/allwinner/clkng/aw_clk_mipi.c)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ stable/12/sys/arm/allwinner/clkng/aw_clk_mipi.c	Thu Nov 28 17:30:16 2019	(r355174, copy of r352848, head/sys/arm/allwinner/clkng/aw_clk_mipi.c)
@@ -0,0 +1,300 @@
+/*-
+ * 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_mipi.h>
+
+#include "clkdev_if.h"
+
+/* #define	dprintf(format, arg...)	printf("%s:(%s)" format, __func__, clknode_get_name(clk), arg) */
+#define	dprintf(format, arg...)
+
+/*
+ * clknode for PLL_MIPI :
+ *
+ * clk = (pll_video0 * n * k) / m when vfb_sel=0
+ * clk depend on sint_frac, sdiv2, s6p25_7p5, pll_feedback_div when vfb_sel=1
+ *
+ */
+
+struct aw_clk_mipi_sc {
+	uint32_t	offset;
+
+	struct aw_clk_factor	k;
+	struct aw_clk_factor	m;
+	struct aw_clk_factor	n;
+
+	uint64_t		min_freq;
+	uint64_t		max_freq;
+
+	uint32_t	gate_shift;
+	uint32_t	lock_shift;
+	uint32_t	lock_retries;
+
+	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))
+
+#define	LDO1_EN_SHIFT	23
+#define	LDO2_EN_SHIFT	22
+#define	VFB_SEL_SHIFT	16
+
+static int
+aw_clk_mipi_init(struct clknode *clk, device_t dev)
+{
+	struct aw_clk_mipi_sc *sc;
+
+	sc = clknode_get_softc(clk);
+
+	clknode_init_parent_idx(clk, 0);
+	return (0);
+}
+
+static int
+aw_clk_mipi_set_gate(struct clknode *clk, bool enable)
+{
+	struct aw_clk_mipi_sc *sc;
+	uint32_t val;
+
+	sc = clknode_get_softc(clk);
+
+	dprintf("%sabling gate\n", enable ? "En" : "Dis");
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	if (enable) {
+		val |= (1 << sc->gate_shift);
+		val |= (1 << LDO1_EN_SHIFT);
+		val |= (1 << LDO2_EN_SHIFT);
+	} else {
+		val &= ~(1 << sc->gate_shift);
+		val &= ~(1 << LDO1_EN_SHIFT);
+		val &= ~(1 << LDO2_EN_SHIFT);
+	}
+	WRITE4(clk, sc->offset, val);
+	DEVICE_UNLOCK(clk);
+
+	return (0);
+}
+
+static uint64_t
+aw_clk_mipi_find_best(struct aw_clk_mipi_sc *sc, uint64_t fparent, uint64_t *fout,
+    uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_n)
+{
+	uint64_t cur, best;
+	uint32_t n, k, m;
+
+	best = 0;
+	*factor_n = 0;
+	*factor_k = 0;
+	*factor_m = 0;
+
+	for (n = aw_clk_factor_get_min(&sc->n); n <= aw_clk_factor_get_max(&sc->n); ) {
+		for (k = aw_clk_factor_get_min(&sc->k); k <= aw_clk_factor_get_max(&sc->k); ) {
+			for (m = aw_clk_factor_get_min(&sc->m); m <= aw_clk_factor_get_max(&sc->m); ) {
+				cur = (fparent * n * k) / m;
+				if ((*fout - cur) < (*fout - best)) {
+					best = cur;
+					*factor_n = n;
+					*factor_k = k;
+					*factor_m = m;
+				}
+				if (best == *fout)
+					return (best);
+					m++;
+			}
+				k++;
+		}
+			n++;
+	}
+
+	return best;
+}
+
+static int
+aw_clk_mipi_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct aw_clk_mipi_sc *sc;
+	uint64_t best = 0;
+	uint32_t best_k, best_m, best_n;
+	uint32_t k, m, n;
+	uint32_t val;
+	uint32_t retry;
+
+	sc = clknode_get_softc(clk);
+
+	best = aw_clk_mipi_find_best(sc, fparent, fout, &best_k, &best_m, &best_n);
+
+	if (best < sc->min_freq ||
+	    best > sc->max_freq) {
+		printf("%s: Cannot set %ju for %s (min=%ju max=%ju)\n",
+		    __func__, best, clknode_get_name(clk),
+		    sc->min_freq, sc->max_freq);
+		return (ERANGE);
+	}
+	if ((flags & CLK_SET_DRYRUN) != 0) {
+		*fout = best;
+		*stop = 1;
+		return (0);
+	}
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+	/* Disable clock during freq changes */
+	val &= ~(1 << sc->gate_shift);
+	WRITE4(clk, sc->offset, val);

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***



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