Index: sys/arm/allwinner/clkng/aw_ccung.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_ccung.h @@ -0,0 +1,59 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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__ */ Index: sys/arm/allwinner/clkng/aw_ccung.c =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_ccung.c @@ -0,0 +1,339 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include + +#include +#include +#include + +#include "clkdev_if.h" +#include "hwreset_if.h" + +static struct resource_spec aw_ccung_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, + { -1, 0 } +}; + +#define H3_CCU 1 + +static struct ofw_compat_data compat_data[] = { + { "allwinner,sun8i-h3-ccu", H3_CCU }, + {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) { + case H3_CCU: + ccu_h3_register_clocks(sc); + break; + } + + 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); Index: sys/arm/allwinner/clkng/aw_clk.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk.h @@ -0,0 +1,317 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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__ */ Index: sys/arm/allwinner/clkng/aw_clk_nkmp.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_nkmp.h @@ -0,0 +1,53 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 + +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__ */ Index: sys/arm/allwinner/clkng/aw_clk_nkmp.c =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_nkmp.c @@ -0,0 +1,362 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include + +#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)) + 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) + p <<= 1; + else + p++; + } + if (sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) + m <<= 1; + else + m++; + } + if (sc->k.flags & AW_CLK_FACTOR_POWER_OF_TWO) + k <<= 1; + else + k++; + } + if (sc->n.flags & AW_CLK_FACTOR_POWER_OF_TWO) + 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) { + 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) { + *fout = best; + *stop = 1; + return (0); + } + + if ((best < *fout) && + (!(flags & CLK_SET_ROUND_DOWN))) { + *stop = 1; + return (ERANGE); + } + if ((best > *fout) && + (!(flags & CLK_SET_ROUND_UP))) { + *stop = 1; + return (ERANGE); + } + + if (sc->flags & AW_CLK_SCALE_CHANGE) + 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) { + 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); +} Index: sys/arm/allwinner/clkng/aw_clk_nm.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_nm.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 + +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__ */ Index: sys/arm/allwinner/clkng/aw_clk_nm.c =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_nm.c @@ -0,0 +1,315 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include + +#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) { + 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)) + 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)) + 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) + n <<= 1; + else + n++; + } + if (sc->m.flags & AW_CLK_FACTOR_POWER_OF_TWO) + 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) { + 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) { + *fout = best; + *stop = 1; + return (0); + } + + if ((best < *fout) && + (!(flags & CLK_SET_ROUND_DOWN))) { + *stop = 1; + return (ERANGE); + } + if ((best > *fout) && + (!(flags & CLK_SET_ROUND_UP))) { + *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); +} Index: sys/arm/allwinner/clkng/aw_clk_prediv_mux.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_prediv_mux.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 + +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__ */ Index: sys/arm/allwinner/clkng/aw_clk_prediv_mux.c =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/aw_clk_prediv_mux.c @@ -0,0 +1,169 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include + +#include +#include + +#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); +} Index: sys/arm/allwinner/clkng/ccu_h3.h =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/ccu_h3.h @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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__ */ Index: sys/arm/allwinner/clkng/ccu_h3.c =================================================================== --- /dev/null +++ sys/arm/allwinner/clkng/ccu_h3.c @@ -0,0 +1,475 @@ +/*- + * Copyright (c) 2017 Emmanuel Vadot + * 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 +__FBSDID("$FreeBSD$"); + +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + +#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, 6, 0, 0, /* n factor */ + 0, 0, 1, AW_CLK_FACTOR_FIXED, /* k factor (fake) */ + 0, 4, 0, 0, /* m factor */ + 16, 3, 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, 3, 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, 3, 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, 4, /* 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, mask */ + 0, NULL) /* flags, div table */ + DIV_CLK(H3_CLK_APB1, /* id */ + "apb1", apb1_parents, /* name, parents */ + 0x54, /* offset */ + 8, 2, /* shift, mask */ + CLK_DIV_WITH_TABLE, /* flags */ + apb1_div_table) /* div table */ + DIV_CLK(0, /* id */ + "thsdiv", ths_parents, /* name, parents */ + 0x74, /* offset */ + 0, 3, /* shift, mask */ + 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]); +} Index: sys/arm/allwinner/files.allwinner =================================================================== --- sys/arm/allwinner/files.allwinner +++ sys/arm/allwinner/files.allwinner @@ -56,3 +56,8 @@ 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 Index: sys/arm/allwinner/h3/files.h3 =================================================================== --- sys/arm/allwinner/h3/files.h3 +++ sys/arm/allwinner/h3/files.h3 @@ -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