aw_ahbclk.c revision 309771
11638Srgrimes/*- 21638Srgrimes * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 31638Srgrimes * All rights reserved. 41638Srgrimes * 51638Srgrimes * Redistribution and use in source and binary forms, with or without 61638Srgrimes * modification, are permitted provided that the following conditions 71638Srgrimes * are met: 81638Srgrimes * 1. Redistributions of source code must retain the above copyright 91638Srgrimes * notice, this list of conditions and the following disclaimer. 101638Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111638Srgrimes * notice, this list of conditions and the following disclaimer in the 12263142Seadler * documentation and/or other materials provided with the distribution. 131638Srgrimes * 141638Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 151638Srgrimes * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 161638Srgrimes * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 171638Srgrimes * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 181638Srgrimes * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 191638Srgrimes * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 201638Srgrimes * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 211638Srgrimes * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 221638Srgrimes * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 231638Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 241638Srgrimes * SUCH DAMAGE. 251638Srgrimes * 261638Srgrimes * $FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $ 271638Srgrimes */ 281638Srgrimes 291638Srgrimes/* 301638Srgrimes * Allwinner AHB clock 311638Srgrimes */ 321638Srgrimes 331638Srgrimes#include <sys/cdefs.h> 341638Srgrimes__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_ahbclk.c 309771 2016-12-09 21:17:40Z manu $"); 351638Srgrimes 361638Srgrimes#include <sys/param.h> 371638Srgrimes#include <sys/systm.h> 381638Srgrimes#include <sys/bus.h> 391638Srgrimes#include <sys/rman.h> 401638Srgrimes#include <sys/kernel.h> 411638Srgrimes#include <sys/module.h> 421638Srgrimes#include <machine/bus.h> 431638Srgrimes 441638Srgrimes#include <dev/ofw/ofw_bus.h> 451638Srgrimes#include <dev/ofw/ofw_bus_subr.h> 461638Srgrimes#include <dev/ofw/ofw_subr.h> 471638Srgrimes 481638Srgrimes#include <dev/extres/clk/clk.h> 491638Srgrimes 501638Srgrimes#include "clkdev_if.h" 511638Srgrimes 521638Srgrimes#define A10_AHB_CLK_DIV_RATIO (0x3 << 4) 531638Srgrimes#define A10_AHB_CLK_DIV_RATIO_SHIFT 4 541638Srgrimes 551638Srgrimes#define A13_AHB_CLK_SRC_SEL (0x3 << 6) 561638Srgrimes#define A13_AHB_CLK_SRC_SEL_MAX 3 571638Srgrimes#define A13_AHB_CLK_SRC_SEL_SHIFT 6 581638Srgrimes 591638Srgrimes#define A31_AHB1_PRE_DIV (0x3 << 6) 601638Srgrimes#define A31_AHB1_PRE_DIV_SHIFT 6 611638Srgrimes#define A31_AHB1_CLK_SRC_SEL (0x3 << 12) 621638Srgrimes#define A31_AHB1_CLK_SRC_SEL_PLL6 3 631638Srgrimes#define A31_AHB1_CLK_SRC_SEL_MAX 3 641638Srgrimes#define A31_AHB1_CLK_SRC_SEL_SHIFT 12 651638Srgrimes 661638Srgrimes#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12) 671638Srgrimes#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2) 681638Srgrimes#define A83T_AHB1_CLK_SRC_SEL_MAX 3 691638Srgrimes#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12 701638Srgrimes#define A83T_AHB1_PRE_DIV (0x3 << 6) 711638Srgrimes#define A83T_AHB1_PRE_DIV_SHIFT 6 721638Srgrimes#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4) 731638Srgrimes#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4 741638Srgrimes 751638Srgrimes#define H3_AHB2_CLK_CFG (0x3 << 0) 761638Srgrimes#define H3_AHB2_CLK_CFG_SHIFT 0 771638Srgrimes#define H3_AHB2_CLK_CFG_AHB1 0 781638Srgrimes#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1 791638Srgrimes#define H3_AHB2_CLK_CFG_MAX 1 801638Srgrimes 811638Srgrimesenum aw_ahbclk_type { 821638Srgrimes AW_A10_AHB = 1, 831638Srgrimes AW_A13_AHB, 841638Srgrimes AW_A31_AHB1, 851638Srgrimes AW_A83T_AHB1, 861638Srgrimes AW_H3_AHB2, 871638Srgrimes}; 881638Srgrimes 891638Srgrimesstatic struct ofw_compat_data compat_data[] = { 901638Srgrimes { "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB }, 911638Srgrimes { "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB }, 921638Srgrimes { "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 }, 931638Srgrimes { "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 }, 941638Srgrimes { "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 }, 951638Srgrimes { NULL, 0 } 961638Srgrimes}; 97 98struct aw_ahbclk_sc { 99 device_t clkdev; 100 bus_addr_t reg; 101 enum aw_ahbclk_type type; 102}; 103 104#define AHBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) 105#define AHBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) 106#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) 107#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) 108 109static int 110aw_ahbclk_init(struct clknode *clk, device_t dev) 111{ 112 struct aw_ahbclk_sc *sc; 113 uint32_t val, index; 114 115 sc = clknode_get_softc(clk); 116 117 switch (sc->type) { 118 case AW_A10_AHB: 119 index = 0; 120 break; 121 case AW_A13_AHB: 122 DEVICE_LOCK(sc); 123 AHBCLK_READ(sc, &val); 124 DEVICE_UNLOCK(sc); 125 index = (val & A13_AHB_CLK_SRC_SEL) >> 126 A13_AHB_CLK_SRC_SEL_SHIFT; 127 break; 128 case AW_A31_AHB1: 129 DEVICE_LOCK(sc); 130 AHBCLK_READ(sc, &val); 131 DEVICE_UNLOCK(sc); 132 index = (val & A31_AHB1_CLK_SRC_SEL) >> 133 A31_AHB1_CLK_SRC_SEL_SHIFT; 134 break; 135 case AW_A83T_AHB1: 136 DEVICE_LOCK(sc); 137 AHBCLK_READ(sc, &val); 138 DEVICE_UNLOCK(sc); 139 index = (val & A83T_AHB1_CLK_SRC_SEL) >> 140 A83T_AHB1_CLK_SRC_SEL_SHIFT; 141 break; 142 case AW_H3_AHB2: 143 /* Set source to PLL_PERIPH/2 */ 144 index = H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2; 145 DEVICE_LOCK(sc); 146 AHBCLK_READ(sc, &val); 147 val &= ~H3_AHB2_CLK_CFG; 148 val |= (index << H3_AHB2_CLK_CFG_SHIFT); 149 AHBCLK_WRITE(sc, val); 150 DEVICE_UNLOCK(sc); 151 break; 152 default: 153 return (ENXIO); 154 } 155 156 clknode_init_parent_idx(clk, index); 157 return (0); 158} 159 160static int 161aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq) 162{ 163 struct aw_ahbclk_sc *sc; 164 uint32_t val, src_sel, div, pre_div; 165 166 sc = clknode_get_softc(clk); 167 168 DEVICE_LOCK(sc); 169 AHBCLK_READ(sc, &val); 170 DEVICE_UNLOCK(sc); 171 172 switch (sc->type) { 173 case AW_A31_AHB1: 174 div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> 175 A10_AHB_CLK_DIV_RATIO_SHIFT); 176 src_sel = (val & A31_AHB1_CLK_SRC_SEL) >> 177 A31_AHB1_CLK_SRC_SEL_SHIFT; 178 if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6) 179 pre_div = ((val & A31_AHB1_PRE_DIV) >> 180 A31_AHB1_PRE_DIV_SHIFT) + 1; 181 else 182 pre_div = 1; 183 break; 184 case AW_A83T_AHB1: 185 div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >> 186 A83T_AHB1_CLK_DIV_RATIO_SHIFT); 187 src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >> 188 A83T_AHB1_CLK_SRC_SEL_SHIFT; 189 if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel)) 190 pre_div = ((val & A83T_AHB1_PRE_DIV) >> 191 A83T_AHB1_PRE_DIV_SHIFT) + 1; 192 else 193 pre_div = 1; 194 break; 195 case AW_H3_AHB2: 196 div = pre_div = 1; 197 break; 198 default: 199 div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> 200 A10_AHB_CLK_DIV_RATIO_SHIFT); 201 pre_div = 1; 202 break; 203 } 204 205 *freq = *freq / pre_div / div; 206 207 return (0); 208} 209 210static int 211aw_ahbclk_set_mux(struct clknode *clk, int index) 212{ 213 struct aw_ahbclk_sc *sc; 214 uint32_t val; 215 216 sc = clknode_get_softc(clk); 217 218 switch (sc->type) { 219 case AW_A10_AHB: 220 if (index != 0) 221 return (ERANGE); 222 break; 223 case AW_A13_AHB: 224 if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX) 225 return (ERANGE); 226 DEVICE_LOCK(sc); 227 AHBCLK_READ(sc, &val); 228 val &= ~A13_AHB_CLK_SRC_SEL; 229 val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT); 230 AHBCLK_WRITE(sc, val); 231 DEVICE_UNLOCK(sc); 232 break; 233 case AW_A83T_AHB1: 234 if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX) 235 return (ERANGE); 236 DEVICE_LOCK(sc); 237 AHBCLK_READ(sc, &val); 238 val &= ~A83T_AHB1_CLK_SRC_SEL; 239 val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT); 240 AHBCLK_WRITE(sc, val); 241 DEVICE_UNLOCK(sc); 242 break; 243 case AW_H3_AHB2: 244 if (index < 0 || index > H3_AHB2_CLK_CFG) 245 return (ERANGE); 246 DEVICE_LOCK(sc); 247 AHBCLK_READ(sc, &val); 248 val &= ~H3_AHB2_CLK_CFG; 249 val |= (index << H3_AHB2_CLK_CFG_SHIFT); 250 AHBCLK_WRITE(sc, val); 251 DEVICE_UNLOCK(sc); 252 break; 253 default: 254 return (ENXIO); 255 } 256 257 return (0); 258} 259 260static clknode_method_t aw_ahbclk_clknode_methods[] = { 261 /* Device interface */ 262 CLKNODEMETHOD(clknode_init, aw_ahbclk_init), 263 CLKNODEMETHOD(clknode_recalc_freq, aw_ahbclk_recalc_freq), 264 CLKNODEMETHOD(clknode_set_mux, aw_ahbclk_set_mux), 265 CLKNODEMETHOD_END 266}; 267DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class, 268 aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class); 269 270static int 271aw_ahbclk_probe(device_t dev) 272{ 273 if (!ofw_bus_status_okay(dev)) 274 return (ENXIO); 275 276 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 277 return (ENXIO); 278 279 device_set_desc(dev, "Allwinner AHB Clock"); 280 return (BUS_PROBE_DEFAULT); 281} 282 283static int 284aw_ahbclk_attach(device_t dev) 285{ 286 struct clknode_init_def def; 287 struct aw_ahbclk_sc *sc; 288 struct clkdom *clkdom; 289 struct clknode *clk; 290 clk_t clk_parent; 291 bus_addr_t paddr; 292 bus_size_t psize; 293 phandle_t node; 294 int error, ncells, i; 295 296 node = ofw_bus_get_node(dev); 297 298 if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { 299 device_printf(dev, "cannot parse 'reg' property\n"); 300 return (ENXIO); 301 } 302 303 error = ofw_bus_parse_xref_list_get_length(node, "clocks", 304 "#clock-cells", &ncells); 305 if (error != 0) { 306 device_printf(dev, "cannot get clock count\n"); 307 return (error); 308 } 309 310 clkdom = clkdom_create(dev); 311 312 memset(&def, 0, sizeof(def)); 313 def.id = 1; 314 def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, 315 M_WAITOK); 316 for (i = 0; i < ncells; i++) { 317 error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); 318 if (error != 0) { 319 device_printf(dev, "cannot get clock %d\n", i); 320 goto fail; 321 } 322 def.parent_names[i] = clk_get_name(clk_parent); 323 clk_release(clk_parent); 324 } 325 def.parent_cnt = ncells; 326 327 error = clk_parse_ofw_clk_name(dev, node, &def.name); 328 if (error != 0) { 329 device_printf(dev, "cannot parse clock name\n"); 330 error = ENXIO; 331 goto fail; 332 } 333 334 clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def); 335 if (clk == NULL) { 336 device_printf(dev, "cannot create clknode\n"); 337 error = ENXIO; 338 goto fail; 339 } 340 sc = clknode_get_softc(clk); 341 sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 342 sc->reg = paddr; 343 sc->clkdev = device_get_parent(dev); 344 345 clknode_register(clkdom, clk); 346 347 if (clkdom_finit(clkdom) != 0) { 348 device_printf(dev, "cannot finalize clkdom initialization\n"); 349 error = ENXIO; 350 goto fail; 351 } 352 353 error = clk_set_assigned(dev, node); 354 if (error != 0 && error != ENOENT) { 355 device_printf(dev, "cannot set assigned parents: %d\n", error); 356 goto fail; 357 } 358 359 if (bootverbose) 360 clkdom_dump(clkdom); 361 362 return (0); 363 364fail: 365 return (error); 366} 367 368static device_method_t aw_ahbclk_methods[] = { 369 /* Device interface */ 370 DEVMETHOD(device_probe, aw_ahbclk_probe), 371 DEVMETHOD(device_attach, aw_ahbclk_attach), 372 373 DEVMETHOD_END 374}; 375 376static driver_t aw_ahbclk_driver = { 377 "aw_ahbclk", 378 aw_ahbclk_methods, 379 0 380}; 381 382static devclass_t aw_ahbclk_devclass; 383 384EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver, 385 aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 386