aw_ccu.c revision 299688
1139825Simp/*- 2167755Ssam * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 3167755Ssam * All rights reserved. 4167755Ssam * 5167755Ssam * Redistribution and use in source and binary forms, with or without 6167755Ssam * modification, are permitted provided that the following conditions 7167755Ssam * are met: 8167755Ssam * 1. Redistributions of source code must retain the above copyright 9167755Ssam * notice, this list of conditions and the following disclaimer. 10167755Ssam * 2. Redistributions in binary form must reproduce the above copyright 11167755Ssam * notice, this list of conditions and the following disclaimer in the 12167755Ssam * documentation and/or other materials provided with the distribution. 13167755Ssam * 14167755Ssam * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 15167755Ssam * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 16167755Ssam * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 17167755Ssam * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 18167755Ssam * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 19167755Ssam * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20167755Ssam * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 21167755Ssam * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22167755Ssam * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23167755Ssam * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24167755Ssam * SUCH DAMAGE. 25167755Ssam * 26167755Ssam * $FreeBSD: head/sys/arm/allwinner/aw_ccu.c 299688 2016-05-13 18:20:54Z manu $ 27167755Ssam */ 28167755Ssam 29167755Ssam/* 30167755Ssam * Allwinner oscillator clock 31167755Ssam */ 32167755Ssam 33167755Ssam#include <sys/cdefs.h> 34167755Ssam__FBSDID("$FreeBSD: head/sys/arm/allwinner/aw_ccu.c 299688 2016-05-13 18:20:54Z manu $"); 35167755Ssam 36167755Ssam#include <sys/param.h> 37104476Ssam#include <sys/systm.h> 38104476Ssam#include <sys/bus.h> 39104476Ssam#include <sys/rman.h> 40104476Ssam#include <sys/kernel.h> 41104476Ssam#include <sys/module.h> 42104476Ssam#include <machine/bus.h> 43104476Ssam 44104476Ssam#include <dev/fdt/simplebus.h> 45104476Ssam#include <dev/fdt/fdt_common.h> 46104476Ssam 47104476Ssam#include <dev/ofw/ofw_bus.h> 48104476Ssam#include <dev/ofw/ofw_bus_subr.h> 49104476Ssam 50104476Ssam#include <dev/extres/clk/clk.h> 51104476Ssam 52104476Ssam#include "clkdev_if.h" 53104476Ssam 54104476Ssam#define CCU_BASE 0x01c20000 55104476Ssam#define CCU_SIZE 0x400 56116191Sobrien 57108587Ssam#define PRCM_BASE 0x01f01400 58104476Ssam#define PRCM_SIZE 0x200 59167755Ssam 60167755Ssam#define SYSCTRL_BASE 0x01c00000 61104476Ssam#define SYSCTRL_SIZE 0x34 62104476Ssam 63104476Ssamstruct aw_ccu_softc { 64104476Ssam struct simplebus_softc sc; 65104476Ssam bus_space_tag_t bst; 66104476Ssam bus_space_handle_t ccu_bsh; 67129880Sphk bus_space_handle_t prcm_bsh; 68104476Ssam bus_space_handle_t sysctrl_bsh; 69104476Ssam struct mtx mtx; 70104476Ssam int flags; 71199884Sbz}; 72104476Ssam 73104476Ssam#define CLOCK_CCU (1 << 0) 74167755Ssam#define CLOCK_PRCM (1 << 1) 75167755Ssam#define CLOCK_SYSCTRL (1 << 2) 76104476Ssam 77104476Ssamstatic struct ofw_compat_data compat_data[] = { 78104628Ssam { "allwinner,sun4i-a10", CLOCK_CCU }, 79104476Ssam { "allwinner,sun7i-a20", CLOCK_CCU }, 80167755Ssam { "allwinner,sun6i-a31", CLOCK_CCU }, 81167755Ssam { "allwinner,sun6i-a31s", CLOCK_CCU }, 82167755Ssam { "allwinner,sun8i-a83t", CLOCK_CCU|CLOCK_PRCM|CLOCK_SYSCTRL }, 83167755Ssam { "allwinner,sun8i-h3", CLOCK_CCU }, 84208834Skib { NULL, 0 } 85208834Skib}; 86208834Skib 87208834Skibstatic int 88199884Sbzaw_ccu_check_addr(struct aw_ccu_softc *sc, bus_addr_t addr, 89199884Sbz bus_space_handle_t *pbsh, bus_size_t *poff) 90104476Ssam{ 91104476Ssam if (addr >= CCU_BASE && addr < (CCU_BASE + CCU_SIZE) && 92104476Ssam (sc->flags & CLOCK_CCU) != 0) { 93104476Ssam *poff = addr - CCU_BASE; 94104476Ssam *pbsh = sc->ccu_bsh; 95104476Ssam return (0); 96104476Ssam } 97104476Ssam if (addr >= PRCM_BASE && addr < (PRCM_BASE + PRCM_SIZE) && 98167755Ssam (sc->flags & CLOCK_PRCM) != 0) { 99167755Ssam *poff = addr - PRCM_BASE; 100167755Ssam *pbsh = sc->prcm_bsh; 101167755Ssam return (0); 102167755Ssam } 103167755Ssam if (addr >= SYSCTRL_BASE && addr < (SYSCTRL_BASE + SYSCTRL_SIZE) && 104167755Ssam (sc->flags & CLOCK_SYSCTRL) != 0) { 105167755Ssam *poff = addr - SYSCTRL_BASE; 106167755Ssam *pbsh = sc->sysctrl_bsh; 107167755Ssam return (0); 108167755Ssam } 109167755Ssam return (EINVAL); 110167755Ssam} 111167755Ssam 112167755Ssamstatic int 113167755Ssamaw_ccu_write_4(device_t dev, bus_addr_t addr, uint32_t val) 114167755Ssam{ 115167755Ssam struct aw_ccu_softc *sc; 116167755Ssam bus_space_handle_t bsh; 117167755Ssam bus_size_t reg; 118167755Ssam 119167755Ssam sc = device_get_softc(dev); 120167755Ssam 121167755Ssam if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 122167755Ssam return (EINVAL); 123167755Ssam 124167755Ssam mtx_assert(&sc->mtx, MA_OWNED); 125104476Ssam bus_space_write_4(sc->bst, bsh, reg, val); 126104476Ssam 127104476Ssam return (0); 128104476Ssam} 129104476Ssam 130104476Ssamstatic int 131104476Ssamaw_ccu_read_4(device_t dev, bus_addr_t addr, uint32_t *val) 132104476Ssam{ 133104476Ssam struct aw_ccu_softc *sc; 134104476Ssam bus_space_handle_t bsh; 135158827Spjd bus_size_t reg; 136104476Ssam 137104476Ssam sc = device_get_softc(dev); 138104476Ssam 139104476Ssam if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 140104476Ssam return (EINVAL); 141104476Ssam 142104476Ssam mtx_assert(&sc->mtx, MA_OWNED); 143104476Ssam *val = bus_space_read_4(sc->bst, bsh, reg); 144104476Ssam 145104476Ssam return (0); 146104476Ssam} 147104476Ssam 148104476Ssamstatic int 149104476Ssamaw_ccu_modify_4(device_t dev, bus_addr_t addr, uint32_t clr, uint32_t set) 150104476Ssam{ 151104476Ssam struct aw_ccu_softc *sc; 152104476Ssam bus_space_handle_t bsh; 153104476Ssam bus_size_t reg; 154104476Ssam uint32_t val; 155158826Spjd 156104476Ssam sc = device_get_softc(dev); 157104476Ssam 158104476Ssam if (aw_ccu_check_addr(sc, addr, &bsh, ®) != 0) 159104476Ssam return (EINVAL); 160104476Ssam 161104476Ssam mtx_assert(&sc->mtx, MA_OWNED); 162104476Ssam val = bus_space_read_4(sc->bst, bsh, reg); 163104476Ssam val &= ~clr; 164262994Sjmg val |= set; 165104476Ssam bus_space_write_4(sc->bst, bsh, reg, val); 166104476Ssam 167262994Sjmg return (0); 168104476Ssam} 169104476Ssam 170104476Ssamstatic void 171108588Ssamaw_ccu_device_lock(device_t dev) 172108588Ssam{ 173108588Ssam struct aw_ccu_softc *sc; 174108588Ssam 175108588Ssam sc = device_get_softc(dev); 176158702Spjd mtx_lock(&sc->mtx); 177167755Ssam} 178108588Ssam 179108587Ssamstatic void 180108587Ssamaw_ccu_device_unlock(device_t dev) 181108587Ssam{ 182108587Ssam struct aw_ccu_softc *sc; 183108587Ssam 184108587Ssam sc = device_get_softc(dev); 185108587Ssam mtx_unlock(&sc->mtx); 186108587Ssam} 187108587Ssam 188108587Ssamstatic const struct ofw_compat_data * 189108588Ssamaw_ccu_search_compatible(void) 190104476Ssam{ 191104476Ssam const struct ofw_compat_data *compat; 192108588Ssam phandle_t root; 193108588Ssam 194115746Ssam root = OF_finddevice("/"); 195115746Ssam for (compat = compat_data; compat_data->ocd_str != NULL; compat++) 196108588Ssam if (fdt_is_compatible(root, compat->ocd_str)) 197108588Ssam break; 198108588Ssam 199115746Ssam return (compat); 200108588Ssam} 201108588Ssam 202108588Ssamstatic int 203115746Ssamaw_ccu_probe(device_t dev) 204108588Ssam{ 205104476Ssam const char *name; 206104476Ssam 207104476Ssam name = ofw_bus_get_name(dev); 208104476Ssam 209104476Ssam if (name == NULL || strcmp(name, "clocks") != 0) 210104476Ssam return (ENXIO); 211108588Ssam 212108588Ssam if (aw_ccu_search_compatible()->ocd_data == 0) 213108588Ssam return (ENXIO); 214108588Ssam 215108588Ssam device_set_desc(dev, "Allwinner Clock Control Unit"); 216104476Ssam return (BUS_PROBE_SPECIFIC); 217104476Ssam} 218104476Ssam 219104476Ssamstatic int 220108588Ssamaw_ccu_attach(device_t dev) 221108588Ssam{ 222108588Ssam struct aw_ccu_softc *sc; 223108588Ssam phandle_t node, child; 224108588Ssam device_t cdev; 225104476Ssam int error; 226172836Sjulian 227108588Ssam sc = device_get_softc(dev); 228108588Ssam node = ofw_bus_get_node(dev); 229108588Ssam 230108588Ssam simplebus_init(dev, node); 231108588Ssam 232108588Ssam sc->flags = aw_ccu_search_compatible()->ocd_data; 233104476Ssam 234172836Sjulian /* 235108588Ssam * Map registers. The DT doesn't have a "reg" property 236108588Ssam * for the /clocks node and child nodes have conflicting "reg" 237108588Ssam * properties. 238108588Ssam */ 239108588Ssam sc->bst = bus_get_bus_tag(dev); 240108588Ssam if (sc->flags & CLOCK_CCU) { 241108588Ssam error = bus_space_map(sc->bst, CCU_BASE, CCU_SIZE, 0, 242108588Ssam &sc->ccu_bsh); 243108588Ssam if (error != 0) { 244108588Ssam device_printf(dev, "couldn't map CCU: %d\n", error); 245104476Ssam return (error); 246104476Ssam } 247104476Ssam } 248108588Ssam if (sc->flags & CLOCK_PRCM) { 249108588Ssam error = bus_space_map(sc->bst, PRCM_BASE, PRCM_SIZE, 0, 250108588Ssam &sc->prcm_bsh); 251108588Ssam if (error != 0) { 252108588Ssam device_printf(dev, "couldn't map PRCM: %d\n", error); 253108588Ssam return (error); 254108588Ssam } 255108588Ssam } 256108588Ssam if (sc->flags & CLOCK_SYSCTRL) { 257108588Ssam error = bus_space_map(sc->bst, SYSCTRL_BASE, SYSCTRL_SIZE, 0, 258108588Ssam &sc->sysctrl_bsh); 259108588Ssam if (error != 0) { 260108588Ssam device_printf(dev, "couldn't map SYSCTRL: %d\n", error); 261108588Ssam return (error); 262108588Ssam } 263108588Ssam } 264108588Ssam 265108588Ssam mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); 266108588Ssam 267108588Ssam /* Attach child devices */ 268108588Ssam for (child = OF_child(node); child > 0; child = OF_peer(child)) { 269108588Ssam cdev = simplebus_add_device(dev, child, 0, NULL, -1, NULL); 270108588Ssam if (cdev != NULL) 271108588Ssam device_probe_and_attach(cdev); 272108588Ssam } 273108588Ssam 274108588Ssam return (bus_generic_attach(dev)); 275108588Ssam} 276108588Ssam 277108588Ssamstatic device_method_t aw_ccu_methods[] = { 278108588Ssam /* Device interface */ 279108588Ssam DEVMETHOD(device_probe, aw_ccu_probe), 280108588Ssam DEVMETHOD(device_attach, aw_ccu_attach), 281108588Ssam 282108588Ssam /* clkdev interface */ 283108588Ssam DEVMETHOD(clkdev_write_4, aw_ccu_write_4), 284108588Ssam DEVMETHOD(clkdev_read_4, aw_ccu_read_4), 285108588Ssam DEVMETHOD(clkdev_modify_4, aw_ccu_modify_4), 286108588Ssam DEVMETHOD(clkdev_device_lock, aw_ccu_device_lock), 287108588Ssam DEVMETHOD(clkdev_device_unlock, aw_ccu_device_unlock), 288108588Ssam 289108588Ssam DEVMETHOD_END 290108588Ssam}; 291108588Ssam 292108588SsamDEFINE_CLASS_1(aw_ccu, aw_ccu_driver, aw_ccu_methods, 293108588Ssam sizeof(struct aw_ccu_softc), simplebus_driver); 294108588Ssam 295108588Ssamstatic devclass_t aw_ccu_devclass; 296108588Ssam 297108588SsamEARLY_DRIVER_MODULE(aw_ccu, simplebus, aw_ccu_driver, aw_ccu_devclass, 298108588Ssam 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 299108588Ssam 300167755SsamMODULE_VERSION(aw_ccu, 1); 301167755Ssam