|
| 1 | +/* $NetBSD $ */ |
| 2 | + |
| 3 | +/*- |
| 4 | +* Copyright (c) 2026 The NetBSD Foundation, Inc. |
| 5 | +* All rights reserved. |
| 6 | +* |
| 7 | +* This code is derived from software contributed to The NetBSD Foundation |
| 8 | +* by Yuri Honegger. |
| 9 | +* |
| 10 | +* Redistribution and use in source and binary forms, with or without |
| 11 | +* modification, are permitted provided that the following conditions |
| 12 | +* are met: |
| 13 | +* 1. Redistributions of source code must retain the above copyright |
| 14 | +* notice, this list of conditions and the following disclaimer. |
| 15 | +* 2. Redistributions in binary form must reproduce the above copyright |
| 16 | +* notice, this list of conditions and the following disclaimer in the |
| 17 | +* documentation and/or other materials provided with the distribution. |
| 18 | +* |
| 19 | +* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
| 20 | +* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
| 21 | +* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 22 | +* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
| 23 | +* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| 24 | +* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| 25 | +* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| 26 | +* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| 27 | +* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 28 | +* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 29 | +* POSSIBILITY OF SUCH DAMAGE. |
| 30 | + */ |
| 31 | + |
| 32 | +/* |
| 33 | + * Drivers for the async clock gates in the syscfg block of the TI AM1808. |
| 34 | + */ |
| 35 | + |
| 36 | +#include <sys/param.h> |
| 37 | +#include <sys/bus.h> |
| 38 | +#include <sys/cdefs.h> |
| 39 | +#include <sys/device.h> |
| 40 | + |
| 41 | +#include <dev/clk/clk_backend.h> |
| 42 | +#include <dev/fdt/fdtvar.h> |
| 43 | +#include <dev/fdt/syscon.h> |
| 44 | + |
| 45 | +#include <arm/fdt/arm_fdtvar.h> |
| 46 | + |
| 47 | +struct am18xx_aclk_config { |
| 48 | + struct clk *clk; /* double wrap clk because the config must be const */ |
| 49 | + uint32_t mask; |
| 50 | +}; |
| 51 | + |
| 52 | +struct am18xx_aclk_softc { |
| 53 | + struct clk_domain sc_clkdom; |
| 54 | + const struct am18xx_aclk_config *sc_config; |
| 55 | + struct clk *sc_parent_clk; |
| 56 | +}; |
| 57 | + |
| 58 | +static int am18xx_aclk_match(device_t, cfdata_t, void *); |
| 59 | +static void am18xx_aclk_attach(device_t, device_t, void *); |
| 60 | +static struct clk * am18xx_aclk_decode(device_t, int, const void *, size_t); |
| 61 | +static struct clk * am18xx_aclk_clk_get(void *, const char *); |
| 62 | +static u_int am18xx_aclk_clk_get_rate(void *, struct clk *); |
| 63 | +static struct clk * am18xx_aclk_clk_get_parent(void *, struct clk *); |
| 64 | + |
| 65 | +CFATTACH_DECL_NEW(am18xxaclk, sizeof(struct am18xx_aclk_softc), |
| 66 | + am18xx_aclk_match, am18xx_aclk_attach, NULL, NULL); |
| 67 | + |
| 68 | +#define AM18XX_ACLK_CFGCHIP3 0xC |
| 69 | +#define AM18XX_ACLK_CFGCHIP3_ASYNC3_CLKSRC __BIT(4) |
| 70 | +#define AM18XX_ACLK_CFGCHIP3_ASYNC1_CLKSRC __BIT(2) |
| 71 | + |
| 72 | +static struct clk am18xx_aclk_async1_clk = { |
| 73 | + .name = "async1", |
| 74 | + .flags = 0 |
| 75 | +}; |
| 76 | +static struct clk am18xx_aclk_async3_clk = { |
| 77 | + .name = "async3", |
| 78 | + .flags = 0 |
| 79 | +}; |
| 80 | + |
| 81 | +static const struct am18xx_aclk_config am18xx_aclk_async1_config = { |
| 82 | + .clk = &am18xx_aclk_async1_clk, |
| 83 | + .mask = AM18XX_ACLK_CFGCHIP3_ASYNC1_CLKSRC, |
| 84 | +}; |
| 85 | +static const struct am18xx_aclk_config am18xx_aclk_async3_config = { |
| 86 | + .clk = &am18xx_aclk_async3_clk, |
| 87 | + .mask = AM18XX_ACLK_CFGCHIP3_ASYNC3_CLKSRC, |
| 88 | +}; |
| 89 | + |
| 90 | +static const struct fdtbus_clock_controller_func am18xx_aclk_clk_fdt_funcs = { |
| 91 | + .decode = am18xx_aclk_decode, |
| 92 | +}; |
| 93 | + |
| 94 | +static const struct clk_funcs am18xx_aclk_clk_funcs = { |
| 95 | + .get = am18xx_aclk_clk_get, |
| 96 | + .get_rate = am18xx_aclk_clk_get_rate, |
| 97 | + .get_parent = am18xx_aclk_clk_get_parent, |
| 98 | +}; |
| 99 | + |
| 100 | +static const struct device_compatible_entry compat_data[] = { |
| 101 | + { .compat = "ti,da850-async1-clksrc", |
| 102 | + .data = &am18xx_aclk_async1_config }, |
| 103 | + { .compat = "ti,da850-async3-clksrc", |
| 104 | + .data = &am18xx_aclk_async3_config }, |
| 105 | + DEVICE_COMPAT_EOL |
| 106 | +}; |
| 107 | + |
| 108 | +static struct clk * |
| 109 | +am18xx_aclk_clk_get(void *priv, const char *name) |
| 110 | +{ |
| 111 | + struct am18xx_aclk_softc * const sc = priv; |
| 112 | + |
| 113 | + if (strcmp(sc->sc_config->clk->name, name) == 0) { |
| 114 | + return sc->sc_config->clk; |
| 115 | + } |
| 116 | + |
| 117 | + return NULL; |
| 118 | +} |
| 119 | + |
| 120 | +static u_int |
| 121 | +am18xx_aclk_clk_get_rate(void *priv, struct clk *clkp) |
| 122 | +{ |
| 123 | + struct am18xx_aclk_softc * const sc = priv; |
| 124 | + |
| 125 | + return clk_get_rate(sc->sc_parent_clk); |
| 126 | +} |
| 127 | + |
| 128 | +static struct clk * |
| 129 | +am18xx_aclk_clk_get_parent(void *priv, struct clk *clkp) |
| 130 | +{ |
| 131 | + struct am18xx_aclk_softc * const sc = priv; |
| 132 | + |
| 133 | + return sc->sc_parent_clk; |
| 134 | +} |
| 135 | + |
| 136 | +static struct clk * |
| 137 | +am18xx_aclk_decode(device_t dev, int cc_phandle, const void *data, size_t len) |
| 138 | +{ |
| 139 | + struct am18xx_aclk_softc * const sc = device_private(dev); |
| 140 | + |
| 141 | + return sc->sc_config->clk; |
| 142 | +} |
| 143 | + |
| 144 | +int |
| 145 | +am18xx_aclk_match(device_t parent, cfdata_t cf, void *aux) |
| 146 | +{ |
| 147 | + struct fdt_attach_args * const faa = aux; |
| 148 | + |
| 149 | + return of_compatible_match(faa->faa_phandle, compat_data); |
| 150 | +} |
| 151 | + |
| 152 | +void |
| 153 | +am18xx_aclk_attach(device_t parent, device_t self, void *aux) |
| 154 | +{ |
| 155 | + struct am18xx_aclk_softc * const sc = device_private(self); |
| 156 | + struct fdt_attach_args * const faa = aux; |
| 157 | + const int phandle = faa->faa_phandle; |
| 158 | + |
| 159 | + sc->sc_config = of_compatible_lookup(phandle, compat_data)->data; |
| 160 | + |
| 161 | + /* ensure we have a clock parent */ |
| 162 | + sc->sc_parent_clk = fdtbus_clock_get_index(phandle, 0); |
| 163 | + if (sc->sc_parent_clk == NULL) { |
| 164 | + aprint_error(": couldn't get parent clock"); |
| 165 | + return; |
| 166 | + } |
| 167 | + |
| 168 | + /* ensure clock gate bits are as expected */ |
| 169 | + struct syscon *syscon = fdtbus_syscon_lookup(OF_parent(phandle)); |
| 170 | + if (syscon == NULL) { |
| 171 | + aprint_error(": couldn't get syscon registers\n"); |
| 172 | + return; |
| 173 | + } |
| 174 | + syscon_lock(syscon); |
| 175 | + uint32_t cfgchip_reg3 = syscon_read_4(syscon, AM18XX_ACLK_CFGCHIP3); |
| 176 | + syscon_unlock(syscon); |
| 177 | + if ((cfgchip_reg3 & sc->sc_config->mask) != 0) { |
| 178 | + aprint_error(": unexpected clock gate bits\n"); |
| 179 | + return; |
| 180 | + } |
| 181 | + |
| 182 | + /* attach a clock controller */ |
| 183 | + sc->sc_clkdom.name = device_xname(self); |
| 184 | + sc->sc_clkdom.funcs = &am18xx_aclk_clk_funcs; |
| 185 | + sc->sc_clkdom.priv = sc; |
| 186 | + |
| 187 | + sc->sc_config->clk->domain = &sc->sc_clkdom; |
| 188 | + clk_attach(sc->sc_config->clk); |
| 189 | + |
| 190 | + fdtbus_register_clock_controller(self, phandle, &am18xx_aclk_clk_fdt_funcs); |
| 191 | + |
| 192 | + aprint_normal("\n"); |
| 193 | +} |
0 commit comments