1/*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 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 62struct 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 73static int ti_dpll_probe(device_t dev); 74static int ti_dpll_attach(device_t dev); 75static 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 96static 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 117static int 118register_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 143static int 144ti_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 157static int 158parse_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} 261static int 262ti_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 318static int 319ti_dpll_detach(device_t dev) 320{ 321 return (EBUSY); 322} 323 324static void 325ti_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 356static 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 368DEFINE_CLASS_0(ti_dpll, ti_dpll_driver, ti_dpll_methods, 369 sizeof(struct ti_dpll_softc)); 370 371static devclass_t ti_dpll_devclass; 372 373EARLY_DRIVER_MODULE(ti_dpll, simplebus, ti_dpll_driver, 374 ti_dpll_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 375MODULE_VERSION(ti_dpll, 1); 376