Index: sys/arm/ti/clk/ti_clk_div.h =================================================================== --- sys/arm/ti/clk/ti_clk_div.h +++ sys/arm/ti/clk/ti_clk_div.h @@ -0,0 +1,57 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ARM_TI_CLK_CLK_DIV_H_ +#define _ARM_TI_CLK_CLK_DIV_H_ + +#include +#include + +#define CLK_DIV_ZERO_BASED 0x0001 /* Zero based divider. */ +#define CLK_DIV_WITH_TABLE 0x0002 /* Table to lookup the real value */ + +struct clk_div_table { + uint32_t value; + uint32_t divider; +}; + +struct ti_clk_div_def { + struct clknode_init_def clkdef; + uint32_t offset; /* Divider register offset */ + uint32_t i_shift; /* Pos of div bits in reg */ + uint32_t i_width; /* Width of div bit field */ + uint32_t f_shift; /* Fractional divide bits, */ + uint32_t f_width; /* set to 0 for int divider */ + int div_flags; /* Divider-specific flags */ + struct clk_div_table *div_table; /* Divider table */ + struct syscon *sysc; +}; + +int ti_clknode_div_register(struct clkdom *clkdom, struct ti_clk_div_def *clkdef); + +#endif /*_ARM_TI_CLK_CLK_DIV_H_*/ Index: sys/arm/ti/clk/ti_clk_div.c =================================================================== --- sys/arm/ti/clk/ti_clk_div.c +++ sys/arm/ti/clk/ti_clk_div.c @@ -0,0 +1,285 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ti_clk_div.h" +#include "clkdev_if.h" +#include "syscon_if.h" + +#define WR4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define RD4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define MD4(_clk, off, clr, set ) \ + CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int ti_clknode_div_init(struct clknode *clk, device_t dev); +static int ti_clknode_div_recalc(struct clknode *clk, uint64_t *req); +static int ti_clknode_div_set_freq(struct clknode *clknode, uint64_t fin, + uint64_t *fout, int flag, int *stop); + +struct ti_clknode_div_sc { + struct mtx *mtx; + struct resource *mem_res; + uint32_t offset; + uint32_t i_shift; + uint32_t i_mask; + uint32_t i_width; + uint32_t f_shift; + uint32_t f_mask; + uint32_t f_width; + int div_flags; + uint32_t divider; /* in natural form */ + + struct clk_div_table *div_table; + + struct syscon *sysc; +}; + +static clknode_method_t ti_clknode_div_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, ti_clknode_div_init), + CLKNODEMETHOD(clknode_recalc_freq, ti_clknode_div_recalc), + CLKNODEMETHOD(clknode_set_freq, ti_clknode_div_set_freq), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(ti_clknode_div, ti_clknode_div_class, ti_clknode_div_methods, + sizeof(struct ti_clknode_div_sc), clknode_class); + +static uint32_t +ti_clknode_div_table_get_divider(struct ti_clknode_div_sc *sc, uint32_t divider) +{ + struct clk_div_table *table; + + if (!(sc->div_flags & CLK_DIV_WITH_TABLE)) + return (divider); + + for (table = sc->div_table; table->divider != 0; table++) + if (table->value == sc->divider) + return (table->divider); + + return (0); +} + +static int +ti_clknode_div_table_get_value(struct ti_clknode_div_sc *sc, uint32_t *divider) +{ + struct clk_div_table *table; + + if (!(sc->div_flags & CLK_DIV_WITH_TABLE)) + return (0); + + for (table = sc->div_table; table->divider != 0; table++) + if (table->divider == *divider) { + *divider = table->value; + return (0); + } + + return (ENOENT); +} + +static int +ti_clknode_div_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct ti_clknode_div_sc *sc; + uint32_t i_div, f_div; + int rv; + + sc = clknode_get_softc(clk); + + if (sc->sysc) { + reg = SYSCON_READ_4(sc->sysc, sc->offset); + } else { + DEVICE_LOCK(clk); + rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + if (rv != 0) + return (rv); + } + + i_div = (reg >> sc->i_shift) & sc->i_mask; + if (!(sc->div_flags & CLK_DIV_ZERO_BASED)) + i_div++; + f_div = (reg >> sc->f_shift) & sc->f_mask; + sc->divider = i_div << sc->f_width | f_div; + + sc->divider = ti_clknode_div_table_get_divider(sc, sc->divider); + if (sc->divider == 0) + panic("%s: divider is zero!\n", clknode_get_name(clk)); + + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +ti_clknode_div_recalc(struct clknode *clk, uint64_t *freq) +{ + struct ti_clknode_div_sc *sc; + + sc = clknode_get_softc(clk); + if (sc->divider == 0) { + printf("%s: %s divider is zero!\n", clknode_get_name(clk), + __func__); + *freq = 0; + return (EINVAL); + } + *freq = (*freq << sc->f_width) / sc->divider; + return (0); +} + +static int +ti_clknode_div_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, + int flags, int *stop) +{ + struct ti_clknode_div_sc *sc; + uint64_t divider, _fin, _fout; + uint32_t div_value, reg, i_div, f_div, hw_i_div; + int rv; + + sc = clknode_get_softc(clk); + + /* For fractional divider. */ + _fin = fin << sc->f_width; + divider = (_fin + *fout / 2) / *fout; + _fout = _fin / divider; + + /* Rounding. */ + if ((flags & CLK_SET_ROUND_UP) && (*fout < _fout)) + divider--; + else if ((flags & CLK_SET_ROUND_DOWN) && (*fout > _fout)) + divider++; + + /* Break divider into integer and fractional parts. */ + i_div = divider >> sc->f_width; + f_div = divider & sc->f_mask; + + if (i_div == 0) { + printf("%s: %s integer divider is zero!\n", + clknode_get_name(clk), __func__); + return (EINVAL); + } + + hw_i_div = i_div; + if (!(sc->div_flags & CLK_DIV_ZERO_BASED)) + hw_i_div--; + + *stop = 1; + if (hw_i_div > sc->i_mask && + ((sc->div_flags & CLK_DIV_WITH_TABLE) == 0)) { + /* XXX Or only return error? */ + printf("%s: %s integer divider is too big: %u\n", + clknode_get_name(clk), __func__, hw_i_div); + hw_i_div = sc->i_mask; + *stop = 0; + } + + i_div = hw_i_div; + if (!(sc->div_flags & CLK_DIV_ZERO_BASED)) + i_div++; + divider = i_div << sc->f_width | f_div; + + if ((flags & CLK_SET_DRYRUN) == 0) { + if ((*stop != 0) && + ((flags & (CLK_SET_ROUND_UP | CLK_SET_ROUND_DOWN)) == 0) && + (*fout != (_fin / divider))) + return (ERANGE); + + div_value = divider; + if (ti_clknode_div_table_get_value(sc, &div_value) != 0) + return (ERANGE); + if (div_value != divider) + i_div = div_value; + + if (sc->sysc) { + SYSCON_MODIFY_4(sc->sysc, sc->offset, + (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift), + (i_div << sc->i_shift) | (f_div << sc->f_shift)); + reg = SYSCON_READ_4(sc->sysc, sc->offset); + } else { + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, + (sc->i_mask << sc->i_shift) | (sc->f_mask << sc->f_shift), + (i_div << sc->i_shift) | (f_div << sc->f_shift)); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + } + + sc->divider = divider; + } + + *fout = _fin / divider; + return (0); +} + +int +ti_clknode_div_register(struct clkdom *clkdom, struct ti_clk_div_def *clkdef) +{ + struct clknode *clk; + struct ti_clknode_div_sc *sc; + + clk = clknode_create(clkdom, &ti_clknode_div_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->offset = clkdef->offset; + sc->i_shift = clkdef->i_shift; + sc->i_width = clkdef->i_width; + sc->i_mask = (1 << clkdef->i_width) - 1; + sc->f_shift = clkdef->f_shift; + sc->f_width = clkdef->f_width; + sc->f_mask = (1 << clkdef->f_width) - 1; + sc->div_flags = clkdef->div_flags; + sc->div_table = clkdef->div_table; + + sc->sysc = clkdef->sysc; + + clknode_register(clkdom, clk); + return (0); +} Index: sys/arm/ti/clk/ti_clk_gate.h =================================================================== --- sys/arm/ti/clk/ti_clk_gate.h +++ sys/arm/ti/clk/ti_clk_gate.h @@ -0,0 +1,49 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _ARM_TI_CLK_CLK_GATE_H_ +#define _ARM_TI_CLK_CLK_GATE_H_ + +#include +#include + +struct ti_clk_gate_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t mask; + uint32_t on_value; + uint32_t off_value; + int gate_flags; + + struct syscon *sysc; +}; + +int ti_clknode_gate_register(struct clkdom *clkdom, struct ti_clk_gate_def *clkdef); + +#endif /* _ARM_TI_CLK_CLK_GATE_H_ */ Index: sys/arm/ti/clk/ti_clk_gate.c =================================================================== --- sys/arm/ti/clk/ti_clk_gate.c +++ sys/arm/ti/clk/ti_clk_gate.c @@ -0,0 +1,153 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ti_clk_gate.h" +#include "clkdev_if.h" +#include "syscon_if.h" + +#define WR4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define RD4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define MD4(_clk, off, clr, set ) \ + CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int ti_clknode_gate_init(struct clknode *clk, device_t dev); +static int ti_clknode_gate_set_gate(struct clknode *clk, bool enable); +struct ti_clknode_gate_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + uint32_t on_value; + uint32_t off_value; + int gate_flags; + bool ungated; + + struct syscon *sysc; +}; + +static clknode_method_t ti_clknode_gate_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, ti_clknode_gate_init), + CLKNODEMETHOD(clknode_set_gate, ti_clknode_gate_set_gate), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(ti_clknode_gate, ti_clknode_gate_class, ti_clknode_gate_methods, + sizeof(struct ti_clknode_gate_sc), clknode_class); + +static int +ti_clknode_gate_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct ti_clknode_gate_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + if (sc->sysc) { + reg = SYSCON_READ_4(sc->sysc, sc->offset); + rv = 0; + } else { + DEVICE_LOCK(clk); + rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + } + if (rv != 0) + return (rv); + reg = (reg >> sc->shift) & sc->mask; + sc->ungated = reg == sc->on_value ? 1 : 0; + clknode_init_parent_idx(clk, 0); + return (0); +} + +static int +ti_clknode_gate_set_gate(struct clknode *clk, bool enable) +{ + uint32_t reg; + struct ti_clknode_gate_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + sc->ungated = enable; + if (sc->sysc) { + SYSCON_MODIFY_4(sc->sysc, sc->offset, sc->mask << sc->shift, + (sc->ungated ? sc->on_value : sc->off_value) << sc->shift); + reg = SYSCON_READ_4(sc->sysc, sc->offset); + } else { + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, sc->mask << sc->shift, + (sc->ungated ? sc->on_value : sc->off_value) << sc->shift); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + } + return (0); +} + +int +ti_clknode_gate_register(struct clkdom *clkdom, struct ti_clk_gate_def *clkdef) +{ + struct clknode *clk; + struct ti_clknode_gate_sc *sc; + + clk = clknode_create(clkdom, &ti_clknode_gate_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->offset = clkdef->offset; + sc->shift = clkdef->shift; + sc->mask = clkdef->mask; + sc->on_value = clkdef->on_value; + sc->off_value = clkdef->off_value; + sc->gate_flags = clkdef->gate_flags; + + sc->sysc = clkdef->sysc; + + clknode_register(clkdom, clk); + return (0); +} Index: sys/arm/ti/clk/ti_clk_mux.h =================================================================== --- sys/arm/ti/clk/ti_clk_mux.h +++ sys/arm/ti/clk/ti_clk_mux.h @@ -0,0 +1,46 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef _ARM_TI_CLK_CLK_MUX_H_ +#define _ARM_TI_CLK_CLK_MUX_H_ + +#include +#include + +struct ti_clk_mux_def { + struct clknode_init_def clkdef; + uint32_t offset; + uint32_t shift; + uint32_t width; + int mux_flags; + + struct syscon *sysc; +}; + +int ti_clknode_mux_register(struct clkdom *clkdom, struct ti_clk_mux_def *clkdef); + +#endif /* _ARM_TI_CLK_CLK_MUX_H_ */ Index: sys/arm/ti/clk/ti_clk_mux.c =================================================================== --- sys/arm/ti/clk/ti_clk_mux.c +++ sys/arm/ti/clk/ti_clk_mux.c @@ -0,0 +1,151 @@ +/*- + * Copyright 2016 Michal Meloun + * 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 AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "ti_clk_mux.h" +#include "clkdev_if.h" +#include "syscon_if.h" + +#define WR4(_clk, off, val) \ + CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) +#define RD4(_clk, off, val) \ + CLKDEV_READ_4(clknode_get_device(_clk), off, val) +#define MD4(_clk, off, clr, set ) \ + CLKDEV_MODIFY_4(clknode_get_device(_clk), off, clr, set) +#define DEVICE_LOCK(_clk) \ + CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) +#define DEVICE_UNLOCK(_clk) \ + CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) + +static int ti_clknode_mux_init(struct clknode *clk, device_t dev); +static int ti_clknode_mux_set_mux(struct clknode *clk, int idx); + +struct ti_clknode_mux_sc { + uint32_t offset; + uint32_t shift; + uint32_t mask; + int mux_flags; + + struct syscon *sysc; +}; + +static clknode_method_t ti_clknode_mux_methods[] = { + /* Device interface */ + CLKNODEMETHOD(clknode_init, ti_clknode_mux_init), + CLKNODEMETHOD(clknode_set_mux, ti_clknode_mux_set_mux), + CLKNODEMETHOD_END +}; +DEFINE_CLASS_1(ti_clknode_mux, ti_clknode_mux_class, ti_clknode_mux_methods, + sizeof(struct ti_clknode_mux_sc), clknode_class); + + +static int +ti_clknode_mux_init(struct clknode *clk, device_t dev) +{ + uint32_t reg; + struct ti_clknode_mux_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + + if (sc->sysc) { + reg = SYSCON_READ_4(sc->sysc, sc->offset); + } else { + DEVICE_LOCK(clk); + rv = RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + if (rv != 0) { + return (rv); + } + } + reg = (reg >> sc->shift) & sc->mask; + clknode_init_parent_idx(clk, reg); + return (0); +} + +static int +ti_clknode_mux_set_mux(struct clknode *clk, int idx) +{ + uint32_t reg; + struct ti_clknode_mux_sc *sc; + int rv; + + sc = clknode_get_softc(clk); + + if (sc->sysc) { + SYSCON_MODIFY_4(sc->sysc, sc->offset, sc->mask << sc->shift, + (idx & sc->mask) << sc->shift); + reg = SYSCON_READ_4(sc->sysc, sc->offset); + } else { + DEVICE_LOCK(clk); + rv = MD4(clk, sc->offset, sc->mask << sc->shift, + (idx & sc->mask) << sc->shift); + if (rv != 0) { + DEVICE_UNLOCK(clk); + return (rv); + } + RD4(clk, sc->offset, ®); + DEVICE_UNLOCK(clk); + } + + return (0); +} + +int +ti_clknode_mux_register(struct clkdom *clkdom, struct ti_clk_mux_def *clkdef) +{ + struct clknode *clk; + struct ti_clknode_mux_sc *sc; + + clk = clknode_create(clkdom, &ti_clknode_mux_class, &clkdef->clkdef); + if (clk == NULL) + return (1); + + sc = clknode_get_softc(clk); + sc->offset = clkdef->offset; + sc->shift = clkdef->shift; + sc->mask = (1 << clkdef->width) - 1; + sc->mux_flags = clkdef->mux_flags; + + sc->sysc = clkdef->sysc; + + clknode_register(clkdom, clk); + return (0); +} Index: sys/arm/ti/clk/ti_divider_clock.c =================================================================== --- sys/arm/ti/clk/ti_divider_clock.c +++ sys/arm/ti/clk/ti_divider_clock.c @@ -41,11 +41,12 @@ #include #include -#include #include #include +#include "ti_clk_div.h" #include "clock_common.h" +#include "syscon_if.h" #if 0 #define DPRINTF(dev, msg...) device_printf(dev, msg) @@ -61,7 +62,7 @@ struct ti_divider_softc { device_t sc_dev; bool attach_done; - struct clk_div_def div_def; + struct ti_clk_div_def div_def; struct clock_cell_info clock_cell; struct clkdom *clkdom; @@ -91,7 +92,7 @@ return (ENXIO); } - err = clknode_div_register(sc->clkdom, &sc->div_def); + err = ti_clknode_div_register(sc->clkdom, &sc->div_def); if (err) { DPRINTF(sc->sc_dev, "clknode_div_register failed %x\n", err); return (ENXIO); @@ -132,6 +133,9 @@ sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); + + /* Get syscon */ + SYSCON_GET_HANDLE(dev, &sc->div_def.sysc); /* Grab the content of reg properties */ OF_getencprop(node, "reg", &value, sizeof(value)); Index: sys/arm/ti/clk/ti_gate_clock.c =================================================================== --- sys/arm/ti/clk/ti_gate_clock.c +++ sys/arm/ti/clk/ti_gate_clock.c @@ -41,11 +41,12 @@ #include #include -#include #include #include +#include "ti_clk_gate.h" #include "clock_common.h" +#include "syscon_if.h" #define DEBUG_GATE 0 @@ -65,7 +66,7 @@ bool attach_done; uint8_t sc_type; - struct clk_gate_def gate_def; + struct ti_clk_gate_def gate_def; struct clock_cell_info clock_cell; struct clkdom *clkdom; }; @@ -117,7 +118,7 @@ return ENXIO; } - err = clknode_gate_register(sc->clkdom, &sc->gate_def); + err = ti_clknode_gate_register(sc->clkdom, &sc->gate_def); if (err) { DPRINTF(sc->sc_dev, "clknode_gate_register failed %x\n", err); return ENXIO; @@ -143,6 +144,9 @@ sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); + + /* Get syscon */ + SYSCON_GET_HANDLE(dev, &sc->gate_def.sysc); /* Get the compatible type */ sc->sc_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; Index: sys/arm/ti/clk/ti_mux_clock.c =================================================================== --- sys/arm/ti/clk/ti_mux_clock.c +++ sys/arm/ti/clk/ti_mux_clock.c @@ -43,11 +43,12 @@ #include #include -#include #include #include +#include "ti_clk_mux.h" #include "clock_common.h" +#include "syscon_if.h" #if 0 #define DPRINTF(dev, msg...) device_printf(dev, msg) @@ -64,7 +65,7 @@ device_t sc_dev; bool attach_done; - struct clk_mux_def mux_def; + struct ti_clk_mux_def mux_def; struct clock_cell_info clock_cell; struct clkdom *clkdom; }; @@ -107,7 +108,7 @@ return ENXIO; } - err = clknode_mux_register(sc->clkdom, &sc->mux_def); + err = ti_clknode_mux_register(sc->clkdom, &sc->mux_def); if (err) { DPRINTF(sc->sc_dev, "clknode_mux_register failed %x\n", err); return ENXIO; @@ -133,6 +134,9 @@ sc = device_get_softc(dev); sc->sc_dev = dev; node = ofw_bus_get_node(dev); + + /* Get syscon */ + SYSCON_GET_HANDLE(dev, &sc->mux_def.sysc); /* Grab the content of reg properties */ OF_getencprop(node, "reg", &value, sizeof(value)); Index: sys/arm/ti/files.ti =================================================================== --- sys/arm/ti/files.ti +++ sys/arm/ti/files.ti @@ -5,7 +5,6 @@ arm/ti/ti_prcm.c standard arm/ti/ti_omap4_cm.c standard arm/ti/ti_scm.c standard -arm/ti/ti_scm_syscon.c standard arm/ti/ti_pinmux.c standard dev/mbox/mbox_if.m optional ti_mbox arm/ti/ti_mbox.c optional ti_mbox @@ -22,8 +21,12 @@ arm/ti/clk/clock_common.c standard arm/ti/clk/ti_clk_clkctrl.c standard -arm/ti/clk/ti_clkctrl.c standard arm/ti/clk/ti_clk_dpll.c standard +arm/ti/clk/ti_clk_div.c standard +arm/ti/clk/ti_clk_gate.c standard +arm/ti/clk/ti_clk_mux.c standard + +arm/ti/clk/ti_clkctrl.c standard arm/ti/clk/ti_dpll_clock.c standard arm/ti/clk/ti_mux_clock.c standard arm/ti/clk/ti_divider_clock.c standard Index: sys/arm/ti/ti_scm_syscon.c =================================================================== --- sys/arm/ti/ti_scm_syscon.c +++ sys/arm/ti/ti_scm_syscon.c @@ -1,293 +0,0 @@ -/*- - * Copyright (c) 2019 Emmanuel Vadot - * - * Copyright (c) 2020 Oskar Holmlund - * - * 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$ - */ -/* Based on sys/arm/ti/ti_sysc.c */ - -#include -__FBSDID("$FreeBSD$"); - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include - -#include "syscon_if.h" -#include -#include "clkdev_if.h" - -#if 0 -#define DPRINTF(dev, msg...) device_printf(dev, msg) -#else -#define DPRINTF(dev, msg...) -#endif - -MALLOC_DECLARE(M_SYSCON); - -struct ti_scm_syscon_softc { - struct simplebus_softc sc_simplebus; - device_t dev; - struct syscon * syscon; - struct resource * res[1]; - bus_space_tag_t bst; - bus_space_handle_t bsh; - struct mtx mtx; -}; - -static struct resource_spec ti_scm_syscon_res_spec[] = { - { SYS_RES_MEMORY, 0, RF_ACTIVE | RF_SHAREABLE }, - { -1, 0 } -}; - -/* Device */ -static struct ofw_compat_data compat_data[] = { - { "syscon", 1 }, - { NULL, 0 } -}; - -/* --- dev/extres/syscon syscon_method_t interface --- */ -static int -ti_scm_syscon_write_4(struct syscon *syscon, bus_size_t offset, uint32_t val) -{ - struct ti_scm_syscon_softc *sc; - - sc = device_get_softc(syscon->pdev); - DPRINTF(sc->dev, "offset=%lx write %x\n", offset, val); - mtx_lock(&sc->mtx); - bus_space_write_4(sc->bst, sc->bsh, offset, val); - mtx_unlock(&sc->mtx); - return (0); -} - -static uint32_t -ti_scm_syscon_read_4(struct syscon *syscon, bus_size_t offset) -{ - struct ti_scm_syscon_softc *sc; - uint32_t val; - - sc = device_get_softc(syscon->pdev); - - mtx_lock(&sc->mtx); - val = bus_space_read_4(sc->bst, sc->bsh, offset); - mtx_unlock(&sc->mtx); - DPRINTF(sc->dev, "offset=%lx Read %x\n", offset, val); - return (val); -} -static int -ti_scm_syscon_modify_4(struct syscon *syscon, bus_size_t offset, uint32_t clr, uint32_t set) -{ - struct ti_scm_syscon_softc *sc; - uint32_t reg; - - sc = device_get_softc(syscon->pdev); - - mtx_lock(&sc->mtx); - reg = bus_space_read_4(sc->bst, sc->bsh, offset); - reg &= ~clr; - reg |= set; - bus_space_write_4(sc->bst, sc->bsh, offset, reg); - mtx_unlock(&sc->mtx); - DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", offset, reg, clr, set); - - return (0); -} - -static syscon_method_t ti_scm_syscon_reg_methods[] = { - SYSCONMETHOD(syscon_read_4, ti_scm_syscon_read_4), - SYSCONMETHOD(syscon_write_4, ti_scm_syscon_write_4), - SYSCONMETHOD(syscon_modify_4, ti_scm_syscon_modify_4), - - SYSCONMETHOD_END -}; - -DEFINE_CLASS_1(ti_scm_syscon_reg, ti_scm_syscon_reg_class, ti_scm_syscon_reg_methods, - 0, syscon_class); - -/* device interface */ -static int -ti_scm_syscon_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, "TI OMAP Control Module Syscon"); - return(BUS_PROBE_DEFAULT); -} - -static int -ti_scm_syscon_attach(device_t dev) -{ - struct ti_scm_syscon_softc *sc; - phandle_t node, child; - int err; - - sc = device_get_softc(dev); - sc->dev = dev; - - if (bus_alloc_resources(dev, ti_scm_syscon_res_spec, sc->res)) { - device_printf(sc->dev, "Cant allocate resources\n"); - return (ENXIO); - } - - sc->dev = dev; - sc->bst = rman_get_bustag(sc->res[0]); - sc->bsh = rman_get_bushandle(sc->res[0]); - - mtx_init(&sc->mtx, device_get_nameunit(sc->dev), NULL, MTX_DEF); - node = ofw_bus_get_node(sc->dev); - - /* dev/extres/syscon interface */ - sc->syscon = syscon_create_ofw_node(dev, &ti_scm_syscon_reg_class, node); - if (sc->syscon == NULL) { - device_printf(dev, "Failed to create/register syscon\n"); - return (ENXIO); - } - - simplebus_init(sc->dev, node); - - err = bus_generic_probe(sc->dev); - for (child = OF_child(node); child != 0; child = OF_peer(child)) { - simplebus_add_device(sc->dev, child, 0, NULL, -1, NULL); - } - - return (bus_generic_attach(sc->dev)); -} - -/* syscon interface */ -static int -ti_scm_syscon_get_handle(device_t dev, struct syscon **syscon) -{ - struct ti_scm_syscon_softc *sc; - - sc = device_get_softc(dev); - *syscon = sc->syscon; - if (*syscon == NULL) - return (ENODEV); - return (0); -} - -/* clkdev interface */ -static int -ti_scm_syscon_clk_write_4(device_t dev, bus_addr_t addr, uint32_t val) -{ - struct ti_scm_syscon_softc *sc; - - sc = device_get_softc(dev); - DPRINTF(sc->dev, "offset=%lx write %x\n", addr, val); - bus_space_write_4(sc->bst, sc->bsh, addr, val); - return (0); -} - -static int -ti_scm_syscon_clk_read_4(device_t dev, bus_addr_t addr, uint32_t *val) -{ - struct ti_scm_syscon_softc *sc; - - sc = device_get_softc(dev); - - *val = bus_space_read_4(sc->bst, sc->bsh, addr); - DPRINTF(sc->dev, "offset=%lx Read %x\n", addr, *val); - return (0); -} - -static int -ti_scm_syscon_clk_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) -{ - struct ti_scm_syscon_softc *sc; - uint32_t reg; - - sc = device_get_softc(dev); - - reg = bus_space_read_4(sc->bst, sc->bsh, addr); - reg &= ~clr; - reg |= set; - bus_space_write_4(sc->bst, sc->bsh, addr, reg); - DPRINTF(sc->dev, "offset=%lx reg: %x (clr %x set %x)\n", addr, reg, clr, set); - - return (0); -} - -static void -ti_scm_syscon_clk_device_lock(device_t dev) -{ - struct ti_scm_syscon_softc *sc; - - sc = device_get_softc(dev); - mtx_lock(&sc->mtx); -} - -static void -ti_scm_syscon_clk_device_unlock(device_t dev) -{ - struct ti_scm_syscon_softc *sc; - sc = device_get_softc(dev); - mtx_unlock(&sc->mtx); -} - -static device_method_t ti_scm_syscon_methods[] = { - DEVMETHOD(device_probe, ti_scm_syscon_probe), - DEVMETHOD(device_attach, ti_scm_syscon_attach), - - /* syscon interface */ - DEVMETHOD(syscon_get_handle, ti_scm_syscon_get_handle), - - /* clkdev interface */ - DEVMETHOD(clkdev_write_4, ti_scm_syscon_clk_write_4), - DEVMETHOD(clkdev_read_4, ti_scm_syscon_clk_read_4), - DEVMETHOD(clkdev_modify_4, ti_scm_syscon_clk_modify_4), - DEVMETHOD(clkdev_device_lock, ti_scm_syscon_clk_device_lock), - DEVMETHOD(clkdev_device_unlock, ti_scm_syscon_clk_device_unlock), - - DEVMETHOD_END -}; - -DEFINE_CLASS_1(ti_scm_syscon, ti_scm_syscon_driver, ti_scm_syscon_methods, - sizeof(struct ti_scm_syscon_softc), simplebus_driver); - -static devclass_t ti_scm_syscon_devclass; - -EARLY_DRIVER_MODULE(ti_scm_syscon, simplebus, ti_scm_syscon_driver, - ti_scm_syscon_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); -MODULE_VERSION(ti_scm_syscon, 1); -MODULE_DEPEND(ti_scm_syscon, ti_scm, 1, 1, 1);