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/resource.h> 58 #include <machine/intr.h> 59 60 #include <arm/ti/ti_prcm.h> 61 62 /** 63 * ti_clk_devmap - Array of clock devices, should be defined one per SoC 64 * 65 * This array is typically defined in one of the targeted *_prcm_clk.c 66 * files and is specific to the given SoC platform. Each entry in the array 67 * corresponds to an individual clock device. 68 */ 69 extern struct ti_clock_dev ti_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 = &(ti_clk_devmap[0]); 94 while (clk_dev->id != INVALID_CLK_IDENT) { 95 if (clk_dev->id == clk) { 96 return (clk_dev); 97 } 98 clk_dev++; 99 } 100 101 /* Sanity check we managed to find the clock */ 102 printf("ti_prcm: Failed to find clock device (%d)\n", clk); 103 return (NULL); 104 } 105 106 /** 107 * ti_prcm_clk_valid - enables a clock for a particular module 108 * @clk: identifier for the module to enable, see ti_prcm.h for a list 109 * of possible modules. 110 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 111 * 112 * This function can enable either a functional or interface clock. 113 * 114 * The real work done to enable the clock is really done in the callback 115 * function associated with the clock, this function is simply a wrapper 116 * around that. 117 * 118 * LOCKING: 119 * Internally locks the driver context. 120 * 121 * RETURNS: 122 * Returns 0 on success or positive error code on failure. 123 */ 124 int 125 ti_prcm_clk_valid(clk_ident_t clk) 126 { 127 int ret = 0; 128 129 if (ti_prcm_clk_dev(clk) == NULL) 130 ret = EINVAL; 131 132 return (ret); 133 } 134 135 136 /** 137 * ti_prcm_clk_enable - enables a clock for a particular module 138 * @clk: identifier for the module to enable, see ti_prcm.h for a list 139 * of possible modules. 140 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 141 * 142 * This function can enable either a functional or interface clock. 143 * 144 * The real work done to enable the clock is really done in the callback 145 * function associated with the clock, this function is simply a wrapper 146 * around that. 147 * 148 * LOCKING: 149 * Internally locks the driver context. 150 * 151 * RETURNS: 152 * Returns 0 on success or positive error code on failure. 153 */ 154 int 155 ti_prcm_clk_enable(clk_ident_t clk) 156 { 157 struct ti_clock_dev *clk_dev; 158 int ret; 159 160 /* Find the clock within the devmap - it's a bit inefficent having a for 161 * loop for this, but this function should only called when a driver is 162 * being activated so IMHO not a big issue. 163 */ 164 clk_dev = ti_prcm_clk_dev(clk); 165 166 /* Sanity check we managed to find the clock */ 167 if (clk_dev == NULL) 168 return (EINVAL); 169 170 /* Activate the clock */ 171 if (clk_dev->clk_activate) 172 ret = clk_dev->clk_activate(clk_dev); 173 else 174 ret = EINVAL; 175 176 return (ret); 177 } 178 179 180 /** 181 * ti_prcm_clk_disable - disables a clock for a particular module 182 * @clk: identifier for the module to enable, see ti_prcm.h for a list 183 * of possible modules. 184 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 185 * 186 * This function can enable either a functional or interface clock. 187 * 188 * The real work done to enable the clock is really done in the callback 189 * function associated with the clock, this function is simply a wrapper 190 * around that. 191 * 192 * LOCKING: 193 * Internally locks the driver context. 194 * 195 * RETURNS: 196 * Returns 0 on success or positive error code on failure. 197 */ 198 int 199 ti_prcm_clk_disable(clk_ident_t clk) 200 { 201 struct ti_clock_dev *clk_dev; 202 int ret; 203 204 /* Find the clock within the devmap - it's a bit inefficent having a for 205 * loop for this, but this function should only called when a driver is 206 * being activated so IMHO not a big issue. 207 */ 208 clk_dev = ti_prcm_clk_dev(clk); 209 210 /* Sanity check we managed to find the clock */ 211 if (clk_dev == NULL) 212 return (EINVAL); 213 214 /* Activate the clock */ 215 if (clk_dev->clk_deactivate) 216 ret = clk_dev->clk_deactivate(clk_dev); 217 else 218 ret = EINVAL; 219 220 return (ret); 221 } 222 223 /** 224 * ti_prcm_clk_set_source - sets the source 225 * @clk: identifier for the module to enable, see ti_prcm.h for a list 226 * of possible modules. 227 * Example: OMAP3_MODULE_MMC1_ICLK or OMAP3_MODULE_GPTIMER10_FCLK. 228 * 229 * This function can enable either a functional or interface clock. 230 * 231 * The real work done to enable the clock is really done in the callback 232 * function associated with the clock, this function is simply a wrapper 233 * around that. 234 * 235 * LOCKING: 236 * Internally locks the driver context. 237 * 238 * RETURNS: 239 * Returns 0 on success or positive error code on failure. 240 */ 241 int 242 ti_prcm_clk_set_source(clk_ident_t clk, clk_src_t clksrc) 243 { 244 struct ti_clock_dev *clk_dev; 245 int ret; 246 247 /* Find the clock within the devmap - it's a bit inefficent having a for 248 * loop for this, but this function should only called when a driver is 249 * being activated so IMHO not a big issue. 250 */ 251 clk_dev = ti_prcm_clk_dev(clk); 252 253 /* Sanity check we managed to find the clock */ 254 if (clk_dev == NULL) 255 return (EINVAL); 256 257 /* Activate the clock */ 258 if (clk_dev->clk_set_source) 259 ret = clk_dev->clk_set_source(clk_dev, clksrc); 260 else 261 ret = EINVAL; 262 263 return (ret); 264 } 265 266 267 /** 268 * ti_prcm_clk_get_source_freq - gets the source clock frequency 269 * @clk: identifier for the module to enable, see ti_prcm.h for a list 270 * of possible modules. 271 * @freq: pointer to an integer that upon return will contain the src freq 272 * 273 * This function returns the frequency of the source clock. 274 * 275 * The real work done to enable the clock is really done in the callback 276 * function associated with the clock, this function is simply a wrapper 277 * around that. 278 * 279 * LOCKING: 280 * Internally locks the driver context. 281 * 282 * RETURNS: 283 * Returns 0 on success or positive error code on failure. 284 */ 285 int 286 ti_prcm_clk_get_source_freq(clk_ident_t clk, unsigned int *freq) 287 { 288 struct ti_clock_dev *clk_dev; 289 int ret; 290 291 /* Find the clock within the devmap - it's a bit inefficent having a for 292 * loop for this, but this function should only called when a driver is 293 * being activated so IMHO not a big issue. 294 */ 295 clk_dev = ti_prcm_clk_dev(clk); 296 297 /* Sanity check we managed to find the clock */ 298 if (clk_dev == NULL) 299 return (EINVAL); 300 301 /* Get the source frequency of the clock */ 302 if (clk_dev->clk_get_source_freq) 303 ret = clk_dev->clk_get_source_freq(clk_dev, freq); 304 else 305 ret = EINVAL; 306 307 return (ret); 308 } 309