1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2020 Oskar Holmlund <oskar.holmlund@ohdata.se> 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/cdefs.h> 29 __FBSDID("$FreeBSD$"); 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/malloc.h> 35 36 #include <dev/extres/clk/clk.h> 37 38 #include <arm/ti/clk/ti_clk_clkctrl.h> 39 40 #include "clkdev_if.h" 41 42 #if 0 43 #define DPRINTF(dev, msg...) device_printf(dev, msg) 44 #else 45 #define DPRINTF(dev, msg...) 46 #endif 47 48 /* 49 * clknode for clkctrl, implements gate and mux (for gpioc) 50 */ 51 52 #define GPIO_X_GDBCLK_MASK 0x00040000 53 #define IDLEST_MASK 0x00030000 54 #define MODULEMODE_MASK 0x00000003 55 56 #define GPIOX_GDBCLK_ENABLE 0x00040000 57 #define GPIOX_GDBCLK_DISABLE 0x00000000 58 #define IDLEST_FUNC 0x00000000 59 #define IDLEST_TRANS 0x00010000 60 #define IDLEST_IDLE 0x00020000 61 #define IDLEST_DISABLE 0x00030000 62 63 #define MODULEMODE_DISABLE 0x0 64 #define MODULEMODE_ENABLE 0x2 65 66 struct ti_clkctrl_clknode_sc { 67 device_t dev; 68 bool gdbclk; 69 /* omap4-cm range.host + ti,clkctrl reg[0] */ 70 uint32_t register_offset; 71 }; 72 73 #define WRITE4(_clk, off, val) \ 74 CLKDEV_WRITE_4(clknode_get_device(_clk), off, val) 75 #define READ4(_clk, off, val) \ 76 CLKDEV_READ_4(clknode_get_device(_clk), off, val) 77 #define DEVICE_LOCK(_clk) \ 78 CLKDEV_DEVICE_LOCK(clknode_get_device(_clk)) 79 #define DEVICE_UNLOCK(_clk) \ 80 CLKDEV_DEVICE_UNLOCK(clknode_get_device(_clk)) 81 82 static int 83 ti_clkctrl_init(struct clknode *clk, device_t dev) 84 { 85 struct ti_clkctrl_clknode_sc *sc; 86 87 sc = clknode_get_softc(clk); 88 sc->dev = dev; 89 90 clknode_init_parent_idx(clk, 0); 91 return (0); 92 } 93 94 static int 95 ti_clkctrl_set_gdbclk_gate(struct clknode *clk, bool enable) 96 { 97 struct ti_clkctrl_clknode_sc *sc; 98 uint32_t val, gpio_x_gdbclk; 99 uint32_t timeout = 100; 100 101 sc = clknode_get_softc(clk); 102 103 READ4(clk, sc->register_offset, &val); 104 DPRINTF(sc->dev, "val(%x) & (%x | %x = %x)\n", 105 val, GPIO_X_GDBCLK_MASK, MODULEMODE_MASK, 106 GPIO_X_GDBCLK_MASK | MODULEMODE_MASK); 107 108 if (enable) { 109 val = val & MODULEMODE_MASK; 110 val |= GPIOX_GDBCLK_ENABLE; 111 } else { 112 val = val & MODULEMODE_MASK; 113 val |= GPIOX_GDBCLK_DISABLE; 114 } 115 116 DPRINTF(sc->dev, "val %x\n", val); 117 WRITE4(clk, sc->register_offset, val); 118 119 /* Wait */ 120 while (timeout) { 121 READ4(clk, sc->register_offset, &val); 122 gpio_x_gdbclk = val & GPIO_X_GDBCLK_MASK; 123 if (enable && (gpio_x_gdbclk == GPIOX_GDBCLK_ENABLE)) 124 break; 125 else if (!enable && (gpio_x_gdbclk == GPIOX_GDBCLK_DISABLE)) 126 break; 127 DELAY(10); 128 timeout--; 129 } 130 if (timeout == 0) { 131 device_printf(sc->dev, "ti_clkctrl_set_gdbclk_gate: Timeout\n"); 132 return (1); 133 } 134 135 return (0); 136 } 137 138 static int 139 ti_clkctrl_set_gate(struct clknode *clk, bool enable) 140 { 141 struct ti_clkctrl_clknode_sc *sc; 142 uint32_t val, idlest, module; 143 uint32_t timeout=100; 144 int err; 145 146 sc = clknode_get_softc(clk); 147 148 if (sc->gdbclk) { 149 err = ti_clkctrl_set_gdbclk_gate(clk, enable); 150 return (err); 151 } 152 153 READ4(clk, sc->register_offset, &val); 154 155 if (enable) 156 WRITE4(clk, sc->register_offset, MODULEMODE_ENABLE); 157 else 158 WRITE4(clk, sc->register_offset, MODULEMODE_DISABLE); 159 160 while (timeout) { 161 READ4(clk, sc->register_offset, &val); 162 idlest = val & IDLEST_MASK; 163 module = val & MODULEMODE_MASK; 164 if (enable && 165 (idlest == IDLEST_FUNC || idlest == IDLEST_TRANS) && 166 module == MODULEMODE_ENABLE) 167 break; 168 else if (!enable && 169 idlest == IDLEST_DISABLE && 170 module == MODULEMODE_DISABLE) 171 break; 172 DELAY(10); 173 timeout--; 174 } 175 176 if (timeout == 0) { 177 device_printf(sc->dev, "ti_clkctrl_set_gate: Timeout\n"); 178 return (1); 179 } 180 181 return (0); 182 } 183 184 static clknode_method_t ti_clkctrl_clknode_methods[] = { 185 /* Device interface */ 186 CLKNODEMETHOD(clknode_init, ti_clkctrl_init), 187 CLKNODEMETHOD(clknode_set_gate, ti_clkctrl_set_gate), 188 CLKNODEMETHOD_END 189 }; 190 191 DEFINE_CLASS_1(ti_clkctrl_clknode, ti_clkctrl_clknode_class, 192 ti_clkctrl_clknode_methods, sizeof(struct ti_clkctrl_clknode_sc), 193 clknode_class); 194 195 int 196 ti_clknode_clkctrl_register(struct clkdom *clkdom, 197 struct ti_clk_clkctrl_def *clkdef) 198 { 199 struct clknode *clk; 200 struct ti_clkctrl_clknode_sc *sc; 201 202 clk = clknode_create(clkdom, &ti_clkctrl_clknode_class, 203 &clkdef->clkdef); 204 205 if (clk == NULL) { 206 return (1); 207 } 208 209 sc = clknode_get_softc(clk); 210 sc->register_offset = clkdef->register_offset; 211 sc->gdbclk = clkdef->gdbclk; 212 213 if (clknode_register(clkdom, clk) == NULL) { 214 return (2); 215 } 216 return (0); 217 } 218