| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 1 | /* | 
|  | 2 | * omap_hwmod implementation for OMAP2/3/4 | 
|  | 3 | * | 
|  | 4 | * Copyright (C) 2009 Nokia Corporation | 
|  | 5 | * Paul Walmsley | 
|  | 6 | * With fixes and testing from Kevin Hilman | 
|  | 7 | * | 
|  | 8 | * Created in collaboration with (alphabetical order): Benoit Cousson, | 
|  | 9 | * Kevin Hilman, Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari | 
|  | 10 | * Poussa, Anand Sawant, Santosh Shilimkar, Richard Woodruff | 
|  | 11 | * | 
|  | 12 | * This program is free software; you can redistribute it and/or modify | 
|  | 13 | * it under the terms of the GNU General Public License version 2 as | 
|  | 14 | * published by the Free Software Foundation. | 
|  | 15 | * | 
|  | 16 | * This code manages "OMAP modules" (on-chip devices) and their | 
|  | 17 | * integration with Linux device driver and bus code. | 
|  | 18 | * | 
|  | 19 | * References: | 
|  | 20 | * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064) | 
|  | 21 | * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090) | 
|  | 22 | * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108) | 
|  | 23 | * - OMAP4430 Multimedia Device Silicon Revision 1.0 (SWPU140) | 
|  | 24 | * - Open Core Protocol Specification 2.2 | 
|  | 25 | * | 
|  | 26 | * To do: | 
|  | 27 | * - pin mux handling | 
|  | 28 | * - handle IO mapping | 
|  | 29 | * - bus throughput & module latency measurement code | 
|  | 30 | * | 
|  | 31 | * XXX add tests at the beginning of each function to ensure the hwmod is | 
|  | 32 | * in the appropriate state | 
|  | 33 | * XXX error return values should be checked to ensure that they are | 
|  | 34 | * appropriate | 
|  | 35 | */ | 
|  | 36 | #undef DEBUG | 
|  | 37 |  | 
|  | 38 | #include <linux/kernel.h> | 
|  | 39 | #include <linux/errno.h> | 
|  | 40 | #include <linux/io.h> | 
|  | 41 | #include <linux/clk.h> | 
|  | 42 | #include <linux/delay.h> | 
|  | 43 | #include <linux/err.h> | 
|  | 44 | #include <linux/list.h> | 
|  | 45 | #include <linux/mutex.h> | 
|  | 46 | #include <linux/bootmem.h> | 
|  | 47 |  | 
| Paul Walmsley | 6f8b7ff | 2009-12-08 16:33:16 -0700 | [diff] [blame] | 48 | #include <plat/common.h> | 
| Tony Lindgren | ce491cf | 2009-10-20 09:40:47 -0700 | [diff] [blame] | 49 | #include <plat/cpu.h> | 
|  | 50 | #include <plat/clockdomain.h> | 
|  | 51 | #include <plat/powerdomain.h> | 
|  | 52 | #include <plat/clock.h> | 
|  | 53 | #include <plat/omap_hwmod.h> | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 54 |  | 
|  | 55 | #include "cm.h" | 
|  | 56 |  | 
|  | 57 | /* Maximum microseconds to wait for OMAP module to reset */ | 
|  | 58 | #define MAX_MODULE_RESET_WAIT		10000 | 
|  | 59 |  | 
|  | 60 | /* Name of the OMAP hwmod for the MPU */ | 
|  | 61 | #define MPU_INITIATOR_NAME		"mpu_hwmod" | 
|  | 62 |  | 
|  | 63 | /* omap_hwmod_list contains all registered struct omap_hwmods */ | 
|  | 64 | static LIST_HEAD(omap_hwmod_list); | 
|  | 65 |  | 
|  | 66 | static DEFINE_MUTEX(omap_hwmod_mutex); | 
|  | 67 |  | 
|  | 68 | /* mpu_oh: used to add/remove MPU initiator from sleepdep list */ | 
|  | 69 | static struct omap_hwmod *mpu_oh; | 
|  | 70 |  | 
|  | 71 | /* inited: 0 if omap_hwmod_init() has not yet been called; 1 otherwise */ | 
|  | 72 | static u8 inited; | 
|  | 73 |  | 
|  | 74 |  | 
|  | 75 | /* Private functions */ | 
|  | 76 |  | 
|  | 77 | /** | 
|  | 78 | * _update_sysc_cache - return the module OCP_SYSCONFIG register, keep copy | 
|  | 79 | * @oh: struct omap_hwmod * | 
|  | 80 | * | 
|  | 81 | * Load the current value of the hwmod OCP_SYSCONFIG register into the | 
|  | 82 | * struct omap_hwmod for later use.  Returns -EINVAL if the hwmod has no | 
|  | 83 | * OCP_SYSCONFIG register or 0 upon success. | 
|  | 84 | */ | 
|  | 85 | static int _update_sysc_cache(struct omap_hwmod *oh) | 
|  | 86 | { | 
|  | 87 | if (!oh->sysconfig) { | 
|  | 88 | WARN(!oh->sysconfig, "omap_hwmod: %s: cannot read " | 
|  | 89 | "OCP_SYSCONFIG: not defined on hwmod\n", oh->name); | 
|  | 90 | return -EINVAL; | 
|  | 91 | } | 
|  | 92 |  | 
|  | 93 | /* XXX ensure module interface clock is up */ | 
|  | 94 |  | 
|  | 95 | oh->_sysc_cache = omap_hwmod_readl(oh, oh->sysconfig->sysc_offs); | 
|  | 96 |  | 
|  | 97 | oh->_int_flags |= _HWMOD_SYSCONFIG_LOADED; | 
|  | 98 |  | 
|  | 99 | return 0; | 
|  | 100 | } | 
|  | 101 |  | 
|  | 102 | /** | 
|  | 103 | * _write_sysconfig - write a value to the module's OCP_SYSCONFIG register | 
|  | 104 | * @v: OCP_SYSCONFIG value to write | 
|  | 105 | * @oh: struct omap_hwmod * | 
|  | 106 | * | 
|  | 107 | * Write @v into the module OCP_SYSCONFIG register, if it has one.  No | 
|  | 108 | * return value. | 
|  | 109 | */ | 
|  | 110 | static void _write_sysconfig(u32 v, struct omap_hwmod *oh) | 
|  | 111 | { | 
|  | 112 | if (!oh->sysconfig) { | 
|  | 113 | WARN(!oh->sysconfig, "omap_hwmod: %s: cannot write " | 
|  | 114 | "OCP_SYSCONFIG: not defined on hwmod\n", oh->name); | 
|  | 115 | return; | 
|  | 116 | } | 
|  | 117 |  | 
|  | 118 | /* XXX ensure module interface clock is up */ | 
|  | 119 |  | 
|  | 120 | if (oh->_sysc_cache != v) { | 
|  | 121 | oh->_sysc_cache = v; | 
|  | 122 | omap_hwmod_writel(v, oh, oh->sysconfig->sysc_offs); | 
|  | 123 | } | 
|  | 124 | } | 
|  | 125 |  | 
|  | 126 | /** | 
|  | 127 | * _set_master_standbymode: set the OCP_SYSCONFIG MIDLEMODE field in @v | 
|  | 128 | * @oh: struct omap_hwmod * | 
|  | 129 | * @standbymode: MIDLEMODE field bits | 
|  | 130 | * @v: pointer to register contents to modify | 
|  | 131 | * | 
|  | 132 | * Update the master standby mode bits in @v to be @standbymode for | 
|  | 133 | * the @oh hwmod.  Does not write to the hardware.  Returns -EINVAL | 
|  | 134 | * upon error or 0 upon success. | 
|  | 135 | */ | 
|  | 136 | static int _set_master_standbymode(struct omap_hwmod *oh, u8 standbymode, | 
|  | 137 | u32 *v) | 
|  | 138 | { | 
|  | 139 | if (!oh->sysconfig || | 
|  | 140 | !(oh->sysconfig->sysc_flags & SYSC_HAS_MIDLEMODE)) | 
|  | 141 | return -EINVAL; | 
|  | 142 |  | 
|  | 143 | *v &= ~SYSC_MIDLEMODE_MASK; | 
|  | 144 | *v |= __ffs(standbymode) << SYSC_MIDLEMODE_SHIFT; | 
|  | 145 |  | 
|  | 146 | return 0; | 
|  | 147 | } | 
|  | 148 |  | 
|  | 149 | /** | 
|  | 150 | * _set_slave_idlemode: set the OCP_SYSCONFIG SIDLEMODE field in @v | 
|  | 151 | * @oh: struct omap_hwmod * | 
|  | 152 | * @idlemode: SIDLEMODE field bits | 
|  | 153 | * @v: pointer to register contents to modify | 
|  | 154 | * | 
|  | 155 | * Update the slave idle mode bits in @v to be @idlemode for the @oh | 
|  | 156 | * hwmod.  Does not write to the hardware.  Returns -EINVAL upon error | 
|  | 157 | * or 0 upon success. | 
|  | 158 | */ | 
|  | 159 | static int _set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode, u32 *v) | 
|  | 160 | { | 
|  | 161 | if (!oh->sysconfig || | 
|  | 162 | !(oh->sysconfig->sysc_flags & SYSC_HAS_SIDLEMODE)) | 
|  | 163 | return -EINVAL; | 
|  | 164 |  | 
|  | 165 | *v &= ~SYSC_SIDLEMODE_MASK; | 
|  | 166 | *v |= __ffs(idlemode) << SYSC_SIDLEMODE_SHIFT; | 
|  | 167 |  | 
|  | 168 | return 0; | 
|  | 169 | } | 
|  | 170 |  | 
|  | 171 | /** | 
|  | 172 | * _set_clockactivity: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v | 
|  | 173 | * @oh: struct omap_hwmod * | 
|  | 174 | * @clockact: CLOCKACTIVITY field bits | 
|  | 175 | * @v: pointer to register contents to modify | 
|  | 176 | * | 
|  | 177 | * Update the clockactivity mode bits in @v to be @clockact for the | 
|  | 178 | * @oh hwmod.  Used for additional powersaving on some modules.  Does | 
|  | 179 | * not write to the hardware.  Returns -EINVAL upon error or 0 upon | 
|  | 180 | * success. | 
|  | 181 | */ | 
|  | 182 | static int _set_clockactivity(struct omap_hwmod *oh, u8 clockact, u32 *v) | 
|  | 183 | { | 
|  | 184 | if (!oh->sysconfig || | 
|  | 185 | !(oh->sysconfig->sysc_flags & SYSC_HAS_CLOCKACTIVITY)) | 
|  | 186 | return -EINVAL; | 
|  | 187 |  | 
|  | 188 | *v &= ~SYSC_CLOCKACTIVITY_MASK; | 
|  | 189 | *v |= clockact << SYSC_CLOCKACTIVITY_SHIFT; | 
|  | 190 |  | 
|  | 191 | return 0; | 
|  | 192 | } | 
|  | 193 |  | 
|  | 194 | /** | 
|  | 195 | * _set_softreset: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v | 
|  | 196 | * @oh: struct omap_hwmod * | 
|  | 197 | * @v: pointer to register contents to modify | 
|  | 198 | * | 
|  | 199 | * Set the SOFTRESET bit in @v for hwmod @oh.  Returns -EINVAL upon | 
|  | 200 | * error or 0 upon success. | 
|  | 201 | */ | 
|  | 202 | static int _set_softreset(struct omap_hwmod *oh, u32 *v) | 
|  | 203 | { | 
|  | 204 | if (!oh->sysconfig || | 
|  | 205 | !(oh->sysconfig->sysc_flags & SYSC_HAS_SOFTRESET)) | 
|  | 206 | return -EINVAL; | 
|  | 207 |  | 
|  | 208 | *v |= SYSC_SOFTRESET_MASK; | 
|  | 209 |  | 
|  | 210 | return 0; | 
|  | 211 | } | 
|  | 212 |  | 
|  | 213 | /** | 
| Paul Walmsley | 726072e | 2009-12-08 16:34:15 -0700 | [diff] [blame] | 214 | * _set_module_autoidle: set the OCP_SYSCONFIG AUTOIDLE field in @v | 
|  | 215 | * @oh: struct omap_hwmod * | 
|  | 216 | * @autoidle: desired AUTOIDLE bitfield value (0 or 1) | 
|  | 217 | * @v: pointer to register contents to modify | 
|  | 218 | * | 
|  | 219 | * Update the module autoidle bit in @v to be @autoidle for the @oh | 
|  | 220 | * hwmod.  The autoidle bit controls whether the module can gate | 
|  | 221 | * internal clocks automatically when it isn't doing anything; the | 
|  | 222 | * exact function of this bit varies on a per-module basis.  This | 
|  | 223 | * function does not write to the hardware.  Returns -EINVAL upon | 
|  | 224 | * error or 0 upon success. | 
|  | 225 | */ | 
|  | 226 | static int _set_module_autoidle(struct omap_hwmod *oh, u8 autoidle, | 
|  | 227 | u32 *v) | 
|  | 228 | { | 
|  | 229 | if (!oh->sysconfig || | 
|  | 230 | !(oh->sysconfig->sysc_flags & SYSC_HAS_AUTOIDLE)) | 
|  | 231 | return -EINVAL; | 
|  | 232 |  | 
|  | 233 | *v &= ~SYSC_AUTOIDLE_MASK; | 
|  | 234 | *v |= autoidle << SYSC_AUTOIDLE_SHIFT; | 
|  | 235 |  | 
|  | 236 | return 0; | 
|  | 237 | } | 
|  | 238 |  | 
|  | 239 | /** | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 240 | * _enable_wakeup: set OCP_SYSCONFIG.ENAWAKEUP bit in the hardware | 
|  | 241 | * @oh: struct omap_hwmod * | 
|  | 242 | * | 
|  | 243 | * Allow the hardware module @oh to send wakeups.  Returns -EINVAL | 
|  | 244 | * upon error or 0 upon success. | 
|  | 245 | */ | 
|  | 246 | static int _enable_wakeup(struct omap_hwmod *oh) | 
|  | 247 | { | 
|  | 248 | u32 v; | 
|  | 249 |  | 
|  | 250 | if (!oh->sysconfig || | 
|  | 251 | !(oh->sysconfig->sysc_flags & SYSC_HAS_ENAWAKEUP)) | 
|  | 252 | return -EINVAL; | 
|  | 253 |  | 
|  | 254 | v = oh->_sysc_cache; | 
|  | 255 | v |= SYSC_ENAWAKEUP_MASK; | 
|  | 256 | _write_sysconfig(v, oh); | 
|  | 257 |  | 
|  | 258 | /* XXX test pwrdm_get_wken for this hwmod's subsystem */ | 
|  | 259 |  | 
|  | 260 | oh->_int_flags |= _HWMOD_WAKEUP_ENABLED; | 
|  | 261 |  | 
|  | 262 | return 0; | 
|  | 263 | } | 
|  | 264 |  | 
|  | 265 | /** | 
|  | 266 | * _disable_wakeup: clear OCP_SYSCONFIG.ENAWAKEUP bit in the hardware | 
|  | 267 | * @oh: struct omap_hwmod * | 
|  | 268 | * | 
|  | 269 | * Prevent the hardware module @oh to send wakeups.  Returns -EINVAL | 
|  | 270 | * upon error or 0 upon success. | 
|  | 271 | */ | 
|  | 272 | static int _disable_wakeup(struct omap_hwmod *oh) | 
|  | 273 | { | 
|  | 274 | u32 v; | 
|  | 275 |  | 
|  | 276 | if (!oh->sysconfig || | 
|  | 277 | !(oh->sysconfig->sysc_flags & SYSC_HAS_ENAWAKEUP)) | 
|  | 278 | return -EINVAL; | 
|  | 279 |  | 
|  | 280 | v = oh->_sysc_cache; | 
|  | 281 | v &= ~SYSC_ENAWAKEUP_MASK; | 
|  | 282 | _write_sysconfig(v, oh); | 
|  | 283 |  | 
|  | 284 | /* XXX test pwrdm_get_wken for this hwmod's subsystem */ | 
|  | 285 |  | 
|  | 286 | oh->_int_flags &= ~_HWMOD_WAKEUP_ENABLED; | 
|  | 287 |  | 
|  | 288 | return 0; | 
|  | 289 | } | 
|  | 290 |  | 
|  | 291 | /** | 
|  | 292 | * _add_initiator_dep: prevent @oh from smart-idling while @init_oh is active | 
|  | 293 | * @oh: struct omap_hwmod * | 
|  | 294 | * | 
|  | 295 | * Prevent the hardware module @oh from entering idle while the | 
|  | 296 | * hardare module initiator @init_oh is active.  Useful when a module | 
|  | 297 | * will be accessed by a particular initiator (e.g., if a module will | 
|  | 298 | * be accessed by the IVA, there should be a sleepdep between the IVA | 
|  | 299 | * initiator and the module).  Only applies to modules in smart-idle | 
|  | 300 | * mode.  Returns -EINVAL upon error or passes along | 
|  | 301 | * pwrdm_add_sleepdep() value upon success. | 
|  | 302 | */ | 
|  | 303 | static int _add_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) | 
|  | 304 | { | 
|  | 305 | if (!oh->_clk) | 
|  | 306 | return -EINVAL; | 
|  | 307 |  | 
|  | 308 | return pwrdm_add_sleepdep(oh->_clk->clkdm->pwrdm.ptr, | 
|  | 309 | init_oh->_clk->clkdm->pwrdm.ptr); | 
|  | 310 | } | 
|  | 311 |  | 
|  | 312 | /** | 
|  | 313 | * _del_initiator_dep: allow @oh to smart-idle even if @init_oh is active | 
|  | 314 | * @oh: struct omap_hwmod * | 
|  | 315 | * | 
|  | 316 | * Allow the hardware module @oh to enter idle while the hardare | 
|  | 317 | * module initiator @init_oh is active.  Useful when a module will not | 
|  | 318 | * be accessed by a particular initiator (e.g., if a module will not | 
|  | 319 | * be accessed by the IVA, there should be no sleepdep between the IVA | 
|  | 320 | * initiator and the module).  Only applies to modules in smart-idle | 
|  | 321 | * mode.  Returns -EINVAL upon error or passes along | 
|  | 322 | * pwrdm_add_sleepdep() value upon success. | 
|  | 323 | */ | 
|  | 324 | static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh) | 
|  | 325 | { | 
|  | 326 | if (!oh->_clk) | 
|  | 327 | return -EINVAL; | 
|  | 328 |  | 
|  | 329 | return pwrdm_del_sleepdep(oh->_clk->clkdm->pwrdm.ptr, | 
|  | 330 | init_oh->_clk->clkdm->pwrdm.ptr); | 
|  | 331 | } | 
|  | 332 |  | 
|  | 333 | /** | 
|  | 334 | * _init_main_clk - get a struct clk * for the the hwmod's main functional clk | 
|  | 335 | * @oh: struct omap_hwmod * | 
|  | 336 | * | 
|  | 337 | * Called from _init_clocks().  Populates the @oh _clk (main | 
|  | 338 | * functional clock pointer) if a main_clk is present.  Returns 0 on | 
|  | 339 | * success or -EINVAL on error. | 
|  | 340 | */ | 
|  | 341 | static int _init_main_clk(struct omap_hwmod *oh) | 
|  | 342 | { | 
|  | 343 | struct clk *c; | 
|  | 344 | int ret = 0; | 
|  | 345 |  | 
|  | 346 | if (!oh->clkdev_con_id) | 
|  | 347 | return 0; | 
|  | 348 |  | 
|  | 349 | c = clk_get_sys(oh->clkdev_dev_id, oh->clkdev_con_id); | 
|  | 350 | WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get main_clk %s.%s\n", | 
|  | 351 | oh->name, oh->clkdev_dev_id, oh->clkdev_con_id); | 
|  | 352 | if (IS_ERR(c)) | 
|  | 353 | ret = -EINVAL; | 
|  | 354 | oh->_clk = c; | 
|  | 355 |  | 
| Kevin Hilman | 81d7c6f | 2009-12-08 16:34:24 -0700 | [diff] [blame] | 356 | WARN(!c->clkdm, "omap_hwmod: %s: missing clockdomain for %s.\n", | 
|  | 357 | oh->clkdev_con_id, c->name); | 
|  | 358 |  | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 359 | return ret; | 
|  | 360 | } | 
|  | 361 |  | 
|  | 362 | /** | 
|  | 363 | * _init_interface_clk - get a struct clk * for the the hwmod's interface clks | 
|  | 364 | * @oh: struct omap_hwmod * | 
|  | 365 | * | 
|  | 366 | * Called from _init_clocks().  Populates the @oh OCP slave interface | 
|  | 367 | * clock pointers.  Returns 0 on success or -EINVAL on error. | 
|  | 368 | */ | 
|  | 369 | static int _init_interface_clks(struct omap_hwmod *oh) | 
|  | 370 | { | 
|  | 371 | struct omap_hwmod_ocp_if *os; | 
|  | 372 | struct clk *c; | 
|  | 373 | int i; | 
|  | 374 | int ret = 0; | 
|  | 375 |  | 
|  | 376 | if (oh->slaves_cnt == 0) | 
|  | 377 | return 0; | 
|  | 378 |  | 
|  | 379 | for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) { | 
|  | 380 | if (!os->clkdev_con_id) | 
|  | 381 | continue; | 
|  | 382 |  | 
|  | 383 | c = clk_get_sys(os->clkdev_dev_id, os->clkdev_con_id); | 
|  | 384 | WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get " | 
|  | 385 | "interface_clk %s.%s\n", oh->name, | 
|  | 386 | os->clkdev_dev_id, os->clkdev_con_id); | 
|  | 387 | if (IS_ERR(c)) | 
|  | 388 | ret = -EINVAL; | 
|  | 389 | os->_clk = c; | 
|  | 390 | } | 
|  | 391 |  | 
|  | 392 | return ret; | 
|  | 393 | } | 
|  | 394 |  | 
|  | 395 | /** | 
|  | 396 | * _init_opt_clk - get a struct clk * for the the hwmod's optional clocks | 
|  | 397 | * @oh: struct omap_hwmod * | 
|  | 398 | * | 
|  | 399 | * Called from _init_clocks().  Populates the @oh omap_hwmod_opt_clk | 
|  | 400 | * clock pointers.  Returns 0 on success or -EINVAL on error. | 
|  | 401 | */ | 
|  | 402 | static int _init_opt_clks(struct omap_hwmod *oh) | 
|  | 403 | { | 
|  | 404 | struct omap_hwmod_opt_clk *oc; | 
|  | 405 | struct clk *c; | 
|  | 406 | int i; | 
|  | 407 | int ret = 0; | 
|  | 408 |  | 
|  | 409 | for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) { | 
|  | 410 | c = clk_get_sys(oc->clkdev_dev_id, oc->clkdev_con_id); | 
|  | 411 | WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get opt_clk " | 
|  | 412 | "%s.%s\n", oh->name, oc->clkdev_dev_id, | 
|  | 413 | oc->clkdev_con_id); | 
|  | 414 | if (IS_ERR(c)) | 
|  | 415 | ret = -EINVAL; | 
|  | 416 | oc->_clk = c; | 
|  | 417 | } | 
|  | 418 |  | 
|  | 419 | return ret; | 
|  | 420 | } | 
|  | 421 |  | 
|  | 422 | /** | 
|  | 423 | * _enable_clocks - enable hwmod main clock and interface clocks | 
|  | 424 | * @oh: struct omap_hwmod * | 
|  | 425 | * | 
|  | 426 | * Enables all clocks necessary for register reads and writes to succeed | 
|  | 427 | * on the hwmod @oh.  Returns 0. | 
|  | 428 | */ | 
|  | 429 | static int _enable_clocks(struct omap_hwmod *oh) | 
|  | 430 | { | 
|  | 431 | struct omap_hwmod_ocp_if *os; | 
|  | 432 | int i; | 
|  | 433 |  | 
|  | 434 | pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name); | 
|  | 435 |  | 
|  | 436 | if (oh->_clk && !IS_ERR(oh->_clk)) | 
|  | 437 | clk_enable(oh->_clk); | 
|  | 438 |  | 
|  | 439 | if (oh->slaves_cnt > 0) { | 
|  | 440 | for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) { | 
|  | 441 | struct clk *c = os->_clk; | 
|  | 442 |  | 
|  | 443 | if (c && !IS_ERR(c) && (os->flags & OCPIF_SWSUP_IDLE)) | 
|  | 444 | clk_enable(c); | 
|  | 445 | } | 
|  | 446 | } | 
|  | 447 |  | 
|  | 448 | /* The opt clocks are controlled by the device driver. */ | 
|  | 449 |  | 
|  | 450 | return 0; | 
|  | 451 | } | 
|  | 452 |  | 
|  | 453 | /** | 
|  | 454 | * _disable_clocks - disable hwmod main clock and interface clocks | 
|  | 455 | * @oh: struct omap_hwmod * | 
|  | 456 | * | 
|  | 457 | * Disables the hwmod @oh main functional and interface clocks.  Returns 0. | 
|  | 458 | */ | 
|  | 459 | static int _disable_clocks(struct omap_hwmod *oh) | 
|  | 460 | { | 
|  | 461 | struct omap_hwmod_ocp_if *os; | 
|  | 462 | int i; | 
|  | 463 |  | 
|  | 464 | pr_debug("omap_hwmod: %s: disabling clocks\n", oh->name); | 
|  | 465 |  | 
|  | 466 | if (oh->_clk && !IS_ERR(oh->_clk)) | 
|  | 467 | clk_disable(oh->_clk); | 
|  | 468 |  | 
|  | 469 | if (oh->slaves_cnt > 0) { | 
|  | 470 | for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) { | 
|  | 471 | struct clk *c = os->_clk; | 
|  | 472 |  | 
|  | 473 | if (c && !IS_ERR(c) && (os->flags & OCPIF_SWSUP_IDLE)) | 
|  | 474 | clk_disable(c); | 
|  | 475 | } | 
|  | 476 | } | 
|  | 477 |  | 
|  | 478 | /* The opt clocks are controlled by the device driver. */ | 
|  | 479 |  | 
|  | 480 | return 0; | 
|  | 481 | } | 
|  | 482 |  | 
|  | 483 | /** | 
|  | 484 | * _find_mpu_port_index - find hwmod OCP slave port ID intended for MPU use | 
|  | 485 | * @oh: struct omap_hwmod * | 
|  | 486 | * | 
|  | 487 | * Returns the array index of the OCP slave port that the MPU | 
|  | 488 | * addresses the device on, or -EINVAL upon error or not found. | 
|  | 489 | */ | 
|  | 490 | static int _find_mpu_port_index(struct omap_hwmod *oh) | 
|  | 491 | { | 
|  | 492 | struct omap_hwmod_ocp_if *os; | 
|  | 493 | int i; | 
|  | 494 | int found = 0; | 
|  | 495 |  | 
|  | 496 | if (!oh || oh->slaves_cnt == 0) | 
|  | 497 | return -EINVAL; | 
|  | 498 |  | 
|  | 499 | for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) { | 
|  | 500 | if (os->user & OCP_USER_MPU) { | 
|  | 501 | found = 1; | 
|  | 502 | break; | 
|  | 503 | } | 
|  | 504 | } | 
|  | 505 |  | 
|  | 506 | if (found) | 
|  | 507 | pr_debug("omap_hwmod: %s: MPU OCP slave port ID  %d\n", | 
|  | 508 | oh->name, i); | 
|  | 509 | else | 
|  | 510 | pr_debug("omap_hwmod: %s: no MPU OCP slave port found\n", | 
|  | 511 | oh->name); | 
|  | 512 |  | 
|  | 513 | return (found) ? i : -EINVAL; | 
|  | 514 | } | 
|  | 515 |  | 
|  | 516 | /** | 
|  | 517 | * _find_mpu_rt_base - find hwmod register target base addr accessible by MPU | 
|  | 518 | * @oh: struct omap_hwmod * | 
|  | 519 | * | 
|  | 520 | * Return the virtual address of the base of the register target of | 
|  | 521 | * device @oh, or NULL on error. | 
|  | 522 | */ | 
|  | 523 | static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index) | 
|  | 524 | { | 
|  | 525 | struct omap_hwmod_ocp_if *os; | 
|  | 526 | struct omap_hwmod_addr_space *mem; | 
|  | 527 | int i; | 
|  | 528 | int found = 0; | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 529 | void __iomem *va_start; | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 530 |  | 
|  | 531 | if (!oh || oh->slaves_cnt == 0) | 
|  | 532 | return NULL; | 
|  | 533 |  | 
|  | 534 | os = *oh->slaves + index; | 
|  | 535 |  | 
|  | 536 | for (i = 0, mem = os->addr; i < os->addr_cnt; i++, mem++) { | 
|  | 537 | if (mem->flags & ADDR_TYPE_RT) { | 
|  | 538 | found = 1; | 
|  | 539 | break; | 
|  | 540 | } | 
|  | 541 | } | 
|  | 542 |  | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 543 | if (found) { | 
|  | 544 | va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start); | 
|  | 545 | if (!va_start) { | 
|  | 546 | pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); | 
|  | 547 | return NULL; | 
|  | 548 | } | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 549 | pr_debug("omap_hwmod: %s: MPU register target at va %p\n", | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 550 | oh->name, va_start); | 
|  | 551 | } else { | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 552 | pr_debug("omap_hwmod: %s: no MPU register target found\n", | 
|  | 553 | oh->name); | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 554 | } | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 555 |  | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 556 | return (found) ? va_start : NULL; | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 557 | } | 
|  | 558 |  | 
|  | 559 | /** | 
|  | 560 | * _sysc_enable - try to bring a module out of idle via OCP_SYSCONFIG | 
|  | 561 | * @oh: struct omap_hwmod * | 
|  | 562 | * | 
|  | 563 | * If module is marked as SWSUP_SIDLE, force the module out of slave | 
|  | 564 | * idle; otherwise, configure it for smart-idle.  If module is marked | 
|  | 565 | * as SWSUP_MSUSPEND, force the module out of master standby; | 
|  | 566 | * otherwise, configure it for smart-standby.  No return value. | 
|  | 567 | */ | 
|  | 568 | static void _sysc_enable(struct omap_hwmod *oh) | 
|  | 569 | { | 
|  | 570 | u8 idlemode; | 
|  | 571 | u32 v; | 
|  | 572 |  | 
|  | 573 | if (!oh->sysconfig) | 
|  | 574 | return; | 
|  | 575 |  | 
|  | 576 | v = oh->_sysc_cache; | 
|  | 577 |  | 
|  | 578 | if (oh->sysconfig->sysc_flags & SYSC_HAS_SIDLEMODE) { | 
|  | 579 | idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ? | 
|  | 580 | HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART; | 
|  | 581 | _set_slave_idlemode(oh, idlemode, &v); | 
|  | 582 | } | 
|  | 583 |  | 
|  | 584 | if (oh->sysconfig->sysc_flags & SYSC_HAS_MIDLEMODE) { | 
|  | 585 | idlemode = (oh->flags & HWMOD_SWSUP_MSTANDBY) ? | 
|  | 586 | HWMOD_IDLEMODE_NO : HWMOD_IDLEMODE_SMART; | 
|  | 587 | _set_master_standbymode(oh, idlemode, &v); | 
|  | 588 | } | 
|  | 589 |  | 
| Paul Walmsley | 726072e | 2009-12-08 16:34:15 -0700 | [diff] [blame] | 590 | if (oh->sysconfig->sysc_flags & SYSC_HAS_AUTOIDLE) { | 
|  | 591 | idlemode = (oh->flags & HWMOD_NO_OCP_AUTOIDLE) ? | 
|  | 592 | 0 : 1; | 
|  | 593 | _set_module_autoidle(oh, idlemode, &v); | 
|  | 594 | } | 
|  | 595 |  | 
|  | 596 | /* XXX OCP ENAWAKEUP bit? */ | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 597 |  | 
| Paul Walmsley | a16b1f7 | 2009-12-08 16:34:17 -0700 | [diff] [blame] | 598 | /* | 
|  | 599 | * XXX The clock framework should handle this, by | 
|  | 600 | * calling into this code.  But this must wait until the | 
|  | 601 | * clock structures are tagged with omap_hwmod entries | 
|  | 602 | */ | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 603 | if (oh->flags & HWMOD_SET_DEFAULT_CLOCKACT && | 
|  | 604 | oh->sysconfig->sysc_flags & SYSC_HAS_CLOCKACTIVITY) | 
|  | 605 | _set_clockactivity(oh, oh->sysconfig->clockact, &v); | 
|  | 606 |  | 
|  | 607 | _write_sysconfig(v, oh); | 
|  | 608 | } | 
|  | 609 |  | 
|  | 610 | /** | 
|  | 611 | * _sysc_idle - try to put a module into idle via OCP_SYSCONFIG | 
|  | 612 | * @oh: struct omap_hwmod * | 
|  | 613 | * | 
|  | 614 | * If module is marked as SWSUP_SIDLE, force the module into slave | 
|  | 615 | * idle; otherwise, configure it for smart-idle.  If module is marked | 
|  | 616 | * as SWSUP_MSUSPEND, force the module into master standby; otherwise, | 
|  | 617 | * configure it for smart-standby.  No return value. | 
|  | 618 | */ | 
|  | 619 | static void _sysc_idle(struct omap_hwmod *oh) | 
|  | 620 | { | 
|  | 621 | u8 idlemode; | 
|  | 622 | u32 v; | 
|  | 623 |  | 
|  | 624 | if (!oh->sysconfig) | 
|  | 625 | return; | 
|  | 626 |  | 
|  | 627 | v = oh->_sysc_cache; | 
|  | 628 |  | 
|  | 629 | if (oh->sysconfig->sysc_flags & SYSC_HAS_SIDLEMODE) { | 
|  | 630 | idlemode = (oh->flags & HWMOD_SWSUP_SIDLE) ? | 
|  | 631 | HWMOD_IDLEMODE_FORCE : HWMOD_IDLEMODE_SMART; | 
|  | 632 | _set_slave_idlemode(oh, idlemode, &v); | 
|  | 633 | } | 
|  | 634 |  | 
|  | 635 | if (oh->sysconfig->sysc_flags & SYSC_HAS_MIDLEMODE) { | 
|  | 636 | idlemode = (oh->flags & HWMOD_SWSUP_MSTANDBY) ? | 
|  | 637 | HWMOD_IDLEMODE_FORCE : HWMOD_IDLEMODE_SMART; | 
|  | 638 | _set_master_standbymode(oh, idlemode, &v); | 
|  | 639 | } | 
|  | 640 |  | 
|  | 641 | _write_sysconfig(v, oh); | 
|  | 642 | } | 
|  | 643 |  | 
|  | 644 | /** | 
|  | 645 | * _sysc_shutdown - force a module into idle via OCP_SYSCONFIG | 
|  | 646 | * @oh: struct omap_hwmod * | 
|  | 647 | * | 
|  | 648 | * Force the module into slave idle and master suspend. No return | 
|  | 649 | * value. | 
|  | 650 | */ | 
|  | 651 | static void _sysc_shutdown(struct omap_hwmod *oh) | 
|  | 652 | { | 
|  | 653 | u32 v; | 
|  | 654 |  | 
|  | 655 | if (!oh->sysconfig) | 
|  | 656 | return; | 
|  | 657 |  | 
|  | 658 | v = oh->_sysc_cache; | 
|  | 659 |  | 
|  | 660 | if (oh->sysconfig->sysc_flags & SYSC_HAS_SIDLEMODE) | 
|  | 661 | _set_slave_idlemode(oh, HWMOD_IDLEMODE_FORCE, &v); | 
|  | 662 |  | 
|  | 663 | if (oh->sysconfig->sysc_flags & SYSC_HAS_MIDLEMODE) | 
|  | 664 | _set_master_standbymode(oh, HWMOD_IDLEMODE_FORCE, &v); | 
|  | 665 |  | 
| Paul Walmsley | 726072e | 2009-12-08 16:34:15 -0700 | [diff] [blame] | 666 | if (oh->sysconfig->sysc_flags & SYSC_HAS_AUTOIDLE) | 
|  | 667 | _set_module_autoidle(oh, 1, &v); | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 668 |  | 
|  | 669 | _write_sysconfig(v, oh); | 
|  | 670 | } | 
|  | 671 |  | 
|  | 672 | /** | 
|  | 673 | * _lookup - find an omap_hwmod by name | 
|  | 674 | * @name: find an omap_hwmod by name | 
|  | 675 | * | 
|  | 676 | * Return a pointer to an omap_hwmod by name, or NULL if not found. | 
|  | 677 | * Caller must hold omap_hwmod_mutex. | 
|  | 678 | */ | 
|  | 679 | static struct omap_hwmod *_lookup(const char *name) | 
|  | 680 | { | 
|  | 681 | struct omap_hwmod *oh, *temp_oh; | 
|  | 682 |  | 
|  | 683 | oh = NULL; | 
|  | 684 |  | 
|  | 685 | list_for_each_entry(temp_oh, &omap_hwmod_list, node) { | 
|  | 686 | if (!strcmp(name, temp_oh->name)) { | 
|  | 687 | oh = temp_oh; | 
|  | 688 | break; | 
|  | 689 | } | 
|  | 690 | } | 
|  | 691 |  | 
|  | 692 | return oh; | 
|  | 693 | } | 
|  | 694 |  | 
|  | 695 | /** | 
|  | 696 | * _init_clocks - clk_get() all clocks associated with this hwmod | 
|  | 697 | * @oh: struct omap_hwmod * | 
|  | 698 | * | 
|  | 699 | * Called by omap_hwmod_late_init() (after omap2_clk_init()). | 
|  | 700 | * Resolves all clock names embedded in the hwmod.  Must be called | 
|  | 701 | * with omap_hwmod_mutex held.  Returns -EINVAL if the omap_hwmod | 
|  | 702 | * has not yet been registered or if the clocks have already been | 
|  | 703 | * initialized, 0 on success, or a non-zero error on failure. | 
|  | 704 | */ | 
|  | 705 | static int _init_clocks(struct omap_hwmod *oh) | 
|  | 706 | { | 
|  | 707 | int ret = 0; | 
|  | 708 |  | 
|  | 709 | if (!oh || (oh->_state != _HWMOD_STATE_REGISTERED)) | 
|  | 710 | return -EINVAL; | 
|  | 711 |  | 
|  | 712 | pr_debug("omap_hwmod: %s: looking up clocks\n", oh->name); | 
|  | 713 |  | 
|  | 714 | ret |= _init_main_clk(oh); | 
|  | 715 | ret |= _init_interface_clks(oh); | 
|  | 716 | ret |= _init_opt_clks(oh); | 
|  | 717 |  | 
|  | 718 | oh->_state = _HWMOD_STATE_CLKS_INITED; | 
|  | 719 |  | 
|  | 720 | return ret; | 
|  | 721 | } | 
|  | 722 |  | 
|  | 723 | /** | 
|  | 724 | * _wait_target_ready - wait for a module to leave slave idle | 
|  | 725 | * @oh: struct omap_hwmod * | 
|  | 726 | * | 
|  | 727 | * Wait for a module @oh to leave slave idle.  Returns 0 if the module | 
|  | 728 | * does not have an IDLEST bit or if the module successfully leaves | 
|  | 729 | * slave idle; otherwise, pass along the return value of the | 
|  | 730 | * appropriate *_cm_wait_module_ready() function. | 
|  | 731 | */ | 
|  | 732 | static int _wait_target_ready(struct omap_hwmod *oh) | 
|  | 733 | { | 
|  | 734 | struct omap_hwmod_ocp_if *os; | 
|  | 735 | int ret; | 
|  | 736 |  | 
|  | 737 | if (!oh) | 
|  | 738 | return -EINVAL; | 
|  | 739 |  | 
|  | 740 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT) | 
|  | 741 | return 0; | 
|  | 742 |  | 
|  | 743 | os = *oh->slaves + oh->_mpu_port_index; | 
|  | 744 |  | 
|  | 745 | if (!(os->flags & OCPIF_HAS_IDLEST)) | 
|  | 746 | return 0; | 
|  | 747 |  | 
|  | 748 | /* XXX check module SIDLEMODE */ | 
|  | 749 |  | 
|  | 750 | /* XXX check clock enable states */ | 
|  | 751 |  | 
|  | 752 | if (cpu_is_omap24xx() || cpu_is_omap34xx()) { | 
|  | 753 | ret = omap2_cm_wait_module_ready(oh->prcm.omap2.module_offs, | 
|  | 754 | oh->prcm.omap2.idlest_reg_id, | 
|  | 755 | oh->prcm.omap2.idlest_idle_bit); | 
|  | 756 | #if 0 | 
|  | 757 | } else if (cpu_is_omap44xx()) { | 
|  | 758 | ret = omap4_cm_wait_module_ready(oh->prcm.omap4.module_offs, | 
|  | 759 | oh->prcm.omap4.device_offs); | 
|  | 760 | #endif | 
|  | 761 | } else { | 
|  | 762 | BUG(); | 
|  | 763 | }; | 
|  | 764 |  | 
|  | 765 | return ret; | 
|  | 766 | } | 
|  | 767 |  | 
|  | 768 | /** | 
|  | 769 | * _reset - reset an omap_hwmod | 
|  | 770 | * @oh: struct omap_hwmod * | 
|  | 771 | * | 
|  | 772 | * Resets an omap_hwmod @oh via the OCP_SYSCONFIG bit.  hwmod must be | 
|  | 773 | * enabled for this to work.  Must be called with omap_hwmod_mutex | 
|  | 774 | * held.  Returns -EINVAL if the hwmod cannot be reset this way or if | 
|  | 775 | * the hwmod is in the wrong state, -ETIMEDOUT if the module did not | 
|  | 776 | * reset in time, or 0 upon success. | 
|  | 777 | */ | 
|  | 778 | static int _reset(struct omap_hwmod *oh) | 
|  | 779 | { | 
|  | 780 | u32 r, v; | 
| Paul Walmsley | 6f8b7ff | 2009-12-08 16:33:16 -0700 | [diff] [blame] | 781 | int c = 0; | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 782 |  | 
|  | 783 | if (!oh->sysconfig || | 
|  | 784 | !(oh->sysconfig->sysc_flags & SYSC_HAS_SOFTRESET) || | 
|  | 785 | (oh->sysconfig->sysc_flags & SYSS_MISSING)) | 
|  | 786 | return -EINVAL; | 
|  | 787 |  | 
|  | 788 | /* clocks must be on for this operation */ | 
|  | 789 | if (oh->_state != _HWMOD_STATE_ENABLED) { | 
|  | 790 | WARN(1, "omap_hwmod: %s: reset can only be entered from " | 
|  | 791 | "enabled state\n", oh->name); | 
|  | 792 | return -EINVAL; | 
|  | 793 | } | 
|  | 794 |  | 
|  | 795 | pr_debug("omap_hwmod: %s: resetting\n", oh->name); | 
|  | 796 |  | 
|  | 797 | v = oh->_sysc_cache; | 
|  | 798 | r = _set_softreset(oh, &v); | 
|  | 799 | if (r) | 
|  | 800 | return r; | 
|  | 801 | _write_sysconfig(v, oh); | 
|  | 802 |  | 
| Paul Walmsley | 6f8b7ff | 2009-12-08 16:33:16 -0700 | [diff] [blame] | 803 | omap_test_timeout((omap_hwmod_readl(oh, oh->sysconfig->syss_offs) & | 
|  | 804 | SYSS_RESETDONE_MASK), | 
|  | 805 | MAX_MODULE_RESET_WAIT, c); | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 806 |  | 
|  | 807 | if (c == MAX_MODULE_RESET_WAIT) | 
|  | 808 | WARN(1, "omap_hwmod: %s: failed to reset in %d usec\n", | 
|  | 809 | oh->name, MAX_MODULE_RESET_WAIT); | 
|  | 810 | else | 
| Paul Walmsley | 02bfc03 | 2009-09-03 20:14:05 +0300 | [diff] [blame] | 811 | pr_debug("omap_hwmod: %s: reset in %d usec\n", oh->name, c); | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 812 |  | 
|  | 813 | /* | 
|  | 814 | * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from | 
|  | 815 | * _wait_target_ready() or _reset() | 
|  | 816 | */ | 
|  | 817 |  | 
|  | 818 | return (c == MAX_MODULE_RESET_WAIT) ? -ETIMEDOUT : 0; | 
|  | 819 | } | 
|  | 820 |  | 
|  | 821 | /** | 
|  | 822 | * _enable - enable an omap_hwmod | 
|  | 823 | * @oh: struct omap_hwmod * | 
|  | 824 | * | 
|  | 825 | * Enables an omap_hwmod @oh such that the MPU can access the hwmod's | 
|  | 826 | * register target.  Must be called with omap_hwmod_mutex held. | 
|  | 827 | * Returns -EINVAL if the hwmod is in the wrong state or passes along | 
|  | 828 | * the return value of _wait_target_ready(). | 
|  | 829 | */ | 
|  | 830 | static int _enable(struct omap_hwmod *oh) | 
|  | 831 | { | 
|  | 832 | int r; | 
|  | 833 |  | 
|  | 834 | if (oh->_state != _HWMOD_STATE_INITIALIZED && | 
|  | 835 | oh->_state != _HWMOD_STATE_IDLE && | 
|  | 836 | oh->_state != _HWMOD_STATE_DISABLED) { | 
|  | 837 | WARN(1, "omap_hwmod: %s: enabled state can only be entered " | 
|  | 838 | "from initialized, idle, or disabled state\n", oh->name); | 
|  | 839 | return -EINVAL; | 
|  | 840 | } | 
|  | 841 |  | 
|  | 842 | pr_debug("omap_hwmod: %s: enabling\n", oh->name); | 
|  | 843 |  | 
|  | 844 | /* XXX mux balls */ | 
|  | 845 |  | 
|  | 846 | _add_initiator_dep(oh, mpu_oh); | 
|  | 847 | _enable_clocks(oh); | 
|  | 848 |  | 
|  | 849 | if (oh->sysconfig) { | 
|  | 850 | if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED)) | 
|  | 851 | _update_sysc_cache(oh); | 
|  | 852 | _sysc_enable(oh); | 
|  | 853 | } | 
|  | 854 |  | 
|  | 855 | r = _wait_target_ready(oh); | 
|  | 856 | if (!r) | 
|  | 857 | oh->_state = _HWMOD_STATE_ENABLED; | 
|  | 858 |  | 
|  | 859 | return r; | 
|  | 860 | } | 
|  | 861 |  | 
|  | 862 | /** | 
|  | 863 | * _idle - idle an omap_hwmod | 
|  | 864 | * @oh: struct omap_hwmod * | 
|  | 865 | * | 
|  | 866 | * Idles an omap_hwmod @oh.  This should be called once the hwmod has | 
|  | 867 | * no further work.  Returns -EINVAL if the hwmod is in the wrong | 
|  | 868 | * state or returns 0. | 
|  | 869 | */ | 
|  | 870 | static int _idle(struct omap_hwmod *oh) | 
|  | 871 | { | 
|  | 872 | if (oh->_state != _HWMOD_STATE_ENABLED) { | 
|  | 873 | WARN(1, "omap_hwmod: %s: idle state can only be entered from " | 
|  | 874 | "enabled state\n", oh->name); | 
|  | 875 | return -EINVAL; | 
|  | 876 | } | 
|  | 877 |  | 
|  | 878 | pr_debug("omap_hwmod: %s: idling\n", oh->name); | 
|  | 879 |  | 
|  | 880 | if (oh->sysconfig) | 
|  | 881 | _sysc_idle(oh); | 
|  | 882 | _del_initiator_dep(oh, mpu_oh); | 
|  | 883 | _disable_clocks(oh); | 
|  | 884 |  | 
|  | 885 | oh->_state = _HWMOD_STATE_IDLE; | 
|  | 886 |  | 
|  | 887 | return 0; | 
|  | 888 | } | 
|  | 889 |  | 
|  | 890 | /** | 
|  | 891 | * _shutdown - shutdown an omap_hwmod | 
|  | 892 | * @oh: struct omap_hwmod * | 
|  | 893 | * | 
|  | 894 | * Shut down an omap_hwmod @oh.  This should be called when the driver | 
|  | 895 | * used for the hwmod is removed or unloaded or if the driver is not | 
|  | 896 | * used by the system.  Returns -EINVAL if the hwmod is in the wrong | 
|  | 897 | * state or returns 0. | 
|  | 898 | */ | 
|  | 899 | static int _shutdown(struct omap_hwmod *oh) | 
|  | 900 | { | 
|  | 901 | if (oh->_state != _HWMOD_STATE_IDLE && | 
|  | 902 | oh->_state != _HWMOD_STATE_ENABLED) { | 
|  | 903 | WARN(1, "omap_hwmod: %s: disabled state can only be entered " | 
|  | 904 | "from idle, or enabled state\n", oh->name); | 
|  | 905 | return -EINVAL; | 
|  | 906 | } | 
|  | 907 |  | 
|  | 908 | pr_debug("omap_hwmod: %s: disabling\n", oh->name); | 
|  | 909 |  | 
|  | 910 | if (oh->sysconfig) | 
|  | 911 | _sysc_shutdown(oh); | 
|  | 912 | _del_initiator_dep(oh, mpu_oh); | 
|  | 913 | /* XXX what about the other system initiators here? DMA, tesla, d2d */ | 
|  | 914 | _disable_clocks(oh); | 
|  | 915 | /* XXX Should this code also force-disable the optional clocks? */ | 
|  | 916 |  | 
|  | 917 | /* XXX mux any associated balls to safe mode */ | 
|  | 918 |  | 
|  | 919 | oh->_state = _HWMOD_STATE_DISABLED; | 
|  | 920 |  | 
|  | 921 | return 0; | 
|  | 922 | } | 
|  | 923 |  | 
|  | 924 | /** | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 925 | * _setup - do initial configuration of omap_hwmod | 
|  | 926 | * @oh: struct omap_hwmod * | 
|  | 927 | * | 
|  | 928 | * Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh | 
|  | 929 | * OCP_SYSCONFIG register.  Must be called with omap_hwmod_mutex | 
|  | 930 | * held.  Returns -EINVAL if the hwmod is in the wrong state or returns | 
|  | 931 | * 0. | 
|  | 932 | */ | 
|  | 933 | static int _setup(struct omap_hwmod *oh) | 
|  | 934 | { | 
|  | 935 | struct omap_hwmod_ocp_if *os; | 
|  | 936 | int i; | 
|  | 937 |  | 
|  | 938 | if (!oh) | 
|  | 939 | return -EINVAL; | 
|  | 940 |  | 
|  | 941 | /* Set iclk autoidle mode */ | 
|  | 942 | if (oh->slaves_cnt > 0) { | 
|  | 943 | for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) { | 
|  | 944 | struct clk *c = os->_clk; | 
|  | 945 |  | 
|  | 946 | if (!c || IS_ERR(c)) | 
|  | 947 | continue; | 
|  | 948 |  | 
|  | 949 | if (os->flags & OCPIF_SWSUP_IDLE) { | 
|  | 950 | /* XXX omap_iclk_deny_idle(c); */ | 
|  | 951 | } else { | 
|  | 952 | /* XXX omap_iclk_allow_idle(c); */ | 
|  | 953 | clk_enable(c); | 
|  | 954 | } | 
|  | 955 | } | 
|  | 956 | } | 
|  | 957 |  | 
|  | 958 | oh->_state = _HWMOD_STATE_INITIALIZED; | 
|  | 959 |  | 
|  | 960 | _enable(oh); | 
|  | 961 |  | 
| Paul Walmsley | b835d01 | 2009-12-08 16:34:14 -0700 | [diff] [blame] | 962 | if (!(oh->flags & HWMOD_INIT_NO_RESET)) { | 
|  | 963 | /* | 
|  | 964 | * XXX Do the OCP_SYSCONFIG bits need to be | 
|  | 965 | * reprogrammed after a reset?  If not, then this can | 
|  | 966 | * be removed.  If they do, then probably the | 
|  | 967 | * _enable() function should be split to avoid the | 
|  | 968 | * rewrite of the OCP_SYSCONFIG register. | 
|  | 969 | */ | 
|  | 970 | if (oh->sysconfig) { | 
|  | 971 | _update_sysc_cache(oh); | 
|  | 972 | _sysc_enable(oh); | 
|  | 973 | } | 
|  | 974 | } | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 975 |  | 
|  | 976 | if (!(oh->flags & HWMOD_INIT_NO_IDLE)) | 
|  | 977 | _idle(oh); | 
|  | 978 |  | 
|  | 979 | return 0; | 
|  | 980 | } | 
|  | 981 |  | 
|  | 982 |  | 
|  | 983 |  | 
|  | 984 | /* Public functions */ | 
|  | 985 |  | 
|  | 986 | u32 omap_hwmod_readl(struct omap_hwmod *oh, u16 reg_offs) | 
|  | 987 | { | 
|  | 988 | return __raw_readl(oh->_rt_va + reg_offs); | 
|  | 989 | } | 
|  | 990 |  | 
|  | 991 | void omap_hwmod_writel(u32 v, struct omap_hwmod *oh, u16 reg_offs) | 
|  | 992 | { | 
|  | 993 | __raw_writel(v, oh->_rt_va + reg_offs); | 
|  | 994 | } | 
|  | 995 |  | 
|  | 996 | /** | 
|  | 997 | * omap_hwmod_register - register a struct omap_hwmod | 
|  | 998 | * @oh: struct omap_hwmod * | 
|  | 999 | * | 
|  | 1000 | * Registers the omap_hwmod @oh.  Returns -EEXIST if an omap_hwmod already | 
|  | 1001 | * has been registered by the same name; -EINVAL if the omap_hwmod is in the | 
|  | 1002 | * wrong state, or 0 on success. | 
|  | 1003 | * | 
|  | 1004 | * XXX The data should be copied into bootmem, so the original data | 
|  | 1005 | * should be marked __initdata and freed after init.  This would allow | 
|  | 1006 | * unneeded omap_hwmods to be freed on multi-OMAP configurations.  Note | 
|  | 1007 | * that the copy process would be relatively complex due to the large number | 
|  | 1008 | * of substructures. | 
|  | 1009 | */ | 
|  | 1010 | int omap_hwmod_register(struct omap_hwmod *oh) | 
|  | 1011 | { | 
|  | 1012 | int ret, ms_id; | 
|  | 1013 |  | 
|  | 1014 | if (!oh || (oh->_state != _HWMOD_STATE_UNKNOWN)) | 
|  | 1015 | return -EINVAL; | 
|  | 1016 |  | 
|  | 1017 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1018 |  | 
|  | 1019 | pr_debug("omap_hwmod: %s: registering\n", oh->name); | 
|  | 1020 |  | 
|  | 1021 | if (_lookup(oh->name)) { | 
|  | 1022 | ret = -EEXIST; | 
|  | 1023 | goto ohr_unlock; | 
|  | 1024 | } | 
|  | 1025 |  | 
|  | 1026 | ms_id = _find_mpu_port_index(oh); | 
|  | 1027 | if (!IS_ERR_VALUE(ms_id)) { | 
|  | 1028 | oh->_mpu_port_index = ms_id; | 
|  | 1029 | oh->_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index); | 
|  | 1030 | } else { | 
|  | 1031 | oh->_int_flags |= _HWMOD_NO_MPU_PORT; | 
|  | 1032 | } | 
|  | 1033 |  | 
|  | 1034 | list_add_tail(&oh->node, &omap_hwmod_list); | 
|  | 1035 |  | 
|  | 1036 | oh->_state = _HWMOD_STATE_REGISTERED; | 
|  | 1037 |  | 
|  | 1038 | ret = 0; | 
|  | 1039 |  | 
|  | 1040 | ohr_unlock: | 
|  | 1041 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1042 | return ret; | 
|  | 1043 | } | 
|  | 1044 |  | 
|  | 1045 | /** | 
|  | 1046 | * omap_hwmod_lookup - look up a registered omap_hwmod by name | 
|  | 1047 | * @name: name of the omap_hwmod to look up | 
|  | 1048 | * | 
|  | 1049 | * Given a @name of an omap_hwmod, return a pointer to the registered | 
|  | 1050 | * struct omap_hwmod *, or NULL upon error. | 
|  | 1051 | */ | 
|  | 1052 | struct omap_hwmod *omap_hwmod_lookup(const char *name) | 
|  | 1053 | { | 
|  | 1054 | struct omap_hwmod *oh; | 
|  | 1055 |  | 
|  | 1056 | if (!name) | 
|  | 1057 | return NULL; | 
|  | 1058 |  | 
|  | 1059 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1060 | oh = _lookup(name); | 
|  | 1061 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1062 |  | 
|  | 1063 | return oh; | 
|  | 1064 | } | 
|  | 1065 |  | 
|  | 1066 | /** | 
|  | 1067 | * omap_hwmod_for_each - call function for each registered omap_hwmod | 
|  | 1068 | * @fn: pointer to a callback function | 
|  | 1069 | * | 
|  | 1070 | * Call @fn for each registered omap_hwmod, passing @data to each | 
|  | 1071 | * function.  @fn must return 0 for success or any other value for | 
|  | 1072 | * failure.  If @fn returns non-zero, the iteration across omap_hwmods | 
|  | 1073 | * will stop and the non-zero return value will be passed to the | 
|  | 1074 | * caller of omap_hwmod_for_each().  @fn is called with | 
|  | 1075 | * omap_hwmod_for_each() held. | 
|  | 1076 | */ | 
|  | 1077 | int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh)) | 
|  | 1078 | { | 
|  | 1079 | struct omap_hwmod *temp_oh; | 
|  | 1080 | int ret; | 
|  | 1081 |  | 
|  | 1082 | if (!fn) | 
|  | 1083 | return -EINVAL; | 
|  | 1084 |  | 
|  | 1085 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1086 | list_for_each_entry(temp_oh, &omap_hwmod_list, node) { | 
|  | 1087 | ret = (*fn)(temp_oh); | 
|  | 1088 | if (ret) | 
|  | 1089 | break; | 
|  | 1090 | } | 
|  | 1091 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1092 |  | 
|  | 1093 | return ret; | 
|  | 1094 | } | 
|  | 1095 |  | 
|  | 1096 |  | 
|  | 1097 | /** | 
|  | 1098 | * omap_hwmod_init - init omap_hwmod code and register hwmods | 
|  | 1099 | * @ohs: pointer to an array of omap_hwmods to register | 
|  | 1100 | * | 
|  | 1101 | * Intended to be called early in boot before the clock framework is | 
|  | 1102 | * initialized.  If @ohs is not null, will register all omap_hwmods | 
|  | 1103 | * listed in @ohs that are valid for this chip.  Returns -EINVAL if | 
|  | 1104 | * omap_hwmod_init() has already been called or 0 otherwise. | 
|  | 1105 | */ | 
|  | 1106 | int omap_hwmod_init(struct omap_hwmod **ohs) | 
|  | 1107 | { | 
|  | 1108 | struct omap_hwmod *oh; | 
|  | 1109 | int r; | 
|  | 1110 |  | 
|  | 1111 | if (inited) | 
|  | 1112 | return -EINVAL; | 
|  | 1113 |  | 
|  | 1114 | inited = 1; | 
|  | 1115 |  | 
|  | 1116 | if (!ohs) | 
|  | 1117 | return 0; | 
|  | 1118 |  | 
|  | 1119 | oh = *ohs; | 
|  | 1120 | while (oh) { | 
|  | 1121 | if (omap_chip_is(oh->omap_chip)) { | 
|  | 1122 | r = omap_hwmod_register(oh); | 
|  | 1123 | WARN(r, "omap_hwmod: %s: omap_hwmod_register returned " | 
|  | 1124 | "%d\n", oh->name, r); | 
|  | 1125 | } | 
|  | 1126 | oh = *++ohs; | 
|  | 1127 | } | 
|  | 1128 |  | 
|  | 1129 | return 0; | 
|  | 1130 | } | 
|  | 1131 |  | 
|  | 1132 | /** | 
|  | 1133 | * omap_hwmod_late_init - do some post-clock framework initialization | 
|  | 1134 | * | 
|  | 1135 | * Must be called after omap2_clk_init().  Resolves the struct clk names | 
|  | 1136 | * to struct clk pointers for each registered omap_hwmod.  Also calls | 
|  | 1137 | * _setup() on each hwmod.  Returns 0. | 
|  | 1138 | */ | 
|  | 1139 | int omap_hwmod_late_init(void) | 
|  | 1140 | { | 
|  | 1141 | int r; | 
|  | 1142 |  | 
|  | 1143 | /* XXX check return value */ | 
|  | 1144 | r = omap_hwmod_for_each(_init_clocks); | 
|  | 1145 | WARN(r, "omap_hwmod: omap_hwmod_late_init(): _init_clocks failed\n"); | 
|  | 1146 |  | 
|  | 1147 | mpu_oh = omap_hwmod_lookup(MPU_INITIATOR_NAME); | 
|  | 1148 | WARN(!mpu_oh, "omap_hwmod: could not find MPU initiator hwmod %s\n", | 
|  | 1149 | MPU_INITIATOR_NAME); | 
|  | 1150 |  | 
|  | 1151 | omap_hwmod_for_each(_setup); | 
|  | 1152 |  | 
|  | 1153 | return 0; | 
|  | 1154 | } | 
|  | 1155 |  | 
|  | 1156 | /** | 
|  | 1157 | * omap_hwmod_unregister - unregister an omap_hwmod | 
|  | 1158 | * @oh: struct omap_hwmod * | 
|  | 1159 | * | 
|  | 1160 | * Unregisters a previously-registered omap_hwmod @oh.  There's probably | 
|  | 1161 | * no use case for this, so it is likely to be removed in a later version. | 
|  | 1162 | * | 
|  | 1163 | * XXX Free all of the bootmem-allocated structures here when that is | 
|  | 1164 | * implemented.  Make it clear that core code is the only code that is | 
|  | 1165 | * expected to unregister modules. | 
|  | 1166 | */ | 
|  | 1167 | int omap_hwmod_unregister(struct omap_hwmod *oh) | 
|  | 1168 | { | 
|  | 1169 | if (!oh) | 
|  | 1170 | return -EINVAL; | 
|  | 1171 |  | 
|  | 1172 | pr_debug("omap_hwmod: %s: unregistering\n", oh->name); | 
|  | 1173 |  | 
|  | 1174 | mutex_lock(&omap_hwmod_mutex); | 
| Tony Lindgren | 986a13f | 2009-10-19 15:25:22 -0700 | [diff] [blame] | 1175 | iounmap(oh->_rt_va); | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 1176 | list_del(&oh->node); | 
|  | 1177 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1178 |  | 
|  | 1179 | return 0; | 
|  | 1180 | } | 
|  | 1181 |  | 
|  | 1182 | /** | 
|  | 1183 | * omap_hwmod_enable - enable an omap_hwmod | 
|  | 1184 | * @oh: struct omap_hwmod * | 
|  | 1185 | * | 
|  | 1186 | * Enable an omap_hwomd @oh.  Intended to be called by omap_device_enable(). | 
|  | 1187 | * Returns -EINVAL on error or passes along the return value from _enable(). | 
|  | 1188 | */ | 
|  | 1189 | int omap_hwmod_enable(struct omap_hwmod *oh) | 
|  | 1190 | { | 
|  | 1191 | int r; | 
|  | 1192 |  | 
|  | 1193 | if (!oh) | 
|  | 1194 | return -EINVAL; | 
|  | 1195 |  | 
|  | 1196 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1197 | r = _enable(oh); | 
|  | 1198 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1199 |  | 
|  | 1200 | return r; | 
|  | 1201 | } | 
|  | 1202 |  | 
|  | 1203 | /** | 
|  | 1204 | * omap_hwmod_idle - idle an omap_hwmod | 
|  | 1205 | * @oh: struct omap_hwmod * | 
|  | 1206 | * | 
|  | 1207 | * Idle an omap_hwomd @oh.  Intended to be called by omap_device_idle(). | 
|  | 1208 | * Returns -EINVAL on error or passes along the return value from _idle(). | 
|  | 1209 | */ | 
|  | 1210 | int omap_hwmod_idle(struct omap_hwmod *oh) | 
|  | 1211 | { | 
|  | 1212 | if (!oh) | 
|  | 1213 | return -EINVAL; | 
|  | 1214 |  | 
|  | 1215 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1216 | _idle(oh); | 
|  | 1217 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1218 |  | 
|  | 1219 | return 0; | 
|  | 1220 | } | 
|  | 1221 |  | 
|  | 1222 | /** | 
|  | 1223 | * omap_hwmod_shutdown - shutdown an omap_hwmod | 
|  | 1224 | * @oh: struct omap_hwmod * | 
|  | 1225 | * | 
|  | 1226 | * Shutdown an omap_hwomd @oh.  Intended to be called by | 
|  | 1227 | * omap_device_shutdown().  Returns -EINVAL on error or passes along | 
|  | 1228 | * the return value from _shutdown(). | 
|  | 1229 | */ | 
|  | 1230 | int omap_hwmod_shutdown(struct omap_hwmod *oh) | 
|  | 1231 | { | 
|  | 1232 | if (!oh) | 
|  | 1233 | return -EINVAL; | 
|  | 1234 |  | 
|  | 1235 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1236 | _shutdown(oh); | 
|  | 1237 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1238 |  | 
|  | 1239 | return 0; | 
|  | 1240 | } | 
|  | 1241 |  | 
|  | 1242 | /** | 
|  | 1243 | * omap_hwmod_enable_clocks - enable main_clk, all interface clocks | 
|  | 1244 | * @oh: struct omap_hwmod *oh | 
|  | 1245 | * | 
|  | 1246 | * Intended to be called by the omap_device code. | 
|  | 1247 | */ | 
|  | 1248 | int omap_hwmod_enable_clocks(struct omap_hwmod *oh) | 
|  | 1249 | { | 
|  | 1250 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1251 | _enable_clocks(oh); | 
|  | 1252 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1253 |  | 
|  | 1254 | return 0; | 
|  | 1255 | } | 
|  | 1256 |  | 
|  | 1257 | /** | 
|  | 1258 | * omap_hwmod_disable_clocks - disable main_clk, all interface clocks | 
|  | 1259 | * @oh: struct omap_hwmod *oh | 
|  | 1260 | * | 
|  | 1261 | * Intended to be called by the omap_device code. | 
|  | 1262 | */ | 
|  | 1263 | int omap_hwmod_disable_clocks(struct omap_hwmod *oh) | 
|  | 1264 | { | 
|  | 1265 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1266 | _disable_clocks(oh); | 
|  | 1267 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1268 |  | 
|  | 1269 | return 0; | 
|  | 1270 | } | 
|  | 1271 |  | 
|  | 1272 | /** | 
|  | 1273 | * omap_hwmod_ocp_barrier - wait for posted writes against the hwmod to complete | 
|  | 1274 | * @oh: struct omap_hwmod *oh | 
|  | 1275 | * | 
|  | 1276 | * Intended to be called by drivers and core code when all posted | 
|  | 1277 | * writes to a device must complete before continuing further | 
|  | 1278 | * execution (for example, after clearing some device IRQSTATUS | 
|  | 1279 | * register bits) | 
|  | 1280 | * | 
|  | 1281 | * XXX what about targets with multiple OCP threads? | 
|  | 1282 | */ | 
|  | 1283 | void omap_hwmod_ocp_barrier(struct omap_hwmod *oh) | 
|  | 1284 | { | 
|  | 1285 | BUG_ON(!oh); | 
|  | 1286 |  | 
|  | 1287 | if (!oh->sysconfig || !oh->sysconfig->sysc_flags) { | 
|  | 1288 | WARN(1, "omap_device: %s: OCP barrier impossible due to " | 
|  | 1289 | "device configuration\n", oh->name); | 
|  | 1290 | return; | 
|  | 1291 | } | 
|  | 1292 |  | 
|  | 1293 | /* | 
|  | 1294 | * Forces posted writes to complete on the OCP thread handling | 
|  | 1295 | * register writes | 
|  | 1296 | */ | 
|  | 1297 | omap_hwmod_readl(oh, oh->sysconfig->sysc_offs); | 
|  | 1298 | } | 
|  | 1299 |  | 
|  | 1300 | /** | 
|  | 1301 | * omap_hwmod_reset - reset the hwmod | 
|  | 1302 | * @oh: struct omap_hwmod * | 
|  | 1303 | * | 
|  | 1304 | * Under some conditions, a driver may wish to reset the entire device. | 
|  | 1305 | * Called from omap_device code.  Returns -EINVAL on error or passes along | 
|  | 1306 | * the return value from _reset()/_enable(). | 
|  | 1307 | */ | 
|  | 1308 | int omap_hwmod_reset(struct omap_hwmod *oh) | 
|  | 1309 | { | 
|  | 1310 | int r; | 
|  | 1311 |  | 
|  | 1312 | if (!oh || !(oh->_state & _HWMOD_STATE_ENABLED)) | 
|  | 1313 | return -EINVAL; | 
|  | 1314 |  | 
|  | 1315 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1316 | r = _reset(oh); | 
|  | 1317 | if (!r) | 
|  | 1318 | r = _enable(oh); | 
|  | 1319 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1320 |  | 
|  | 1321 | return r; | 
|  | 1322 | } | 
|  | 1323 |  | 
|  | 1324 | /** | 
|  | 1325 | * omap_hwmod_count_resources - count number of struct resources needed by hwmod | 
|  | 1326 | * @oh: struct omap_hwmod * | 
|  | 1327 | * @res: pointer to the first element of an array of struct resource to fill | 
|  | 1328 | * | 
|  | 1329 | * Count the number of struct resource array elements necessary to | 
|  | 1330 | * contain omap_hwmod @oh resources.  Intended to be called by code | 
|  | 1331 | * that registers omap_devices.  Intended to be used to determine the | 
|  | 1332 | * size of a dynamically-allocated struct resource array, before | 
|  | 1333 | * calling omap_hwmod_fill_resources().  Returns the number of struct | 
|  | 1334 | * resource array elements needed. | 
|  | 1335 | * | 
|  | 1336 | * XXX This code is not optimized.  It could attempt to merge adjacent | 
|  | 1337 | * resource IDs. | 
|  | 1338 | * | 
|  | 1339 | */ | 
|  | 1340 | int omap_hwmod_count_resources(struct omap_hwmod *oh) | 
|  | 1341 | { | 
|  | 1342 | int ret, i; | 
|  | 1343 |  | 
|  | 1344 | ret = oh->mpu_irqs_cnt + oh->sdma_chs_cnt; | 
|  | 1345 |  | 
|  | 1346 | for (i = 0; i < oh->slaves_cnt; i++) | 
|  | 1347 | ret += (*oh->slaves + i)->addr_cnt; | 
|  | 1348 |  | 
|  | 1349 | return ret; | 
|  | 1350 | } | 
|  | 1351 |  | 
|  | 1352 | /** | 
|  | 1353 | * omap_hwmod_fill_resources - fill struct resource array with hwmod data | 
|  | 1354 | * @oh: struct omap_hwmod * | 
|  | 1355 | * @res: pointer to the first element of an array of struct resource to fill | 
|  | 1356 | * | 
|  | 1357 | * Fill the struct resource array @res with resource data from the | 
|  | 1358 | * omap_hwmod @oh.  Intended to be called by code that registers | 
|  | 1359 | * omap_devices.  See also omap_hwmod_count_resources().  Returns the | 
|  | 1360 | * number of array elements filled. | 
|  | 1361 | */ | 
|  | 1362 | int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) | 
|  | 1363 | { | 
|  | 1364 | int i, j; | 
|  | 1365 | int r = 0; | 
|  | 1366 |  | 
|  | 1367 | /* For each IRQ, DMA, memory area, fill in array.*/ | 
|  | 1368 |  | 
|  | 1369 | for (i = 0; i < oh->mpu_irqs_cnt; i++) { | 
| Paul Walmsley | 718bfd7 | 2009-12-08 16:34:16 -0700 | [diff] [blame] | 1370 | (res + r)->name = (oh->mpu_irqs + i)->name; | 
|  | 1371 | (res + r)->start = (oh->mpu_irqs + i)->irq; | 
|  | 1372 | (res + r)->end = (oh->mpu_irqs + i)->irq; | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 1373 | (res + r)->flags = IORESOURCE_IRQ; | 
|  | 1374 | r++; | 
|  | 1375 | } | 
|  | 1376 |  | 
|  | 1377 | for (i = 0; i < oh->sdma_chs_cnt; i++) { | 
|  | 1378 | (res + r)->name = (oh->sdma_chs + i)->name; | 
|  | 1379 | (res + r)->start = (oh->sdma_chs + i)->dma_ch; | 
|  | 1380 | (res + r)->end = (oh->sdma_chs + i)->dma_ch; | 
|  | 1381 | (res + r)->flags = IORESOURCE_DMA; | 
|  | 1382 | r++; | 
|  | 1383 | } | 
|  | 1384 |  | 
|  | 1385 | for (i = 0; i < oh->slaves_cnt; i++) { | 
|  | 1386 | struct omap_hwmod_ocp_if *os; | 
|  | 1387 |  | 
|  | 1388 | os = *oh->slaves + i; | 
|  | 1389 |  | 
|  | 1390 | for (j = 0; j < os->addr_cnt; j++) { | 
|  | 1391 | (res + r)->start = (os->addr + j)->pa_start; | 
|  | 1392 | (res + r)->end = (os->addr + j)->pa_end; | 
|  | 1393 | (res + r)->flags = IORESOURCE_MEM; | 
|  | 1394 | r++; | 
|  | 1395 | } | 
|  | 1396 | } | 
|  | 1397 |  | 
|  | 1398 | return r; | 
|  | 1399 | } | 
|  | 1400 |  | 
|  | 1401 | /** | 
|  | 1402 | * omap_hwmod_get_pwrdm - return pointer to this module's main powerdomain | 
|  | 1403 | * @oh: struct omap_hwmod * | 
|  | 1404 | * | 
|  | 1405 | * Return the powerdomain pointer associated with the OMAP module | 
|  | 1406 | * @oh's main clock.  If @oh does not have a main clk, return the | 
|  | 1407 | * powerdomain associated with the interface clock associated with the | 
|  | 1408 | * module's MPU port. (XXX Perhaps this should use the SDMA port | 
|  | 1409 | * instead?)  Returns NULL on error, or a struct powerdomain * on | 
|  | 1410 | * success. | 
|  | 1411 | */ | 
|  | 1412 | struct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh) | 
|  | 1413 | { | 
|  | 1414 | struct clk *c; | 
|  | 1415 |  | 
|  | 1416 | if (!oh) | 
|  | 1417 | return NULL; | 
|  | 1418 |  | 
|  | 1419 | if (oh->_clk) { | 
|  | 1420 | c = oh->_clk; | 
|  | 1421 | } else { | 
|  | 1422 | if (oh->_int_flags & _HWMOD_NO_MPU_PORT) | 
|  | 1423 | return NULL; | 
|  | 1424 | c = oh->slaves[oh->_mpu_port_index]->_clk; | 
|  | 1425 | } | 
|  | 1426 |  | 
|  | 1427 | return c->clkdm->pwrdm.ptr; | 
|  | 1428 |  | 
|  | 1429 | } | 
|  | 1430 |  | 
|  | 1431 | /** | 
|  | 1432 | * omap_hwmod_add_initiator_dep - add sleepdep from @init_oh to @oh | 
|  | 1433 | * @oh: struct omap_hwmod * | 
|  | 1434 | * @init_oh: struct omap_hwmod * (initiator) | 
|  | 1435 | * | 
|  | 1436 | * Add a sleep dependency between the initiator @init_oh and @oh. | 
|  | 1437 | * Intended to be called by DSP/Bridge code via platform_data for the | 
|  | 1438 | * DSP case; and by the DMA code in the sDMA case.  DMA code, *Bridge | 
|  | 1439 | * code needs to add/del initiator dependencies dynamically | 
|  | 1440 | * before/after accessing a device.  Returns the return value from | 
|  | 1441 | * _add_initiator_dep(). | 
|  | 1442 | * | 
|  | 1443 | * XXX Keep a usecount in the clockdomain code | 
|  | 1444 | */ | 
|  | 1445 | int omap_hwmod_add_initiator_dep(struct omap_hwmod *oh, | 
|  | 1446 | struct omap_hwmod *init_oh) | 
|  | 1447 | { | 
|  | 1448 | return _add_initiator_dep(oh, init_oh); | 
|  | 1449 | } | 
|  | 1450 |  | 
|  | 1451 | /* | 
|  | 1452 | * XXX what about functions for drivers to save/restore ocp_sysconfig | 
|  | 1453 | * for context save/restore operations? | 
|  | 1454 | */ | 
|  | 1455 |  | 
|  | 1456 | /** | 
|  | 1457 | * omap_hwmod_del_initiator_dep - remove sleepdep from @init_oh to @oh | 
|  | 1458 | * @oh: struct omap_hwmod * | 
|  | 1459 | * @init_oh: struct omap_hwmod * (initiator) | 
|  | 1460 | * | 
|  | 1461 | * Remove a sleep dependency between the initiator @init_oh and @oh. | 
|  | 1462 | * Intended to be called by DSP/Bridge code via platform_data for the | 
|  | 1463 | * DSP case; and by the DMA code in the sDMA case.  DMA code, *Bridge | 
|  | 1464 | * code needs to add/del initiator dependencies dynamically | 
|  | 1465 | * before/after accessing a device.  Returns the return value from | 
|  | 1466 | * _del_initiator_dep(). | 
|  | 1467 | * | 
|  | 1468 | * XXX Keep a usecount in the clockdomain code | 
|  | 1469 | */ | 
|  | 1470 | int omap_hwmod_del_initiator_dep(struct omap_hwmod *oh, | 
|  | 1471 | struct omap_hwmod *init_oh) | 
|  | 1472 | { | 
|  | 1473 | return _del_initiator_dep(oh, init_oh); | 
|  | 1474 | } | 
|  | 1475 |  | 
|  | 1476 | /** | 
| Paul Walmsley | 63c8523 | 2009-09-03 20:14:03 +0300 | [diff] [blame] | 1477 | * omap_hwmod_enable_wakeup - allow device to wake up the system | 
|  | 1478 | * @oh: struct omap_hwmod * | 
|  | 1479 | * | 
|  | 1480 | * Sets the module OCP socket ENAWAKEUP bit to allow the module to | 
|  | 1481 | * send wakeups to the PRCM.  Eventually this should sets PRCM wakeup | 
|  | 1482 | * registers to cause the PRCM to receive wakeup events from the | 
|  | 1483 | * module.  Does not set any wakeup routing registers beyond this | 
|  | 1484 | * point - if the module is to wake up any other module or subsystem, | 
|  | 1485 | * that must be set separately.  Called by omap_device code.  Returns | 
|  | 1486 | * -EINVAL on error or 0 upon success. | 
|  | 1487 | */ | 
|  | 1488 | int omap_hwmod_enable_wakeup(struct omap_hwmod *oh) | 
|  | 1489 | { | 
|  | 1490 | if (!oh->sysconfig || | 
|  | 1491 | !(oh->sysconfig->sysc_flags & SYSC_HAS_ENAWAKEUP)) | 
|  | 1492 | return -EINVAL; | 
|  | 1493 |  | 
|  | 1494 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1495 | _enable_wakeup(oh); | 
|  | 1496 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1497 |  | 
|  | 1498 | return 0; | 
|  | 1499 | } | 
|  | 1500 |  | 
|  | 1501 | /** | 
|  | 1502 | * omap_hwmod_disable_wakeup - prevent device from waking the system | 
|  | 1503 | * @oh: struct omap_hwmod * | 
|  | 1504 | * | 
|  | 1505 | * Clears the module OCP socket ENAWAKEUP bit to prevent the module | 
|  | 1506 | * from sending wakeups to the PRCM.  Eventually this should clear | 
|  | 1507 | * PRCM wakeup registers to cause the PRCM to ignore wakeup events | 
|  | 1508 | * from the module.  Does not set any wakeup routing registers beyond | 
|  | 1509 | * this point - if the module is to wake up any other module or | 
|  | 1510 | * subsystem, that must be set separately.  Called by omap_device | 
|  | 1511 | * code.  Returns -EINVAL on error or 0 upon success. | 
|  | 1512 | */ | 
|  | 1513 | int omap_hwmod_disable_wakeup(struct omap_hwmod *oh) | 
|  | 1514 | { | 
|  | 1515 | if (!oh->sysconfig || | 
|  | 1516 | !(oh->sysconfig->sysc_flags & SYSC_HAS_ENAWAKEUP)) | 
|  | 1517 | return -EINVAL; | 
|  | 1518 |  | 
|  | 1519 | mutex_lock(&omap_hwmod_mutex); | 
|  | 1520 | _disable_wakeup(oh); | 
|  | 1521 | mutex_unlock(&omap_hwmod_mutex); | 
|  | 1522 |  | 
|  | 1523 | return 0; | 
|  | 1524 | } |