1280905Sganbold/*- 2280905Sganbold * Copyright 2014-2015 John Wehle <john@feith.com> 3280905Sganbold * All rights reserved. 4280905Sganbold * 5280905Sganbold * Redistribution and use in source and binary forms, with or without 6280905Sganbold * modification, are permitted provided that the following conditions 7280905Sganbold * are met: 8280905Sganbold * 1. Redistributions of source code must retain the above copyright 9280905Sganbold * notice, this list of conditions and the following disclaimer. 10280905Sganbold * 2. Redistributions in binary form must reproduce the above copyright 11280905Sganbold * notice, this list of conditions and the following disclaimer in the 12280905Sganbold * documentation and/or other materials provided with the distribution. 13280905Sganbold * 14280905Sganbold * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15280905Sganbold * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16280905Sganbold * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17280905Sganbold * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18280905Sganbold * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19280905Sganbold * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20280905Sganbold * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21280905Sganbold * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22280905Sganbold * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23280905Sganbold * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24280905Sganbold * SUCH DAMAGE. 25280905Sganbold * 26280905Sganbold */ 27280905Sganbold 28280905Sganbold/* 29280905Sganbold * Amlogic aml8726 clock measurement driver. 30280905Sganbold * 31280905Sganbold * This allows various clock rates to be determine at runtime. 32280905Sganbold * The measurements are done once and are not expected to change 33280905Sganbold * (i.e. FDT fixup provides clk81 as bus-frequency to the MMC 34280905Sganbold * and UART drivers which use the value when programming the 35280905Sganbold * hardware). 36280905Sganbold */ 37280905Sganbold 38280905Sganbold#include <sys/cdefs.h> 39280905Sganbold__FBSDID("$FreeBSD: releng/11.0/sys/arm/amlogic/aml8726/aml8726_clkmsr.c 298352 2016-04-20 15:45:55Z pfg $"); 40280905Sganbold 41280905Sganbold#include <sys/param.h> 42280905Sganbold#include <sys/systm.h> 43280905Sganbold#include <sys/bus.h> 44280905Sganbold#include <sys/kernel.h> 45280905Sganbold#include <sys/module.h> 46280905Sganbold#include <sys/malloc.h> 47280905Sganbold#include <sys/rman.h> 48280905Sganbold#include <sys/timetc.h> 49280905Sganbold#include <sys/timeet.h> 50280905Sganbold 51280905Sganbold#include <machine/bus.h> 52280905Sganbold#include <machine/cpu.h> 53280905Sganbold#include <machine/fdt.h> 54280905Sganbold 55280905Sganbold#include <dev/fdt/fdt_common.h> 56280905Sganbold#include <dev/ofw/ofw_bus.h> 57280905Sganbold#include <dev/ofw/ofw_bus_subr.h> 58280905Sganbold 59281418Sganbold#include <arm/amlogic/aml8726/aml8726_soc.h> 60280905Sganbold#include <arm/amlogic/aml8726/aml8726_clkmsr.h> 61280905Sganbold 62280905Sganbold 63280905Sganboldstatic struct aml8726_clkmsr_clk { 64280905Sganbold const char * name; 65280905Sganbold uint32_t mux; 66280905Sganbold} aml8726_clkmsr_clks[] = { 67280905Sganbold { "clk81", 7 }, 68280905Sganbold}; 69280905Sganbold 70280905Sganbold#define AML_CLKMSR_CLK81 0 71280905Sganbold 72298352Spfg#define AML_CLKMSR_NCLKS nitems(aml8726_clkmsr_clks) 73280905Sganbold 74280905Sganboldstruct aml8726_clkmsr_softc { 75280905Sganbold device_t dev; 76280905Sganbold struct resource * res[1]; 77280905Sganbold}; 78280905Sganbold 79280905Sganboldstatic struct resource_spec aml8726_clkmsr_spec[] = { 80280905Sganbold { SYS_RES_MEMORY, 0, RF_ACTIVE }, 81280905Sganbold { -1, 0 } 82280905Sganbold}; 83280905Sganbold 84280905Sganbold/* 85280905Sganbold * Duration can range from 1uS to 65535 uS and should be chosen 86280905Sganbold * based on the expected frequency result so to maximize resolution 87280905Sganbold * and avoid overflowing the 16 bit result counter. 88280905Sganbold */ 89280905Sganbold#define AML_CLKMSR_DURATION 32 90280905Sganbold 91280905Sganbold#define AML_CLKMSR_DUTY_REG 0 92280905Sganbold#define AML_CLKMSR_0_REG 4 93280905Sganbold#define AML_CLKMSR_0_BUSY (1U << 31) 94280905Sganbold#define AML_CLKMSR_0_MUX_MASK (0x3f << 20) 95280905Sganbold#define AML_CLKMSR_0_MUX_SHIFT 20 96280905Sganbold#define AML_CLKMSR_0_MUX_EN (1 << 19) 97280905Sganbold#define AML_CLKMSR_0_MEASURE (1 << 16) 98280905Sganbold#define AML_CLKMSR_0_DURATION_MASK 0xffff 99280905Sganbold#define AML_CLKMSR_0_DURATION_SHIFT 0 100280905Sganbold#define AML_CLKMSR_1_REG 8 101280905Sganbold#define AML_CLKMSR_2_REG 12 102280905Sganbold#define AML_CLKMSR_2_RESULT_MASK 0xffff 103280905Sganbold#define AML_CLKMSR_2_RESULT_SHIFT 0 104280905Sganbold 105280905Sganbold#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 106280905Sganbold#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 107280905Sganbold#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ 108280905Sganbold (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 109280905Sganbold 110280905Sganboldstatic int 111280905Sganboldaml8726_clkmsr_clock_frequency(struct aml8726_clkmsr_softc *sc, unsigned clock) 112280905Sganbold{ 113280905Sganbold uint32_t value; 114280905Sganbold 115280905Sganbold if (clock >= AML_CLKMSR_NCLKS) 116280905Sganbold return (0); 117280905Sganbold 118280905Sganbold /* 119280905Sganbold * Locking is not used as this is only expected to be called from 120280905Sganbold * FDT fixup (which occurs prior to driver initialization) or attach. 121280905Sganbold */ 122280905Sganbold 123280905Sganbold CSR_WRITE_4(sc, AML_CLKMSR_0_REG, 0); 124280905Sganbold 125280905Sganbold CSR_BARRIER(sc, AML_CLKMSR_0_REG); 126280905Sganbold 127280905Sganbold value = (aml8726_clkmsr_clks[clock].mux << AML_CLKMSR_0_MUX_SHIFT) 128280905Sganbold | ((AML_CLKMSR_DURATION - 1) << AML_CLKMSR_0_DURATION_SHIFT) 129280905Sganbold | AML_CLKMSR_0_MUX_EN 130280905Sganbold | AML_CLKMSR_0_MEASURE; 131280905Sganbold CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); 132280905Sganbold 133280905Sganbold CSR_BARRIER(sc, AML_CLKMSR_0_REG); 134280905Sganbold 135280905Sganbold while ((CSR_READ_4(sc, AML_CLKMSR_0_REG) & AML_CLKMSR_0_BUSY) != 0) 136280905Sganbold cpu_spinwait(); 137280905Sganbold 138280905Sganbold value &= ~AML_CLKMSR_0_MEASURE; 139280905Sganbold CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); 140280905Sganbold 141280905Sganbold CSR_BARRIER(sc, AML_CLKMSR_0_REG); 142280905Sganbold 143280905Sganbold value = (((CSR_READ_4(sc, AML_CLKMSR_2_REG) & AML_CLKMSR_2_RESULT_MASK) 144280905Sganbold >> AML_CLKMSR_2_RESULT_SHIFT) + AML_CLKMSR_DURATION / 2) / 145280905Sganbold AML_CLKMSR_DURATION; 146280905Sganbold 147280905Sganbold return value; 148280905Sganbold} 149280905Sganbold 150281418Sganboldstatic void 151281418Sganboldaml8726_clkmsr_fixup_clk81(struct aml8726_clkmsr_softc *sc, int freq) 152281418Sganbold{ 153281418Sganbold pcell_t prop; 154281418Sganbold ssize_t len; 155281418Sganbold phandle_t clk_node; 156281418Sganbold phandle_t node; 157281418Sganbold 158281418Sganbold node = ofw_bus_get_node(sc->dev); 159281418Sganbold 160281418Sganbold len = OF_getencprop(node, "clocks", &prop, sizeof(prop)); 161281418Sganbold if ((len / sizeof(prop)) != 1 || prop == 0 || 162281418Sganbold (clk_node = OF_node_from_xref(prop)) == 0) 163281418Sganbold return; 164281418Sganbold 165281418Sganbold len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop)); 166281418Sganbold if ((len / sizeof(prop)) != 1 || prop != 0) 167281418Sganbold return; 168281418Sganbold 169281418Sganbold freq = cpu_to_fdt32(freq); 170281418Sganbold 171281418Sganbold OF_setprop(clk_node, "clock-frequency", (void *)&freq, sizeof(freq)); 172281418Sganbold} 173281418Sganbold 174280905Sganboldstatic int 175280905Sganboldaml8726_clkmsr_probe(device_t dev) 176280905Sganbold{ 177280905Sganbold 178280905Sganbold if (!ofw_bus_status_okay(dev)) 179280905Sganbold return (ENXIO); 180280905Sganbold 181280905Sganbold if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-clkmsr")) 182280905Sganbold return (ENXIO); 183280905Sganbold 184280905Sganbold device_set_desc(dev, "Amlogic aml8726 clkmsr"); 185280905Sganbold 186280905Sganbold return (BUS_PROBE_DEFAULT); 187280905Sganbold} 188280905Sganbold 189280905Sganboldstatic int 190280905Sganboldaml8726_clkmsr_attach(device_t dev) 191280905Sganbold{ 192280905Sganbold struct aml8726_clkmsr_softc *sc = device_get_softc(dev); 193280905Sganbold int freq; 194280905Sganbold 195280905Sganbold sc->dev = dev; 196280905Sganbold 197280905Sganbold if (bus_alloc_resources(dev, aml8726_clkmsr_spec, sc->res)) { 198280905Sganbold device_printf(dev, "can not allocate resources for device\n"); 199280905Sganbold return (ENXIO); 200280905Sganbold } 201280905Sganbold 202280905Sganbold freq = aml8726_clkmsr_clock_frequency(sc, AML_CLKMSR_CLK81); 203280905Sganbold device_printf(sc->dev, "bus clock %u MHz\n", freq); 204280905Sganbold 205281418Sganbold aml8726_clkmsr_fixup_clk81(sc, freq * 1000000); 206281418Sganbold 207280905Sganbold return (0); 208280905Sganbold} 209280905Sganbold 210280905Sganboldstatic int 211280905Sganboldaml8726_clkmsr_detach(device_t dev) 212280905Sganbold{ 213280905Sganbold struct aml8726_clkmsr_softc *sc = device_get_softc(dev); 214280905Sganbold 215280905Sganbold bus_release_resources(dev, aml8726_clkmsr_spec, sc->res); 216280905Sganbold 217280905Sganbold return (0); 218280905Sganbold} 219280905Sganbold 220280905Sganbold 221280905Sganboldstatic device_method_t aml8726_clkmsr_methods[] = { 222280905Sganbold /* Device interface */ 223280905Sganbold DEVMETHOD(device_probe, aml8726_clkmsr_probe), 224280905Sganbold DEVMETHOD(device_attach, aml8726_clkmsr_attach), 225280905Sganbold DEVMETHOD(device_detach, aml8726_clkmsr_detach), 226280905Sganbold 227280905Sganbold DEVMETHOD_END 228280905Sganbold}; 229280905Sganbold 230280905Sganboldstatic driver_t aml8726_clkmsr_driver = { 231280905Sganbold "clkmsr", 232280905Sganbold aml8726_clkmsr_methods, 233280905Sganbold sizeof(struct aml8726_clkmsr_softc), 234280905Sganbold}; 235280905Sganbold 236280905Sganboldstatic devclass_t aml8726_clkmsr_devclass; 237280905Sganbold 238281418SganboldEARLY_DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver, 239281418Sganbold aml8726_clkmsr_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 240280905Sganbold 241280905Sganboldint 242280905Sganboldaml8726_clkmsr_bus_frequency() 243280905Sganbold{ 244280905Sganbold struct resource mem; 245280905Sganbold struct aml8726_clkmsr_softc sc; 246280905Sganbold phandle_t node; 247280905Sganbold u_long pbase, psize; 248280905Sganbold u_long start, size; 249280905Sganbold int freq; 250280905Sganbold 251281418Sganbold KASSERT(aml8726_soc_hw_rev != AML_SOC_HW_REV_UNKNOWN, 252281418Sganbold ("aml8726_soc_hw_rev isn't initialized")); 253281418Sganbold 254280905Sganbold /* 255280905Sganbold * Try to access the clkmsr node directly i.e. through /aliases/. 256280905Sganbold */ 257280905Sganbold 258280905Sganbold if ((node = OF_finddevice("clkmsr")) != 0) 259280905Sganbold if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr")) 260280905Sganbold goto moveon; 261280905Sganbold 262280905Sganbold /* 263280905Sganbold * Find the node the long way. 264280905Sganbold */ 265280905Sganbold if ((node = OF_finddevice("/soc")) == 0) 266280905Sganbold return (0); 267280905Sganbold 268280905Sganbold if ((node = fdt_find_compatible(node, 269280905Sganbold "amlogic,aml8726-clkmsr", 1)) == 0) 270280905Sganbold return (0); 271280905Sganbold 272280905Sganboldmoveon: 273280905Sganbold if (fdt_get_range(OF_parent(node), 0, &pbase, &psize) != 0 274280905Sganbold || fdt_regsize(node, &start, &size) != 0) 275280905Sganbold return (0); 276280905Sganbold 277280905Sganbold start += pbase; 278280905Sganbold 279280905Sganbold memset(&mem, 0, sizeof(mem)); 280280905Sganbold 281280905Sganbold mem.r_bustag = fdtbus_bs_tag; 282280905Sganbold 283280905Sganbold if (bus_space_map(mem.r_bustag, start, size, 0, &mem.r_bushandle) != 0) 284280905Sganbold return (0); 285280905Sganbold 286280905Sganbold /* 287280905Sganbold * Build an incomplete (however sufficient for the purpose 288280905Sganbold * of calling aml8726_clkmsr_clock_frequency) softc. 289280905Sganbold */ 290280905Sganbold 291280905Sganbold memset(&sc, 0, sizeof(sc)); 292280905Sganbold 293280905Sganbold sc.res[0] = &mem; 294280905Sganbold 295280905Sganbold freq = aml8726_clkmsr_clock_frequency(&sc, AML_CLKMSR_CLK81) * 1000000; 296280905Sganbold 297280905Sganbold bus_space_unmap(mem.r_bustag, mem.r_bushandle, size); 298280905Sganbold 299280905Sganbold return (freq); 300280905Sganbold} 301