aw_apbclk.c revision 297627
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 297627 2016-04-06 23:11:03Z 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 297627 2016-04-06 23:11:03Z 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 52297627Sjmcneill#define APB0_CLK_RATIO (0x3 << 8) 53297627Sjmcneill#define APB0_CLK_RATIO_SHIFT 8 54297627Sjmcneill#define APB1_CLK_SRC_SEL (0x3 << 24) 55297627Sjmcneill#define APB1_CLK_SRC_SEL_SHIFT 24 56297627Sjmcneill#define APB1_CLK_SRC_SEL_MAX 0x3 57297627Sjmcneill#define APB1_CLK_RAT_N (0x3 << 16) 58297627Sjmcneill#define APB1_CLK_RAT_N_SHIFT 16 59297627Sjmcneill#define APB1_CLK_RAT_M (0x1f << 0) 60297627Sjmcneill#define APB1_CLK_RAT_M_SHIFT 0 61297627Sjmcneill 62297627Sjmcneillenum aw_apbclk_type { 63297627Sjmcneill AW_A10_APB0 = 1, 64297627Sjmcneill AW_A10_APB1, 65297627Sjmcneill}; 66297627Sjmcneill 67297627Sjmcneillstatic struct ofw_compat_data compat_data[] = { 68297627Sjmcneill { "allwinner,sun4i-a10-apb0-clk", AW_A10_APB0 }, 69297627Sjmcneill { "allwinner,sun4i-a10-apb1-clk", AW_A10_APB1 }, 70297627Sjmcneill { NULL, 0 } 71297627Sjmcneill}; 72297627Sjmcneill 73297627Sjmcneillstruct aw_apbclk_sc { 74297627Sjmcneill device_t clkdev; 75297627Sjmcneill bus_addr_t reg; 76297627Sjmcneill enum aw_apbclk_type type; 77297627Sjmcneill}; 78297627Sjmcneill 79297627Sjmcneill#define APBCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) 80297627Sjmcneill#define APBCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) 81297627Sjmcneill#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) 82297627Sjmcneill#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) 83297627Sjmcneill 84297627Sjmcneillstatic int 85297627Sjmcneillaw_apbclk_init(struct clknode *clk, device_t dev) 86297627Sjmcneill{ 87297627Sjmcneill struct aw_apbclk_sc *sc; 88297627Sjmcneill uint32_t val, index; 89297627Sjmcneill 90297627Sjmcneill sc = clknode_get_softc(clk); 91297627Sjmcneill 92297627Sjmcneill switch (sc->type) { 93297627Sjmcneill case AW_A10_APB0: 94297627Sjmcneill index = 0; 95297627Sjmcneill break; 96297627Sjmcneill case AW_A10_APB1: 97297627Sjmcneill DEVICE_LOCK(sc); 98297627Sjmcneill APBCLK_READ(sc, &val); 99297627Sjmcneill DEVICE_UNLOCK(sc); 100297627Sjmcneill index = (val & APB1_CLK_SRC_SEL) >> APB1_CLK_SRC_SEL_SHIFT; 101297627Sjmcneill break; 102297627Sjmcneill default: 103297627Sjmcneill return (ENXIO); 104297627Sjmcneill } 105297627Sjmcneill 106297627Sjmcneill clknode_init_parent_idx(clk, index); 107297627Sjmcneill return (0); 108297627Sjmcneill} 109297627Sjmcneill 110297627Sjmcneillstatic int 111297627Sjmcneillaw_apbclk_recalc_freq(struct clknode *clk, uint64_t *freq) 112297627Sjmcneill{ 113297627Sjmcneill struct aw_apbclk_sc *sc; 114297627Sjmcneill uint32_t val, div, m, n; 115297627Sjmcneill 116297627Sjmcneill sc = clknode_get_softc(clk); 117297627Sjmcneill 118297627Sjmcneill DEVICE_LOCK(sc); 119297627Sjmcneill APBCLK_READ(sc, &val); 120297627Sjmcneill DEVICE_UNLOCK(sc); 121297627Sjmcneill 122297627Sjmcneill switch (sc->type) { 123297627Sjmcneill case AW_A10_APB0: 124297627Sjmcneill div = 1 << ((val & APB0_CLK_RATIO) >> APB0_CLK_RATIO_SHIFT); 125297627Sjmcneill if (div == 1) 126297627Sjmcneill div = 2; 127297627Sjmcneill *freq = *freq / div; 128297627Sjmcneill break; 129297627Sjmcneill case AW_A10_APB1: 130297627Sjmcneill n = 1 << ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_N_SHIFT); 131297627Sjmcneill m = ((val & APB1_CLK_RAT_N) >> APB1_CLK_RAT_M_SHIFT) + 1; 132297627Sjmcneill *freq = *freq / n / m; 133297627Sjmcneill break; 134297627Sjmcneill default: 135297627Sjmcneill return (ENXIO); 136297627Sjmcneill } 137297627Sjmcneill 138297627Sjmcneill return (0); 139297627Sjmcneill} 140297627Sjmcneill 141297627Sjmcneillstatic int 142297627Sjmcneillaw_apbclk_set_mux(struct clknode *clk, int index) 143297627Sjmcneill{ 144297627Sjmcneill struct aw_apbclk_sc *sc; 145297627Sjmcneill uint32_t val; 146297627Sjmcneill 147297627Sjmcneill sc = clknode_get_softc(clk); 148297627Sjmcneill 149297627Sjmcneill if (sc->type != AW_A10_APB1) 150297627Sjmcneill return (ENXIO); 151297627Sjmcneill 152297627Sjmcneill if (index < 0 || index > APB1_CLK_SRC_SEL_MAX) 153297627Sjmcneill return (ERANGE); 154297627Sjmcneill 155297627Sjmcneill DEVICE_LOCK(sc); 156297627Sjmcneill APBCLK_READ(sc, &val); 157297627Sjmcneill val &= ~APB1_CLK_SRC_SEL; 158297627Sjmcneill val |= (index << APB1_CLK_SRC_SEL_SHIFT); 159297627Sjmcneill APBCLK_WRITE(sc, val); 160297627Sjmcneill DEVICE_UNLOCK(sc); 161297627Sjmcneill 162297627Sjmcneill return (0); 163297627Sjmcneill} 164297627Sjmcneill 165297627Sjmcneillstatic clknode_method_t aw_apbclk_clknode_methods[] = { 166297627Sjmcneill /* Device interface */ 167297627Sjmcneill CLKNODEMETHOD(clknode_init, aw_apbclk_init), 168297627Sjmcneill CLKNODEMETHOD(clknode_recalc_freq, aw_apbclk_recalc_freq), 169297627Sjmcneill CLKNODEMETHOD(clknode_set_mux, aw_apbclk_set_mux), 170297627Sjmcneill CLKNODEMETHOD_END 171297627Sjmcneill}; 172297627SjmcneillDEFINE_CLASS_1(aw_apbclk_clknode, aw_apbclk_clknode_class, 173297627Sjmcneill aw_apbclk_clknode_methods, sizeof(struct aw_apbclk_sc), clknode_class); 174297627Sjmcneill 175297627Sjmcneillstatic int 176297627Sjmcneillaw_apbclk_probe(device_t dev) 177297627Sjmcneill{ 178297627Sjmcneill if (!ofw_bus_status_okay(dev)) 179297627Sjmcneill return (ENXIO); 180297627Sjmcneill 181297627Sjmcneill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 182297627Sjmcneill return (ENXIO); 183297627Sjmcneill 184297627Sjmcneill device_set_desc(dev, "Allwinner APB Clock"); 185297627Sjmcneill return (BUS_PROBE_DEFAULT); 186297627Sjmcneill} 187297627Sjmcneill 188297627Sjmcneillstatic int 189297627Sjmcneillaw_apbclk_attach(device_t dev) 190297627Sjmcneill{ 191297627Sjmcneill struct clknode_init_def def; 192297627Sjmcneill struct aw_apbclk_sc *sc; 193297627Sjmcneill struct clkdom *clkdom; 194297627Sjmcneill struct clknode *clk; 195297627Sjmcneill clk_t clk_parent; 196297627Sjmcneill bus_addr_t paddr; 197297627Sjmcneill bus_size_t psize; 198297627Sjmcneill phandle_t node; 199297627Sjmcneill int error, ncells, i; 200297627Sjmcneill 201297627Sjmcneill node = ofw_bus_get_node(dev); 202297627Sjmcneill 203297627Sjmcneill if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { 204297627Sjmcneill device_printf(dev, "cannot parse 'reg' property\n"); 205297627Sjmcneill return (ENXIO); 206297627Sjmcneill } 207297627Sjmcneill 208297627Sjmcneill error = ofw_bus_parse_xref_list_get_length(node, "clocks", 209297627Sjmcneill "#clock-cells", &ncells); 210297627Sjmcneill if (error != 0) { 211297627Sjmcneill device_printf(dev, "cannot get clock count\n"); 212297627Sjmcneill return (error); 213297627Sjmcneill } 214297627Sjmcneill 215297627Sjmcneill clkdom = clkdom_create(dev); 216297627Sjmcneill 217297627Sjmcneill memset(&def, 0, sizeof(def)); 218297627Sjmcneill error = clk_parse_ofw_clk_name(dev, node, &def.name); 219297627Sjmcneill if (error != 0) { 220297627Sjmcneill device_printf(dev, "cannot parse clock name\n"); 221297627Sjmcneill error = ENXIO; 222297627Sjmcneill goto fail; 223297627Sjmcneill } 224297627Sjmcneill def.id = 1; 225297627Sjmcneill def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); 226297627Sjmcneill for (i = 0; i < ncells; i++) { 227297627Sjmcneill error = clk_get_by_ofw_index(dev, i, &clk_parent); 228297627Sjmcneill if (error != 0) { 229297627Sjmcneill device_printf(dev, "cannot get clock %d\n", i); 230297627Sjmcneill goto fail; 231297627Sjmcneill } 232297627Sjmcneill def.parent_names[i] = clk_get_name(clk_parent); 233297627Sjmcneill clk_release(clk_parent); 234297627Sjmcneill } 235297627Sjmcneill def.parent_cnt = ncells; 236297627Sjmcneill 237297627Sjmcneill clk = clknode_create(clkdom, &aw_apbclk_clknode_class, &def); 238297627Sjmcneill if (clk == NULL) { 239297627Sjmcneill device_printf(dev, "cannot create clknode\n"); 240297627Sjmcneill error = ENXIO; 241297627Sjmcneill goto fail; 242297627Sjmcneill } 243297627Sjmcneill 244297627Sjmcneill sc = clknode_get_softc(clk); 245297627Sjmcneill sc->type = ofw_bus_search_compatible(dev, compat_data)->ocd_data; 246297627Sjmcneill sc->reg = paddr; 247297627Sjmcneill sc->clkdev = device_get_parent(dev); 248297627Sjmcneill 249297627Sjmcneill clknode_register(clkdom, clk); 250297627Sjmcneill 251297627Sjmcneill if (clkdom_finit(clkdom) != 0) { 252297627Sjmcneill device_printf(dev, "cannot finalize clkdom initialization\n"); 253297627Sjmcneill error = ENXIO; 254297627Sjmcneill goto fail; 255297627Sjmcneill } 256297627Sjmcneill 257297627Sjmcneill if (bootverbose) 258297627Sjmcneill clkdom_dump(clkdom); 259297627Sjmcneill 260297627Sjmcneill return (0); 261297627Sjmcneill 262297627Sjmcneillfail: 263297627Sjmcneill return (error); 264297627Sjmcneill} 265297627Sjmcneill 266297627Sjmcneillstatic device_method_t aw_apbclk_methods[] = { 267297627Sjmcneill /* Device interface */ 268297627Sjmcneill DEVMETHOD(device_probe, aw_apbclk_probe), 269297627Sjmcneill DEVMETHOD(device_attach, aw_apbclk_attach), 270297627Sjmcneill 271297627Sjmcneill DEVMETHOD_END 272297627Sjmcneill}; 273297627Sjmcneill 274297627Sjmcneillstatic driver_t aw_apbclk_driver = { 275297627Sjmcneill "aw_apbclk", 276297627Sjmcneill aw_apbclk_methods, 277297627Sjmcneill 0 278297627Sjmcneill}; 279297627Sjmcneill 280297627Sjmcneillstatic devclass_t aw_apbclk_devclass; 281297627Sjmcneill 282297627SjmcneillEARLY_DRIVER_MODULE(aw_apbclk, simplebus, aw_apbclk_driver, 283297627Sjmcneill aw_apbclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 284