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 <m...@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 <m...@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 <m...@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 <m...@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 ***
_______________________________________________
svn-src-head@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-head
To unsubscribe, send any mail to "svn-src-head-unsubscr...@freebsd.org"

Reply via email to