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