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