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: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $ 27297627Sjmcneill */ 28297627Sjmcneill 29297627Sjmcneill/* 30297627Sjmcneill * Allwinner display backend clocks 31297627Sjmcneill */ 32297627Sjmcneill 33297627Sjmcneill#include <sys/cdefs.h> 34297627Sjmcneill__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_debeclk.c 308324 2016-11-05 04:17:32Z mmel $"); 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#include <dev/extres/hwreset/hwreset.h> 50297627Sjmcneill 51297627Sjmcneill#include "clkdev_if.h" 52297627Sjmcneill#include "hwreset_if.h" 53297627Sjmcneill 54297627Sjmcneill#define SCLK_GATING (1 << 31) 55297627Sjmcneill#define BE_RST (1 << 30) 56297627Sjmcneill#define CLK_SRC_SEL (0x3 << 24) 57297627Sjmcneill#define CLK_SRC_SEL_SHIFT 24 58297627Sjmcneill#define CLK_SRC_SEL_MAX 2 59297627Sjmcneill#define CLK_SRC_SEL_PLL3 0 60297627Sjmcneill#define CLK_SRC_SEL_PLL7 1 61297627Sjmcneill#define CLK_SRC_SEL_PLL5 2 62297627Sjmcneill#define CLK_RATIO_M (0xf << 0) 63297627Sjmcneill#define CLK_RATIO_M_SHIFT 0 64297627Sjmcneill#define CLK_RATIO_M_MAX 0xf 65297627Sjmcneill 66297627Sjmcneillstatic struct ofw_compat_data compat_data[] = { 67297627Sjmcneill { "allwinner,sun4i-a10-de-be-clk", 1 }, 68297627Sjmcneill { NULL, 0 } 69297627Sjmcneill}; 70297627Sjmcneill 71297627Sjmcneillstruct aw_debeclk_softc { 72297627Sjmcneill device_t clkdev; 73297627Sjmcneill bus_addr_t reg; 74297627Sjmcneill}; 75297627Sjmcneill 76297627Sjmcneill#define DEBECLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) 77297627Sjmcneill#define DEBECLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) 78297627Sjmcneill#define DEBECLK_MODIFY(sc, clr, set) \ 79297627Sjmcneill CLKDEV_MODIFY_4((sc)->clkdev, (sc)->reg, (clr), (set)) 80297627Sjmcneill#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) 81297627Sjmcneill#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) 82297627Sjmcneill 83297627Sjmcneillstatic int 84297627Sjmcneillaw_debeclk_hwreset_assert(device_t dev, intptr_t id, bool value) 85297627Sjmcneill{ 86297627Sjmcneill struct aw_debeclk_softc *sc; 87297627Sjmcneill int error; 88297627Sjmcneill 89297627Sjmcneill sc = device_get_softc(dev); 90297627Sjmcneill 91297627Sjmcneill DEVICE_LOCK(sc); 92297627Sjmcneill error = DEBECLK_MODIFY(sc, BE_RST, value ? 0 : BE_RST); 93297627Sjmcneill DEVICE_UNLOCK(sc); 94297627Sjmcneill 95297627Sjmcneill return (error); 96297627Sjmcneill} 97297627Sjmcneill 98297627Sjmcneillstatic int 99297627Sjmcneillaw_debeclk_hwreset_is_asserted(device_t dev, intptr_t id, bool *value) 100297627Sjmcneill{ 101297627Sjmcneill struct aw_debeclk_softc *sc; 102297627Sjmcneill uint32_t val; 103297627Sjmcneill int error; 104297627Sjmcneill 105297627Sjmcneill sc = device_get_softc(dev); 106297627Sjmcneill 107297627Sjmcneill DEVICE_LOCK(sc); 108297627Sjmcneill error = DEBECLK_READ(sc, &val); 109297627Sjmcneill DEVICE_UNLOCK(sc); 110297627Sjmcneill 111297627Sjmcneill if (error) 112297627Sjmcneill return (error); 113297627Sjmcneill 114297627Sjmcneill *value = (val & BE_RST) != 0 ? false : true; 115297627Sjmcneill 116297627Sjmcneill return (0); 117297627Sjmcneill} 118297627Sjmcneill 119297627Sjmcneillstatic int 120297627Sjmcneillaw_debeclk_init(struct clknode *clk, device_t dev) 121297627Sjmcneill{ 122297627Sjmcneill struct aw_debeclk_softc *sc; 123297627Sjmcneill uint32_t val, index; 124297627Sjmcneill 125297627Sjmcneill sc = clknode_get_softc(clk); 126297627Sjmcneill 127297627Sjmcneill /* Set BE source to PLL5 (DDR external peripheral clock) */ 128297627Sjmcneill index = CLK_SRC_SEL_PLL5; 129297627Sjmcneill 130297627Sjmcneill DEVICE_LOCK(sc); 131297627Sjmcneill DEBECLK_READ(sc, &val); 132297627Sjmcneill val &= ~CLK_SRC_SEL; 133297627Sjmcneill val |= (index << CLK_SRC_SEL_SHIFT); 134297627Sjmcneill DEBECLK_WRITE(sc, val); 135297627Sjmcneill DEVICE_UNLOCK(sc); 136297627Sjmcneill 137297627Sjmcneill clknode_init_parent_idx(clk, index); 138297627Sjmcneill return (0); 139297627Sjmcneill} 140297627Sjmcneill 141297627Sjmcneillstatic int 142297627Sjmcneillaw_debeclk_set_mux(struct clknode *clk, int index) 143297627Sjmcneill{ 144297627Sjmcneill struct aw_debeclk_softc *sc; 145297627Sjmcneill uint32_t val; 146297627Sjmcneill 147297627Sjmcneill sc = clknode_get_softc(clk); 148297627Sjmcneill 149297627Sjmcneill if (index < 0 || index > CLK_SRC_SEL_MAX) 150297627Sjmcneill return (ERANGE); 151297627Sjmcneill 152297627Sjmcneill DEVICE_LOCK(sc); 153297627Sjmcneill DEBECLK_READ(sc, &val); 154297627Sjmcneill val &= ~CLK_SRC_SEL; 155297627Sjmcneill val |= (index << CLK_SRC_SEL_SHIFT); 156297627Sjmcneill DEBECLK_WRITE(sc, val); 157297627Sjmcneill DEVICE_UNLOCK(sc); 158297627Sjmcneill 159297627Sjmcneill return (0); 160297627Sjmcneill} 161297627Sjmcneill 162297627Sjmcneillstatic int 163297627Sjmcneillaw_debeclk_set_gate(struct clknode *clk, bool enable) 164297627Sjmcneill{ 165297627Sjmcneill struct aw_debeclk_softc *sc; 166297627Sjmcneill uint32_t val; 167297627Sjmcneill 168297627Sjmcneill sc = clknode_get_softc(clk); 169297627Sjmcneill 170297627Sjmcneill DEVICE_LOCK(sc); 171297627Sjmcneill DEBECLK_READ(sc, &val); 172297627Sjmcneill if (enable) 173297627Sjmcneill val |= SCLK_GATING; 174297627Sjmcneill else 175297627Sjmcneill val &= ~SCLK_GATING; 176297627Sjmcneill DEBECLK_WRITE(sc, val); 177297627Sjmcneill DEVICE_UNLOCK(sc); 178297627Sjmcneill 179297627Sjmcneill return (0); 180297627Sjmcneill} 181297627Sjmcneill 182297627Sjmcneillstatic int 183297627Sjmcneillaw_debeclk_recalc_freq(struct clknode *clk, uint64_t *freq) 184297627Sjmcneill{ 185297627Sjmcneill struct aw_debeclk_softc *sc; 186297627Sjmcneill uint32_t val, m; 187297627Sjmcneill 188297627Sjmcneill sc = clknode_get_softc(clk); 189297627Sjmcneill 190297627Sjmcneill DEVICE_LOCK(sc); 191297627Sjmcneill DEBECLK_READ(sc, &val); 192297627Sjmcneill DEVICE_UNLOCK(sc); 193297627Sjmcneill 194297627Sjmcneill m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; 195297627Sjmcneill 196297627Sjmcneill *freq = *freq / m; 197297627Sjmcneill 198297627Sjmcneill return (0); 199297627Sjmcneill} 200297627Sjmcneill 201297627Sjmcneillstatic int 202297627Sjmcneillaw_debeclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 203297627Sjmcneill int flags, int *stop) 204297627Sjmcneill{ 205297627Sjmcneill struct aw_debeclk_softc *sc; 206297627Sjmcneill uint32_t val, m; 207297627Sjmcneill 208297627Sjmcneill sc = clknode_get_softc(clk); 209297627Sjmcneill 210297627Sjmcneill m = howmany(fin, *fout) - 1; 211297627Sjmcneill 212297627Sjmcneill DEVICE_LOCK(sc); 213297627Sjmcneill DEBECLK_READ(sc, &val); 214297627Sjmcneill val &= ~CLK_RATIO_M; 215297627Sjmcneill val |= (m << CLK_RATIO_M_SHIFT); 216297627Sjmcneill DEBECLK_WRITE(sc, val); 217297627Sjmcneill DEVICE_UNLOCK(sc); 218297627Sjmcneill 219297627Sjmcneill *fout = fin / (m + 1); 220297627Sjmcneill *stop = 1; 221297627Sjmcneill 222297627Sjmcneill return (0); 223297627Sjmcneill} 224297627Sjmcneill 225297627Sjmcneillstatic clknode_method_t aw_debeclk_clknode_methods[] = { 226297627Sjmcneill /* Device interface */ 227297627Sjmcneill CLKNODEMETHOD(clknode_init, aw_debeclk_init), 228297627Sjmcneill CLKNODEMETHOD(clknode_set_gate, aw_debeclk_set_gate), 229297627Sjmcneill CLKNODEMETHOD(clknode_set_mux, aw_debeclk_set_mux), 230297627Sjmcneill CLKNODEMETHOD(clknode_recalc_freq, aw_debeclk_recalc_freq), 231297627Sjmcneill CLKNODEMETHOD(clknode_set_freq, aw_debeclk_set_freq), 232297627Sjmcneill CLKNODEMETHOD_END 233297627Sjmcneill}; 234297627SjmcneillDEFINE_CLASS_1(aw_debeclk_clknode, aw_debeclk_clknode_class, 235297627Sjmcneill aw_debeclk_clknode_methods, sizeof(struct aw_debeclk_softc), clknode_class); 236297627Sjmcneill 237297627Sjmcneillstatic int 238297627Sjmcneillaw_debeclk_probe(device_t dev) 239297627Sjmcneill{ 240297627Sjmcneill if (!ofw_bus_status_okay(dev)) 241297627Sjmcneill return (ENXIO); 242297627Sjmcneill 243297627Sjmcneill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 244297627Sjmcneill return (ENXIO); 245297627Sjmcneill 246297627Sjmcneill device_set_desc(dev, "Allwinner Display Engine Backend Clock"); 247297627Sjmcneill return (BUS_PROBE_DEFAULT); 248297627Sjmcneill} 249297627Sjmcneill 250297627Sjmcneillstatic int 251297627Sjmcneillaw_debeclk_attach(device_t dev) 252297627Sjmcneill{ 253297627Sjmcneill struct clknode_init_def def; 254297627Sjmcneill struct aw_debeclk_softc *sc, *clk_sc; 255297627Sjmcneill struct clkdom *clkdom; 256297627Sjmcneill struct clknode *clk; 257297627Sjmcneill clk_t clk_parent; 258297627Sjmcneill bus_size_t psize; 259297627Sjmcneill phandle_t node; 260297627Sjmcneill int error, ncells, i; 261297627Sjmcneill 262297627Sjmcneill sc = device_get_softc(dev); 263297627Sjmcneill sc->clkdev = device_get_parent(dev); 264297627Sjmcneill node = ofw_bus_get_node(dev); 265297627Sjmcneill 266297627Sjmcneill if (ofw_reg_to_paddr(node, 0, &sc->reg, &psize, NULL) != 0) { 267297627Sjmcneill device_printf(dev, "cannot parse 'reg' property\n"); 268297627Sjmcneill return (ENXIO); 269297627Sjmcneill } 270297627Sjmcneill 271297627Sjmcneill error = ofw_bus_parse_xref_list_get_length(node, "clocks", 272297627Sjmcneill "#clock-cells", &ncells); 273297627Sjmcneill if (error != 0) { 274297627Sjmcneill device_printf(dev, "cannot get clock count\n"); 275297627Sjmcneill return (error); 276297627Sjmcneill } 277297627Sjmcneill 278297627Sjmcneill clkdom = clkdom_create(dev); 279297627Sjmcneill 280297627Sjmcneill memset(&def, 0, sizeof(def)); 281297627Sjmcneill error = clk_parse_ofw_clk_name(dev, node, &def.name); 282297627Sjmcneill if (error != 0) { 283297627Sjmcneill device_printf(dev, "cannot parse clock name\n"); 284297627Sjmcneill error = ENXIO; 285297627Sjmcneill goto fail; 286297627Sjmcneill } 287297627Sjmcneill def.id = 1; 288297627Sjmcneill def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); 289297627Sjmcneill for (i = 0; i < ncells; i++) { 290308324Smmel error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); 291297627Sjmcneill if (error != 0) { 292297627Sjmcneill device_printf(dev, "cannot get clock %d\n", i); 293297627Sjmcneill goto fail; 294297627Sjmcneill } 295297627Sjmcneill def.parent_names[i] = clk_get_name(clk_parent); 296297627Sjmcneill clk_release(clk_parent); 297297627Sjmcneill } 298297627Sjmcneill def.parent_cnt = ncells; 299297627Sjmcneill 300297627Sjmcneill clk = clknode_create(clkdom, &aw_debeclk_clknode_class, &def); 301297627Sjmcneill if (clk == NULL) { 302297627Sjmcneill device_printf(dev, "cannot create clknode\n"); 303297627Sjmcneill error = ENXIO; 304297627Sjmcneill goto fail; 305297627Sjmcneill } 306297627Sjmcneill 307297627Sjmcneill clk_sc = clknode_get_softc(clk); 308297627Sjmcneill clk_sc->reg = sc->reg; 309297627Sjmcneill clk_sc->clkdev = device_get_parent(dev); 310297627Sjmcneill 311297627Sjmcneill clknode_register(clkdom, clk); 312297627Sjmcneill 313297627Sjmcneill if (clkdom_finit(clkdom) != 0) { 314297627Sjmcneill device_printf(dev, "cannot finalize clkdom initialization\n"); 315297627Sjmcneill error = ENXIO; 316297627Sjmcneill goto fail; 317297627Sjmcneill } 318297627Sjmcneill 319297627Sjmcneill if (bootverbose) 320297627Sjmcneill clkdom_dump(clkdom); 321297627Sjmcneill 322297627Sjmcneill hwreset_register_ofw_provider(dev); 323297627Sjmcneill 324297627Sjmcneill return (0); 325297627Sjmcneill 326297627Sjmcneillfail: 327297627Sjmcneill return (error); 328297627Sjmcneill} 329297627Sjmcneill 330297627Sjmcneillstatic device_method_t aw_debeclk_methods[] = { 331297627Sjmcneill /* Device interface */ 332297627Sjmcneill DEVMETHOD(device_probe, aw_debeclk_probe), 333297627Sjmcneill DEVMETHOD(device_attach, aw_debeclk_attach), 334297627Sjmcneill 335297627Sjmcneill /* Reset interface */ 336297627Sjmcneill DEVMETHOD(hwreset_assert, aw_debeclk_hwreset_assert), 337297627Sjmcneill DEVMETHOD(hwreset_is_asserted, aw_debeclk_hwreset_is_asserted), 338297627Sjmcneill 339297627Sjmcneill DEVMETHOD_END 340297627Sjmcneill}; 341297627Sjmcneill 342297627Sjmcneillstatic driver_t aw_debeclk_driver = { 343297627Sjmcneill "aw_debeclk", 344297627Sjmcneill aw_debeclk_methods, 345297627Sjmcneill sizeof(struct aw_debeclk_softc) 346297627Sjmcneill}; 347297627Sjmcneill 348297627Sjmcneillstatic devclass_t aw_debeclk_devclass; 349297627Sjmcneill 350297627SjmcneillEARLY_DRIVER_MODULE(aw_debeclk, simplebus, aw_debeclk_driver, 351297627Sjmcneill aw_debeclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 352