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