1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 Emmanuel Vadot <manu@freebsd.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/param.h> 29 #include <sys/systm.h> 30 #include <sys/bus.h> 31 #include <sys/rman.h> 32 #include <sys/kernel.h> 33 #include <sys/module.h> 34 #include <machine/bus.h> 35 36 #include <dev/fdt/simplebus.h> 37 38 #include <dev/ofw/ofw_bus.h> 39 #include <dev/ofw/ofw_bus_subr.h> 40 41 #include <dev/clk/clk_div.h> 42 #include <dev/clk/clk_fixed.h> 43 #include <dev/clk/clk_mux.h> 44 45 #include <dev/clk/allwinner/aw_ccung.h> 46 47 #include <dt-bindings/clock/sun50i-h6-r-ccu.h> 48 #include <dt-bindings/reset/sun50i-h6-r-ccu.h> 49 50 /* Non-exported clocks */ 51 #define CLK_R_AHB 1 52 #define CLK_R_APB2 3 53 54 static struct aw_ccung_reset ccu_sun50i_h6_r_resets[] = { 55 CCU_RESET(RST_R_APB1_TIMER, 0x11c, 16) 56 CCU_RESET(RST_R_APB1_TWD, 0x12c, 16) 57 CCU_RESET(RST_R_APB1_PWM, 0x13c, 16) 58 CCU_RESET(RST_R_APB2_UART, 0x18c, 16) 59 CCU_RESET(RST_R_APB2_I2C, 0x19c, 16) 60 CCU_RESET(RST_R_APB1_IR, 0x1cc, 16) 61 CCU_RESET(RST_R_APB1_W1, 0x1ec, 16) 62 }; 63 64 static struct aw_ccung_gate ccu_sun50i_h6_r_gates[] = { 65 CCU_GATE(CLK_R_APB1_TIMER, "r_apb1-timer", "r_apb1", 0x11c, 0) 66 CCU_GATE(CLK_R_APB1_TWD, "r_apb1-twd", "r_apb1", 0x12c, 0) 67 CCU_GATE(CLK_R_APB1_PWM, "r_apb1-pwm", "r_apb1", 0x13c, 0) 68 CCU_GATE(CLK_R_APB2_UART, "r_apb1-uart", "r_apb2", 0x18c, 0) 69 CCU_GATE(CLK_R_APB2_I2C, "r_apb1-i2c", "r_apb2", 0x19c, 0) 70 CCU_GATE(CLK_R_APB1_IR, "r_apb1-ir", "r_apb1", 0x1cc, 0) 71 CCU_GATE(CLK_R_APB1_W1, "r_apb1-w1", "r_apb1", 0x1ec, 0) 72 }; 73 74 static const char *ar100_parents[] = {"osc24M", "osc32k", "pll_periph0", "iosc"}; 75 PREDIV_CLK(ar100_clk, CLK_AR100, /* id */ 76 "ar100", ar100_parents, /* name, parents */ 77 0x00, /* offset */ 78 16, 2, /* mux */ 79 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */ 80 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */ 81 16, 2, 2); /* prediv condition */ 82 83 static const char *r_ahb_parents[] = {"ar100"}; 84 FIXED_CLK(r_ahb_clk, 85 CLK_R_AHB, /* id */ 86 "r_ahb", /* name */ 87 r_ahb_parents, /* parent */ 88 0, /* freq */ 89 1, /* mult */ 90 1, /* div */ 91 0); /* flags */ 92 93 static const char *r_apb1_parents[] = {"r_ahb"}; 94 DIV_CLK(r_apb1_clk, 95 CLK_R_APB1, /* id */ 96 "r_apb1", r_apb1_parents, /* name, parents */ 97 0x0c, /* offset */ 98 0, 2, /* shift, width */ 99 0, NULL); /* flags, div table */ 100 101 static const char *r_apb2_parents[] = {"osc24M", "osc32k", "pll_periph0", "iosc"}; 102 PREDIV_CLK(r_apb2_clk, CLK_R_APB2, /* id */ 103 "r_apb2", r_apb2_parents, /* name, parents */ 104 0x10, /* offset */ 105 16, 2, /* mux */ 106 4, 2, 0, AW_CLK_FACTOR_POWER_OF_TWO, /* div */ 107 8, 5, 0, AW_CLK_FACTOR_HAS_COND, /* prediv */ 108 16, 2, 2); /* prediv condition */ 109 110 static struct aw_ccung_clk clks[] = { 111 { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &ar100_clk}, 112 { .type = AW_CLK_FIXED, .clk.fixed = &r_ahb_clk}, 113 { .type = AW_CLK_DIV, .clk.div = &r_apb1_clk}, 114 { .type = AW_CLK_PREDIV_MUX, .clk.prediv_mux = &r_apb2_clk}, 115 }; 116 117 static struct ofw_compat_data compat_data[] = { 118 { "allwinner,sun50i-h6-r-ccu", 1 }, 119 { NULL, 0}, 120 }; 121 122 static int 123 ccu_sun50i_h6_r_probe(device_t dev) 124 { 125 126 if (!ofw_bus_status_okay(dev)) 127 return (ENXIO); 128 129 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 130 return (ENXIO); 131 132 device_set_desc(dev, "Allwinner SUN50I_H6_R Clock Control Unit NG"); 133 return (BUS_PROBE_DEFAULT); 134 } 135 136 static int 137 ccu_sun50i_h6_r_attach(device_t dev) 138 { 139 struct aw_ccung_softc *sc; 140 141 sc = device_get_softc(dev); 142 143 sc->resets = ccu_sun50i_h6_r_resets; 144 sc->nresets = nitems(ccu_sun50i_h6_r_resets); 145 sc->gates = ccu_sun50i_h6_r_gates; 146 sc->ngates = nitems(ccu_sun50i_h6_r_gates); 147 sc->clks = clks; 148 sc->nclks = nitems(clks); 149 150 return (aw_ccung_attach(dev)); 151 } 152 153 static device_method_t ccu_sun50i_h6_r_methods[] = { 154 /* Device interface */ 155 DEVMETHOD(device_probe, ccu_sun50i_h6_r_probe), 156 DEVMETHOD(device_attach, ccu_sun50i_h6_r_attach), 157 158 DEVMETHOD_END 159 }; 160 161 DEFINE_CLASS_1(ccu_sun50i_h6_r, ccu_sun50i_h6_r_driver, ccu_sun50i_h6_r_methods, 162 sizeof(struct aw_ccung_softc), aw_ccung_driver); 163 164 EARLY_DRIVER_MODULE(ccu_sun50i_h6_r, simplebus, ccu_sun50i_h6_r_driver, 0, 0, 165 BUS_PASS_RESOURCE + BUS_PASS_ORDER_MIDDLE); 166