Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 8 Nov 2019 19:13:12 +0000 (UTC)
From:      Michal Meloun <mmel@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org
Subject:   svn commit: r354556 - in head/sys: arm64/rockchip/clk conf
Message-ID:  <201911081913.xA8JDC8L003999@repo.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mmel
Date: Fri Nov  8 19:13:11 2019
New Revision: 354556
URL: https://svnweb.freebsd.org/changeset/base/354556

Log:
  Enhance Rockchip clocks implementation.
  - add support for fractional dividers
  - allow to declare fixed and linked clock
  
  MFC after:	3 weeks
  Reviewed by:	manu
  Differential Revision:  https://reviews.freebsd.org/D22282

Added:
  head/sys/arm64/rockchip/clk/rk_clk_fract.c   (contents, props changed)
  head/sys/arm64/rockchip/clk/rk_clk_fract.h   (contents, props changed)
Modified:
  head/sys/arm64/rockchip/clk/rk_cru.c
  head/sys/arm64/rockchip/clk/rk_cru.h
  head/sys/conf/files.arm64

Added: head/sys/arm64/rockchip/clk/rk_clk_fract.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/rockchip/clk/rk_clk_fract.c	Fri Nov  8 19:13:11 2019	(r354556)
@@ -0,0 +1,249 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2019 Michal Meloun <mmel@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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+
+#include <dev/extres/clk/clk.h>
+
+#include <arm64/rockchip/clk/rk_clk_fract.h>
+
+#include "clkdev_if.h"
+
+#define	WR4(_clk, off, val)						\
+	CLKDEV_WRITE_4(clknode_get_device(_clk), off, val)
+#define	RD4(_clk, off, val)						\
+	CLKDEV_READ_4(clknode_get_device(_clk), off, val)
+#define	MD4(_clk, off, clr, set )					\
+	CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set)
+#define	DEVICE_LOCK(_clk)						\
+	CLKDEV_DEVICE_LOCK(clknode_get_device(_clk))
+#define	DEVICE_UNLOCK(_clk)						\
+	CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk))
+
+static int rk_clk_fract_init(struct clknode *clk, device_t dev);
+static int rk_clk_fract_recalc(struct clknode *clk, uint64_t *req);
+static int rk_clk_fract_set_freq(struct clknode *clknode, uint64_t fin,
+    uint64_t *fout, int flag, int *stop);
+
+struct rk_clk_fract_sc {
+	uint32_t	flags;
+	uint32_t	offset;
+	uint32_t	numerator;
+	uint32_t	denominator;
+};
+
+static clknode_method_t rk_clk_fract_methods[] = {
+	/* Device interface */
+	CLKNODEMETHOD(clknode_init,		rk_clk_fract_init),
+	CLKNODEMETHOD(clknode_recalc_freq,	rk_clk_fract_recalc),
+	CLKNODEMETHOD(clknode_set_freq,		rk_clk_fract_set_freq),
+	CLKNODEMETHOD_END
+};
+DEFINE_CLASS_1(rk_clk_fract, rk_clk_fract_class, rk_clk_fract_methods,
+   sizeof(struct rk_clk_fract_sc), clknode_class);
+
+
+/*
+ * Compute best rational approximation of input fraction
+ * for fixed sized fractional divider registers.
+ * http://en.wikipedia.org/wiki/Continued_fraction
+ *
+ * - n_input, d_input	Given input fraction
+ * - n_max, d_max	Maximum vaues of divider registers
+ * - n_out, d_out	Computed approximation
+ */
+
+static void
+clk_compute_fract_div(
+	uint64_t n_input, uint64_t d_input,
+	uint64_t n_max, uint64_t d_max,
+	uint64_t *n_out, uint64_t *d_out)
+{
+	uint64_t n_prev, d_prev;	/* previous convergents */
+	uint64_t n_cur, d_cur;		/* current  convergents */
+	uint64_t n_rem, d_rem;		/* fraction remainder */
+	uint64_t tmp, fact;
+
+	/* Initialize fraction reminder */
+	n_rem = n_input;
+	d_rem = d_input;
+
+	/* Init convergents to 0/1 and 1/0 */
+	n_prev = 0;
+	d_prev = 1;
+	n_cur = 1;
+	d_cur = 0;
+
+	while (d_rem != 0 && n_cur < n_max && d_cur < d_max) {
+		/* Factor for this step. */
+		fact = n_rem / d_rem;
+
+		/* Adjust fraction reminder */
+		tmp = d_rem;
+		d_rem = n_rem % d_rem;
+		n_rem = tmp;
+
+		/* Compute new nominator and save last one */
+		tmp = n_prev + fact * n_cur;
+		n_prev = n_cur;
+		n_cur = tmp;
+
+		/* Compute new denominator and save last one */
+		tmp = d_prev + fact * d_cur;
+		d_prev = d_cur;
+		d_cur = tmp;
+	}
+
+	if (n_cur > n_max || d_cur > d_max) {
+		*n_out = n_prev;
+		*d_out = d_prev;
+	} else {
+		*n_out = n_cur;
+		*d_out = d_cur;
+	}
+}
+
+static int
+rk_clk_fract_init(struct clknode *clk, device_t dev)
+{
+	uint32_t reg;
+	struct rk_clk_fract_sc *sc;
+
+	sc = clknode_get_softc(clk);
+	DEVICE_LOCK(clk);
+	RD4(clk, sc->offset, &reg);
+	DEVICE_UNLOCK(clk);
+
+	sc->numerator  = (reg >> 16) & 0xFFFF;
+	sc->denominator  = reg & 0xFFFF;
+	clknode_init_parent_idx(clk, 0);
+
+	return(0);
+}
+static int
+rk_clk_fract_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+    int flags, int *stop);
+static int
+rk_clk_fract_recalc(struct clknode *clk, uint64_t *freq)
+{
+	struct rk_clk_fract_sc *sc;
+
+	sc = clknode_get_softc(clk);
+	if (sc->denominator == 0) {
+		printf("%s: %s denominator is zero!\n", clknode_get_name(clk),
+		__func__);
+		*freq = 0;
+		return(EINVAL);
+	}
+
+	*freq *= sc->numerator;
+	*freq /= sc->denominator;
+
+	return (0);
+}
+
+static int
+rk_clk_fract_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct rk_clk_fract_sc *sc;
+	uint64_t div_n, div_d, _fout;
+
+	sc = clknode_get_softc(clk);
+
+	clk_compute_fract_div(*fout, fin, 0xFFFF, 0xFFFF, &div_n, &div_d);
+	_fout = fin * div_n;
+	_fout /= div_d;
+
+	/* Rounding. */
+	if ((flags & CLK_SET_ROUND_UP) && (_fout < *fout)) {
+		if (div_n > div_d && div_d > 1)
+			div_n++;
+		else
+			div_d--;
+	} else if ((flags & CLK_SET_ROUND_DOWN) && (_fout > *fout)) {
+		if (div_n > div_d && div_n > 1)
+			div_n--;
+		else
+			div_d++;
+	}
+
+	/* Check range after rounding */
+	if (div_n > 0xFFFF || div_d > 0xFFFF)
+		return (ERANGE);
+
+	if (div_d == 0) {
+		printf("%s: %s divider is zero!\n",
+		     clknode_get_name(clk), __func__);
+		return(EINVAL);
+	}
+	/* Recompute final output frequency */
+	_fout = fin * div_n;
+	_fout /= div_d;
+
+	*stop = 1;
+
+	if ((flags & CLK_SET_DRYRUN) == 0) {
+		if (*stop != 0 &&
+		    (flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0 &&
+		    *fout != _fout)
+			return (ERANGE);
+
+		sc->numerator  = (uint32_t)div_n;
+		sc->denominator = (uint32_t)div_d;
+
+		DEVICE_LOCK(clk);
+		WR4(clk, sc->offset, sc->numerator << 16 | sc->denominator);
+		DEVICE_UNLOCK(clk);
+	}
+
+	*fout = _fout;
+	return (0);
+}
+
+int
+rk_clk_fract_register(struct clkdom *clkdom, struct rk_clk_fract_def *clkdef)
+{
+	struct clknode *clk;
+	struct rk_clk_fract_sc *sc;
+
+	clk = clknode_create(clkdom, &rk_clk_fract_class, &clkdef->clkdef);
+	if (clk == NULL)
+		return (1);
+
+	sc = clknode_get_softc(clk);
+	sc->flags = clkdef->flags;
+	sc->offset = clkdef->offset;
+
+	clknode_register(clkdom, clk);
+	return (0);
+}

Added: head/sys/arm64/rockchip/clk/rk_clk_fract.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm64/rockchip/clk/rk_clk_fract.h	Fri Nov  8 19:13:11 2019	(r354556)
@@ -0,0 +1,44 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright 2019 Michal Meloun <mmel@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 AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _RK_CLK_FRACT_H_
+#define _RK_CLK_FRACT_H_
+
+#include <dev/extres/clk/clk.h>
+
+struct rk_clk_fract_def {
+	struct clknode_init_def clkdef;
+	uint32_t		offset;
+	uint32_t		flags;
+};
+
+int rk_clk_fract_register(struct clkdom *clkdom,
+    struct rk_clk_fract_def *clkdef);
+
+#endif /* _RK_CLK_FRACT_H_ */

Modified: head/sys/arm64/rockchip/clk/rk_cru.c
==============================================================================
--- head/sys/arm64/rockchip/clk/rk_cru.c	Fri Nov  8 19:03:34 2019	(r354555)
+++ head/sys/arm64/rockchip/clk/rk_cru.c	Fri Nov  8 19:13:11 2019	(r354556)
@@ -52,6 +52,8 @@ __FBSDID("$FreeBSD$");
 
 #include <dev/extres/clk/clk.h>
 #include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_link.h>
 #include <dev/extres/hwreset/hwreset.h>
 
 #include <arm64/rockchip/clk/rk_clk_composite.h>
@@ -251,12 +253,24 @@ rk_cru_attach(device_t dev)
 			rk_clk_armclk_register(sc->clkdom,
 			    sc->clks[i].clk.armclk);
 			break;
+		case RK_CLK_FIXED:
+			clknode_fixed_register(sc->clkdom,
+			    sc->clks[i].clk.fixed);
+			break;
+		case RK_CLK_FRACT:
+			rk_clk_fract_register(sc->clkdom,
+			    sc->clks[i].clk.fract);
+			break;
+		case RK_CLK_LINK:
+			clknode_link_register(sc->clkdom,
+			    sc->clks[i].clk.link);
+			break;
 		default:
 			device_printf(dev, "Unknown clock type\n");
 			return (ENXIO);
-			break;
 		}
 	}
+
 	if (sc->gates)
 		rk_cru_register_gates(sc);
 

Modified: head/sys/arm64/rockchip/clk/rk_cru.h
==============================================================================
--- head/sys/arm64/rockchip/clk/rk_cru.h	Fri Nov  8 19:03:34 2019	(r354555)
+++ head/sys/arm64/rockchip/clk/rk_cru.h	Fri Nov  8 19:13:11 2019	(r354556)
@@ -31,8 +31,15 @@
 #ifndef __RK_CRU_H__
 #define __RK_CRU_H__
 
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_div.h>
+#include <dev/extres/clk/clk_gate.h>
+#include <dev/extres/clk/clk_fixed.h>
+#include <dev/extres/clk/clk_link.h>
+
 #include <arm64/rockchip/clk/rk_clk_armclk.h>
 #include <arm64/rockchip/clk/rk_clk_composite.h>
+#include <arm64/rockchip/clk/rk_clk_fract.h>
 #include <arm64/rockchip/clk/rk_clk_gate.h>
 #include <arm64/rockchip/clk/rk_clk_mux.h>
 #include <arm64/rockchip/clk/rk_clk_pll.h>
@@ -59,8 +66,11 @@ enum rk_clk_type {
 	RK3328_CLK_PLL,
 	RK3399_CLK_PLL,
 	RK_CLK_COMPOSITE,
+	RK_CLK_FIXED,
+	RK_CLK_FRACT,
 	RK_CLK_MUX,
 	RK_CLK_ARMCLK,
+	RK_CLK_LINK,
 };
 
 struct rk_clk {
@@ -70,6 +80,9 @@ struct rk_clk {
 		struct rk_clk_composite_def	*composite;
 		struct rk_clk_mux_def		*mux;
 		struct rk_clk_armclk_def	*armclk;
+		struct clk_fixed_def		*fixed;
+		struct rk_clk_fract_def		*fract;
+		struct clk_link_def		*link;
 	} clk;
 };
 

Modified: head/sys/conf/files.arm64
==============================================================================
--- head/sys/conf/files.arm64	Fri Nov  8 19:03:34 2019	(r354555)
+++ head/sys/conf/files.arm64	Fri Nov  8 19:13:11 2019	(r354556)
@@ -309,6 +309,7 @@ dev/dwc/if_dwc_if.m			optional fdt dwc_rk soc_rockchip
 arm64/rockchip/clk/rk_cru.c		optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/clk/rk_clk_armclk.c	optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/clk/rk_clk_composite.c	optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
+arm64/rockchip/clk/rk_clk_fract.c	optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/clk/rk_clk_gate.c	optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/clk/rk_clk_mux.c		optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399
 arm64/rockchip/clk/rk_clk_pll.c		optional fdt soc_rockchip_rk3328 | fdt soc_rockchip_rk3399



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