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: releng/11.0/sys/arm/allwinner/aw_ccu.c 301082 2016-05-31 21:58:09Z jmcneill $ 27297627Sjmcneill */ 28297627Sjmcneill 29297627Sjmcneill/* 30297627Sjmcneill * Allwinner oscillator clock 31297627Sjmcneill */ 32297627Sjmcneill 33297627Sjmcneill#include <sys/cdefs.h> 34297627Sjmcneill__FBSDID("$FreeBSD: releng/11.0/sys/arm/allwinner/aw_ccu.c 301082 2016-05-31 21:58:09Z 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/fdt/simplebus.h> 45299113Sjmcneill#include <dev/fdt/fdt_common.h> 46297627Sjmcneill 47297627Sjmcneill#include <dev/ofw/ofw_bus.h> 48297627Sjmcneill#include <dev/ofw/ofw_bus_subr.h> 49297627Sjmcneill 50297627Sjmcneill#include <dev/extres/clk/clk.h> 51297627Sjmcneill 52297627Sjmcneill#include "clkdev_if.h" 53297627Sjmcneill 54297627Sjmcneill#define CCU_BASE 0x01c20000 55297627Sjmcneill#define CCU_SIZE 0x400 56297627Sjmcneill 57299113Sjmcneill#define PRCM_BASE 0x01f01400 58299113Sjmcneill#define PRCM_SIZE 0x200 59299113Sjmcneill 60299113Sjmcneill#define SYSCTRL_BASE 0x01c00000 61299113Sjmcneill#define SYSCTRL_SIZE 0x34 62299113Sjmcneill 63297627Sjmcneillstruct aw_ccu_softc { 64297627Sjmcneill struct simplebus_softc sc; 65297627Sjmcneill bus_space_tag_t bst; 66299113Sjmcneill bus_space_handle_t ccu_bsh; 67299113Sjmcneill bus_space_handle_t prcm_bsh; 68299113Sjmcneill bus_space_handle_t sysctrl_bsh; 69297627Sjmcneill struct mtx mtx; 70299113Sjmcneill int flags; 71297627Sjmcneill}; 72297627Sjmcneill 73299113Sjmcneill#define CLOCK_CCU (1 << 0) 74299113Sjmcneill#define CLOCK_PRCM (1 << 1) 75299113Sjmcneill#define CLOCK_SYSCTRL (1 << 2) 76299113Sjmcneill 77297627Sjmcneillstatic struct ofw_compat_data compat_data[] = { 78299113Sjmcneill { "allwinner,sun4i-a10", CLOCK_CCU }, 79299113Sjmcneill { "allwinner,sun7i-a20", CLOCK_CCU }, 80299113Sjmcneill { "allwinner,sun6i-a31", CLOCK_CCU }, 81299113Sjmcneill { "allwinner,sun6i-a31s", CLOCK_CCU }, 82299113Sjmcneill { "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL }, 83299688Smanu { "allwinner,sun8i-h3", CLOCK_CCU }, 84297627Sjmcneill { NULL, 0 } 85297627Sjmcneill}; 86297627Sjmcneill 87297627Sjmcneillstatic int 88299113Sjmcneillaw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr, 89299113Sjmcneill bus_space_handle_t *pbsh, bus_size_t *poff) 90297627Sjmcneill{ 91299113Sjmcneill if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) && 92299113Sjmcneill (sc->flags & CLOCK_CCU) != 0) { 93299113Sjmcneill *poff = addr - CCU_BASE; 94299113Sjmcneill *pbsh = sc->ccu_bsh; 95299113Sjmcneill return (0); 96299113Sjmcneill } 97299113Sjmcneill if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) && 98299113Sjmcneill (sc->flags & CLOCK_PRCM) != 0) { 99299113Sjmcneill *poff = addr - PRCM_BASE; 100299113Sjmcneill *pbsh = sc->prcm_bsh; 101299113Sjmcneill return (0); 102299113Sjmcneill } 103299113Sjmcneill if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) && 104299113Sjmcneill (sc->flags & CLOCK_SYSCTRL) != 0) { 105299113Sjmcneill *poff = addr - SYSCTRL_BASE; 106299113Sjmcneill *pbsh = sc->sysctrl_bsh; 107299113Sjmcneill return (0); 108299113Sjmcneill } 109299113Sjmcneill return (EINVAL); 110297627Sjmcneill} 111297627Sjmcneill 112297627Sjmcneillstatic int 113297627Sjmcneillaw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) 114297627Sjmcneill{ 115297627Sjmcneill struct aw_ccu_softc *sc; 116299113Sjmcneill bus_space_handle_t bsh; 117299113Sjmcneill bus_size_t reg; 118297627Sjmcneill 119299113Sjmcneill sc = device_get_softc(dev); 120299113Sjmcneill 121299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 122297627Sjmcneill return (EINVAL); 123297627Sjmcneill 124297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 125299113Sjmcneill bus_space_write_4(sc->bst, bsh, reg, val); 126297627Sjmcneill 127297627Sjmcneill return (0); 128297627Sjmcneill} 129297627Sjmcneill 130297627Sjmcneillstatic int 131297627Sjmcneillaw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 132297627Sjmcneill{ 133297627Sjmcneill struct aw_ccu_softc *sc; 134299113Sjmcneill bus_space_handle_t bsh; 135299113Sjmcneill bus_size_t reg; 136297627Sjmcneill 137299113Sjmcneill sc = device_get_softc(dev); 138299113Sjmcneill 139299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 140297627Sjmcneill return (EINVAL); 141297627Sjmcneill 142297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 143299113Sjmcneill *val = bus_space_read_4(sc->bst, bsh, reg); 144297627Sjmcneill 145297627Sjmcneill return (0); 146297627Sjmcneill} 147297627Sjmcneill 148297627Sjmcneillstatic int 149297627Sjmcneillaw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) 150297627Sjmcneill{ 151297627Sjmcneill struct aw_ccu_softc *sc; 152299113Sjmcneill bus_space_handle_t bsh; 153299113Sjmcneill bus_size_t reg; 154297627Sjmcneill uint32_t val; 155297627Sjmcneill 156299113Sjmcneill sc = device_get_softc(dev); 157299113Sjmcneill 158299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 159297627Sjmcneill return (EINVAL); 160297627Sjmcneill 161297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 162299113Sjmcneill val = bus_space_read_4(sc->bst, bsh, reg); 163297627Sjmcneill val &= ~clr; 164297627Sjmcneill val |= set; 165299113Sjmcneill bus_space_write_4(sc->bst, bsh, reg, val); 166297627Sjmcneill 167297627Sjmcneill return (0); 168297627Sjmcneill} 169297627Sjmcneill 170297627Sjmcneillstatic void 171297627Sjmcneillaw_ccu_device_lock(device_t dev) 172297627Sjmcneill{ 173297627Sjmcneill struct aw_ccu_softc *sc; 174297627Sjmcneill 175297627Sjmcneill sc = device_get_softc(dev); 176297627Sjmcneill mtx_lock(&sc->mtx); 177297627Sjmcneill} 178297627Sjmcneill 179297627Sjmcneillstatic void 180297627Sjmcneillaw_ccu_device_unlock(device_t dev) 181297627Sjmcneill{ 182297627Sjmcneill struct aw_ccu_softc *sc; 183297627Sjmcneill 184297627Sjmcneill sc = device_get_softc(dev); 185297627Sjmcneill mtx_unlock(&sc->mtx); 186297627Sjmcneill} 187297627Sjmcneill 188299113Sjmcneillstatic const struct ofw_compat_data * 189299113Sjmcneillaw_ccu_search_compatible(void) 190299113Sjmcneill{ 191299113Sjmcneill const struct ofw_compat_data *compat; 192299113Sjmcneill phandle_t root; 193299113Sjmcneill 194299113Sjmcneill root = OF_finddevice("/"); 195301082Sjmcneill for (compat = compat_data; compat->ocd_str != NULL; compat++) 196299113Sjmcneill if (fdt_is_compatible(root, compat->ocd_str)) 197299113Sjmcneill break; 198299113Sjmcneill 199299113Sjmcneill return (compat); 200299113Sjmcneill} 201299113Sjmcneill 202297627Sjmcneillstatic int 203297627Sjmcneillaw_ccu_probe(device_t dev) 204297627Sjmcneill{ 205297627Sjmcneill const char *name; 206297627Sjmcneill 207297627Sjmcneill name = ofw_bus_get_name(dev); 208297627Sjmcneill 209297627Sjmcneill if (name == NULL || strcmp(name, "clocks") != 0) 210297627Sjmcneill return (ENXIO); 211297627Sjmcneill 212299113Sjmcneill if (aw_ccu_search_compatible()->ocd_data == 0) 213299113Sjmcneill return (ENXIO); 214297627Sjmcneill 215297627Sjmcneill device_set_desc(dev, "Allwinner Clock Control Unit"); 216297627Sjmcneill return (BUS_PROBE_SPECIFIC); 217297627Sjmcneill} 218297627Sjmcneill 219297627Sjmcneillstatic int 220297627Sjmcneillaw_ccu_attach(device_t dev) 221297627Sjmcneill{ 222297627Sjmcneill struct aw_ccu_softc *sc; 223297627Sjmcneill phandle_t node, child; 224297627Sjmcneill device_t cdev; 225297627Sjmcneill int error; 226297627Sjmcneill 227297627Sjmcneill sc = device_get_softc(dev); 228297627Sjmcneill node = ofw_bus_get_node(dev); 229297627Sjmcneill 230297627Sjmcneill simplebus_init(dev, node); 231297627Sjmcneill 232299113Sjmcneill sc->flags = aw_ccu_search_compatible()->ocd_data; 233299113Sjmcneill 234297627Sjmcneill /* 235299113Sjmcneill * Map registers. The DT doesn't have a "reg" property 236299113Sjmcneill * for the /clocks node and child nodes have conflicting "reg" 237299113Sjmcneill * properties. 238297627Sjmcneill */ 239297627Sjmcneill sc->bst = bus_get_bus_tag(dev); 240299113Sjmcneill if (sc->flags & CLOCK_CCU) { 241299113Sjmcneill error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, 242299113Sjmcneill &sc->ccu_bsh); 243299113Sjmcneill if (error != 0) { 244299113Sjmcneill device_printf(dev, "couldn't map CCU: %d\n", error); 245299113Sjmcneill return (error); 246299113Sjmcneill } 247297627Sjmcneill } 248299113Sjmcneill if (sc->flags & CLOCK_PRCM) { 249299113Sjmcneill error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0, 250299113Sjmcneill &sc->prcm_bsh); 251299113Sjmcneill if (error != 0) { 252299113Sjmcneill device_printf(dev, "couldn't map PRCM: %d\n", error); 253299113Sjmcneill return (error); 254299113Sjmcneill } 255299113Sjmcneill } 256299113Sjmcneill if (sc->flags & CLOCK_SYSCTRL) { 257299113Sjmcneill error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0, 258299113Sjmcneill &sc->sysctrl_bsh); 259299113Sjmcneill if (error != 0) { 260299113Sjmcneill device_printf(dev, "couldn't map SYSCTRL: %d\n", error); 261299113Sjmcneill return (error); 262299113Sjmcneill } 263299113Sjmcneill } 264297627Sjmcneill 265297627Sjmcneill mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 266297627Sjmcneill 267297627Sjmcneill /* Attach child devices */ 268297627Sjmcneill for (child = OF_child(node); child > 0; child = OF_peer(child)) { 269297627Sjmcneill cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); 270297627Sjmcneill if (cdev != NULL) 271297627Sjmcneill device_probe_and_attach(cdev); 272297627Sjmcneill } 273297627Sjmcneill 274297627Sjmcneill return (bus_generic_attach(dev)); 275297627Sjmcneill} 276297627Sjmcneill 277297627Sjmcneillstatic device_method_t aw_ccu_methods[] = { 278297627Sjmcneill /* Device interface */ 279297627Sjmcneill DEVMETHOD(device_probe, aw_ccu_probe), 280297627Sjmcneill DEVMETHOD(device_attach, aw_ccu_attach), 281297627Sjmcneill 282297627Sjmcneill /* clkdev interface */ 283297627Sjmcneill DEVMETHOD(clkdev_write_4, aw_ccu_write_4), 284297627Sjmcneill DEVMETHOD(clkdev_read_4, aw_ccu_read_4), 285297627Sjmcneill DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4), 286297627Sjmcneill DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock), 287297627Sjmcneill DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock), 288297627Sjmcneill 289297627Sjmcneill DEVMETHOD_END 290297627Sjmcneill}; 291297627Sjmcneill 292297627SjmcneillDEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods, 293297627Sjmcneill sizeof(struct aw_ccu_softc), simplebus_driver); 294297627Sjmcneill 295297627Sjmcneillstatic devclass_t aw_ccu_devclass; 296297627Sjmcneill 297297627SjmcneillEARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass, 298297627Sjmcneill 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 299297627Sjmcneill 300297627SjmcneillMODULE_VERSION(aw_ccu, 1); 301