aw_apbclk.c revision 299113
1297627Sjmcneill/*- 2297627Sjmcneill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3297627Sjmcneill * All rights reserved. 4297627Sjmcneill * 5297627Sjmcneill * Redistribution and use in source and binary forms, with or without 6297627Sjmcneill * modification, are permitted provided that the following conditions 7297627Sjmcneill * are met: 8297627Sjmcneill * 1. Redistributions of source code must retain the above copyright 9297627Sjmcneill * notice, this list of conditions and the following disclaimer. 10297627Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright 11297627Sjmcneill * notice, this list of conditions and the following disclaimer in the 12297627Sjmcneill * documentation and/or other materials provided with the distribution. 13297627Sjmcneill * 14297627Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15297627Sjmcneill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16297627Sjmcneill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17297627Sjmcneill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18297627Sjmcneill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19297627Sjmcneill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20297627Sjmcneill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21297627Sjmcneill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22297627Sjmcneill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23297627Sjmcneill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24297627Sjmcneill * SUCH DAMAGE. 25297627Sjmcneill * 26297627Sjmcneill * $FreeBSD: head/sys/arm/allwinner/clk/aw_apbclk.c 299113 2016-05-05 09:41:57Z jmcneill $ 27297627Sjmcneill */ 28297627Sjmcneill 29297627Sjmcneill/* 30297627Sjmcneill * Allwinner APB clock 31297627Sjmcneill */ 32297627Sjmcneill 33297627Sjmcneill#include <sys/cdefs.h> 34297627Sjmcneill__FBSDID("$FreeBSD: head/sys/arm/allwinner/clk/aw_apbclk.c 299113 2016-05-05 09:41:57Z jmcneill $"); 35297627Sjmcneill 36297627Sjmcneill#include <sys/param.h> 37297627Sjmcneill#include <sys/systm.h> 38297627Sjmcneill#include <sys/bus.h> 39297627Sjmcneill#include <sys/rman.h> 40297627Sjmcneill#include <sys/kernel.h> 41297627Sjmcneill#include <sys/module.h> 42297627Sjmcneill#include <machine/bus.h> 43297627Sjmcneill 44297627Sjmcneill#include <dev/ofw/ofw_bus.h> 45297627Sjmcneill#include <dev/ofw/ofw_bus_subr.h> 46297627Sjmcneill#include <dev/ofw/ofw_subr.h> 47297627Sjmcneill 48297627Sjmcneill#include <dev/extres/clk/clk.h> 49297627Sjmcneill 50297627Sjmcneill#include "clkdev_if.h" 51297627Sjmcneill 52299113Sjmcneill#define A10_APB0_CLK_RATIO (0x3 << 8) 53299113Sjmcneill#define A10_APB0_CLK_RATIO_SHIFT 8 54299113Sjmcneill#define A10_APB1_CLK_SRC_SEL (0x3 << 24) 55299113Sjmcneill#define A10_APB1_CLK_SRC_SEL_SHIFT 24 56299113Sjmcneill#define A10_APB1_CLK_SRC_SEL_MAX 0x3 57299113Sjmcneill#define A10_APB1_CLK_RAT_N (0x3 << 16) 58299113Sjmcneill#define A10_APB1_CLK_RAT_N_SHIFT 16 59299113Sjmcneill#define A10_APB1_CLK_RAT_M (0x1f << 0) 60299113Sjmcneill#define A10_APB1_CLK_RAT_M_SHIFT 0 61299113Sjmcneill#define A23_APB0_CLK_RATIO (0x3 << 0) 62299113Sjmcneill#define A23_APB0_CLK_RATIO_SHIFT 0 63299113Sjmcneill#define A83T_APB1_CLK_RATIO (0x3 << 8) 64299113Sjmcneill#define A83T_APB1_CLK_RATIO_SHIFT 8 65297627Sjmcneill 66297627Sjmcneillenum aw_apbclk_type { 67297627Sjmcneill AW_A10_APB0 = 1, 68297627Sjmcneill AW_A10_APB1, 69299113Sjmcneill AW_A23_APB0, 70299113Sjmcneill AW_A83T_APB1, 71297627Sjmcneill}; 72297627Sjmcneill 73297627Sjmcneillstatic struct ofw_compat_data compat_data[] = { 74297627Sjmcneill { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 }, 75297627Sjmcneill { "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 }, 76299113Sjmcneill { "allwinner,sun8i-a23-apb0-clk", AW_A23_APB0 }, 77299113Sjmcneill { "allwinner,sun8i-a83t-apb1-clk", AW_A83T_APB1 }, 78297627Sjmcneill { NULL, 0 } 79297627Sjmcneill}; 80297627Sjmcneill 81297627Sjmcneillstruct aw_apbclk_sc { 82297627Sjmcneill device_t clkdev; 83297627Sjmcneill bus_addr_t reg; 84297627Sjmcneill enum aw_apbclk_type type; 85297627Sjmcneill}; 86297627Sjmcneill 87297627Sjmcneill#define APBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) 88297627Sjmcneill#define APBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) 89297627Sjmcneill#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) 90297627Sjmcneill#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) 91297627Sjmcneill 92297627Sjmcneillstatic int 93297627Sjmcneillaw_apbclk_init(struct clknode *clk, device_t dev) 94297627Sjmcneill{ 95297627Sjmcneill struct aw_apbclk_sc *sc; 96297627Sjmcneill uint32_t val, index; 97297627Sjmcneill 98297627Sjmcneill sc = clknode_get_softc(clk); 99297627Sjmcneill 100297627Sjmcneill switch (sc->type) { 101297627Sjmcneill case AW_A10_APB0: 102299113Sjmcneill case AW_A23_APB0: 103299113Sjmcneill case AW_A83T_APB1: 104297627Sjmcneill index = 0; 105297627Sjmcneill break; 106297627Sjmcneill case AW_A10_APB1: 107297627Sjmcneill DEVICE_LOCK(sc); 108297627Sjmcneill APBCLK_READ(sc, &val); 109297627Sjmcneill DEVICE_UNLOCK(sc); 110299113Sjmcneill index = (val & A10_APB1_CLK_SRC_SEL) >> 111299113Sjmcneill A10_APB1_CLK_SRC_SEL_SHIFT; 112297627Sjmcneill break; 113297627Sjmcneill default: 114297627Sjmcneill return (ENXIO); 115297627Sjmcneill } 116297627Sjmcneill 117297627Sjmcneill clknode_init_parent_idx(clk, index); 118297627Sjmcneill return (0); 119297627Sjmcneill} 120297627Sjmcneill 121297627Sjmcneillstatic int 122297627Sjmcneillaw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq) 123297627Sjmcneill{ 124297627Sjmcneill struct aw_apbclk_sc *sc; 125297627Sjmcneill uint32_t val, div, m, n; 126297627Sjmcneill 127297627Sjmcneill sc = clknode_get_softc(clk); 128297627Sjmcneill 129297627Sjmcneill DEVICE_LOCK(sc); 130297627Sjmcneill APBCLK_READ(sc, &val); 131297627Sjmcneill DEVICE_UNLOCK(sc); 132297627Sjmcneill 133297627Sjmcneill switch (sc->type) { 134297627Sjmcneill case AW_A10_APB0: 135299113Sjmcneill div = 1 << ((val & A10_APB0_CLK_RATIO) >> 136299113Sjmcneill A10_APB0_CLK_RATIO_SHIFT); 137297627Sjmcneill if (div == 1) 138297627Sjmcneill div = 2; 139297627Sjmcneill *freq = *freq / div; 140297627Sjmcneill break; 141297627Sjmcneill case AW_A10_APB1: 142299113Sjmcneill n = 1 << ((val & A10_APB1_CLK_RAT_N) >> 143299113Sjmcneill A10_APB1_CLK_RAT_N_SHIFT); 144299113Sjmcneill m = ((val & A10_APB1_CLK_RAT_N) >> 145299113Sjmcneill A10_APB1_CLK_RAT_M_SHIFT) + 1; 146297627Sjmcneill *freq = *freq / n / m; 147297627Sjmcneill break; 148299113Sjmcneill case AW_A23_APB0: 149299113Sjmcneill div = 1 << ((val & A23_APB0_CLK_RATIO) >> 150299113Sjmcneill A23_APB0_CLK_RATIO_SHIFT); 151299113Sjmcneill *freq = *freq / div; 152299113Sjmcneill break; 153299113Sjmcneill case AW_A83T_APB1: 154299113Sjmcneill div = ((val & A83T_APB1_CLK_RATIO) >> 155299113Sjmcneill A83T_APB1_CLK_RATIO_SHIFT) + 1; 156299113Sjmcneill *freq = *freq / div; 157299113Sjmcneill break; 158297627Sjmcneill default: 159297627Sjmcneill return (ENXIO); 160297627Sjmcneill } 161297627Sjmcneill 162297627Sjmcneill return (0); 163297627Sjmcneill} 164297627Sjmcneill 165297627Sjmcneillstatic int 166297627Sjmcneillaw_apbclk_set_mux(struct clknode *clk, int index) 167297627Sjmcneill{ 168297627Sjmcneill struct aw_apbclk_sc *sc; 169297627Sjmcneill uint32_t val; 170297627Sjmcneill 171297627Sjmcneill sc = clknode_get_softc(clk); 172297627Sjmcneill 173297627Sjmcneill if (sc->type != AW_A10_APB1) 174297627Sjmcneill return (ENXIO); 175297627Sjmcneill 176299113Sjmcneill if (index < 0 || index > A10_APB1_CLK_SRC_SEL_MAX) 177297627Sjmcneill return (ERANGE); 178297627Sjmcneill 179297627Sjmcneill DEVICE_LOCK(sc); 180297627Sjmcneill APBCLK_READ(sc, &val); 181299113Sjmcneill val &= ~A10_APB1_CLK_SRC_SEL; 182299113Sjmcneill val |= (index << A10_APB1_CLK_SRC_SEL_SHIFT); 183297627Sjmcneill APBCLK_WRITE(sc, val); 184297627Sjmcneill DEVICE_UNLOCK(sc); 185297627Sjmcneill 186297627Sjmcneill return (0); 187297627Sjmcneill} 188297627Sjmcneill 189297627Sjmcneillstatic clknode_method_t aw_apbclk_clknode_methods[] = { 190297627Sjmcneill /* Device interface */ 191297627Sjmcneill CLKNODEMETHOD(clknode_init, aw_apbclk_init), 192297627Sjmcneill CLKNODEMETHOD(clknode_recalc_freq, aw_apbclk_recalc_freq), 193297627Sjmcneill CLKNODEMETHOD(clknode_set_mux, aw_apbclk_set_mux), 194297627Sjmcneill CLKNODEMETHOD_END 195297627Sjmcneill}; 196297627SjmcneillDEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class, 197297627Sjmcneill aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class); 198297627Sjmcneill 199297627Sjmcneillstatic int 200297627Sjmcneillaw_apbclk_probe(device_t dev) 201297627Sjmcneill{ 202297627Sjmcneill if (!ofw_bus_status_okay(dev)) 203297627Sjmcneill return (ENXIO); 204297627Sjmcneill 205297627Sjmcneill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 206297627Sjmcneill return (ENXIO); 207297627Sjmcneill 208297627Sjmcneill device_set_desc(dev, "Allwinner APB Clock"); 209297627Sjmcneill return (BUS_PROBE_DEFAULT); 210297627Sjmcneill} 211297627Sjmcneill 212297627Sjmcneillstatic int 213297627Sjmcneillaw_apbclk_attach(device_t dev) 214297627Sjmcneill{ 215297627Sjmcneill struct clknode_init_def def; 216297627Sjmcneill struct aw_apbclk_sc *sc; 217297627Sjmcneill struct clkdom *clkdom; 218297627Sjmcneill struct clknode *clk; 219297627Sjmcneill clk_t clk_parent; 220297627Sjmcneill bus_addr_t paddr; 221297627Sjmcneill bus_size_t psize; 222297627Sjmcneill phandle_t node; 223297627Sjmcneill int error, ncells, i; 224297627Sjmcneill 225297627Sjmcneill node = ofw_bus_get_node(dev); 226297627Sjmcneill 227297627Sjmcneill if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { 228297627Sjmcneill device_printf(dev, "cannot parse 'reg' property\n"); 229297627Sjmcneill return (ENXIO); 230297627Sjmcneill } 231297627Sjmcneill 232297627Sjmcneill error = ofw_bus_parse_xref_list_get_length(node, "clocks", 233297627Sjmcneill "#clock-cells", &ncells); 234297627Sjmcneill if (error != 0) { 235297627Sjmcneill device_printf(dev, "cannot get clock count\n"); 236297627Sjmcneill return (error); 237297627Sjmcneill } 238297627Sjmcneill 239297627Sjmcneill clkdom = clkdom_create(dev); 240297627Sjmcneill 241297627Sjmcneill memset(&def, 0, sizeof(def)); 242297627Sjmcneill error = clk_parse_ofw_clk_name(dev, node, &def.name); 243297627Sjmcneill if (error != 0) { 244297627Sjmcneill device_printf(dev, "cannot parse clock name\n"); 245297627Sjmcneill error = ENXIO; 246297627Sjmcneill goto fail; 247297627Sjmcneill } 248297627Sjmcneill def.id = 1; 249297627Sjmcneill def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); 250297627Sjmcneill for (i = 0; i < ncells; i++) { 251297627Sjmcneill error = clk_get_by_ofw_index(dev, i, &clk_parent); 252297627Sjmcneill if (error != 0) { 253297627Sjmcneill device_printf(dev, "cannot get clock %d\n", i); 254297627Sjmcneill goto fail; 255297627Sjmcneill } 256297627Sjmcneill def.parent_names[i] = clk_get_name(clk_parent); 257297627Sjmcneill clk_release(clk_parent); 258297627Sjmcneill } 259297627Sjmcneill def.parent_cnt = ncells; 260297627Sjmcneill 261297627Sjmcneill clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def); 262297627Sjmcneill if (clk == NULL) { 263297627Sjmcneill device_printf(dev, "cannot create clknode\n"); 264297627Sjmcneill error = ENXIO; 265297627Sjmcneill goto fail; 266297627Sjmcneill } 267297627Sjmcneill 268297627Sjmcneill sc = clknode_get_softc(clk); 269297627Sjmcneill sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 270297627Sjmcneill sc->reg = paddr; 271297627Sjmcneill sc->clkdev = device_get_parent(dev); 272297627Sjmcneill 273297627Sjmcneill clknode_register(clkdom, clk); 274297627Sjmcneill 275297627Sjmcneill if (clkdom_finit(clkdom) != 0) { 276297627Sjmcneill device_printf(dev, "cannot finalize clkdom initialization\n"); 277297627Sjmcneill error = ENXIO; 278297627Sjmcneill goto fail; 279297627Sjmcneill } 280297627Sjmcneill 281297627Sjmcneill if (bootverbose) 282297627Sjmcneill clkdom_dump(clkdom); 283297627Sjmcneill 284297627Sjmcneill return (0); 285297627Sjmcneill 286297627Sjmcneillfail: 287297627Sjmcneill return (error); 288297627Sjmcneill} 289297627Sjmcneill 290297627Sjmcneillstatic device_method_t aw_apbclk_methods[] = { 291297627Sjmcneill /* Device interface */ 292297627Sjmcneill DEVMETHOD(device_probe, aw_apbclk_probe), 293297627Sjmcneill DEVMETHOD(device_attach, aw_apbclk_attach), 294297627Sjmcneill 295297627Sjmcneill DEVMETHOD_END 296297627Sjmcneill}; 297297627Sjmcneill 298297627Sjmcneillstatic driver_t aw_apbclk_driver = { 299297627Sjmcneill "aw_apbclk", 300297627Sjmcneill aw_apbclk_methods, 301297627Sjmcneill 0 302297627Sjmcneill}; 303297627Sjmcneill 304297627Sjmcneillstatic devclass_t aw_apbclk_devclass; 305297627Sjmcneill 306297627SjmcneillEARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver, 307297627Sjmcneill aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 308