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
This commit is contained in:
manu 2017-02-26 16:00:20 +00:00
parent 5153811035
commit a30f538779
13 changed files with 2392 additions and 0 deletions

View File

@ -0,0 +1,348 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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);

View File

@ -0,0 +1,59 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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__ */

View File

@ -0,0 +1,317 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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__ */

View File

@ -0,0 +1,362 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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);
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);
}
}
}
*fout = best;
*stop = 1;
return (0);
}
static int
aw_clk_nkmp_recalc(struct clknode *clk, uint64_t *freq)
{
struct aw_clk_nkmp_sc *sc;
uint32_t val, m, n, k, p;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
DEVICE_UNLOCK(clk);
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);
*freq = (*freq * n * k) / (m * p);
return (0);
}
static clknode_method_t aw_nkmp_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, aw_clk_nkmp_init),
CLKNODEMETHOD(clknode_set_gate, aw_clk_nkmp_set_gate),
CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nkmp_recalc),
CLKNODEMETHOD(clknode_set_freq, aw_clk_nkmp_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(aw_nkmp_clknode, aw_nkmp_clknode_class, aw_nkmp_clknode_methods,
sizeof(struct aw_clk_nkmp_sc), clknode_class);
int
aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef)
{
struct clknode *clk;
struct aw_clk_nkmp_sc *sc;
clk = clknode_create(clkdom, &aw_nkmp_clknode_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->n.shift = clkdef->n.shift;
sc->n.width = clkdef->n.width;
sc->n.mask = ((1 << clkdef->n.width) - 1) << sc->n.shift;
sc->n.value = clkdef->n.value;
sc->n.flags = clkdef->n.flags;
sc->k.shift = clkdef->k.shift;
sc->k.width = clkdef->k.width;
sc->k.mask = ((1 << clkdef->k.width) - 1) << sc->k.shift;
sc->k.value = clkdef->k.value;
sc->k.flags = clkdef->k.flags;
sc->m.shift = clkdef->m.shift;
sc->m.width = clkdef->m.width;
sc->m.mask = ((1 << clkdef->m.width) - 1) << sc->m.shift;
sc->m.value = clkdef->m.value;
sc->m.flags = clkdef->m.flags;
sc->p.shift = clkdef->p.shift;
sc->p.width = clkdef->p.width;
sc->p.mask = ((1 << clkdef->p.width) - 1) << sc->p.shift;
sc->p.value = clkdef->p.value;
sc->p.flags = clkdef->p.flags;
sc->gate_shift = clkdef->gate_shift;
sc->lock_shift = clkdef->lock_shift;
sc->lock_retries = clkdef->lock_retries;
sc->flags = clkdef->flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_NKMP_H__
#define __AW_CLK_NKMP_H__
#include <arm/allwinner/clkng/aw_clk.h>
struct aw_clk_nkmp_def {
struct clknode_init_def clkdef;
uint32_t offset;
struct aw_clk_factor m;
struct aw_clk_factor k;
struct aw_clk_factor n;
struct aw_clk_factor p;
uint32_t gate_shift;
uint32_t lock_shift;
uint32_t lock_retries;
uint32_t flags;
};
int aw_clk_nkmp_register(struct clkdom *clkdom, struct aw_clk_nkmp_def *clkdef);
#endif /* __AW_CLK_NKMP_H__ */

View File

@ -0,0 +1,315 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_nm.h>
#include "clkdev_if.h"
/*
* clknode for clocks matching the formula :
*
* clk = clkin / n / m
*
*/
struct aw_clk_nm_sc {
uint32_t offset;
struct aw_clk_factor m;
struct aw_clk_factor n;
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_nm_init(struct clknode *clk, device_t dev)
{
struct aw_clk_nm_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_nm_set_gate(struct clknode *clk, bool enable)
{
struct aw_clk_nm_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_nm_set_mux(struct clknode *clk, int index)
{
struct aw_clk_nm_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 >> sc->mux_shift);
val |= index << sc->mux_shift;
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
return (0);
}
static uint64_t
aw_clk_nm_find_best(struct aw_clk_nm_sc *sc, uint64_t fparent, uint64_t *fout,
uint32_t *factor_n, uint32_t *factor_m)
{
uint64_t cur, best;
uint32_t m, n, max_m, max_n, min_m, min_n;
*factor_n = *factor_m = 0;
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);
for (m = min_m; m <= max_m; ) {
for (n = min_m; n <= max_n; ) {
cur = fparent / n / m;
if ((*fout - cur) < (*fout - best)) {
best = cur;
*factor_n = n;
*factor_m = m;
}
if ((sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
n <<= 1;
else
n++;
}
if ((sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) != 0)
m <<= 1;
else
m++;
}
return (best);
}
static int
aw_clk_nm_set_freq(struct clknode *clk, uint64_t fparent, uint64_t *fout,
int flags, int *stop)
{
struct aw_clk_nm_sc *sc;
struct clknode *p_clk;
const char **p_names;
uint64_t cur, best;
uint32_t val, m, n, best_m, best_n;
int p_idx, best_parent;
sc = clknode_get_softc(clk);
best = cur = 0;
best_parent = 0;
if ((sc->flags & AW_CLK_REPARENT) != 0) {
p_names = clknode_get_parent_names(clk);
for (p_idx = 0; p_idx != clknode_get_parents_num(clk); p_idx++) {
p_clk = clknode_find_by_name(p_names[p_idx]);
clknode_get_freq(p_clk, &fparent);
cur = aw_clk_nm_find_best(sc, fparent, fout, &n, &m);
if ((*fout - cur) < (*fout - best)) {
best = cur;
best_parent = p_idx;
best_n = n;
best_m = m;
}
}
p_idx = clknode_get_parent_idx(clk);
p_clk = clknode_get_parent(clk);
clknode_get_freq(p_clk, &fparent);
} else
best = aw_clk_nm_find_best(sc, fparent, fout, &best_n, &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);
}
if (p_idx != best_parent)
clknode_set_parent_by_idx(clk, best_parent);
n = aw_clk_factor_get_value(&sc->n, best_n);
m = aw_clk_factor_get_value(&sc->m, best_m);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
val &= ~sc->n.mask;
val &= ~sc->m.mask;
val |= n << sc->n.shift;
val |= m << sc->m.shift;
WRITE4(clk, sc->offset, val);
DEVICE_UNLOCK(clk);
*fout = best;
*stop = 1;
return (0);
}
static int
aw_clk_nm_recalc(struct clknode *clk, uint64_t *freq)
{
struct aw_clk_nm_sc *sc;
uint32_t val, m, n;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
DEVICE_UNLOCK(clk);
m = aw_clk_get_factor(val, &sc->m);
n = aw_clk_get_factor(val, &sc->n);
*freq = *freq / n / m;
return (0);
}
static clknode_method_t aw_nm_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, aw_clk_nm_init),
CLKNODEMETHOD(clknode_set_gate, aw_clk_nm_set_gate),
CLKNODEMETHOD(clknode_set_mux, aw_clk_nm_set_mux),
CLKNODEMETHOD(clknode_recalc_freq, aw_clk_nm_recalc),
CLKNODEMETHOD(clknode_set_freq, aw_clk_nm_set_freq),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(aw_nm_clknode, aw_nm_clknode_class, aw_nm_clknode_methods,
sizeof(struct aw_clk_nm_sc), clknode_class);
int
aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef)
{
struct clknode *clk;
struct aw_clk_nm_sc *sc;
clk = clknode_create(clkdom, &aw_nm_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.flags = clkdef->m.flags;
sc->n.shift = clkdef->n.shift;
sc->n.width = clkdef->n.width;
sc->n.mask = ((1 << sc->n.width) - 1) << sc->n.shift;
sc->n.flags = clkdef->n.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);
}

View File

@ -0,0 +1,50 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_NM_H__
#define __AW_CLK_NM_H__
#include <dev/extres/clk/clk.h>
struct aw_clk_nm_def {
struct clknode_init_def clkdef;
uint32_t offset;
struct aw_clk_factor m;
struct aw_clk_factor n;
uint32_t mux_shift;
uint32_t mux_width;
uint32_t gate_shift;
uint32_t flags;
};
int aw_clk_nm_register(struct clkdom *clkdom, struct aw_clk_nm_def *clkdef);
#endif /* __AW_CLK_NM_H__ */

View File

@ -0,0 +1,169 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_prediv_mux.h>
#include "clkdev_if.h"
/*
* clknode for clocks matching the formula :
*
* clk = clkin / prediv / div
*
* and where prediv is conditional
*
*/
struct aw_clk_prediv_mux_sc {
uint32_t offset;
uint32_t mux_shift;
uint32_t mux_mask;
struct aw_clk_factor div;
struct aw_clk_factor prediv;
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_prediv_mux_init(struct clknode *clk, device_t dev)
{
clknode_init_parent_idx(clk, 0);
return (0);
}
static int
aw_clk_prediv_mux_set_mux(struct clknode *clk, int index)
{
struct aw_clk_prediv_mux_sc *sc;
uint32_t val;
sc = clknode_get_softc(clk);
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 int
aw_clk_prediv_mux_recalc(struct clknode *clk, uint64_t *freq)
{
struct aw_clk_prediv_mux_sc *sc;
uint32_t val, div, prediv;
sc = clknode_get_softc(clk);
DEVICE_LOCK(clk);
READ4(clk, sc->offset, &val);
DEVICE_UNLOCK(clk);
div = aw_clk_get_factor(val, &sc->div);
prediv = aw_clk_get_factor(val, &sc->prediv);
*freq = *freq / prediv / div;
return (0);
}
static clknode_method_t aw_prediv_mux_clknode_methods[] = {
/* Device interface */
CLKNODEMETHOD(clknode_init, aw_clk_prediv_mux_init),
CLKNODEMETHOD(clknode_set_mux, aw_clk_prediv_mux_set_mux),
CLKNODEMETHOD(clknode_recalc_freq, aw_clk_prediv_mux_recalc),
CLKNODEMETHOD_END
};
DEFINE_CLASS_1(aw_prediv_mux_clknode, aw_prediv_mux_clknode_class,
aw_prediv_mux_clknode_methods, sizeof(struct aw_clk_prediv_mux_sc),
clknode_class);
int
aw_clk_prediv_mux_register(struct clkdom *clkdom, struct aw_clk_prediv_mux_def *clkdef)
{
struct clknode *clk;
struct aw_clk_prediv_mux_sc *sc;
clk = clknode_create(clkdom, &aw_prediv_mux_clknode_class, &clkdef->clkdef);
if (clk == NULL)
return (1);
sc = clknode_get_softc(clk);
sc->offset = clkdef->offset;
sc->mux_shift = clkdef->mux_shift;
sc->mux_mask = ((1 << clkdef->mux_width) - 1) << sc->mux_shift;
sc->div.shift = clkdef->div.shift;
sc->div.mask = ((1 << clkdef->div.width) - 1) << sc->div.shift;
sc->div.value = clkdef->div.value;
sc->div.cond_shift = clkdef->div.cond_shift;
sc->div.cond_mask = ((1 << clkdef->div.cond_width) - 1) << sc->div.shift;
sc->div.cond_value = clkdef->div.cond_value;
sc->div.flags = clkdef->div.flags;
sc->prediv.shift = clkdef->prediv.shift;
sc->prediv.mask = ((1 << clkdef->prediv.width) - 1) << sc->prediv.shift;
sc->prediv.value = clkdef->prediv.value;
sc->prediv.cond_shift = clkdef->prediv.cond_shift;
sc->prediv.cond_mask = ((1 << clkdef->prediv.cond_width) - 1) << sc->prediv.shift;
sc->prediv.cond_value = clkdef->prediv.cond_value;
sc->prediv.flags = clkdef->prediv.flags;
sc->flags = clkdef->flags;
clknode_register(clkdom, clk);
return (0);
}

View File

@ -0,0 +1,49 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_PREDIV_MUX_H__
#define __AW_CLK_PREDIV_MUX_H__
#include <arm/allwinner/clkng/aw_clk.h>
struct aw_clk_prediv_mux_def {
struct clknode_init_def clkdef;
uint32_t offset;
uint32_t mux_shift;
uint32_t mux_width;
struct aw_clk_factor div;
struct aw_clk_factor prediv;
uint32_t flags;
};
int aw_clk_prediv_mux_register(struct clkdom *clkdom, struct aw_clk_prediv_mux_def *clkdef);
#endif /* __AW_CLK_PREDIV_MUX_H__ */

View File

@ -0,0 +1,475 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_div.h>
#include <dev/extres/clk/clk_fixed.h>
#include <dev/extres/clk/clk_mux.h>
#include <arm/allwinner/clkng/aw_ccung.h>
#include <arm/allwinner/clkng/aw_clk.h>
#include <arm/allwinner/clkng/aw_clk_nm.h>
#include <arm/allwinner/clkng/aw_clk_nkmp.h>
#include <arm/allwinner/clkng/aw_clk_prediv_mux.h>
#include "ccu_h3.h"
static struct aw_ccung_reset h3_ccu_resets[] = {
CCU_RESET(H3_RST_USB_PHY0, 0xcc, 0)
CCU_RESET(H3_RST_USB_PHY1, 0xcc, 1)
CCU_RESET(H3_RST_USB_PHY2, 0xcc, 2)
CCU_RESET(H3_RST_USB_PHY3, 0xcc, 3)
CCU_RESET(H3_RST_MBUS, 0xfc, 31)
CCU_RESET(H3_RST_BUS_CE, 0x2c0, 5)
CCU_RESET(H3_RST_BUS_DMA, 0x2c0, 6)
CCU_RESET(H3_RST_BUS_MMC0, 0x2c0, 8)
CCU_RESET(H3_RST_BUS_MMC1, 0x2c0, 9)
CCU_RESET(H3_RST_BUS_MMC2, 0x2c0, 10)
CCU_RESET(H3_RST_BUS_NAND, 0x2c0, 13)
CCU_RESET(H3_RST_BUS_DRAM, 0x2c0, 14)
CCU_RESET(H3_RST_BUS_EMAC, 0x2c0, 17)
CCU_RESET(H3_RST_BUS_TS, 0x2c0, 18)
CCU_RESET(H3_RST_BUS_HSTIMER, 0x2c0, 19)
CCU_RESET(H3_RST_BUS_SPI0, 0x2c0, 20)
CCU_RESET(H3_RST_BUS_SPI1, 0x2c0, 21)
CCU_RESET(H3_RST_BUS_OTG, 0x2c0, 23)
CCU_RESET(H3_RST_BUS_EHCI0, 0x2c0, 24)
CCU_RESET(H3_RST_BUS_EHCI1, 0x2c0, 25)
CCU_RESET(H3_RST_BUS_EHCI2, 0x2c0, 26)
CCU_RESET(H3_RST_BUS_EHCI3, 0x2c0, 27)
CCU_RESET(H3_RST_BUS_OHCI0, 0x2c0, 28)
CCU_RESET(H3_RST_BUS_OHCI1, 0x2c0, 29)
CCU_RESET(H3_RST_BUS_OHCI2, 0x2c0, 30)
CCU_RESET(H3_RST_BUS_OHCI3, 0x2c0, 31)
CCU_RESET(H3_RST_BUS_VE, 0x2c4, 0)
CCU_RESET(H3_RST_BUS_TCON0, 0x2c4, 3)
CCU_RESET(H3_RST_BUS_TCON1, 0x2c4, 4)
CCU_RESET(H3_RST_BUS_DEINTERLACE, 0x2c4, 5)
CCU_RESET(H3_RST_BUS_CSI, 0x2c4, 8)
CCU_RESET(H3_RST_BUS_TVE, 0x2c4, 9)
CCU_RESET(H3_RST_BUS_HDMI0, 0x2c4, 10)
CCU_RESET(H3_RST_BUS_HDMI1, 0x2c4, 11)
CCU_RESET(H3_RST_BUS_DE, 0x2c4, 12)
CCU_RESET(H3_RST_BUS_GPU, 0x2c4, 20)
CCU_RESET(H3_RST_BUS_MSGBOX, 0x2c4, 21)
CCU_RESET(H3_RST_BUS_SPINLOCK, 0x2c4, 22)
CCU_RESET(H3_RST_BUS_DBG, 0x2c4, 31)
CCU_RESET(H3_RST_BUS_EPHY, 0x2c8, 2)
CCU_RESET(H3_RST_BUS_CODEC, 0x2d0, 0)
CCU_RESET(H3_RST_BUS_SPDIF, 0x2d0, 1)
CCU_RESET(H3_RST_BUS_THS, 0x2d0, 8)
CCU_RESET(H3_RST_BUS_I2S0, 0x2d0, 12)
CCU_RESET(H3_RST_BUS_I2S1, 0x2d0, 13)
CCU_RESET(H3_RST_BUS_I2S2, 0x2d0, 14)
CCU_RESET(H3_RST_BUS_I2C0, 0x2d8, 0)
CCU_RESET(H3_RST_BUS_I2C1, 0x2d8, 1)
CCU_RESET(H3_RST_BUS_I2C2, 0x2d8, 2)
CCU_RESET(H3_RST_BUS_UART0, 0x2d8, 16)
CCU_RESET(H3_RST_BUS_UART1, 0x2d8, 17)
CCU_RESET(H3_RST_BUS_UART2, 0x2d8, 18)
CCU_RESET(H3_RST_BUS_UART3, 0x2d8, 19)
CCU_RESET(H3_RST_BUS_SCR, 0x2d8, 20)
};
static struct aw_ccung_gate h3_ccu_gates[] = {
CCU_GATE(H3_CLK_BUS_CE, "bus-ce", "ahb1", 0x60, 5)
CCU_GATE(H3_CLK_BUS_DMA, "bus-dma", "ahb1", 0x60, 6)
CCU_GATE(H3_CLK_BUS_MMC0, "bus-mmc0", "ahb1", 0x60, 8)
CCU_GATE(H3_CLK_BUS_MMC1, "bus-mmc1", "ahb1", 0x60, 9)
CCU_GATE(H3_CLK_BUS_MMC2, "bus-mmc2", "ahb1", 0x60, 10)
CCU_GATE(H3_CLK_BUS_NAND, "bus-nand", "ahb1", 0x60, 13)
CCU_GATE(H3_CLK_BUS_DRAM, "bus-dram", "ahb1", 0x60, 14)
CCU_GATE(H3_CLK_BUS_EMAC, "bus-emac", "ahb2", 0x60, 17)
CCU_GATE(H3_CLK_BUS_TS, "bus-ts", "ahb1", 0x60, 18)
CCU_GATE(H3_CLK_BUS_HSTIMER, "bus-hstimer", "ahb1", 0x60, 19)
CCU_GATE(H3_CLK_BUS_SPI0, "bus-spi0", "ahb1", 0x60, 20)
CCU_GATE(H3_CLK_BUS_SPI1, "bus-spi1", "ahb1", 0x60, 21)
CCU_GATE(H3_CLK_BUS_OTG, "bus-otg", "ahb1", 0x60, 23)
CCU_GATE(H3_CLK_BUS_EHCI0, "bus-ehci0", "ahb1", 0x60, 24)
CCU_GATE(H3_CLK_BUS_EHCI1, "bus-ehci1", "ahb2", 0x60, 25)
CCU_GATE(H3_CLK_BUS_EHCI2, "bus-ehci2", "ahb2", 0x60, 26)
CCU_GATE(H3_CLK_BUS_EHCI3, "bus-ehci3", "ahb2", 0x60, 27)
CCU_GATE(H3_CLK_BUS_OHCI0, "bus-ohci0", "ahb1", 0x60, 28)
CCU_GATE(H3_CLK_BUS_OHCI1, "bus-ohci1", "ahb2", 0x60, 29)
CCU_GATE(H3_CLK_BUS_OHCI2, "bus-ohci2", "ahb2", 0x60, 30)
CCU_GATE(H3_CLK_BUS_OHCI3, "bus-ohci3", "ahb2", 0x60, 31)
CCU_GATE(H3_CLK_BUS_VE, "bus-ve", "ahb1", 0x64, 0)
CCU_GATE(H3_CLK_BUS_TCON0, "bus-tcon0", "ahb1", 0x64, 3)
CCU_GATE(H3_CLK_BUS_TCON1, "bus-tcon1", "ahb1", 0x64, 4)
CCU_GATE(H3_CLK_BUS_DEINTERLACE, "bus-deinterlace", "ahb1", 0x64, 5)
CCU_GATE(H3_CLK_BUS_CSI, "bus-csi", "ahb1", 0x64, 8)
CCU_GATE(H3_CLK_BUS_TVE, "bus-tve", "ahb1", 0x64, 9)
CCU_GATE(H3_CLK_BUS_HDMI, "bus-hdmi", "ahb1", 0x64, 11)
CCU_GATE(H3_CLK_BUS_DE, "bus-de", "ahb1", 0x64, 12)
CCU_GATE(H3_CLK_BUS_GPU, "bus-gpu", "ahb1", 0x64, 20)
CCU_GATE(H3_CLK_BUS_MSGBOX, "bus-msgbox", "ahb1", 0x64, 21)
CCU_GATE(H3_CLK_BUS_SPINLOCK, "bus-spinlock", "ahb1", 0x64, 22)
CCU_GATE(H3_CLK_BUS_CODEC, "bus-codec", "apb1", 0x68, 0)
CCU_GATE(H3_CLK_BUS_SPDIF, "bus-spdif", "apb1", 0x68, 1)
CCU_GATE(H3_CLK_BUS_PIO, "bus-pio", "apb1", 0x68, 5)
CCU_GATE(H3_CLK_BUS_THS, "bus-ths", "apb1", 0x68, 8)
CCU_GATE(H3_CLK_BUS_I2S0, "bus-i2c0", "apb1", 0x68, 12)
CCU_GATE(H3_CLK_BUS_I2S1, "bus-i2c1", "apb1", 0x68, 13)
CCU_GATE(H3_CLK_BUS_I2S2, "bus-i2c2", "apb1", 0x68, 14)
CCU_GATE(H3_CLK_BUS_I2C0, "bus-i2c0", "apb2", 0x6c, 0)
CCU_GATE(H3_CLK_BUS_I2C1, "bus-i2c1", "apb2", 0x6c, 1)
CCU_GATE(H3_CLK_BUS_I2C2, "bus-i2c2", "apb2", 0x6c, 2)
CCU_GATE(H3_CLK_BUS_UART0, "bus-uart0", "apb2", 0x6c, 16)
CCU_GATE(H3_CLK_BUS_UART1, "bus-uart1", "apb2", 0x6c, 17)
CCU_GATE(H3_CLK_BUS_UART2, "bus-uart2", "apb2", 0x6c, 18)
CCU_GATE(H3_CLK_BUS_UART3, "bus-uart3", "apb2", 0x6c, 19)
CCU_GATE(H3_CLK_BUS_SCR, "bus-scr", "apb2", 0x6c, 20)
CCU_GATE(H3_CLK_BUS_EPHY, "bus-ephy", "ahb1", 0x70, 0)
CCU_GATE(H3_CLK_BUS_DBG, "bus-dbg", "ahb1", 0x70, 7)
CCU_GATE(H3_CLK_USBPHY0, "usb-phy0", "osc24M", 0xcc, 8)
CCU_GATE(H3_CLK_USBPHY1, "usb-phy1", "osc24M", 0xcc, 9)
CCU_GATE(H3_CLK_USBPHY2, "usb-phy2", "osc24M", 0xcc, 10)
CCU_GATE(H3_CLK_USBPHY3, "usb-phy3", "osc24M", 0xcc, 11)
CCU_GATE(H3_CLK_USBOHCI0, "usb-ohci0", "osc24M", 0xcc, 16)
CCU_GATE(H3_CLK_USBOHCI1, "usb-ohci1", "osc24M", 0xcc, 17)
CCU_GATE(H3_CLK_USBOHCI2, "usb-ohci2", "osc24M", 0xcc, 18)
CCU_GATE(H3_CLK_USBOHCI3, "usb-ohci3", "osc24M", 0xcc, 19)
CCU_GATE(H3_CLK_THS, "ths", "thsdiv", 0x74, 31)
CCU_GATE(H3_CLK_I2S0, "i2s0", "i2s0mux", 0xB0, 31)
CCU_GATE(H3_CLK_I2S1, "i2s1", "i2s1mux", 0xB4, 31)
CCU_GATE(H3_CLK_I2S2, "i2s2", "i2s2mux", 0xB8, 31)
};
static const char *pll_cpux_parents[] = {"osc24M"};
static const char *pll_audio_parents[] = {"osc24M"};
static const char *pll_audio_mult_parents[] = {"pll_audio"};
/*
* Need fractional mode on nkmp or a NM fract
static const char *pll_video_parents[] = {"osc24M"};
*/
/*
* Need fractional mode on nkmp or a NM fract
static const char *pll_ve_parents[] = {"osc24M"};
*/
/*
* Needs a update bit on nkmp or special clk
static const char *pll_ddr_parents[] = {"osc24M"};
*/
static const char *pll_periph0_parents[] = {"osc24M"};
static const char *pll_periph0_2x_parents[] = {"pll_periph0"};
/*
* Need fractional mode on nkmp or a NM fract
static const char *pll_gpu_parents[] = {"osc24M"};
*/
static const char *pll_periph1_parents[] = {"osc24M"};
/*
* Need fractional mode on nkmp or a NM fract
static const char *pll_de_parents[] = {"osc24M"};
*/
static struct aw_clk_nkmp_def nkmp_clks[] = {
NKMP_CLK(H3_CLK_PLL_CPUX, /* id */
"pll_cpux", pll_cpux_parents, /* name, parents */
0x00, /* offset */
8, 5, 0, 0, /* n factor */
4, 2, 0, 0, /* k factor */
0, 2, 0, 0, /* m factor */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* p factor */
31, /* gate */
28, 1000, /* lock */
AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK | AW_CLK_SCALE_CHANGE) /* flags */
NKMP_CLK(H3_CLK_PLL_AUDIO, /* id */
"pll_audio", pll_audio_parents, /* name, parents */
0x08, /* offset */
8, 7, 0, 0, /* n factor */
0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */
0, 5, 0, 0, /* m factor */
16, 4, 0, 0, /* p factor */
31, /* gate */
28, 1000, /* lock */
AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK) /* flags */
NKMP_CLK(H3_CLK_PLL_PERIPH0, /* id */
"pll_periph0", pll_periph0_parents, /* name, parents */
0x28, /* offset */
8, 5, 0, 0, /* n factor */
4, 2, 0, 0, /* k factor */
0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
31, /* gate */
28, 1000, /* lock */
AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK) /* flags */
NKMP_CLK(H3_CLK_PLL_PERIPH1, /* id */
"pll_periph1", pll_periph1_parents, /* name, parents */
0x44, /* offset */
8, 5, 0, 0, /* n factor */
4, 2, 0, 0, /* k factor */
0, 0, 2, AW_CLK_FACTOR_FIXED, /* m factor (fake) */
0, 0, 1, AW_CLK_FACTOR_FIXED, /* p factor (fake) */
31, /* gate */
28, 1000, /* lock */
AW_CLK_HAS_GATE | AW_CLK_HAS_LOCK) /* flags */
};
static const char *ahb1_parents[] = {"osc32k", "osc24M", "axi", "pll_periph0"};
static const char *ahb2_parents[] = {"ahb1", "pll_periph0"};
static struct aw_clk_prediv_mux_def prediv_mux_clks[] = {
PREDIV_CLK(H3_CLK_AHB1, /* id */
"ahb1", ahb1_parents, /* name, parents */
0x54, /* offset */
12, 2, /* mux */
4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */
6, 2, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */
12, 2, 3) /* prediv condition */
PREDIV_CLK(H3_CLK_AHB2, /* id */
"ahb2", ahb2_parents, /* name, parents */
0x5c, /* offset */
0, 2, /* mux */
0, 0, 1, AW_CLK_FACTOR_FIXED, /* div */
0, 0, 2, AW_CLK_FACTOR_HAS_COND | AW_CLK_FACTOR_FIXED, /* prediv */
0, 2, 1) /* prediv condition */
};
static const char *apb2_parents[] = {"osc32k", "osc24M", "pll_periph0", "pll_periph0"};
static const char *mod_parents[] = {"osc24M", "pll_periph0", "pll_periph1"};
static const char *ts_parents[] = {"osc24M", "pll_periph0"};
static const char *spdif_parents[] = {"pll_audio"};
static const char *i2s_parents[] = {"pll_audio-8x", "pll_audio-4x", "pll_audio-2x", "pll_audio"};
static struct aw_clk_nm_def nm_clks[] = {
NM_CLK(H3_CLK_APB2, /* id */
"apb2", apb2_parents, /* name, parents */
0x58, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 5, 0, 0, /* m factor */
24, 2, /* mux */
0, /* gate */
AW_CLK_HAS_MUX)
NM_CLK(H3_CLK_NAND, "nand", mod_parents, /* id, name, parents */
0x80, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX) /* flags */
NM_CLK(H3_CLK_MMC0, "mmc0", mod_parents, /* id, name, parents */
0x88, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
AW_CLK_REPARENT) /* flags */
NM_CLK(H3_CLK_MMC1, "mmc1", mod_parents, /* id, name, parents */
0x8c, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
AW_CLK_REPARENT) /* flags */
NM_CLK(H3_CLK_MMC2, "mmc2", mod_parents, /* id, name, parents */
0x90, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
AW_CLK_REPARENT) /* flags */
NM_CLK(H3_CLK_TS, "ts", ts_parents, /* id, name, parents */
0x98, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX) /* flags */
NM_CLK(H3_CLK_CE, "ce", mod_parents, /* id, name, parents */
0x9C, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX) /* flags */
NM_CLK(H3_CLK_SPI0, "spi0", mod_parents, /* id, name, parents */
0xA0, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
AW_CLK_REPARENT) /* flags */
NM_CLK(H3_CLK_SPI1, "spi1", mod_parents, /* id, name, parents */
0xA4, /* offset */
16, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* n factor */
0, 4, 0, 0, /* m factor */
24, 2, /* mux */
31, /* gate */
AW_CLK_HAS_GATE | AW_CLK_HAS_MUX |
AW_CLK_REPARENT) /* flags */
NM_CLK(H3_CLK_SPDIF, "spdif", spdif_parents, /* id, name, parents */
0xC0, /* offset */
0, 0, 1, AW_CLK_FACTOR_FIXED, /* n factor (fake) */
0, 4, 0, 0, /* m factor */
0, 0, /* mux */
31, /* gate */
AW_CLK_HAS_GATE) /* flags */
};
static const char *cpux_parents[] = {"osc32k", "osc24M", "pll_cpux", "pll_cpux"};
static struct clk_mux_def mux_clks[] = {
MUX_CLK(H3_CLK_CPUX, /* id */
"cpux", cpux_parents, /* name, parents */
0x50, 16, 2) /* offset, shift, width */
MUX_CLK(0,
"i2s0mux", i2s_parents,
0xb0, 16, 2)
MUX_CLK(0,
"i2s1mux", i2s_parents,
0xb4, 16, 2)
MUX_CLK(0,
"i2s2mux", i2s_parents,
0xb8, 16, 2)
};
static struct clk_div_table apb1_div_table[] = {
{ .value = 0, .divider = 2, },
{ .value = 1, .divider = 2, },
{ .value = 2, .divider = 4, },
{ .value = 3, .divider = 8, },
{ },
};
static struct clk_div_table ths_div_table[] = {
{ .value = 0, .divider = 1, },
{ .value = 1, .divider = 2, },
{ .value = 2, .divider = 4, },
{ .value = 3, .divider = 6, },
{ },
};
static const char *ths_parents[] = {"osc24M"};
static const char *axi_parents[] = {"cpux"};
static const char *apb1_parents[] = {"ahb1"};
static struct clk_div_def div_clks[] = {
DIV_CLK(H3_CLK_AXI, /* id */
"axi", axi_parents, /* name, parents */
0x50, /* offset */
0, 2, /* shift, width */
0, NULL) /* flags, div table */
DIV_CLK(H3_CLK_APB1, /* id */
"apb1", apb1_parents, /* name, parents */
0x54, /* offset */
8, 2, /* shift, width */
CLK_DIV_WITH_TABLE, /* flags */
apb1_div_table) /* div table */
DIV_CLK(0, /* id */
"thsdiv", ths_parents, /* name, parents */
0x74, /* offset */
0, 2, /* shift, width */
CLK_DIV_WITH_TABLE, /* flags */
ths_div_table) /* div table */
};
static struct clk_fixed_def fixed_factor_clks[] = {
FIXED_CLK(H3_CLK_PLL_PERIPH0_2X, /* id */
"pll_periph0-2x", /* name */
pll_periph0_2x_parents, /* parent */
0, /* freq */
2, /* mult */
1, /* div */
0) /* flags */
FIXED_CLK(H3_CLK_PLL_AUDIO_2X, /* id */
"pll_audio-2x", /* name */
pll_audio_mult_parents, /* parent */
0, /* freq */
2, /* mult */
1, /* div */
0) /* flags */
FIXED_CLK(H3_CLK_PLL_AUDIO_4X, /* id */
"pll_audio-4x", /* name */
pll_audio_mult_parents, /* parent */
0, /* freq */
4, /* mult */
1, /* div */
0) /* flags */
FIXED_CLK(H3_CLK_PLL_AUDIO_8X, /* id */
"pll_audio-8x", /* name */
pll_audio_mult_parents, /* parent */
0, /* freq */
8, /* mult */
1, /* div */
0) /* flags */
};
static struct aw_clk_init init_clks[] = {
{"ahb1", "pll_periph0", 0, false},
{"ahb2", "pll_periph0", 0, false},
};
void
ccu_h3_register_clocks(struct aw_ccung_softc *sc)
{
int i;
sc->resets = h3_ccu_resets;
sc->nresets = nitems(h3_ccu_resets);
sc->gates = h3_ccu_gates;
sc->ngates = nitems(h3_ccu_gates);
sc->clk_init = init_clks;
sc->n_clk_init = nitems(init_clks);
for (i = 0; i < nitems(nkmp_clks); i++)
aw_clk_nkmp_register(sc->clkdom, &nkmp_clks[i]);
for (i = 0; i < nitems(nm_clks); i++)
aw_clk_nm_register(sc->clkdom, &nm_clks[i]);
for (i = 0; i < nitems(prediv_mux_clks); i++)
aw_clk_prediv_mux_register(sc->clkdom, &prediv_mux_clks[i]);
for (i = 0; i < nitems(mux_clks); i++)
clknode_mux_register(sc->clkdom, &mux_clks[i]);
for (i = 0; i < nitems(div_clks); i++)
clknode_div_register(sc->clkdom, &div_clks[i]);
for (i = 0; i < nitems(fixed_factor_clks); i++)
clknode_fixed_register(sc->clkdom, &fixed_factor_clks[i]);
}

View File

@ -0,0 +1,189 @@
/*-
* Copyright (c) 2017 Emmanuel Vadot <manu@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_H3_H__
#define __CCU_H3_H__
#define H3_RST_USB_PHY0 0
#define H3_RST_USB_PHY1 1
#define H3_RST_USB_PHY2 2
#define H3_RST_USB_PHY3 3
#define H3_RST_MBUS 4
#define H3_RST_BUS_CE 5
#define H3_RST_BUS_DMA 6
#define H3_RST_BUS_MMC0 7
#define H3_RST_BUS_MMC1 8
#define H3_RST_BUS_MMC2 9
#define H3_RST_BUS_NAND 10
#define H3_RST_BUS_DRAM 11
#define H3_RST_BUS_EMAC 12
#define H3_RST_BUS_TS 13
#define H3_RST_BUS_HSTIMER 14
#define H3_RST_BUS_SPI0 15
#define H3_RST_BUS_SPI1 16
#define H3_RST_BUS_OTG 17
#define H3_RST_BUS_EHCI0 18
#define H3_RST_BUS_EHCI1 19
#define H3_RST_BUS_EHCI2 20
#define H3_RST_BUS_EHCI3 21
#define H3_RST_BUS_OHCI0 22
#define H3_RST_BUS_OHCI1 23
#define H3_RST_BUS_OHCI2 24
#define H3_RST_BUS_OHCI3 25
#define H3_RST_BUS_VE 26
#define H3_RST_BUS_TCON0 27
#define H3_RST_BUS_TCON1 28
#define H3_RST_BUS_DEINTERLACE 29
#define H3_RST_BUS_CSI 30
#define H3_RST_BUS_TVE 31
#define H3_RST_BUS_HDMI0 32
#define H3_RST_BUS_HDMI1 33
#define H3_RST_BUS_DE 34
#define H3_RST_BUS_GPU 35
#define H3_RST_BUS_MSGBOX 36
#define H3_RST_BUS_SPINLOCK 37
#define H3_RST_BUS_DBG 38
#define H3_RST_BUS_EPHY 39
#define H3_RST_BUS_CODEC 40
#define H3_RST_BUS_SPDIF 41
#define H3_RST_BUS_THS 42
#define H3_RST_BUS_I2S0 43
#define H3_RST_BUS_I2S1 44
#define H3_RST_BUS_I2S2 45
#define H3_RST_BUS_I2C0 46
#define H3_RST_BUS_I2C1 47
#define H3_RST_BUS_I2C2 48
#define H3_RST_BUS_UART0 49
#define H3_RST_BUS_UART1 50
#define H3_RST_BUS_UART2 51
#define H3_RST_BUS_UART3 52
#define H3_RST_BUS_SCR 53
#define H3_CLK_PLL_CPUX 0
#define H3_CLK_PLL_AUDIO_BASE 1
#define H3_CLK_PLL_AUDIO 2
#define H3_CLK_PLL_AUDIO_2X 3
#define H3_CLK_PLL_AUDIO_4X 4
#define H3_CLK_PLL_AUDIO_8X 5
#define H3_CLK_PLL_VIDEO 6
#define H3_CLK_PLL_VE 7
#define H3_CLK_PLL_DDR 8
#define H3_CLK_PLL_PERIPH0 9
#define H3_CLK_PLL_PERIPH0_2X 10
#define H3_CLK_PLL_GPU 11
#define H3_CLK_PLL_PERIPH1 12
#define H3_CLK_PLL_DE 13
#define H3_CLK_CPUX 14
#define H3_CLK_AXI 15
#define H3_CLK_AHB1 16
#define H3_CLK_APB1 17
#define H3_CLK_APB2 18
#define H3_CLK_AHB2 19
#define H3_CLK_BUS_CE 20
#define H3_CLK_BUS_DMA 21
#define H3_CLK_BUS_MMC0 22
#define H3_CLK_BUS_MMC1 23
#define H3_CLK_BUS_MMC2 24
#define H3_CLK_BUS_NAND 25
#define H3_CLK_BUS_DRAM 26
#define H3_CLK_BUS_EMAC 27
#define H3_CLK_BUS_TS 28
#define H3_CLK_BUS_HSTIMER 29
#define H3_CLK_BUS_SPI0 30
#define H3_CLK_BUS_SPI1 31
#define H3_CLK_BUS_OTG 32
#define H3_CLK_BUS_EHCI0 33
#define H3_CLK_BUS_EHCI1 34
#define H3_CLK_BUS_EHCI2 35
#define H3_CLK_BUS_EHCI3 36
#define H3_CLK_BUS_OHCI0 37
#define H3_CLK_BUS_OHCI1 38
#define H3_CLK_BUS_OHCI2 39
#define H3_CLK_BUS_OHCI3 40
#define H3_CLK_BUS_VE 41
#define H3_CLK_BUS_TCON0 42
#define H3_CLK_BUS_TCON1 43
#define H3_CLK_BUS_DEINTERLACE 44
#define H3_CLK_BUS_CSI 45
#define H3_CLK_BUS_TVE 46
#define H3_CLK_BUS_HDMI 47
#define H3_CLK_BUS_DE 48
#define H3_CLK_BUS_GPU 49
#define H3_CLK_BUS_MSGBOX 50
#define H3_CLK_BUS_SPINLOCK 51
#define H3_CLK_BUS_CODEC 52
#define H3_CLK_BUS_SPDIF 53
#define H3_CLK_BUS_PIO 54
#define H3_CLK_BUS_THS 55
#define H3_CLK_BUS_I2S0 56
#define H3_CLK_BUS_I2S1 57
#define H3_CLK_BUS_I2S2 58
#define H3_CLK_BUS_I2C0 59
#define H3_CLK_BUS_I2C1 60
#define H3_CLK_BUS_I2C2 61
#define H3_CLK_BUS_UART0 62
#define H3_CLK_BUS_UART1 63
#define H3_CLK_BUS_UART2 64
#define H3_CLK_BUS_UART3 65
#define H3_CLK_BUS_SCR 66
#define H3_CLK_BUS_EPHY 67
#define H3_CLK_BUS_DBG 68
#define H3_CLK_THS 69
#define H3_CLK_NAND 70
#define H3_CLK_MMC0 71
#define H3_CLK_MMC0_SAMPLE 72
#define H3_CLK_MMC0_OUTPUT 73
#define H3_CLK_MMC1 74
#define H3_CLK_MMC1_SAMPLE 75
#define H3_CLK_MMC1_OUTPUT 76
#define H3_CLK_MMC2 77
#define H3_CLK_MMC2_SAMPLE 78
#define H3_CLK_MMC2_OUTPUT 79
#define H3_CLK_TS 80
#define H3_CLK_CE 81
#define H3_CLK_SPI0 82
#define H3_CLK_SPI1 83
#define H3_CLK_I2S0 84
#define H3_CLK_I2S1 85
#define H3_CLK_I2S2 86
#define H3_CLK_SPDIF 87
#define H3_CLK_USBPHY0 88
#define H3_CLK_USBPHY1 89
#define H3_CLK_USBPHY2 90
#define H3_CLK_USBPHY3 91
#define H3_CLK_USBOHCI0 92
#define H3_CLK_USBOHCI1 93
#define H3_CLK_USBOHCI2 94
#define H3_CLK_USBOHCI3 95
void ccu_h3_register_clocks(struct aw_ccung_softc *sc);
#endif /* __CCU_H3_H__ */

View File

@ -56,3 +56,8 @@ arm/allwinner/clk/aw_oscclk.c standard
arm/allwinner/clk/aw_pll.c standard
arm/allwinner/clk/aw_thsclk.c standard
arm/allwinner/clk/aw_usbclk.c standard
arm/allwinner/clkng/aw_ccung.c standard
arm/allwinner/clkng/aw_clk_nkmp.c standard
arm/allwinner/clkng/aw_clk_nm.c standard
arm/allwinner/clkng/aw_clk_prediv_mux.c standard

View File

@ -1,4 +1,5 @@
# $FreeBSD$
arm/allwinner/clkng/ccu_h3.c standard
arm/allwinner/h3/h3_padconf.c standard
arm/allwinner/h3/h3_r_padconf.c standard