1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/conf.h> 30 #include <sys/bus.h> 31 #include <sys/kernel.h> 32 #include <sys/module.h> 33 #include <sys/systm.h> 34 #include <sys/libkern.h> 35 #include <sys/types.h> 36 #include <sys/malloc.h> 37 38 #include <machine/bus.h> 39 #include <dev/fdt/simplebus.h> 40 41 #include <dev/clk/clk_mux.h> 42 #include <dev/ofw/ofw_bus.h> 43 #include <dev/ofw/ofw_bus_subr.h> 44 45 #include "clock_common.h" 46 47 #if 0 48 #define DPRINTF(dev, msg...) device_printf(dev, msg) 49 #else 50 #define DPRINTF(dev, msg...) 51 #endif 52 53 /* 54 * Devicetree description 55 * Documentation/devicetree/bindings/clock/ti/mux.txt 56 */ 57 58 struct ti_mux_softc { 59 device_t sc_dev; 60 bool attach_done; 61 62 struct clk_mux_def mux_def; 63 struct clock_cell_info clock_cell; 64 struct clkdom *clkdom; 65 }; 66 67 static int ti_mux_probe(device_t dev); 68 static int ti_mux_attach(device_t dev); 69 static int ti_mux_detach(device_t dev); 70 71 #define TI_MUX_CLOCK 2 72 #define TI_COMPOSITE_MUX_CLOCK 1 73 #define TI_MUX_END 0 74 75 static struct ofw_compat_data compat_data[] = { 76 { "ti,mux-clock", TI_MUX_CLOCK }, 77 { "ti,composite-mux-clock", TI_COMPOSITE_MUX_CLOCK }, 78 { NULL, TI_MUX_END } 79 }; 80 81 static int 82 ti_mux_probe(device_t dev) 83 { 84 if (!ofw_bus_status_okay(dev)) 85 return (ENXIO); 86 87 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 88 return (ENXIO); 89 90 device_set_desc(dev, "TI Mux Clock"); 91 92 return (BUS_PROBE_DEFAULT); 93 } 94 95 static int 96 register_clk(struct ti_mux_softc *sc) { 97 int err; 98 99 sc->clkdom = clkdom_create(sc->sc_dev); 100 if (sc->clkdom == NULL) { 101 DPRINTF(sc->sc_dev, "Failed to create clkdom\n"); 102 return ENXIO; 103 } 104 105 err = clknode_mux_register(sc->clkdom, &sc->mux_def); 106 if (err) { 107 DPRINTF(sc->sc_dev, "clknode_mux_register failed %x\n", err); 108 return ENXIO; 109 } 110 111 err = clkdom_finit(sc->clkdom); 112 if (err) { 113 DPRINTF(sc->sc_dev, "Clk domain finit fails %x.\n", err); 114 return ENXIO; 115 } 116 117 return 0; 118 } 119 120 static int 121 ti_mux_attach(device_t dev) 122 { 123 struct ti_mux_softc *sc; 124 phandle_t node; 125 int err; 126 cell_t value; 127 128 sc = device_get_softc(dev); 129 sc->sc_dev = dev; 130 node = ofw_bus_get_node(dev); 131 132 /* Grab the content of reg properties */ 133 OF_getencprop(node, "reg", &value, sizeof(value)); 134 sc->mux_def.offset = value; 135 136 if (OF_hasprop(node, "ti,bit-shift")) { 137 OF_getencprop(node, "ti,bit-shift", &value, sizeof(value)); 138 sc->mux_def.shift = value; 139 DPRINTF(sc->sc_dev, "ti,bit-shift => shift %x\n", sc->mux_def.shift); 140 } 141 if (OF_hasprop(node, "ti,index-starts-at-one")) { 142 /* FIXME: Add support in dev/extres/clk */ 143 /*sc->mux_def.mux_flags = ... */ 144 device_printf(sc->sc_dev, "ti,index-starts-at-one - Not implemented\n"); 145 } 146 147 if (OF_hasprop(node, "ti,set-rate-parent")) 148 device_printf(sc->sc_dev, "ti,set-rate-parent - Not implemented\n"); 149 if (OF_hasprop(node, "ti,latch-bit")) 150 device_printf(sc->sc_dev, "ti,latch-bit - Not implemented\n"); 151 152 read_clock_cells(sc->sc_dev, &sc->clock_cell); 153 154 create_clkdef(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); 155 156 /* Figure out the width from ti_max_div */ 157 if (sc->mux_def.mux_flags) 158 sc->mux_def.width = fls(sc->clock_cell.num_real_clocks-1); 159 else 160 sc->mux_def.width = fls(sc->clock_cell.num_real_clocks); 161 162 DPRINTF(sc->sc_dev, "sc->clock_cell.num_real_clocks %x def.width %x\n", 163 sc->clock_cell.num_real_clocks, sc->mux_def.width); 164 165 err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); 166 167 if (err) { 168 /* free_clkdef will be called in ti_mux_new_pass */ 169 DPRINTF(sc->sc_dev, "find_parent_clock_names failed\n"); 170 return (bus_generic_attach(sc->sc_dev)); 171 } 172 173 err = register_clk(sc); 174 175 if (err) { 176 /* free_clkdef will be called in ti_mux_new_pass */ 177 DPRINTF(sc->sc_dev, "register_clk failed\n"); 178 return (bus_generic_attach(sc->sc_dev)); 179 } 180 181 sc->attach_done = true; 182 183 free_clkdef(&sc->mux_def.clkdef); 184 185 return (bus_generic_attach(sc->sc_dev)); 186 } 187 188 static void 189 ti_mux_new_pass(device_t dev) 190 { 191 struct ti_mux_softc *sc; 192 int err; 193 194 sc = device_get_softc(dev); 195 196 if (sc->attach_done) { 197 return; 198 } 199 200 err = find_parent_clock_names(sc->sc_dev, &sc->clock_cell, &sc->mux_def.clkdef); 201 if (err) { 202 /* free_clkdef will be called in later call to ti_mux_new_pass */ 203 DPRINTF(sc->sc_dev, "ti_mux_new_pass find_parent_clock_names failed\n"); 204 return; 205 } 206 207 err = register_clk(sc); 208 if (err) { 209 /* free_clkdef will be called in later call to ti_mux_new_pass */ 210 DPRINTF(sc->sc_dev, "ti_mux_new_pass register_clk failed\n"); 211 return; 212 } 213 214 sc->attach_done = true; 215 216 free_clkdef(&sc->mux_def.clkdef); 217 } 218 219 static int 220 ti_mux_detach(device_t dev) 221 { 222 return (EBUSY); 223 } 224 225 static device_method_t ti_mux_methods[] = { 226 /* Device interface */ 227 DEVMETHOD(device_probe, ti_mux_probe), 228 DEVMETHOD(device_attach, ti_mux_attach), 229 DEVMETHOD(device_detach, ti_mux_detach), 230 231 /* Bus interface */ 232 DEVMETHOD(bus_new_pass, ti_mux_new_pass), 233 234 DEVMETHOD_END 235 }; 236 237 DEFINE_CLASS_0(ti_mux, ti_mux_driver, ti_mux_methods, 238 sizeof(struct ti_mux_softc)); 239 240 EARLY_DRIVER_MODULE(ti_mux, simplebus, ti_mux_driver, 0, 0, 241 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 242 MODULE_VERSION(ti_mux, 1); 243