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$ 27297627Sjmcneill */ 28297627Sjmcneill 29297627Sjmcneill/* 30297627Sjmcneill * Allwinner oscillator clock 31297627Sjmcneill */ 32297627Sjmcneill 33297627Sjmcneill#include <sys/cdefs.h> 34297627Sjmcneill__FBSDID("$FreeBSD$"); 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 }, 79305436Smanu { "allwinner,sun5i-a13", CLOCK_CCU }, 80299113Sjmcneill { "allwinner,sun7i-a20", CLOCK_CCU }, 81299113Sjmcneill { "allwinner,sun6i-a31", CLOCK_CCU }, 82299113Sjmcneill { "allwinner,sun6i-a31s", CLOCK_CCU }, 83299113Sjmcneill { "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL }, 84299688Smanu { "allwinner,sun8i-h3", CLOCK_CCU }, 85297627Sjmcneill { NULL, 0 } 86297627Sjmcneill}; 87297627Sjmcneill 88297627Sjmcneillstatic int 89299113Sjmcneillaw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr, 90299113Sjmcneill bus_space_handle_t *pbsh, bus_size_t *poff) 91297627Sjmcneill{ 92299113Sjmcneill if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) && 93299113Sjmcneill (sc->flags & CLOCK_CCU) != 0) { 94299113Sjmcneill *poff = addr - CCU_BASE; 95299113Sjmcneill *pbsh = sc->ccu_bsh; 96299113Sjmcneill return (0); 97299113Sjmcneill } 98299113Sjmcneill if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) && 99299113Sjmcneill (sc->flags & CLOCK_PRCM) != 0) { 100299113Sjmcneill *poff = addr - PRCM_BASE; 101299113Sjmcneill *pbsh = sc->prcm_bsh; 102299113Sjmcneill return (0); 103299113Sjmcneill } 104299113Sjmcneill if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) && 105299113Sjmcneill (sc->flags & CLOCK_SYSCTRL) != 0) { 106299113Sjmcneill *poff = addr - SYSCTRL_BASE; 107299113Sjmcneill *pbsh = sc->sysctrl_bsh; 108299113Sjmcneill return (0); 109299113Sjmcneill } 110299113Sjmcneill return (EINVAL); 111297627Sjmcneill} 112297627Sjmcneill 113297627Sjmcneillstatic int 114297627Sjmcneillaw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) 115297627Sjmcneill{ 116297627Sjmcneill struct aw_ccu_softc *sc; 117299113Sjmcneill bus_space_handle_t bsh; 118299113Sjmcneill bus_size_t reg; 119297627Sjmcneill 120299113Sjmcneill sc = device_get_softc(dev); 121299113Sjmcneill 122299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 123297627Sjmcneill return (EINVAL); 124297627Sjmcneill 125297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 126299113Sjmcneill bus_space_write_4(sc->bst, bsh, reg, val); 127297627Sjmcneill 128297627Sjmcneill return (0); 129297627Sjmcneill} 130297627Sjmcneill 131297627Sjmcneillstatic int 132297627Sjmcneillaw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 133297627Sjmcneill{ 134297627Sjmcneill struct aw_ccu_softc *sc; 135299113Sjmcneill bus_space_handle_t bsh; 136299113Sjmcneill bus_size_t reg; 137297627Sjmcneill 138299113Sjmcneill sc = device_get_softc(dev); 139299113Sjmcneill 140299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 141297627Sjmcneill return (EINVAL); 142297627Sjmcneill 143297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 144299113Sjmcneill *val = bus_space_read_4(sc->bst, bsh, reg); 145297627Sjmcneill 146297627Sjmcneill return (0); 147297627Sjmcneill} 148297627Sjmcneill 149297627Sjmcneillstatic int 150297627Sjmcneillaw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) 151297627Sjmcneill{ 152297627Sjmcneill struct aw_ccu_softc *sc; 153299113Sjmcneill bus_space_handle_t bsh; 154299113Sjmcneill bus_size_t reg; 155297627Sjmcneill uint32_t val; 156297627Sjmcneill 157299113Sjmcneill sc = device_get_softc(dev); 158299113Sjmcneill 159299113Sjmcneill if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 160297627Sjmcneill return (EINVAL); 161297627Sjmcneill 162297627Sjmcneill mtx_assert(&sc->mtx, MA_OWNED); 163299113Sjmcneill val = bus_space_read_4(sc->bst, bsh, reg); 164297627Sjmcneill val &= ~clr; 165297627Sjmcneill val |= set; 166299113Sjmcneill bus_space_write_4(sc->bst, bsh, reg, val); 167297627Sjmcneill 168297627Sjmcneill return (0); 169297627Sjmcneill} 170297627Sjmcneill 171297627Sjmcneillstatic void 172297627Sjmcneillaw_ccu_device_lock(device_t dev) 173297627Sjmcneill{ 174297627Sjmcneill struct aw_ccu_softc *sc; 175297627Sjmcneill 176297627Sjmcneill sc = device_get_softc(dev); 177297627Sjmcneill mtx_lock(&sc->mtx); 178297627Sjmcneill} 179297627Sjmcneill 180297627Sjmcneillstatic void 181297627Sjmcneillaw_ccu_device_unlock(device_t dev) 182297627Sjmcneill{ 183297627Sjmcneill struct aw_ccu_softc *sc; 184297627Sjmcneill 185297627Sjmcneill sc = device_get_softc(dev); 186297627Sjmcneill mtx_unlock(&sc->mtx); 187297627Sjmcneill} 188297627Sjmcneill 189299113Sjmcneillstatic const struct ofw_compat_data * 190299113Sjmcneillaw_ccu_search_compatible(void) 191299113Sjmcneill{ 192299113Sjmcneill const struct ofw_compat_data *compat; 193299113Sjmcneill phandle_t root; 194299113Sjmcneill 195299113Sjmcneill root = OF_finddevice("/"); 196301082Sjmcneill for (compat = compat_data; compat->ocd_str != NULL; compat++) 197299113Sjmcneill if (fdt_is_compatible(root, compat->ocd_str)) 198299113Sjmcneill break; 199299113Sjmcneill 200299113Sjmcneill return (compat); 201299113Sjmcneill} 202299113Sjmcneill 203297627Sjmcneillstatic int 204297627Sjmcneillaw_ccu_probe(device_t dev) 205297627Sjmcneill{ 206297627Sjmcneill const char *name; 207297627Sjmcneill 208297627Sjmcneill name = ofw_bus_get_name(dev); 209297627Sjmcneill 210297627Sjmcneill if (name == NULL || strcmp(name, "clocks") != 0) 211297627Sjmcneill return (ENXIO); 212297627Sjmcneill 213299113Sjmcneill if (aw_ccu_search_compatible()->ocd_data == 0) 214299113Sjmcneill return (ENXIO); 215297627Sjmcneill 216297627Sjmcneill device_set_desc(dev, "Allwinner Clock Control Unit"); 217297627Sjmcneill return (BUS_PROBE_SPECIFIC); 218297627Sjmcneill} 219297627Sjmcneill 220297627Sjmcneillstatic int 221297627Sjmcneillaw_ccu_attach(device_t dev) 222297627Sjmcneill{ 223297627Sjmcneill struct aw_ccu_softc *sc; 224297627Sjmcneill phandle_t node, child; 225297627Sjmcneill device_t cdev; 226297627Sjmcneill int error; 227297627Sjmcneill 228297627Sjmcneill sc = device_get_softc(dev); 229297627Sjmcneill node = ofw_bus_get_node(dev); 230297627Sjmcneill 231297627Sjmcneill simplebus_init(dev, node); 232297627Sjmcneill 233299113Sjmcneill sc->flags = aw_ccu_search_compatible()->ocd_data; 234299113Sjmcneill 235297627Sjmcneill /* 236299113Sjmcneill * Map registers. The DT doesn't have a "reg" property 237299113Sjmcneill * for the /clocks node and child nodes have conflicting "reg" 238299113Sjmcneill * properties. 239297627Sjmcneill */ 240297627Sjmcneill sc->bst = bus_get_bus_tag(dev); 241299113Sjmcneill if (sc->flags & CLOCK_CCU) { 242299113Sjmcneill error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, 243299113Sjmcneill &sc->ccu_bsh); 244299113Sjmcneill if (error != 0) { 245299113Sjmcneill device_printf(dev, "couldn't map CCU: %d\n", error); 246299113Sjmcneill return (error); 247299113Sjmcneill } 248297627Sjmcneill } 249299113Sjmcneill if (sc->flags & CLOCK_PRCM) { 250299113Sjmcneill error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0, 251299113Sjmcneill &sc->prcm_bsh); 252299113Sjmcneill if (error != 0) { 253299113Sjmcneill device_printf(dev, "couldn't map PRCM: %d\n", error); 254299113Sjmcneill return (error); 255299113Sjmcneill } 256299113Sjmcneill } 257299113Sjmcneill if (sc->flags & CLOCK_SYSCTRL) { 258299113Sjmcneill error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0, 259299113Sjmcneill &sc->sysctrl_bsh); 260299113Sjmcneill if (error != 0) { 261299113Sjmcneill device_printf(dev, "couldn't map SYSCTRL: %d\n", error); 262299113Sjmcneill return (error); 263299113Sjmcneill } 264299113Sjmcneill } 265297627Sjmcneill 266297627Sjmcneill mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 267297627Sjmcneill 268297627Sjmcneill /* Attach child devices */ 269297627Sjmcneill for (child = OF_child(node); child > 0; child = OF_peer(child)) { 270297627Sjmcneill cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); 271297627Sjmcneill if (cdev != NULL) 272297627Sjmcneill device_probe_and_attach(cdev); 273297627Sjmcneill } 274297627Sjmcneill 275297627Sjmcneill return (bus_generic_attach(dev)); 276297627Sjmcneill} 277297627Sjmcneill 278297627Sjmcneillstatic device_method_t aw_ccu_methods[] = { 279297627Sjmcneill /* Device interface */ 280297627Sjmcneill DEVMETHOD(device_probe, aw_ccu_probe), 281297627Sjmcneill DEVMETHOD(device_attach, aw_ccu_attach), 282297627Sjmcneill 283297627Sjmcneill /* clkdev interface */ 284297627Sjmcneill DEVMETHOD(clkdev_write_4, aw_ccu_write_4), 285297627Sjmcneill DEVMETHOD(clkdev_read_4, aw_ccu_read_4), 286297627Sjmcneill DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4), 287297627Sjmcneill DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock), 288297627Sjmcneill DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock), 289297627Sjmcneill 290297627Sjmcneill DEVMETHOD_END 291297627Sjmcneill}; 292297627Sjmcneill 293297627SjmcneillDEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods, 294297627Sjmcneill sizeof(struct aw_ccu_softc), simplebus_driver); 295297627Sjmcneill 296297627Sjmcneillstatic devclass_t aw_ccu_devclass; 297297627Sjmcneill 298297627SjmcneillEARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass, 299297627Sjmcneill 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 300297627Sjmcneill 301297627SjmcneillMODULE_VERSION(aw_ccu, 1); 302