1 /* 2 * Copyright (c) 2010 3 * Ben Gray <ben.r.gray@gmail.com>. 4 * All rights reserved. 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 * 3. All advertising materials mentioning features or use of this software 15 * must display the following acknowledgement: 16 * This product includes software developed by Ben Gray. 17 * 4. The name of the company nor the name of the author may be used to 18 * endorse or promote products derived from this software without specific 19 * prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY BEN GRAY ``AS IS'' AND ANY EXPRESS OR 22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 24 * IN NO EVENT SHALL BEN GRAY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 26 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 27 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 28 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 29 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 30 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /** 34 * Power, Reset and Clock Managment Module 35 * 36 * This is a very simple driver wrapper around the PRCM set of registers in 37 * the OMAP3 chip. It allows you to turn on and off things like the functional 38 * and interface clocks to the various on-chip modules. 39 * 40 */ 41 #include <sys/cdefs.h> 42 __FBSDID("$FreeBSD$"); 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/module.h> 48 #include <sys/bus.h> 49 #include <sys/resource.h> 50 #include <sys/rman.h> 51 #include <sys/lock.h> 52 #include <sys/mutex.h> 53 54 #include <machine/bus.h> 55 #include <machine/cpu.h> 56 #include <machine/cpufunc.h> 57 #include <machine/frame.h> 58 #include <machine/resource.h> 59 #include <machine/intr.h> 60 61 #include <arm/ti/ti_prcm.h> 62 63 /** 64 * ti_clk_devmap - Array of clock devices, should be defined one per SoC 65 * 66 * This array is typically defined in one of the targeted *_prcm_clk.c 67 * files and is specific to the given SoC platform. Each entry in the array 68 * corresponds to an individual clock device. 69 */ 70 extern struct ti_clock_dev ti_clk_devmap[]; 71 72 /** 73 * ti_prcm_clk_dev - returns a pointer to the clock device with given id 74 * @clk: the ID of the clock device to get 75 * 76 * Simply iterates through the clk_devmap global array and returns a pointer 77 * to the clock device if found. 78 * 79 * LOCKING: 80 * None 81 * 82 * RETURNS: 83 * The pointer to the clock device on success, on failure NULL is returned. 84 */ 85 static struct ti_clock_dev * 86 ti_prcm_clk_dev(clk_ident_t clk) 87 { 88 struct ti_clock_dev *clk_dev; 89 90 /* Find the clock within the devmap - it's a bit inefficent having a for 91 * loop for this, but this function should only called when a driver is 92 * being activated so IMHO not a big issue. 93 */ 94 clk_dev = &(ti_clk_devmap[0]); 95 while (clk_dev->id != INVALID_CLK_IDENT) { 96 if (clk_dev->id == clk) { 97 return (clk_dev); 98 } 99 clk_dev++; 100 } 101 102 /* Sanity check we managed to find the clock */ 103 printf("ti_prcm: Failed to find clock device (%d)\n", clk); 104 return (NULL); 105 } 106 107 /** 108 * ti_prcm_clk_valid - enables a clock for a particular module 109 * @clk: identifier for the module to enable, see ti_prcm.h for a list 110 * of possible modules. 111 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 112 * 113 * This function can enable either a functional or interface clock. 114 * 115 * The real work done to enable the clock is really done in the callback 116 * function associated with the clock, this function is simply a wrapper 117 * around that. 118 * 119 * LOCKING: 120 * Internally locks the driver context. 121 * 122 * RETURNS: 123 * Returns 0 on success or positive error code on failure. 124 */ 125 int 126 ti_prcm_clk_valid(clk_ident_t clk) 127 { 128 int ret = 0; 129 130 if (ti_prcm_clk_dev(clk) == NULL) 131 ret = EINVAL; 132 133 return (ret); 134 } 135 136 137 /** 138 * ti_prcm_clk_enable - enables a clock for a particular module 139 * @clk: identifier for the module to enable, see ti_prcm.h for a list 140 * of possible modules. 141 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 142 * 143 * This function can enable either a functional or interface clock. 144 * 145 * The real work done to enable the clock is really done in the callback 146 * function associated with the clock, this function is simply a wrapper 147 * around that. 148 * 149 * LOCKING: 150 * Internally locks the driver context. 151 * 152 * RETURNS: 153 * Returns 0 on success or positive error code on failure. 154 */ 155 int 156 ti_prcm_clk_enable(clk_ident_t clk) 157 { 158 struct ti_clock_dev *clk_dev; 159 int ret; 160 161 /* Find the clock within the devmap - it's a bit inefficent having a for 162 * loop for this, but this function should only called when a driver is 163 * being activated so IMHO not a big issue. 164 */ 165 clk_dev = ti_prcm_clk_dev(clk); 166 167 /* Sanity check we managed to find the clock */ 168 if (clk_dev == NULL) 169 return (EINVAL); 170 171 /* Activate the clock */ 172 if (clk_dev->clk_activate) 173 ret = clk_dev->clk_activate(clk_dev); 174 else 175 ret = EINVAL; 176 177 return (ret); 178 } 179 180 181 /** 182 * ti_prcm_clk_disable - disables a clock for a particular module 183 * @clk: identifier for the module to enable, see ti_prcm.h for a list 184 * of possible modules. 185 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 186 * 187 * This function can enable either a functional or interface clock. 188 * 189 * The real work done to enable the clock is really done in the callback 190 * function associated with the clock, this function is simply a wrapper 191 * around that. 192 * 193 * LOCKING: 194 * Internally locks the driver context. 195 * 196 * RETURNS: 197 * Returns 0 on success or positive error code on failure. 198 */ 199 int 200 ti_prcm_clk_disable(clk_ident_t clk) 201 { 202 struct ti_clock_dev *clk_dev; 203 int ret; 204 205 /* Find the clock within the devmap - it's a bit inefficent having a for 206 * loop for this, but this function should only called when a driver is 207 * being activated so IMHO not a big issue. 208 */ 209 clk_dev = ti_prcm_clk_dev(clk); 210 211 /* Sanity check we managed to find the clock */ 212 if (clk_dev == NULL) 213 return (EINVAL); 214 215 /* Activate the clock */ 216 if (clk_dev->clk_deactivate) 217 ret = clk_dev->clk_deactivate(clk_dev); 218 else 219 ret = EINVAL; 220 221 return (ret); 222 } 223 224 /** 225 * ti_prcm_clk_set_source - sets the source 226 * @clk: identifier for the module to enable, see ti_prcm.h for a list 227 * of possible modules. 228 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 229 * 230 * This function can enable either a functional or interface clock. 231 * 232 * The real work done to enable the clock is really done in the callback 233 * function associated with the clock, this function is simply a wrapper 234 * around that. 235 * 236 * LOCKING: 237 * Internally locks the driver context. 238 * 239 * RETURNS: 240 * Returns 0 on success or positive error code on failure. 241 */ 242 int 243 ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) 244 { 245 struct ti_clock_dev *clk_dev; 246 int ret; 247 248 /* Find the clock within the devmap - it's a bit inefficent having a for 249 * loop for this, but this function should only called when a driver is 250 * being activated so IMHO not a big issue. 251 */ 252 clk_dev = ti_prcm_clk_dev(clk); 253 254 /* Sanity check we managed to find the clock */ 255 if (clk_dev == NULL) 256 return (EINVAL); 257 258 /* Activate the clock */ 259 if (clk_dev->clk_set_source) 260 ret = clk_dev->clk_set_source(clk_dev, clksrc); 261 else 262 ret = EINVAL; 263 264 return (ret); 265 } 266 267 268 /** 269 * ti_prcm_clk_get_source_freq - gets the source clock frequency 270 * @clk: identifier for the module to enable, see ti_prcm.h for a list 271 * of possible modules. 272 * @freq: pointer to an integer that upon return will contain the src freq 273 * 274 * This function returns the frequency of the source clock. 275 * 276 * The real work done to enable the clock is really done in the callback 277 * function associated with the clock, this function is simply a wrapper 278 * around that. 279 * 280 * LOCKING: 281 * Internally locks the driver context. 282 * 283 * RETURNS: 284 * Returns 0 on success or positive error code on failure. 285 */ 286 int 287 ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) 288 { 289 struct ti_clock_dev *clk_dev; 290 int ret; 291 292 /* Find the clock within the devmap - it's a bit inefficent having a for 293 * loop for this, but this function should only called when a driver is 294 * being activated so IMHO not a big issue. 295 */ 296 clk_dev = ti_prcm_clk_dev(clk); 297 298 /* Sanity check we managed to find the clock */ 299 if (clk_dev == NULL) 300 return (EINVAL); 301 302 /* Get the source frequency of the clock */ 303 if (clk_dev->clk_get_source_freq) 304 ret = clk_dev->clk_get_source_freq(clk_dev, freq); 305 else 306 ret = EINVAL; 307 308 return (ret); 309 } 310