svn commit: r314303 - in head/sys/arm/allwinner: . clkng h3

Emmanuel Vadot manu at FreeBSD.org
Sun Feb 26 16:00:22 UTC 2017


Author: manu
Date: Sun Feb 26 16:00:20 2017
New Revision: 314303
URL: https://svnweb.freebsd.org/changeset/base/314303

Log:
  Add clkng driver for Allwinner SoC
  
  Since Linux 4.9-4.10 DTS doesn't have clocks under /clocks but only a ccu node.
  Currently only H3 is supported with almost the same state as HEAD.
  (video pll aren't supported for now but we don't support video).
  This driver and clocks will also be used for other SoC (A64, A31, H5, H2 etc ...)
  
  Reviewed by:	jmcneill
  Differential Revision:	https://reviews.freebsd.org/D9517

Added:
  head/sys/arm/allwinner/clkng/
  head/sys/arm/allwinner/clkng/aw_ccung.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_ccung.h   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk.h   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_nkmp.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_nkmp.h   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_nm.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_nm.h   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_prediv_mux.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/aw_clk_prediv_mux.h   (contents, props changed)
  head/sys/arm/allwinner/clkng/ccu_h3.c   (contents, props changed)
  head/sys/arm/allwinner/clkng/ccu_h3.h   (contents, props changed)
Modified:
  head/sys/arm/allwinner/files.allwinner
  head/sys/arm/allwinner/h3/files.h3

Added: head/sys/arm/allwinner/clkng/aw_ccung.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_ccung.c	Sun Feb 26 16:00:20 2017	(r314303)
@@ -0,0 +1,348 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu at freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+/*
+ * Allwinner Clock Control Unit
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/rman.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <machine/bus.h>
+
+#include <dev/fdt/simplebus.h>
+
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include <dev/extres/clk/clk.h>
+#include <dev/extres/clk/clk_gate.h>
+
+#include <dev/extres/hwreset/hwreset.h>
+
+#include <arm/allwinner/clkng/aw_ccung.h>
+#include <arm/allwinner/clkng/aw_clk.h>
+
+#if defined(SOC_ALLWINNER_H3)
+#include <arm/allwinner/clkng/ccu_h3.h>
+#endif
+
+#include "clkdev_if.h"
+#include "hwreset_if.h"
+
+static struct resource_spec aw_ccung_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+#if defined(SOC_ALLWINNER_H3)
+#define	H3_CCU	1
+#endif
+
+static struct ofw_compat_data compat_data[] = {
+#if defined(SOC_ALLWINNER_H3)
+	{ "allwinner,sun8i-h3-ccu", H3_CCU },
+#endif
+	{NULL, 0 }
+};
+
+#define	CCU_READ4(sc, reg)		bus_read_4((sc)->res, (reg))
+#define	CCU_WRITE4(sc, reg, val)	bus_write_4((sc)->res, (reg), (val))
+
+static int
+aw_ccung_write_4(device_t dev, bus_addr_t addr, uint32_t val)
+{
+	struct aw_ccung_softc *sc;
+
+	sc = device_get_softc(dev);
+	CCU_WRITE4(sc, addr, val);
+	return (0);
+}
+
+static int
+aw_ccung_read_4(device_t dev, bus_addr_t addr, uint32_t *val)
+{
+	struct aw_ccung_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	*val = CCU_READ4(sc, addr);
+	return (0);
+}
+
+static int
+aw_ccung_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set)
+{
+	struct aw_ccung_softc *sc;
+	uint32_t reg;
+
+	sc = device_get_softc(dev);
+
+	reg = CCU_READ4(sc, addr);
+	reg &= ~clr;
+	reg |= set;
+	CCU_WRITE4(sc, addr, reg);
+
+	return (0);
+}
+
+static int
+aw_ccung_reset_assert(device_t dev, intptr_t id, bool reset)
+{
+	struct aw_ccung_softc *sc;
+	uint32_t val;
+
+	sc = device_get_softc(dev);
+
+	if (id >= sc->nresets || sc->resets[id].offset == 0)
+		return (0);
+
+	mtx_lock(&sc->mtx);
+	val = CCU_READ4(sc, sc->resets[id].offset);
+	if (reset)
+		val &= ~(1 << sc->resets[id].shift);
+	else
+		val |= 1 << sc->resets[id].shift;
+	CCU_WRITE4(sc, sc->resets[id].offset, val);
+	mtx_unlock(&sc->mtx);
+
+	return (0);
+}
+
+static int
+aw_ccung_reset_is_asserted(device_t dev, intptr_t id, bool *reset)
+{
+	struct aw_ccung_softc *sc;
+	uint32_t val;
+
+	sc = device_get_softc(dev);
+
+	if (id >= sc->nresets || sc->resets[id].offset == 0)
+		return (0);
+
+	mtx_lock(&sc->mtx);
+	val = CCU_READ4(sc, sc->resets[id].offset);
+	*reset = (val & (1 << sc->resets[id].shift)) != 0 ? false : true;
+	mtx_unlock(&sc->mtx);
+
+	return (0);
+}
+
+static void
+aw_ccung_device_lock(device_t dev)
+{
+	struct aw_ccung_softc *sc;
+
+	sc = device_get_softc(dev);
+	mtx_lock(&sc->mtx);
+}
+
+static void
+aw_ccung_device_unlock(device_t dev)
+{
+	struct aw_ccung_softc *sc;
+
+	sc = device_get_softc(dev);
+	mtx_unlock(&sc->mtx);
+}
+
+static int
+aw_ccung_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
+		return (ENXIO);
+
+	device_set_desc(dev, "Allwinner Clock Control Unit NG");
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+aw_ccung_register_gates(struct aw_ccung_softc *sc)
+{
+	struct clk_gate_def def;
+	int i;
+
+	for (i = 0; i < sc->ngates; i++) {
+		if (sc->gates[i].name == NULL)
+			continue;
+		memset(&def, 0, sizeof(def));
+		def.clkdef.id = i;
+		def.clkdef.name = sc->gates[i].name;
+		def.clkdef.parent_names = &sc->gates[i].parent_name;
+		def.clkdef.parent_cnt = 1;
+		def.offset = sc->gates[i].offset;
+		def.shift = sc->gates[i].shift;
+		def.mask = 1;
+		def.on_value = 1;
+		def.off_value = 0;
+		clknode_gate_register(sc->clkdom, &def);
+	}
+
+	return (0);
+}
+
+static void
+aw_ccung_init_clocks(struct aw_ccung_softc *sc)
+{
+	struct clknode *clknode;
+	int i, error;
+
+	for (i = 0; i < sc->n_clk_init; i++) {
+		clknode = clknode_find_by_name(sc->clk_init[i].name);
+		if (clknode == NULL) {
+			device_printf(sc->dev, "Cannot find clock %s\n",
+			    sc->clk_init[i].name);
+			continue;
+		}
+
+		if (sc->clk_init[i].parent_name != NULL) {
+			if (bootverbose)
+				device_printf(sc->dev, "Setting %s as parent for %s\n",
+				    sc->clk_init[i].parent_name,
+				    sc->clk_init[i].name);
+			error = clknode_set_parent_by_name(clknode,
+			    sc->clk_init[i].parent_name);
+			if (error != 0) {
+				device_printf(sc->dev,
+				    "Cannot set parent to %s for %s\n",
+				    sc->clk_init[i].parent_name,
+				    sc->clk_init[i].name);
+				continue;
+			}
+		}
+		if (sc->clk_init[i].default_freq != 0) {
+			error = clknode_set_freq(clknode,
+			    sc->clk_init[i].default_freq, 0 , 0);
+			if (error != 0) {
+				device_printf(sc->dev,
+				    "Cannot set frequency for %s to %llu\n",
+				    sc->clk_init[i].name,
+				    sc->clk_init[i].default_freq);
+				continue;
+			}
+		}
+		if (sc->clk_init[i].enable) {
+			error = clknode_enable(clknode);
+			if (error != 0) {
+				device_printf(sc->dev,
+				    "Cannot enable %s\n",
+				    sc->clk_init[i].name);
+				continue;
+			}
+		}
+	}
+}
+
+static int
+aw_ccung_attach(device_t dev)
+{
+	struct aw_ccung_softc *sc;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	if (bus_alloc_resources(dev, aw_ccung_spec, &sc->res) != 0) {
+		device_printf(dev, "cannot allocate resources for device\n");
+		return (ENXIO);
+	}
+
+	mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF);
+
+	sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
+
+	sc->clkdom = clkdom_create(dev);
+	if (sc->clkdom == NULL)
+		panic("Cannot create clkdom\n");
+
+	switch (sc->type) {
+#if defined(SOC_ALLWINNER_H3)
+	case H3_CCU:
+		ccu_h3_register_clocks(sc);
+		break;
+#endif
+	}
+
+	if (sc->gates)
+		aw_ccung_register_gates(sc);
+	if (clkdom_finit(sc->clkdom) != 0)
+		panic("cannot finalize clkdom initialization\n");
+
+	clkdom_xlock(sc->clkdom);
+	aw_ccung_init_clocks(sc);
+	clkdom_unlock(sc->clkdom);
+
+	if (bootverbose)
+		clkdom_dump(sc->clkdom);
+
+	/* If we have resets, register our self as a reset provider */
+	if (sc->resets)
+		hwreset_register_ofw_provider(dev);
+
+	return (0);
+}
+
+static device_method_t aw_ccung_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,		aw_ccung_probe),
+	DEVMETHOD(device_attach,	aw_ccung_attach),
+
+	/* clkdev interface */
+	DEVMETHOD(clkdev_write_4,	aw_ccung_write_4),
+	DEVMETHOD(clkdev_read_4,	aw_ccung_read_4),
+	DEVMETHOD(clkdev_modify_4,	aw_ccung_modify_4),
+	DEVMETHOD(clkdev_device_lock,	aw_ccung_device_lock),
+	DEVMETHOD(clkdev_device_unlock,	aw_ccung_device_unlock),
+
+	/* Reset interface */
+	DEVMETHOD(hwreset_assert,	aw_ccung_reset_assert),
+	DEVMETHOD(hwreset_is_asserted,	aw_ccung_reset_is_asserted),
+
+	DEVMETHOD_END
+};
+
+static driver_t aw_ccung_driver = {
+	"aw_ccung",
+	aw_ccung_methods,
+	sizeof(struct aw_ccung_softc),
+};
+
+static devclass_t aw_ccung_devclass;
+
+EARLY_DRIVER_MODULE(aw_ccung, simplebus, aw_ccung_driver, aw_ccung_devclass,
+    0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE);
+MODULE_VERSION(aw_ccung, 1);

Added: head/sys/arm/allwinner/clkng/aw_ccung.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_ccung.h	Sun Feb 26 16:00:20 2017	(r314303)
@@ -0,0 +1,59 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu at freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef __CCU_NG_H__
+#define __CCU_NG_H__
+
+struct aw_ccung_softc {
+	device_t		dev;
+	struct resource		*res;
+	struct clkdom		*clkdom;
+	struct mtx		mtx;
+	int			type;
+	struct aw_ccung_reset	*resets;
+	int			nresets;
+	struct aw_ccung_gate	*gates;
+	int			ngates;
+	struct aw_clk_init	*clk_init;
+	int			n_clk_init;
+};
+
+struct aw_ccung_reset {
+	uint32_t	offset;
+	uint32_t	shift;
+};
+
+struct aw_ccung_gate {
+	const char	*name;
+	const char	*parent_name;
+	uint32_t	id;
+	uint32_t	offset;
+	uint32_t	shift;
+};
+
+#endif /* __CCU_NG_H__ */

Added: head/sys/arm/allwinner/clkng/aw_clk.h
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_clk.h	Sun Feb 26 16:00:20 2017	(r314303)
@@ -0,0 +1,317 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu at freebsd.org>
+ * 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef	__AW_CLK_H__
+#define __AW_CLK_H__
+
+/*
+  Allwinner clocks formula :
+
+PLLs:
+
+(24MHz*N*K)/(M*P)
+(24MHz*N)/(M*P)
+(24MHz*N*2)/M
+(24MHz*N)/M
+(24MHz*N*K)/M
+(24MHz*N*K/2)
+(24MHz*N)/M
+(24MHz*N*K/2)
+(24MHz*N)/M
+
+Periph clocks:
+
+Clock Source/Divider N/Divider M
+Clock Source/Divider N/Divider M/2
+
+ */
+
+struct aw_clk_init {
+	const char	*name;
+	const char	*parent_name;
+	uint64_t	default_freq;
+	bool		enable;
+};
+
+#define	AW_CLK_HAS_GATE		0x0001
+#define	AW_CLK_HAS_LOCK		0x0002
+#define	AW_CLK_HAS_MUX		0x0004
+#define	AW_CLK_REPARENT		0x0008
+#define	AW_CLK_SCALE_CHANGE	0x0010
+
+#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
+
+struct aw_clk_factor {
+	uint32_t	shift;		/* Shift bits for the factor */
+	uint32_t	mask;		/* Mask to get the factor, will be override by the clk methods */
+	uint32_t	width;		/* Number of bits for the factor */
+	uint32_t	value;		/* Fixed value, depends on AW_CLK_FACTOR_FIXED */
+
+	uint32_t	cond_shift;
+	uint32_t	cond_mask;
+	uint32_t	cond_width;
+	uint32_t	cond_value;
+
+	uint32_t	flags;		/* Flags */
+};
+
+static inline uint32_t
+aw_clk_get_factor(uint32_t val, struct aw_clk_factor *factor)
+{
+	uint32_t factor_val;
+	uint32_t cond;
+
+	if (factor->flags & AW_CLK_FACTOR_HAS_COND) {
+		cond = (val & factor->cond_mask) >> factor->cond_shift;
+		if (cond != factor->cond_value)
+			return (1);
+	}
+
+	if (factor->flags & AW_CLK_FACTOR_FIXED)
+		return (factor->value);
+
+	factor_val = (val & factor->mask) >> factor->shift;
+	if (!(factor->flags & AW_CLK_FACTOR_ZERO_BASED))
+		factor_val += 1;
+	else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO)
+		factor_val = 1 << factor_val;
+
+	return (factor_val);
+}
+
+static inline uint32_t
+aw_clk_factor_get_max(struct aw_clk_factor *factor)
+{
+	uint32_t max;
+
+	if (factor->flags & AW_CLK_FACTOR_FIXED)
+		max = factor->value;
+	else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO)
+		max = 1 << ((1 << factor->width) - 1);
+	else {
+		max = (1 << factor->width);
+	}
+
+	return (max);
+}
+
+static inline uint32_t
+aw_clk_factor_get_min(struct aw_clk_factor *factor)
+{
+	uint32_t min;
+
+	if (factor->flags & AW_CLK_FACTOR_FIXED)
+		min = factor->value;
+	else if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
+		min = 0;
+	else
+		min = 1;
+
+	return (min);
+}
+
+static inline uint32_t
+aw_clk_factor_get_value(struct aw_clk_factor *factor, uint32_t raw)
+{
+	uint32_t val;
+
+	if (factor->flags & AW_CLK_FACTOR_FIXED)
+		return (factor->value);
+
+	if (factor->flags & AW_CLK_FACTOR_ZERO_BASED)
+		val = raw;
+	else if (factor->flags & AW_CLK_FACTOR_POWER_OF_TWO) {
+		for (val = 0; raw != 1; val++)
+			raw >>= 1;
+	} else
+		val = raw - 1;
+
+	return (val);
+}
+
+#define	CCU_RESET(idx, o, s)	\
+	[idx] = {		\
+		.offset = o,	\
+		.shift = s,	\
+	},
+
+#define	CCU_GATE(idx, clkname, pname, o, s)	\
+	[idx] = {				\
+		.name = clkname,		\
+		.parent_name = pname,		\
+		.offset = o,			\
+		.shift = s,			\
+	},
+
+#define NKMP_CLK(_id, _name, _pnames,			\
+  _offset,						\
+  _n_shift, _n_width, _n_value, _n_flags,		\
+  _k_shift, _k_width, _k_value, _k_flags,		\
+  _m_shift, _m_width, _m_value, _m_flags,		\
+  _p_shift, _p_width, _p_value, _p_flags,		\
+  _gate,						\
+  _lock, _lock_retries,					\
+  _flags)						\
+	{						\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = nitems(_pnames),	\
+		},					\
+		.offset = _offset,			\
+		.n.shift = _n_shift,			\
+		.n.width = _n_width,			\
+		.n.value = _n_value,			\
+		.n.flags = _n_flags,			\
+		.k.shift = _k_shift,			\
+		.k.width = _k_width,			\
+		.k.value = _k_value,			\
+		.k.flags = _k_flags,			\
+		.m.shift = _m_shift,			\
+		.m.width = _m_width,			\
+		.m.value = _m_value,			\
+		.m.flags = _m_flags,			\
+		.p.shift = _p_shift,			\
+		.p.width = _p_width,			\
+		.p.value = _p_value,			\
+		.p.flags = _p_flags,			\
+		.gate_shift = _gate,			\
+		.lock_shift = _lock,			\
+		.lock_retries = _lock_retries,		\
+		.flags = _flags,			\
+	},
+
+#define NM_CLK(_id, _name, _pnames,			\
+     _offset,						\
+     _nshift, _nwidth, _nvalue, _nflags,		\
+     _mshift, _mwidth, _mvalue, _mflags,		\
+    _mux_shift, _mux_width,				\
+    _gate_shift,					\
+    _flags)						\
+	{						\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = nitems(_pnames),	\
+		},					\
+		.offset = _offset,			\
+		.n.shift = _nshift,			\
+		.n.width = _nwidth,			\
+		.n.value = _nvalue,			\
+		.n.flags = _nflags,			\
+		.mux_shift = _mux_shift,		\
+		.m.shift = _mshift,			\
+		.m.width = _mwidth,			\
+		.m.value = _mvalue,			\
+		.m.flags = _mflags,			\
+		.mux_width = _mux_width,		\
+		.flags = _flags,			\
+	},
+
+#define PREDIV_CLK(_id, _name, _pnames,		\
+  _offset,	\
+  _mux_shift, _mux_width,	\
+  _div_shift, _div_width, _div_value, _div_flags,	\
+  _prediv_shift, _prediv_width, _prediv_value, _prediv_flags,	\
+  _prediv_cond_shift, _prediv_cond_width, _prediv_cond_value)	\
+	{							\
+		.clkdef = {					\
+			.id = _id,				\
+			.name = _name,				\
+			.parent_names = _pnames,		\
+			.parent_cnt = nitems(_pnames),		\
+		},						\
+		.offset = _offset,				\
+		.mux_shift = _mux_shift,			\
+		.mux_width = _mux_width,			\
+		.div.shift = _div_shift,			\
+		.div.width = _div_width,			\
+		.div.value = _div_value,			\
+		.div.flags = _div_flags,			\
+		.prediv.shift = _prediv_shift,			\
+		.prediv.width = _prediv_width,			\
+		.prediv.value = _prediv_value,			\
+		.prediv.flags = _prediv_flags,			\
+		.prediv.cond_shift = _prediv_cond_shift,	\
+		.prediv.cond_width = _prediv_cond_width,	\
+		.prediv.cond_value = _prediv_cond_value,	\
+	},
+
+#define MUX_CLK(_id, _name, _pnames,			\
+  _offset,  _shift,  _width)				\
+	{						\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = nitems(_pnames)	\
+		},					\
+		.offset = _offset,			\
+		.shift = _shift,			\
+		.width = _width,			\
+	},
+
+#define DIV_CLK(_id, _name, _pnames,			\
+  _offset,						\
+  _i_shift, _i_width,					\
+  _div_flags, _div_table)				\
+	{						\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = nitems(_pnames)	\
+		},					\
+		.offset = _offset,			\
+		.i_shift = _i_shift,			\
+		.i_width = _i_width,			\
+		.div_flags = _div_flags,		\
+		.div_table = _div_table,		\
+	},
+
+#define FIXED_CLK(_id, _name, _pnames,			\
+  _freq, _mult, _div, _flags)				\
+	{						\
+		.clkdef = {				\
+			.id = _id,			\
+			.name = _name,			\
+			.parent_names = _pnames,	\
+			.parent_cnt = 1,		\
+		},					\
+		.freq = _freq,				\
+		.mult = _mult,				\
+		.div = _div,				\
+		.fixed_flags = _flags,			\
+	},
+
+#endif /* __AW_CLK_H__ */

Added: head/sys/arm/allwinner/clkng/aw_clk_nkmp.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/arm/allwinner/clkng/aw_clk_nkmp.c	Sun Feb 26 16:00:20 2017	(r314303)
@@ -0,0 +1,362 @@
+/*-
+ * Copyright (c) 2017 Emmanuel Vadot <manu at freebsd.org>
+ * 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.
+ *
+ * $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_nkmp.h>
+
+#include "clkdev_if.h"
+
+/*
+ * clknode for clocks matching the formula :
+ *
+ * clk = (clkin * n * k) / (m * p)
+ *
+ */
+
+struct aw_clk_nkmp_sc {
+	uint32_t	offset;
+
+	struct aw_clk_factor	n;
+	struct aw_clk_factor	k;
+	struct aw_clk_factor	m;
+	struct aw_clk_factor	p;
+
+	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	MODIFY4(_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
+aw_clk_nkmp_init(struct clknode *clk, device_t dev)
+{
+	clknode_init_parent_idx(clk, 0);
+	return (0);
+}
+
+static int
+aw_clk_nkmp_set_gate(struct clknode *clk, bool enable)
+{
+	struct aw_clk_nkmp_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 uint64_t
+aw_clk_nkmp_find_best(struct aw_clk_nkmp_sc *sc, uint64_t fparent, uint64_t *fout,
+    uint32_t *factor_n, uint32_t *factor_k, uint32_t *factor_m, uint32_t *factor_p)
+{
+	uint64_t cur, best;
+	uint32_t n, k, m, p;
+
+	best = 0;
+	*factor_n = 0;
+	*factor_k = 0;
+	*factor_m = 0;
+	*factor_p = 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); ) {
+				for (p = aw_clk_factor_get_min(&sc->p); p <= aw_clk_factor_get_max(&sc->p); ) {
+					cur = (fparent * n * k) / (m * p);
+					if ((*fout - cur) < (*fout - best)) {
+						best = cur;
+						*factor_n = n;
+						*factor_k = k;
+						*factor_m = m;
+						*factor_p = p;
+					}
+					if (best == *fout)
+						return (best);
+					if ((sc->p.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+						p <<= 1;
+					else
+						p++;
+				}
+				if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+					m <<= 1;
+				else
+					m++;
+			}
+			if ((sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+				k <<= 1;
+			else
+				k++;
+		}
+		if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
+			n <<= 1;
+		else
+			n++;
+	}
+
+	return best;
+}
+
+static void
+aw_clk_nkmp_set_freq_scale(struct clknode *clk, struct aw_clk_nkmp_sc *sc,
+    uint32_t factor_n, uint32_t factor_k, uint32_t factor_m, uint32_t factor_p)
+{
+	uint32_t val, n, k, m, p;
+	int retry;
+
+	DEVICE_LOCK(clk);
+	READ4(clk, sc->offset, &val);
+
+	n = aw_clk_get_factor(val, &sc->n);
+	k = aw_clk_get_factor(val, &sc->k);
+	m = aw_clk_get_factor(val, &sc->m);
+	p = aw_clk_get_factor(val, &sc->p);
+
+	if (p < factor_p) {
+		val &= ~sc->p.mask;
+		val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
+		WRITE4(clk, sc->offset, val);
+		DELAY(2000);
+	}
+
+	if (m < factor_m) {
+		val &= ~sc->m.mask;
+		val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
+		WRITE4(clk, sc->offset, val);
+		DELAY(2000);
+	}
+
+	val &= ~sc->n.mask;
+	val &= ~sc->k.mask;
+	val |= aw_clk_factor_get_value(&sc->n, factor_n) << sc->n.shift;
+	val |= aw_clk_factor_get_value(&sc->k, factor_k) << sc->k.shift;
+	WRITE4(clk, sc->offset, val);
+	DELAY(2000);
+
+	if (m > factor_m) {
+		val &= ~sc->m.mask;
+		val |= aw_clk_factor_get_value(&sc->m, factor_m) << sc->m.shift;
+		WRITE4(clk, sc->offset, val);
+		DELAY(2000);
+	}
+
+	if (p > factor_p) {
+		val &= ~sc->p.mask;
+		val |= aw_clk_factor_get_value(&sc->p, factor_p) << sc->p.shift;
+		WRITE4(clk, sc->offset, val);
+		DELAY(2000);
+	}
+
+	if ((sc->flags & AW_CLK_HAS_LOCK) != 0) {
+		for (retry = 0; retry < sc->lock_retries; retry++) {
+			READ4(clk, sc->offset, &val);
+			if ((val & (1 << sc->lock_shift)) != 0)
+				break;
+			DELAY(1000);
+		}
+	}
+
+	DEVICE_UNLOCK(clk);
+}
+
+static int
+aw_clk_nkmp_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
+    int flags, int *stop)
+{
+	struct aw_clk_nkmp_sc *sc;
+	uint64_t best;
+	uint32_t val, best_n, best_k, best_m, best_p;
+	int retry;
+
+	sc = clknode_get_softc(clk);
+
+	best = aw_clk_nkmp_find_best(sc, fparent, fout,
+	    &best_n, &best_k, &best_m, &best_p);
+	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);
+	}
+
+	if ((sc->flags & AW_CLK_SCALE_CHANGE) != 0)
+		aw_clk_nkmp_set_freq_scale(clk, sc,
+		    best_n, best_k, best_m, best_p);
+	else {
+		DEVICE_LOCK(clk);
+		READ4(clk, sc->offset, &val);
+		val &= ~sc->n.mask;
+		val &= ~sc->k.mask;
+		val &= ~sc->m.mask;
+		val &= ~sc->p.mask;
+		val |= aw_clk_factor_get_value(&sc->n, best_n) << sc->n.shift;
+		val |= aw_clk_factor_get_value(&sc->k, best_k) << sc->k.shift;
+		val |= aw_clk_factor_get_value(&sc->m, best_m) << sc->m.shift;
+		val |= aw_clk_factor_get_value(&sc->p, best_p) << sc->p.shift;
+		WRITE4(clk, sc->offset, val);
+		DELAY(2000);

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


More information about the svn-src-head mailing list