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/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/conf.h> 33 #include <sys/bus.h> 34 #include <sys/kernel.h> 35 #include <sys/module.h> 36 #include <sys/systm.h> 37 #include <sys/libkern.h> 38 39 #include <machine/bus.h> 40 #include <dev/fdt/simplebus.h> 41 42 #include <dev/extres/clk/clk_div.h> 43 #include <dev/ofw/ofw_bus.h> 44 #include <dev/ofw/ofw_bus_subr.h> 45 46 #include <arm/ti/clk/ti_clk_dpll.h> 47 #include "clock_common.h" 48 49 #if 0 50 #define DPRINTF(dev, msg...) device_printf(dev, msg) 51 #else 52 #define DPRINTF(dev, msg...) 53 #endif 54 55 /* 56 * Devicetree description 57 * Documentation/devicetree/bindings/clock/ti/dpll.txt 58 */ 59 60 struct ti_dpll_softc { 61 device_t dev; 62 uint8_t dpll_type; 63 64 bool attach_done; 65 struct ti_clk_dpll_def dpll_def; 66 67 struct clock_cell_info clock_cell; 68 struct clkdom *clkdom; 69 }; 70 71 static int ti_dpll_probe(device_t dev); 72 static int ti_dpll_attach(device_t dev); 73 static int ti_dpll_detach(device_t dev); 74 75 #define TI_OMAP3_DPLL_CLOCK 17 76 #define TI_OMAP3_DPLL_CORE_CLOCK 16 77 #define TI_OMAP3_DPLL_PER_CLOCK 15 78 #define TI_OMAP3_DPLL_PER_J_TYPE_CLOCK 14 79 #define TI_OMAP4_DPLL_CLOCK 13 80 #define TI_OMAP4_DPLL_X2_CLOCK 12 81 #define TI_OMAP4_DPLL_CORE_CLOCK 11 82 #define TI_OMAP4_DPLL_M4XEN_CLOCK 10 83 #define TI_OMAP4_DPLL_J_TYPE_CLOCK 9 84 #define TI_OMAP5_MPU_DPLL_CLOCK 8 85 #define TI_AM3_DPLL_NO_GATE_CLOCK 7 86 #define TI_AM3_DPLL_J_TYPE_CLOCK 6 87 #define TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK 5 88 #define TI_AM3_DPLL_CLOCK 4 89 #define TI_AM3_DPLL_CORE_CLOCK 3 90 #define TI_AM3_DPLL_X2_CLOCK 2 91 #define TI_OMAP2_DPLL_CORE_CLOCK 1 92 #define TI_DPLL_END 0 93 94 static struct ofw_compat_data compat_data[] = { 95 { "ti,omap3-dpll-clock", TI_OMAP3_DPLL_CLOCK }, 96 { "ti,omap3-dpll-core-clock", TI_OMAP3_DPLL_CORE_CLOCK }, 97 { "ti,omap3-dpll-per-clock", TI_OMAP3_DPLL_PER_CLOCK }, 98 { "ti,omap3-dpll-per-j-type-clock",TI_OMAP3_DPLL_PER_J_TYPE_CLOCK }, 99 { "ti,omap4-dpll-clock", TI_OMAP4_DPLL_CLOCK }, 100 { "ti,omap4-dpll-x2-clock", TI_OMAP4_DPLL_X2_CLOCK }, 101 { "ti,omap4-dpll-core-clock", TI_OMAP4_DPLL_CORE_CLOCK }, 102 { "ti,omap4-dpll-m4xen-clock", TI_OMAP4_DPLL_M4XEN_CLOCK }, 103 { "ti,omap4-dpll-j-type-clock", TI_OMAP4_DPLL_J_TYPE_CLOCK }, 104 { "ti,omap5-mpu-dpll-clock", TI_OMAP5_MPU_DPLL_CLOCK }, 105 { "ti,am3-dpll-no-gate-clock", TI_AM3_DPLL_NO_GATE_CLOCK }, 106 { "ti,am3-dpll-j-type-clock", TI_AM3_DPLL_J_TYPE_CLOCK }, 107 { "ti,am3-dpll-no-gate-j-type-clock",TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK }, 108 { "ti,am3-dpll-clock", TI_AM3_DPLL_CLOCK }, 109 { "ti,am3-dpll-core-clock", TI_AM3_DPLL_CORE_CLOCK }, 110 { "ti,am3-dpll-x2-clock", TI_AM3_DPLL_X2_CLOCK }, 111 { "ti,omap2-dpll-core-clock", TI_OMAP2_DPLL_CORE_CLOCK }, 112 { NULL, TI_DPLL_END } 113 }; 114 115 static int 116 register_clk(struct ti_dpll_softc *sc) { 117 int err; 118 119 sc->clkdom = clkdom_create(sc->dev); 120 if (sc->clkdom == NULL) { 121 DPRINTF(sc->dev, "Failed to create clkdom\n"); 122 return (ENXIO); 123 } 124 125 err = ti_clknode_dpll_register(sc->clkdom, &sc->dpll_def); 126 if (err) { 127 DPRINTF(sc->dev, 128 "ti_clknode_dpll_register failed %x\n", err); 129 return (ENXIO); 130 } 131 132 err = clkdom_finit(sc->clkdom); 133 if (err) { 134 DPRINTF(sc->dev, "Clk domain finit fails %x.\n", err); 135 return (ENXIO); 136 } 137 138 return (0); 139 } 140 141 static int 142 ti_dpll_probe(device_t dev) 143 { 144 if (!ofw_bus_status_okay(dev)) 145 return (ENXIO); 146 147 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 148 return (ENXIO); 149 150 device_set_desc(dev, "TI DPLL Clock"); 151 152 return (BUS_PROBE_DEFAULT); 153 } 154 155 static int 156 parse_dpll_reg(struct ti_dpll_softc *sc) { 157 ssize_t numbytes_regs; 158 uint32_t num_regs; 159 phandle_t node; 160 cell_t reg_cells[4]; 161 162 if (sc->dpll_type == TI_AM3_DPLL_X2_CLOCK || 163 sc->dpll_type == TI_OMAP4_DPLL_X2_CLOCK) { 164 sc->dpll_def.ti_clksel_mult.value = 2; 165 sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_FIXED; 166 167 sc->dpll_def.ti_clksel_div.value = 1; 168 sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_FIXED; 169 return (0); 170 } 171 172 node = ofw_bus_get_node(sc->dev); 173 174 numbytes_regs = OF_getproplen(node, "reg"); 175 num_regs = numbytes_regs / sizeof(cell_t); 176 177 /* Sanity check */ 178 if (num_regs > 4) 179 return (ENXIO); 180 181 OF_getencprop(node, "reg", reg_cells, numbytes_regs); 182 183 switch (sc->dpll_type) { 184 case TI_AM3_DPLL_NO_GATE_CLOCK: 185 case TI_AM3_DPLL_J_TYPE_CLOCK: 186 case TI_AM3_DPLL_NO_GATE_J_TYPE_CLOCK: 187 case TI_AM3_DPLL_CLOCK: 188 case TI_AM3_DPLL_CORE_CLOCK: 189 case TI_AM3_DPLL_X2_CLOCK: 190 if (num_regs != 3) 191 return (ENXIO); 192 sc->dpll_def.ti_clkmode_offset = reg_cells[0]; 193 sc->dpll_def.ti_idlest_offset = reg_cells[1]; 194 sc->dpll_def.ti_clksel_offset = reg_cells[2]; 195 break; 196 197 case TI_OMAP2_DPLL_CORE_CLOCK: 198 if (num_regs != 2) 199 return (ENXIO); 200 sc->dpll_def.ti_clkmode_offset = reg_cells[0]; 201 sc->dpll_def.ti_clksel_offset = reg_cells[1]; 202 break; 203 204 default: 205 sc->dpll_def.ti_clkmode_offset = reg_cells[0]; 206 sc->dpll_def.ti_idlest_offset = reg_cells[1]; 207 sc->dpll_def.ti_clksel_offset = reg_cells[2]; 208 sc->dpll_def.ti_autoidle_offset = reg_cells[3]; 209 break; 210 } 211 212 /* AM335x */ 213 if (sc->dpll_def.ti_clksel_offset == CM_CLKSEL_DPLL_PERIPH) { 214 sc->dpll_def.ti_clksel_mult.shift = 8; 215 sc->dpll_def.ti_clksel_mult.mask = 0x000FFF00; 216 sc->dpll_def.ti_clksel_mult.width = 12; 217 sc->dpll_def.ti_clksel_mult.value = 0; 218 sc->dpll_def.ti_clksel_mult.min_value = 2; 219 sc->dpll_def.ti_clksel_mult.max_value = 4095; 220 sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED | 221 TI_CLK_FACTOR_MIN_VALUE | 222 TI_CLK_FACTOR_MAX_VALUE; 223 224 sc->dpll_def.ti_clksel_div.shift = 0; 225 sc->dpll_def.ti_clksel_div.mask = 0x000000FF; 226 sc->dpll_def.ti_clksel_div.width = 8; 227 sc->dpll_def.ti_clksel_div.value = 0; 228 sc->dpll_def.ti_clksel_div.min_value = 0; 229 sc->dpll_def.ti_clksel_div.max_value = 255; 230 sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE | 231 TI_CLK_FACTOR_MAX_VALUE; 232 } else { 233 sc->dpll_def.ti_clksel_mult.shift = 8; 234 sc->dpll_def.ti_clksel_mult.mask = 0x0007FF00; 235 sc->dpll_def.ti_clksel_mult.width = 11; 236 sc->dpll_def.ti_clksel_mult.value = 0; 237 sc->dpll_def.ti_clksel_mult.min_value = 2; 238 sc->dpll_def.ti_clksel_mult.max_value = 2047; 239 sc->dpll_def.ti_clksel_mult.flags = TI_CLK_FACTOR_ZERO_BASED | 240 TI_CLK_FACTOR_MIN_VALUE | 241 TI_CLK_FACTOR_MAX_VALUE; 242 243 sc->dpll_def.ti_clksel_div.shift = 0; 244 sc->dpll_def.ti_clksel_div.mask = 0x0000007F; 245 sc->dpll_def.ti_clksel_div.width = 7; 246 sc->dpll_def.ti_clksel_div.value = 0; 247 sc->dpll_def.ti_clksel_div.min_value = 0; 248 sc->dpll_def.ti_clksel_div.max_value = 127; 249 sc->dpll_def.ti_clksel_div.flags = TI_CLK_FACTOR_MIN_VALUE | 250 TI_CLK_FACTOR_MAX_VALUE; 251 } 252 DPRINTF(sc->dev, "clkmode %x idlest %x clksel %x autoidle %x\n", 253 sc->dpll_def.ti_clkmode_offset, sc->dpll_def.ti_idlest_offset, 254 sc->dpll_def.ti_clksel_offset, 255 sc->dpll_def.ti_autoidle_offset); 256 257 return (0); 258 } 259 static int 260 ti_dpll_attach(device_t dev) 261 { 262 struct ti_dpll_softc *sc; 263 phandle_t node; 264 int err; 265 266 sc = device_get_softc(dev); 267 sc->dev = dev; 268 269 sc->dpll_type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 270 node = ofw_bus_get_node(dev); 271 272 /* Grab the content of reg properties */ 273 parse_dpll_reg(sc); 274 275 /* default flags (OMAP4&AM335x) not present in the dts at moment */ 276 sc->dpll_def.ti_clkmode_flags = MN_BYPASS_MODE_FLAG | LOCK_MODE_FLAG; 277 278 if (OF_hasprop(node, "ti,low-power-stop")) { 279 sc->dpll_def.ti_clkmode_flags |= LOW_POWER_STOP_MODE_FLAG; 280 } 281 if (OF_hasprop(node, "ti,low-power-bypass")) { 282 sc->dpll_def.ti_clkmode_flags |= IDLE_BYPASS_LOW_POWER_MODE_FLAG; 283 } 284 if (OF_hasprop(node, "ti,lock")) { 285 sc->dpll_def.ti_clkmode_flags |= LOCK_MODE_FLAG; 286 } 287 288 read_clock_cells(sc->dev, &sc->clock_cell); 289 290 create_clkdef(sc->dev, &sc->clock_cell, &sc->dpll_def.clkdef); 291 292 err = find_parent_clock_names(sc->dev, &sc->clock_cell, 293 &sc->dpll_def.clkdef); 294 295 if (err) { 296 /* free_clkdef will be called in ti_dpll_new_pass */ 297 DPRINTF(sc->dev, "find_parent_clock_names failed\n"); 298 return (bus_generic_attach(sc->dev)); 299 } 300 301 err = register_clk(sc); 302 303 if (err) { 304 /* free_clkdef will be called in ti_dpll_new_pass */ 305 DPRINTF(sc->dev, "register_clk failed\n"); 306 return (bus_generic_attach(sc->dev)); 307 } 308 309 sc->attach_done = true; 310 311 free_clkdef(&sc->dpll_def.clkdef); 312 313 return (bus_generic_attach(sc->dev)); 314 } 315 316 static int 317 ti_dpll_detach(device_t dev) 318 { 319 return (EBUSY); 320 } 321 322 static void 323 ti_dpll_new_pass(device_t dev) 324 { 325 struct ti_dpll_softc *sc; 326 int err; 327 328 sc = device_get_softc(dev); 329 330 if (sc->attach_done) { 331 return; 332 } 333 334 err = find_parent_clock_names(sc->dev, &sc->clock_cell, 335 &sc->dpll_def.clkdef); 336 if (err) { 337 /* free_clkdef will be called in a later call to ti_dpll_new_pass */ 338 DPRINTF(sc->dev, 339 "new_pass find_parent_clock_names failed\n"); 340 return; 341 } 342 343 err = register_clk(sc); 344 if (err) { 345 /* free_clkdef will be called in a later call to ti_dpll_new_pass */ 346 DPRINTF(sc->dev, "new_pass register_clk failed\n"); 347 return; 348 } 349 350 sc->attach_done = true; 351 free_clkdef(&sc->dpll_def.clkdef); 352 } 353 354 static device_method_t ti_dpll_methods[] = { 355 /* Device interface */ 356 DEVMETHOD(device_probe, ti_dpll_probe), 357 DEVMETHOD(device_attach, ti_dpll_attach), 358 DEVMETHOD(device_detach, ti_dpll_detach), 359 360 /* Bus interface */ 361 DEVMETHOD(bus_new_pass, ti_dpll_new_pass), 362 363 DEVMETHOD_END 364 }; 365 366 DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods, 367 sizeof(struct ti_dpll_softc)); 368 369 EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver, 0, 0, 370 BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 371 MODULE_VERSION(ti_dpll, 1); 372