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