aw_ahbclk.c revision 299113
1/*- 2 * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * $FreeBSD: head/sys/arm/allwinner/clk/aw_ahbclk.c 299113 2016-05-05 09:41:57Z jmcneill $ 27 */ 28 29/* 30 * Allwinner AHB clock 31 */ 32 33#include <sys/cdefs.h> 34__FBSDID("$FreeBSD: head/sys/arm/allwinner/clk/aw_ahbclk.c 299113 2016-05-05 09:41:57Z jmcneill $"); 35 36#include <sys/param.h> 37#include <sys/systm.h> 38#include <sys/bus.h> 39#include <sys/rman.h> 40#include <sys/kernel.h> 41#include <sys/module.h> 42#include <machine/bus.h> 43 44#include <dev/ofw/ofw_bus.h> 45#include <dev/ofw/ofw_bus_subr.h> 46#include <dev/ofw/ofw_subr.h> 47 48#include <dev/extres/clk/clk.h> 49 50#include "clkdev_if.h" 51 52#define A10_AHB_CLK_DIV_RATIO (0x3 << 4) 53#define A10_AHB_CLK_DIV_RATIO_SHIFT 4 54 55#define A13_AHB_CLK_SRC_SEL (0x3 << 6) 56#define A13_AHB_CLK_SRC_SEL_MAX 3 57#define A13_AHB_CLK_SRC_SEL_SHIFT 6 58 59#define A31_AHB1_PRE_DIV (0x3 << 6) 60#define A31_AHB1_PRE_DIV_SHIFT 6 61#define A31_AHB1_CLK_SRC_SEL (0x3 << 12) 62#define A31_AHB1_CLK_SRC_SEL_PLL6 3 63#define A31_AHB1_CLK_SRC_SEL_MAX 3 64#define A31_AHB1_CLK_SRC_SEL_SHIFT 12 65 66#define A83T_AHB1_CLK_SRC_SEL (0x3 << 12) 67#define A83T_AHB1_CLK_SRC_SEL_ISPLL(x) ((x) & 0x2) 68#define A83T_AHB1_CLK_SRC_SEL_MAX 3 69#define A83T_AHB1_CLK_SRC_SEL_SHIFT 12 70#define A83T_AHB1_PRE_DIV (0x3 << 6) 71#define A83T_AHB1_PRE_DIV_SHIFT 6 72#define A83T_AHB1_CLK_DIV_RATIO (0x3 << 4) 73#define A83T_AHB1_CLK_DIV_RATIO_SHIFT 4 74 75#define H3_AHB2_CLK_CFG (0x3 << 0) 76#define H3_AHB2_CLK_CFG_SHIFT 0 77#define H3_AHB2_CLK_CFG_AHB1 0 78#define H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2 1 79#define H3_AHB2_CLK_CFG_MAX 1 80 81enum aw_ahbclk_type { 82 AW_A10_AHB = 1, 83 AW_A13_AHB, 84 AW_A31_AHB1, 85 AW_A83T_AHB1, 86 AW_H3_AHB2, 87}; 88 89static struct ofw_compat_data compat_data[] = { 90 { "allwinner,sun4i-a10-ahb-clk", AW_A10_AHB }, 91 { "allwinner,sun5i-a13-ahb-clk", AW_A13_AHB }, 92 { "allwinner,sun6i-a31-ahb1-clk", AW_A31_AHB1 }, 93 { "allwinner,sun8i-a83t-ahb1-clk", AW_A83T_AHB1 }, 94 { "allwinner,sun8i-h3-ahb2-clk", AW_H3_AHB2 }, 95 { NULL, 0 } 96}; 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 DEVICE_LOCK(sc); 144 AHBCLK_READ(sc, &val); 145 DEVICE_UNLOCK(sc); 146 index = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT; 147 break; 148 default: 149 return (ENXIO); 150 } 151 152 clknode_init_parent_idx(clk, index); 153 return (0); 154} 155 156static int 157aw_ahbclk_recalc_freq(struct clknode *clk, uint64_t *freq) 158{ 159 struct aw_ahbclk_sc *sc; 160 uint32_t val, src_sel, div, pre_div; 161 162 sc = clknode_get_softc(clk); 163 164 DEVICE_LOCK(sc); 165 AHBCLK_READ(sc, &val); 166 DEVICE_UNLOCK(sc); 167 168 switch (sc->type) { 169 case AW_A31_AHB1: 170 div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> 171 A10_AHB_CLK_DIV_RATIO_SHIFT); 172 src_sel = (val & A31_AHB1_CLK_SRC_SEL) >> 173 A31_AHB1_CLK_SRC_SEL_SHIFT; 174 if (src_sel == A31_AHB1_CLK_SRC_SEL_PLL6) 175 pre_div = ((val & A31_AHB1_PRE_DIV) >> 176 A31_AHB1_PRE_DIV_SHIFT) + 1; 177 else 178 pre_div = 1; 179 break; 180 case AW_A83T_AHB1: 181 div = 1 << ((val & A83T_AHB1_CLK_DIV_RATIO) >> 182 A83T_AHB1_CLK_DIV_RATIO_SHIFT); 183 src_sel = (val & A83T_AHB1_CLK_SRC_SEL) >> 184 A83T_AHB1_CLK_SRC_SEL_SHIFT; 185 if (A83T_AHB1_CLK_SRC_SEL_ISPLL(src_sel)) 186 pre_div = ((val & A83T_AHB1_PRE_DIV) >> 187 A83T_AHB1_PRE_DIV_SHIFT) + 1; 188 else 189 pre_div = 1; 190 break; 191 case AW_H3_AHB2: 192 src_sel = (val & H3_AHB2_CLK_CFG) >> H3_AHB2_CLK_CFG_SHIFT; 193 if (src_sel == H3_AHB2_CLK_CFG_PLL_PERIPH_DIV2) 194 div = 2; 195 else 196 div = 1; 197 pre_div = 1; 198 break; 199 default: 200 div = 1 << ((val & A10_AHB_CLK_DIV_RATIO) >> 201 A10_AHB_CLK_DIV_RATIO_SHIFT); 202 pre_div = 1; 203 break; 204 } 205 206 *freq = *freq / pre_div / div; 207 208 return (0); 209} 210 211static int 212aw_ahbclk_set_mux(struct clknode *clk, int index) 213{ 214 struct aw_ahbclk_sc *sc; 215 uint32_t val; 216 217 sc = clknode_get_softc(clk); 218 219 switch (sc->type) { 220 case AW_A10_AHB: 221 if (index != 0) 222 return (ERANGE); 223 break; 224 case AW_A13_AHB: 225 if (index < 0 || index > A13_AHB_CLK_SRC_SEL_MAX) 226 return (ERANGE); 227 DEVICE_LOCK(sc); 228 AHBCLK_READ(sc, &val); 229 val &= ~A13_AHB_CLK_SRC_SEL; 230 val |= (index << A13_AHB_CLK_SRC_SEL_SHIFT); 231 AHBCLK_WRITE(sc, val); 232 DEVICE_UNLOCK(sc); 233 break; 234 case AW_A83T_AHB1: 235 if (index < 0 || index > A83T_AHB1_CLK_SRC_SEL_MAX) 236 return (ERANGE); 237 DEVICE_LOCK(sc); 238 AHBCLK_READ(sc, &val); 239 val &= ~A83T_AHB1_CLK_SRC_SEL; 240 val |= (index << A83T_AHB1_CLK_SRC_SEL_SHIFT); 241 AHBCLK_WRITE(sc, val); 242 DEVICE_UNLOCK(sc); 243 break; 244 case AW_H3_AHB2: 245 if (index < 0 || index > H3_AHB2_CLK_CFG) 246 return (ERANGE); 247 DEVICE_LOCK(sc); 248 AHBCLK_READ(sc, &val); 249 val &= ~H3_AHB2_CLK_CFG; 250 val |= (index << H3_AHB2_CLK_CFG_SHIFT); 251 AHBCLK_WRITE(sc, val); 252 DEVICE_UNLOCK(sc); 253 break; 254 default: 255 return (ENXIO); 256 } 257 258 return (0); 259} 260 261static clknode_method_t aw_ahbclk_clknode_methods[] = { 262 /* Device interface */ 263 CLKNODEMETHOD(clknode_init, aw_ahbclk_init), 264 CLKNODEMETHOD(clknode_recalc_freq, aw_ahbclk_recalc_freq), 265 CLKNODEMETHOD(clknode_set_mux, aw_ahbclk_set_mux), 266 CLKNODEMETHOD_END 267}; 268DEFINE_CLASS_1(aw_ahbclk_clknode, aw_ahbclk_clknode_class, 269 aw_ahbclk_clknode_methods, sizeof(struct aw_ahbclk_sc), clknode_class); 270 271static int 272aw_ahbclk_probe(device_t dev) 273{ 274 if (!ofw_bus_status_okay(dev)) 275 return (ENXIO); 276 277 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 278 return (ENXIO); 279 280 device_set_desc(dev, "Allwinner AHB Clock"); 281 return (BUS_PROBE_DEFAULT); 282} 283 284static int 285aw_ahbclk_attach(device_t dev) 286{ 287 struct clknode_init_def def; 288 struct aw_ahbclk_sc *sc; 289 struct clkdom *clkdom; 290 struct clknode *clk; 291 clk_t clk_parent; 292 bus_addr_t paddr; 293 bus_size_t psize; 294 phandle_t node; 295 int error, ncells, i; 296 297 node = ofw_bus_get_node(dev); 298 299 if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { 300 device_printf(dev, "cannot parse 'reg' property\n"); 301 return (ENXIO); 302 } 303 304 error = ofw_bus_parse_xref_list_get_length(node, "clocks", 305 "#clock-cells", &ncells); 306 if (error != 0) { 307 device_printf(dev, "cannot get clock count\n"); 308 return (error); 309 } 310 311 clkdom = clkdom_create(dev); 312 313 memset(&def, 0, sizeof(def)); 314 def.id = 1; 315 def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, 316 M_WAITOK); 317 for (i = 0; i < ncells; i++) { 318 error = clk_get_by_ofw_index(dev, i, &clk_parent); 319 if (error != 0) { 320 device_printf(dev, "cannot get clock %d\n", i); 321 goto fail; 322 } 323 def.parent_names[i] = clk_get_name(clk_parent); 324 clk_release(clk_parent); 325 } 326 def.parent_cnt = ncells; 327 328 error = clk_parse_ofw_clk_name(dev, node, &def.name); 329 if (error != 0) { 330 device_printf(dev, "cannot parse clock name\n"); 331 error = ENXIO; 332 goto fail; 333 } 334 335 clk = clknode_create(clkdom, &aw_ahbclk_clknode_class, &def); 336 if (clk == NULL) { 337 device_printf(dev, "cannot create clknode\n"); 338 error = ENXIO; 339 goto fail; 340 } 341 sc = clknode_get_softc(clk); 342 sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 343 sc->reg = paddr; 344 sc->clkdev = device_get_parent(dev); 345 346 clknode_register(clkdom, clk); 347 348 if (clkdom_finit(clkdom) != 0) { 349 device_printf(dev, "cannot finalize clkdom initialization\n"); 350 error = ENXIO; 351 goto fail; 352 } 353 354 if (bootverbose) 355 clkdom_dump(clkdom); 356 357 return (0); 358 359fail: 360 return (error); 361} 362 363static device_method_t aw_ahbclk_methods[] = { 364 /* Device interface */ 365 DEVMETHOD(device_probe, aw_ahbclk_probe), 366 DEVMETHOD(device_attach, aw_ahbclk_attach), 367 368 DEVMETHOD_END 369}; 370 371static driver_t aw_ahbclk_driver = { 372 "aw_ahbclk", 373 aw_ahbclk_methods, 374 0 375}; 376 377static devclass_t aw_ahbclk_devclass; 378 379EARLY_DRIVER_MODULE(aw_ahbclk, simplebus, aw_ahbclk_driver, 380 aw_ahbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 381