1/*- 2 * Copyright 2014-2015 John Wehle <john@feith.com> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 */ 27 28/* 29 * Amlogic aml8726 clock measurement driver. 30 * 31 * This allows various clock rates to be determine at runtime. 32 * The measurements are done once and are not expected to change 33 * (i.e. FDT fixup provides clk81 as bus-frequency to the MMC 34 * and UART drivers which use the value when programming the 35 * hardware). 36 */ 37 38#include <sys/cdefs.h> 39__FBSDID("$FreeBSD$"); 40 41#include <sys/param.h> 42#include <sys/systm.h> 43#include <sys/bus.h> 44#include <sys/kernel.h> 45#include <sys/module.h> 46#include <sys/malloc.h> 47#include <sys/rman.h> 48#include <sys/timetc.h> 49#include <sys/timeet.h> 50 51#include <machine/bus.h> 52#include <machine/cpu.h> 53#include <machine/fdt.h> 54 55#include <dev/fdt/fdt_common.h> 56#include <dev/ofw/ofw_bus.h> 57#include <dev/ofw/ofw_bus_subr.h> 58 59#include <arm/amlogic/aml8726/aml8726_soc.h> 60#include <arm/amlogic/aml8726/aml8726_clkmsr.h> 61 62 63static struct aml8726_clkmsr_clk { 64 const char * name; 65 uint32_t mux; 66} aml8726_clkmsr_clks[] = { 67 { "clk81", 7 }, 68}; 69 70#define AML_CLKMSR_CLK81 0 71 72#define AML_CLKMSR_NCLKS nitems(aml8726_clkmsr_clks) 73 74struct aml8726_clkmsr_softc { 75 device_t dev; 76 struct resource * res[1]; 77}; 78 79static struct resource_spec aml8726_clkmsr_spec[] = { 80 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 81 { -1, 0 } 82}; 83 84/* 85 * Duration can range from 1uS to 65535 uS and should be chosen 86 * based on the expected frequency result so to maximize resolution 87 * and avoid overflowing the 16 bit result counter. 88 */ 89#define AML_CLKMSR_DURATION 32 90 91#define AML_CLKMSR_DUTY_REG 0 92#define AML_CLKMSR_0_REG 4 93#define AML_CLKMSR_0_BUSY (1U << 31) 94#define AML_CLKMSR_0_MUX_MASK (0x3f << 20) 95#define AML_CLKMSR_0_MUX_SHIFT 20 96#define AML_CLKMSR_0_MUX_EN (1 << 19) 97#define AML_CLKMSR_0_MEASURE (1 << 16) 98#define AML_CLKMSR_0_DURATION_MASK 0xffff 99#define AML_CLKMSR_0_DURATION_SHIFT 0 100#define AML_CLKMSR_1_REG 8 101#define AML_CLKMSR_2_REG 12 102#define AML_CLKMSR_2_RESULT_MASK 0xffff 103#define AML_CLKMSR_2_RESULT_SHIFT 0 104 105#define CSR_WRITE_4(sc, reg, val) bus_write_4((sc)->res[0], reg, (val)) 106#define CSR_READ_4(sc, reg) bus_read_4((sc)->res[0], reg) 107#define CSR_BARRIER(sc, reg) bus_barrier((sc)->res[0], reg, 4, \ 108 (BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)) 109 110static int 111aml8726_clkmsr_clock_frequency(struct aml8726_clkmsr_softc *sc, unsigned clock) 112{ 113 uint32_t value; 114 115 if (clock >= AML_CLKMSR_NCLKS) 116 return (0); 117 118 /* 119 * Locking is not used as this is only expected to be called from 120 * FDT fixup (which occurs prior to driver initialization) or attach. 121 */ 122 123 CSR_WRITE_4(sc, AML_CLKMSR_0_REG, 0); 124 125 CSR_BARRIER(sc, AML_CLKMSR_0_REG); 126 127 value = (aml8726_clkmsr_clks[clock].mux << AML_CLKMSR_0_MUX_SHIFT) 128 | ((AML_CLKMSR_DURATION - 1) << AML_CLKMSR_0_DURATION_SHIFT) 129 | AML_CLKMSR_0_MUX_EN 130 | AML_CLKMSR_0_MEASURE; 131 CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); 132 133 CSR_BARRIER(sc, AML_CLKMSR_0_REG); 134 135 while ((CSR_READ_4(sc, AML_CLKMSR_0_REG) & AML_CLKMSR_0_BUSY) != 0) 136 cpu_spinwait(); 137 138 value &= ~AML_CLKMSR_0_MEASURE; 139 CSR_WRITE_4(sc, AML_CLKMSR_0_REG, value); 140 141 CSR_BARRIER(sc, AML_CLKMSR_0_REG); 142 143 value = (((CSR_READ_4(sc, AML_CLKMSR_2_REG) & AML_CLKMSR_2_RESULT_MASK) 144 >> AML_CLKMSR_2_RESULT_SHIFT) + AML_CLKMSR_DURATION / 2) / 145 AML_CLKMSR_DURATION; 146 147 return value; 148} 149 150static void 151aml8726_clkmsr_fixup_clk81(struct aml8726_clkmsr_softc *sc, int freq) 152{ 153 pcell_t prop; 154 ssize_t len; 155 phandle_t clk_node; 156 phandle_t node; 157 158 node = ofw_bus_get_node(sc->dev); 159 160 len = OF_getencprop(node, "clocks", &prop, sizeof(prop)); 161 if ((len / sizeof(prop)) != 1 || prop == 0 || 162 (clk_node = OF_node_from_xref(prop)) == 0) 163 return; 164 165 len = OF_getencprop(clk_node, "clock-frequency", &prop, sizeof(prop)); 166 if ((len / sizeof(prop)) != 1 || prop != 0) 167 return; 168 169 freq = cpu_to_fdt32(freq); 170 171 OF_setprop(clk_node, "clock-frequency", (void *)&freq, sizeof(freq)); 172} 173 174static int 175aml8726_clkmsr_probe(device_t dev) 176{ 177 178 if (!ofw_bus_status_okay(dev)) 179 return (ENXIO); 180 181 if (!ofw_bus_is_compatible(dev, "amlogic,aml8726-clkmsr")) 182 return (ENXIO); 183 184 device_set_desc(dev, "Amlogic aml8726 clkmsr"); 185 186 return (BUS_PROBE_DEFAULT); 187} 188 189static int 190aml8726_clkmsr_attach(device_t dev) 191{ 192 struct aml8726_clkmsr_softc *sc = device_get_softc(dev); 193 int freq; 194 195 sc->dev = dev; 196 197 if (bus_alloc_resources(dev, aml8726_clkmsr_spec, sc->res)) { 198 device_printf(dev, "can not allocate resources for device\n"); 199 return (ENXIO); 200 } 201 202 freq = aml8726_clkmsr_clock_frequency(sc, AML_CLKMSR_CLK81); 203 device_printf(sc->dev, "bus clock %u MHz\n", freq); 204 205 aml8726_clkmsr_fixup_clk81(sc, freq * 1000000); 206 207 return (0); 208} 209 210static int 211aml8726_clkmsr_detach(device_t dev) 212{ 213 struct aml8726_clkmsr_softc *sc = device_get_softc(dev); 214 215 bus_release_resources(dev, aml8726_clkmsr_spec, sc->res); 216 217 return (0); 218} 219 220 221static device_method_t aml8726_clkmsr_methods[] = { 222 /* Device interface */ 223 DEVMETHOD(device_probe, aml8726_clkmsr_probe), 224 DEVMETHOD(device_attach, aml8726_clkmsr_attach), 225 DEVMETHOD(device_detach, aml8726_clkmsr_detach), 226 227 DEVMETHOD_END 228}; 229 230static driver_t aml8726_clkmsr_driver = { 231 "clkmsr", 232 aml8726_clkmsr_methods, 233 sizeof(struct aml8726_clkmsr_softc), 234}; 235 236static devclass_t aml8726_clkmsr_devclass; 237 238EARLY_DRIVER_MODULE(clkmsr, simplebus, aml8726_clkmsr_driver, 239 aml8726_clkmsr_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_EARLY); 240 241int 242aml8726_clkmsr_bus_frequency() 243{ 244 struct resource mem; 245 struct aml8726_clkmsr_softc sc; 246 phandle_t node; 247 u_long pbase, psize; 248 u_long start, size; 249 int freq; 250 251 KASSERT(aml8726_soc_hw_rev != AML_SOC_HW_REV_UNKNOWN, 252 ("aml8726_soc_hw_rev isn't initialized")); 253 254 /* 255 * Try to access the clkmsr node directly i.e. through /aliases/. 256 */ 257 258 if ((node = OF_finddevice("clkmsr")) != -1) 259 if (fdt_is_compatible_strict(node, "amlogic,aml8726-clkmsr")) 260 goto moveon; 261 262 /* 263 * Find the node the long way. 264 */ 265 if ((node = OF_finddevice("/soc")) == -1) 266 return (0); 267 268 if ((node = fdt_find_compatible(node, 269 "amlogic,aml8726-clkmsr", 1)) == 0) 270 return (0); 271 272moveon: 273 if (fdt_get_range(OF_parent(node), 0, &pbase, &psize) != 0 274 || fdt_regsize(node, &start, &size) != 0) 275 return (0); 276 277 start += pbase; 278 279 memset(&mem, 0, sizeof(mem)); 280 281 mem.r_bustag = fdtbus_bs_tag; 282 283 if (bus_space_map(mem.r_bustag, start, size, 0, &mem.r_bushandle) != 0) 284 return (0); 285 286 /* 287 * Build an incomplete (however sufficient for the purpose 288 * of calling aml8726_clkmsr_clock_frequency) softc. 289 */ 290 291 memset(&sc, 0, sizeof(sc)); 292 293 sc.res[0] = &mem; 294 295 freq = aml8726_clkmsr_clock_frequency(&sc, AML_CLKMSR_CLK81) * 1000000; 296 297 bus_space_unmap(mem.r_bustag, mem.r_bushandle, size); 298 299 return (freq); 300} 301