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_mmcclk.c 309756 2016-12-09 20:07:01Z manu $ 27297627Sjmcneill */ 28297627Sjmcneill 29297627Sjmcneill/* 30297627Sjmcneill * Allwinner MMC clocks 31297627Sjmcneill */ 32297627Sjmcneill 33297627Sjmcneill#include <sys/cdefs.h> 34297627Sjmcneill__FBSDID("$FreeBSD: stable/11/sys/arm/allwinner/clk/aw_mmcclk.c 309756 2016-12-09 20:07:01Z manu $"); 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_mux.h> 49297627Sjmcneill#include <dev/extres/clk/clk_gate.h> 50297627Sjmcneill 51297627Sjmcneill#include "clkdev_if.h" 52297627Sjmcneill 53297627Sjmcneill#define SCLK_GATING (1 << 31) 54297627Sjmcneill#define CLK_SRC_SEL (0x3 << 24) 55297627Sjmcneill#define CLK_SRC_SEL_SHIFT 24 56297627Sjmcneill#define CLK_SRC_SEL_MAX 0x3 57297627Sjmcneill#define CLK_SRC_SEL_OSC24M 0 58297627Sjmcneill#define CLK_SRC_SEL_PLL6 1 59297627Sjmcneill#define CLK_PHASE_CTR (0x7 << 20) 60297627Sjmcneill#define CLK_PHASE_CTR_SHIFT 20 61297627Sjmcneill#define CLK_RATIO_N (0x3 << 16) 62297627Sjmcneill#define CLK_RATIO_N_SHIFT 16 63297627Sjmcneill#define CLK_RATIO_N_MAX 0x3 64297627Sjmcneill#define OUTPUT_CLK_PHASE_CTR (0x7 << 8) 65297627Sjmcneill#define OUTPUT_CLK_PHASE_CTR_SHIFT 8 66297627Sjmcneill#define CLK_RATIO_M (0xf << 0) 67297627Sjmcneill#define CLK_RATIO_M_SHIFT 0 68297627Sjmcneill#define CLK_RATIO_M_MAX 0xf 69297627Sjmcneill 70297627Sjmcneillstatic struct ofw_compat_data compat_data[] = { 71297627Sjmcneill { "allwinner,sun4i-a10-mmc-clk", 1 }, 72297627Sjmcneill { NULL, 0 } 73297627Sjmcneill}; 74297627Sjmcneill 75297627Sjmcneillstruct aw_mmcclk_sc { 76297627Sjmcneill device_t clkdev; 77297627Sjmcneill bus_addr_t reg; 78297627Sjmcneill}; 79297627Sjmcneill 80297627Sjmcneill#define MODCLK_READ(sc, val) CLKDEV_READ_4((sc)->clkdev, (sc)->reg, (val)) 81297627Sjmcneill#define MODCLK_WRITE(sc, val) CLKDEV_WRITE_4((sc)->clkdev, (sc)->reg, (val)) 82297627Sjmcneill#define DEVICE_LOCK(sc) CLKDEV_DEVICE_LOCK((sc)->clkdev) 83297627Sjmcneill#define DEVICE_UNLOCK(sc) CLKDEV_DEVICE_UNLOCK((sc)->clkdev) 84297627Sjmcneill 85297627Sjmcneillstatic int 86297627Sjmcneillaw_mmcclk_init(struct clknode *clk, device_t dev) 87297627Sjmcneill{ 88297627Sjmcneill struct aw_mmcclk_sc *sc; 89297627Sjmcneill uint32_t val, index; 90297627Sjmcneill 91297627Sjmcneill sc = clknode_get_softc(clk); 92297627Sjmcneill 93297627Sjmcneill DEVICE_LOCK(sc); 94297627Sjmcneill MODCLK_READ(sc, &val); 95297627Sjmcneill DEVICE_UNLOCK(sc); 96297627Sjmcneill 97297627Sjmcneill index = (val & CLK_SRC_SEL) >> CLK_SRC_SEL_SHIFT; 98297627Sjmcneill 99297627Sjmcneill clknode_init_parent_idx(clk, index); 100297627Sjmcneill return (0); 101297627Sjmcneill} 102297627Sjmcneill 103297627Sjmcneillstatic int 104297627Sjmcneillaw_mmcclk_set_mux(struct clknode *clk, int index) 105297627Sjmcneill{ 106297627Sjmcneill struct aw_mmcclk_sc *sc; 107297627Sjmcneill uint32_t val; 108297627Sjmcneill 109297627Sjmcneill sc = clknode_get_softc(clk); 110297627Sjmcneill 111297627Sjmcneill if (index < 0 || index > CLK_SRC_SEL_MAX) 112297627Sjmcneill return (ERANGE); 113297627Sjmcneill 114297627Sjmcneill DEVICE_LOCK(sc); 115297627Sjmcneill MODCLK_READ(sc, &val); 116297627Sjmcneill val &= ~CLK_SRC_SEL; 117297627Sjmcneill val |= (index << CLK_SRC_SEL_SHIFT); 118297627Sjmcneill MODCLK_WRITE(sc, val); 119297627Sjmcneill DEVICE_UNLOCK(sc); 120297627Sjmcneill 121297627Sjmcneill return (0); 122297627Sjmcneill} 123297627Sjmcneill 124297627Sjmcneillstatic int 125297627Sjmcneillaw_mmcclk_set_gate(struct clknode *clk, bool enable) 126297627Sjmcneill{ 127297627Sjmcneill struct aw_mmcclk_sc *sc; 128297627Sjmcneill uint32_t val; 129297627Sjmcneill 130297627Sjmcneill sc = clknode_get_softc(clk); 131297627Sjmcneill 132297627Sjmcneill DEVICE_LOCK(sc); 133297627Sjmcneill MODCLK_READ(sc, &val); 134297627Sjmcneill if (enable) 135297627Sjmcneill val |= SCLK_GATING; 136297627Sjmcneill else 137297627Sjmcneill val &= ~SCLK_GATING; 138297627Sjmcneill MODCLK_WRITE(sc, val); 139297627Sjmcneill DEVICE_UNLOCK(sc); 140297627Sjmcneill 141297627Sjmcneill return (0); 142297627Sjmcneill} 143297627Sjmcneill 144297627Sjmcneillstatic int 145297627Sjmcneillaw_mmcclk_recalc_freq(struct clknode *clk, uint64_t *freq) 146297627Sjmcneill{ 147297627Sjmcneill struct aw_mmcclk_sc *sc; 148297627Sjmcneill uint32_t val, m, n; 149297627Sjmcneill 150297627Sjmcneill sc = clknode_get_softc(clk); 151297627Sjmcneill 152297627Sjmcneill DEVICE_LOCK(sc); 153297627Sjmcneill MODCLK_READ(sc, &val); 154297627Sjmcneill DEVICE_UNLOCK(sc); 155297627Sjmcneill 156297627Sjmcneill n = 1 << ((val & CLK_RATIO_N) >> CLK_RATIO_N_SHIFT); 157297627Sjmcneill m = ((val & CLK_RATIO_M) >> CLK_RATIO_M_SHIFT) + 1; 158297627Sjmcneill 159297627Sjmcneill *freq = *freq / n / m; 160297627Sjmcneill 161297627Sjmcneill return (0); 162297627Sjmcneill} 163297627Sjmcneill 164297627Sjmcneillstatic int 165297627Sjmcneillaw_mmcclk_set_freq(struct clknode *clk, uint64_t fin, uint64_t *fout, 166297627Sjmcneill int flags, int *stop) 167297627Sjmcneill{ 168297627Sjmcneill struct aw_mmcclk_sc *sc; 169297627Sjmcneill uint32_t val, m, n, phase, ophase; 170297627Sjmcneill int parent_idx, error; 171297627Sjmcneill 172297627Sjmcneill sc = clknode_get_softc(clk); 173297627Sjmcneill 174297627Sjmcneill /* XXX 175297627Sjmcneill * The ophase/phase values should be set by the MMC driver, but 176297627Sjmcneill * there is currently no way to do this with the clk API 177297627Sjmcneill */ 178297627Sjmcneill if (*fout <= 400000) { 179297627Sjmcneill parent_idx = CLK_SRC_SEL_OSC24M; 180297627Sjmcneill ophase = 0; 181297627Sjmcneill phase = 0; 182297627Sjmcneill n = 2; 183297627Sjmcneill } else if (*fout <= 25000000) { 184297627Sjmcneill parent_idx = CLK_SRC_SEL_PLL6; 185297627Sjmcneill ophase = 0; 186297627Sjmcneill phase = 5; 187297627Sjmcneill n = 2; 188309756Smanu } else if (*fout <= 52000000) { 189297627Sjmcneill parent_idx = CLK_SRC_SEL_PLL6; 190297627Sjmcneill ophase = 3; 191297627Sjmcneill phase = 5; 192297627Sjmcneill n = 0; 193297627Sjmcneill } else 194297627Sjmcneill return (ERANGE); 195297627Sjmcneill 196297627Sjmcneill /* Switch parent clock, if necessary */ 197297627Sjmcneill if (parent_idx != clknode_get_parent_idx(clk)) { 198297627Sjmcneill error = clknode_set_parent_by_idx(clk, parent_idx); 199297627Sjmcneill if (error != 0) 200297627Sjmcneill return (error); 201297627Sjmcneill 202297627Sjmcneill /* Fetch new input frequency */ 203297627Sjmcneill error = clknode_get_freq(clknode_get_parent(clk), &fin); 204297627Sjmcneill if (error != 0) 205297627Sjmcneill return (error); 206297627Sjmcneill } 207297627Sjmcneill 208297627Sjmcneill m = ((fin / (1 << n)) / *fout) - 1; 209297627Sjmcneill 210297627Sjmcneill DEVICE_LOCK(sc); 211297627Sjmcneill MODCLK_READ(sc, &val); 212297627Sjmcneill val &= ~(CLK_RATIO_N | CLK_RATIO_M | CLK_PHASE_CTR | 213297627Sjmcneill OUTPUT_CLK_PHASE_CTR); 214297627Sjmcneill val |= (n << CLK_RATIO_N_SHIFT); 215297627Sjmcneill val |= (m << CLK_RATIO_M_SHIFT); 216297627Sjmcneill val |= (phase << CLK_PHASE_CTR_SHIFT); 217297627Sjmcneill val |= (ophase << OUTPUT_CLK_PHASE_CTR_SHIFT); 218297627Sjmcneill MODCLK_WRITE(sc, val); 219297627Sjmcneill DEVICE_UNLOCK(sc); 220297627Sjmcneill 221297627Sjmcneill *fout = fin / (1 << n) / (m + 1); 222297627Sjmcneill *stop = 1; 223297627Sjmcneill 224297627Sjmcneill return (0); 225297627Sjmcneill} 226297627Sjmcneill 227297627Sjmcneillstatic clknode_method_t aw_mmcclk_clknode_methods[] = { 228297627Sjmcneill /* Device interface */ 229297627Sjmcneill CLKNODEMETHOD(clknode_init, aw_mmcclk_init), 230297627Sjmcneill CLKNODEMETHOD(clknode_set_gate, aw_mmcclk_set_gate), 231297627Sjmcneill CLKNODEMETHOD(clknode_set_mux, aw_mmcclk_set_mux), 232297627Sjmcneill CLKNODEMETHOD(clknode_recalc_freq, aw_mmcclk_recalc_freq), 233297627Sjmcneill CLKNODEMETHOD(clknode_set_freq, aw_mmcclk_set_freq), 234297627Sjmcneill CLKNODEMETHOD_END 235297627Sjmcneill}; 236297627SjmcneillDEFINE_CLASS_1(aw_mmcclk_clknode, aw_mmcclk_clknode_class, 237297627Sjmcneill aw_mmcclk_clknode_methods, sizeof(struct aw_mmcclk_sc), clknode_class); 238297627Sjmcneill 239297627Sjmcneillstatic int 240297627Sjmcneillaw_mmcclk_probe(device_t dev) 241297627Sjmcneill{ 242297627Sjmcneill if (!ofw_bus_status_okay(dev)) 243297627Sjmcneill return (ENXIO); 244297627Sjmcneill 245297627Sjmcneill if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 246297627Sjmcneill return (ENXIO); 247297627Sjmcneill 248297627Sjmcneill device_set_desc(dev, "Allwinner MMC Clock"); 249297627Sjmcneill return (BUS_PROBE_DEFAULT); 250297627Sjmcneill} 251297627Sjmcneill 252297627Sjmcneillstatic int 253297627Sjmcneillaw_mmcclk_attach(device_t dev) 254297627Sjmcneill{ 255297627Sjmcneill struct clknode_init_def def; 256297627Sjmcneill struct aw_mmcclk_sc *sc; 257297627Sjmcneill struct clkdom *clkdom; 258297627Sjmcneill struct clknode *clk; 259297627Sjmcneill const char **names; 260297627Sjmcneill uint32_t *indices; 261297627Sjmcneill clk_t clk_parent; 262297627Sjmcneill bus_addr_t paddr; 263297627Sjmcneill bus_size_t psize; 264297627Sjmcneill phandle_t node; 265297627Sjmcneill int error, nout, ncells, i; 266297627Sjmcneill 267297627Sjmcneill node = ofw_bus_get_node(dev); 268297627Sjmcneill 269297627Sjmcneill if (ofw_reg_to_paddr(node, 0, &paddr, &psize, NULL) != 0) { 270297627Sjmcneill device_printf(dev, "cannot parse 'reg' property\n"); 271297627Sjmcneill return (ENXIO); 272297627Sjmcneill } 273297627Sjmcneill 274297627Sjmcneill error = ofw_bus_parse_xref_list_get_length(node, "clocks", 275297627Sjmcneill "#clock-cells", &ncells); 276297627Sjmcneill if (error != 0 || ncells == 0) { 277297627Sjmcneill device_printf(dev, "couldn't find parent clocks\n"); 278297627Sjmcneill return (ENXIO); 279297627Sjmcneill } 280297627Sjmcneill 281297627Sjmcneill clkdom = clkdom_create(dev); 282297627Sjmcneill 283297627Sjmcneill nout = clk_parse_ofw_out_names(dev, node, &names, &indices); 284297627Sjmcneill if (nout == 0) { 285297627Sjmcneill device_printf(dev, "no output clocks found\n"); 286297627Sjmcneill error = ENXIO; 287297627Sjmcneill goto fail; 288297627Sjmcneill } 289297627Sjmcneill 290297627Sjmcneill memset(&def, 0, sizeof(def)); 291297627Sjmcneill def.name = names[0]; 292297627Sjmcneill def.id = 0; 293297627Sjmcneill def.parent_names = malloc(sizeof(char *) * ncells, M_OFWPROP, M_WAITOK); 294297627Sjmcneill for (i = 0; i < ncells; i++) { 295308324Smmel error = clk_get_by_ofw_index(dev, 0, i, &clk_parent); 296297627Sjmcneill if (error != 0) { 297297627Sjmcneill device_printf(dev, "cannot get clock %d\n", i); 298297627Sjmcneill goto fail; 299297627Sjmcneill } 300297627Sjmcneill def.parent_names[i] = clk_get_name(clk_parent); 301297627Sjmcneill clk_release(clk_parent); 302297627Sjmcneill } 303297627Sjmcneill def.parent_cnt = ncells; 304297627Sjmcneill def.flags = CLK_NODE_GLITCH_FREE; 305297627Sjmcneill 306297627Sjmcneill clk = clknode_create(clkdom, &aw_mmcclk_clknode_class, &def); 307297627Sjmcneill if (clk == NULL) { 308297627Sjmcneill device_printf(dev, "cannot create clknode\n"); 309297627Sjmcneill error = ENXIO; 310297627Sjmcneill goto fail; 311297627Sjmcneill } 312297627Sjmcneill 313297627Sjmcneill sc = clknode_get_softc(clk); 314297627Sjmcneill sc->reg = paddr; 315297627Sjmcneill sc->clkdev = device_get_parent(dev); 316297627Sjmcneill 317297627Sjmcneill clknode_register(clkdom, clk); 318297627Sjmcneill 319297627Sjmcneill if (clkdom_finit(clkdom) != 0) { 320297627Sjmcneill device_printf(dev, "cannot finalize clkdom initialization\n"); 321297627Sjmcneill error = ENXIO; 322297627Sjmcneill goto fail; 323297627Sjmcneill } 324297627Sjmcneill 325297627Sjmcneill if (bootverbose) 326297627Sjmcneill clkdom_dump(clkdom); 327297627Sjmcneill 328297627Sjmcneill return (0); 329297627Sjmcneill 330297627Sjmcneillfail: 331297627Sjmcneill return (error); 332297627Sjmcneill} 333297627Sjmcneill 334297627Sjmcneillstatic device_method_t aw_mmcclk_methods[] = { 335297627Sjmcneill /* Device interface */ 336297627Sjmcneill DEVMETHOD(device_probe, aw_mmcclk_probe), 337297627Sjmcneill DEVMETHOD(device_attach, aw_mmcclk_attach), 338297627Sjmcneill 339297627Sjmcneill DEVMETHOD_END 340297627Sjmcneill}; 341297627Sjmcneill 342297627Sjmcneillstatic driver_t aw_mmcclk_driver = { 343297627Sjmcneill "aw_mmcclk", 344297627Sjmcneill aw_mmcclk_methods, 345297627Sjmcneill 0 346297627Sjmcneill}; 347297627Sjmcneill 348297627Sjmcneillstatic devclass_t aw_mmcclk_devclass; 349297627Sjmcneill 350297627SjmcneillEARLY_DRIVER_MODULE(aw_mmcclk, simplebus, aw_mmcclk_driver, 351297627Sjmcneill aw_mmcclk_devclass, 0, 0, BUS_PASS_BUS + BUS_PASS_ORDER_MIDDLE); 352