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 Management 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/resource.h> 58 #include <machine/intr.h> 59 60 #include <arm/ti/ti_cpuid.h> 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_omap4_clk_devmap[]; 71 extern struct ti_clock_dev ti_am335x_clk_devmap[]; 72 73 /** 74 * ti_prcm_clk_dev - returns a pointer to the clock device with given id 75 * @clk: the ID of the clock device to get 76 * 77 * Simply iterates through the clk_devmap global array and returns a pointer 78 * to the clock device if found. 79 * 80 * LOCKING: 81 * None 82 * 83 * RETURNS: 84 * The pointer to the clock device on success, on failure NULL is returned. 85 */ 86 static struct ti_clock_dev * 87 ti_prcm_clk_dev(clk_ident_t clk) 88 { 89 struct ti_clock_dev *clk_dev; 90 91 /* Find the clock within the devmap - it's a bit inefficent having a for 92 * loop for this, but this function should only called when a driver is 93 * being activated so IMHO not a big issue. 94 */ 95 clk_dev = NULL; 96 switch(ti_chip()) { 97 #ifdef SOC_OMAP4 98 case CHIP_OMAP_4: 99 clk_dev = &(ti_omap4_clk_devmap[0]); 100 break; 101 #endif 102 #ifdef SOC_TI_AM335X 103 case CHIP_AM335X: 104 clk_dev = &(ti_am335x_clk_devmap[0]); 105 break; 106 #endif 107 } 108 if (clk_dev == NULL) 109 panic("No clock devmap found"); 110 while (clk_dev->id != INVALID_CLK_IDENT) { 111 if (clk_dev->id == clk) { 112 return (clk_dev); 113 } 114 clk_dev++; 115 } 116 117 /* Sanity check we managed to find the clock */ 118 printf("ti_prcm: Failed to find clock device (%d)\n", clk); 119 return (NULL); 120 } 121 122 /** 123 * ti_prcm_clk_valid - enables a clock for a particular module 124 * @clk: identifier for the module to enable, see ti_prcm.h for a list 125 * of possible modules. 126 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 127 * 128 * This function can enable either a functional or interface clock. 129 * 130 * The real work done to enable the clock is really done in the callback 131 * function associated with the clock, this function is simply a wrapper 132 * around that. 133 * 134 * LOCKING: 135 * Internally locks the driver context. 136 * 137 * RETURNS: 138 * Returns 0 on success or positive error code on failure. 139 */ 140 int 141 ti_prcm_clk_valid(clk_ident_t clk) 142 { 143 int ret = 0; 144 145 if (ti_prcm_clk_dev(clk) == NULL) 146 ret = EINVAL; 147 148 return (ret); 149 } 150 151 152 /** 153 * ti_prcm_clk_enable - enables a clock for a particular module 154 * @clk: identifier for the module to enable, see ti_prcm.h for a list 155 * of possible modules. 156 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 157 * 158 * This function can enable either a functional or interface clock. 159 * 160 * The real work done to enable the clock is really done in the callback 161 * function associated with the clock, this function is simply a wrapper 162 * around that. 163 * 164 * LOCKING: 165 * Internally locks the driver context. 166 * 167 * RETURNS: 168 * Returns 0 on success or positive error code on failure. 169 */ 170 int 171 ti_prcm_clk_enable(clk_ident_t clk) 172 { 173 struct ti_clock_dev *clk_dev; 174 int ret; 175 176 /* Find the clock within the devmap - it's a bit inefficent having a for 177 * loop for this, but this function should only called when a driver is 178 * being activated so IMHO not a big issue. 179 */ 180 clk_dev = ti_prcm_clk_dev(clk); 181 182 /* Sanity check we managed to find the clock */ 183 if (clk_dev == NULL) 184 return (EINVAL); 185 186 /* Activate the clock */ 187 if (clk_dev->clk_activate) 188 ret = clk_dev->clk_activate(clk_dev); 189 else 190 ret = EINVAL; 191 192 return (ret); 193 } 194 195 196 /** 197 * ti_prcm_clk_disable - disables a clock for a particular module 198 * @clk: identifier for the module to enable, see ti_prcm.h for a list 199 * of possible modules. 200 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 201 * 202 * This function can enable either a functional or interface clock. 203 * 204 * The real work done to enable the clock is really done in the callback 205 * function associated with the clock, this function is simply a wrapper 206 * around that. 207 * 208 * LOCKING: 209 * Internally locks the driver context. 210 * 211 * RETURNS: 212 * Returns 0 on success or positive error code on failure. 213 */ 214 int 215 ti_prcm_clk_disable(clk_ident_t clk) 216 { 217 struct ti_clock_dev *clk_dev; 218 int ret; 219 220 /* Find the clock within the devmap - it's a bit inefficent having a for 221 * loop for this, but this function should only called when a driver is 222 * being activated so IMHO not a big issue. 223 */ 224 clk_dev = ti_prcm_clk_dev(clk); 225 226 /* Sanity check we managed to find the clock */ 227 if (clk_dev == NULL) 228 return (EINVAL); 229 230 /* Activate the clock */ 231 if (clk_dev->clk_deactivate) 232 ret = clk_dev->clk_deactivate(clk_dev); 233 else 234 ret = EINVAL; 235 236 return (ret); 237 } 238 239 /** 240 * ti_prcm_clk_set_source - sets the source 241 * @clk: identifier for the module to enable, see ti_prcm.h for a list 242 * of possible modules. 243 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 244 * 245 * This function can enable either a functional or interface clock. 246 * 247 * The real work done to enable the clock is really done in the callback 248 * function associated with the clock, this function is simply a wrapper 249 * around that. 250 * 251 * LOCKING: 252 * Internally locks the driver context. 253 * 254 * RETURNS: 255 * Returns 0 on success or positive error code on failure. 256 */ 257 int 258 ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) 259 { 260 struct ti_clock_dev *clk_dev; 261 int ret; 262 263 /* Find the clock within the devmap - it's a bit inefficent having a for 264 * loop for this, but this function should only called when a driver is 265 * being activated so IMHO not a big issue. 266 */ 267 clk_dev = ti_prcm_clk_dev(clk); 268 269 /* Sanity check we managed to find the clock */ 270 if (clk_dev == NULL) 271 return (EINVAL); 272 273 /* Activate the clock */ 274 if (clk_dev->clk_set_source) 275 ret = clk_dev->clk_set_source(clk_dev, clksrc); 276 else 277 ret = EINVAL; 278 279 return (ret); 280 } 281 282 283 /** 284 * ti_prcm_clk_get_source_freq - gets the source clock frequency 285 * @clk: identifier for the module to enable, see ti_prcm.h for a list 286 * of possible modules. 287 * @freq: pointer to an integer that upon return will contain the src freq 288 * 289 * This function returns the frequency of the source clock. 290 * 291 * The real work done to enable the clock is really done in the callback 292 * function associated with the clock, this function is simply a wrapper 293 * around that. 294 * 295 * LOCKING: 296 * Internally locks the driver context. 297 * 298 * RETURNS: 299 * Returns 0 on success or positive error code on failure. 300 */ 301 int 302 ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) 303 { 304 struct ti_clock_dev *clk_dev; 305 int ret; 306 307 /* Find the clock within the devmap - it's a bit inefficent having a for 308 * loop for this, but this function should only called when a driver is 309 * being activated so IMHO not a big issue. 310 */ 311 clk_dev = ti_prcm_clk_dev(clk); 312 313 /* Sanity check we managed to find the clock */ 314 if (clk_dev == NULL) 315 return (EINVAL); 316 317 /* Get the source frequency of the clock */ 318 if (clk_dev->clk_get_source_freq) 319 ret = clk_dev->clk_get_source_freq(clk_dev, freq); 320 else 321 ret = EINVAL; 322 323 return (ret); 324 } 325 326 /** 327 * ti_prcm_clk_set_source_freq - sets the source clock frequency as close to freq as possible 328 * @clk: identifier for the module to enable, see ti_prcm.h for a list 329 * of possible modules. 330 * @freq: requested freq 331 * 332 * LOCKING: 333 * Internally locks the driver context. 334 * 335 * RETURNS: 336 * Returns 0 on success or positive error code on failure. 337 */ 338 int 339 ti_prcm_clk_set_source_freq(clk_ident_t clk, unsigned int freq) 340 { 341 struct ti_clock_dev *clk_dev; 342 int ret; 343 344 clk_dev = ti_prcm_clk_dev(clk); 345 346 /* Sanity check we managed to find the clock */ 347 if (clk_dev == NULL) 348 return (EINVAL); 349 350 /* Get the source frequency of the clock */ 351 if (clk_dev->clk_set_source_freq) 352 ret = clk_dev->clk_set_source_freq(clk_dev, freq); 353 else 354 ret = EINVAL; 355 356 return (ret); 357 } 358