OMAP2+: hwmod/device: update documentation and copyright
[deliverable/linux.git] / arch / arm / mach-omap2 / omap_hwmod.c
index e436dcb19795cd259f91faaad864adfe558705d2..cb911d7d1a3c1535ba88eda74577ea6ef2e1ecd0 100644 (file)
@@ -1,13 +1,13 @@
 /*
  * omap_hwmod implementation for OMAP2/3/4
  *
- * Copyright (C) 2009 Nokia Corporation
- * Paul Walmsley
- * With fixes and testing from Kevin Hilman
+ * Copyright (C) 2009-2010 Nokia Corporation
  *
- * Created in collaboration with (alphabetical order): Benoit Cousson,
- * Kevin Hilman, Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari
- * Poussa, Anand Sawant, Santosh Shilimkar, Richard Woodruff
+ * Paul Walmsley, BenoĆ®t Cousson, Kevin Hilman
+ *
+ * Created in collaboration with (alphabetical order): Thara Gopinath,
+ * Tony Lindgren, Rajendra Nayak, Vikram Pandita, Sakari Poussa, Anand
+ * Sawant, Santosh Shilimkar, Richard Woodruff
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2 as
@@ -43,7 +43,6 @@
 #include <linux/err.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
-#include <linux/bootmem.h>
 
 #include <plat/common.h>
 #include <plat/cpu.h>
@@ -58,7 +57,7 @@
 #define MAX_MODULE_RESET_WAIT          10000
 
 /* Name of the OMAP hwmod for the MPU */
-#define MPU_INITIATOR_NAME             "mpu_hwmod"
+#define MPU_INITIATOR_NAME             "mpu"
 
 /* omap_hwmod_list contains all registered struct omap_hwmods */
 static LIST_HEAD(omap_hwmod_list);
@@ -404,27 +403,27 @@ static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)
  */
 static int _init_main_clk(struct omap_hwmod *oh)
 {
-       struct clk *c;
        int ret = 0;
 
        if (!oh->main_clk)
                return 0;
 
-       c = omap_clk_get_by_name(oh->main_clk);
-       WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get main_clk %s\n",
-            oh->name, oh->main_clk);
-       if (IS_ERR(c))
-               ret = -EINVAL;
-       oh->_clk = c;
+       oh->_clk = omap_clk_get_by_name(oh->main_clk);
+       if (!oh->_clk) {
+               pr_warning("omap_hwmod: %s: cannot clk_get main_clk %s\n",
+                          oh->name, oh->main_clk);
+               return -EINVAL;
+       }
 
-       WARN(!c->clkdm, "omap_hwmod: %s: missing clockdomain for %s.\n",
-            oh->main_clk, c->name);
+       if (!oh->_clk->clkdm)
+               pr_warning("omap_hwmod: %s: missing clockdomain for %s.\n",
+                          oh->main_clk, oh->_clk->name);
 
        return ret;
 }
 
 /**
- * _init_interface_clk - get a struct clk * for the the hwmod's interface clks
+ * _init_interface_clks - get a struct clk * for the the hwmod's interface clks
  * @oh: struct omap_hwmod *
  *
  * Called from _init_clocks().  Populates the @oh OCP slave interface
@@ -432,7 +431,6 @@ static int _init_main_clk(struct omap_hwmod *oh)
  */
 static int _init_interface_clks(struct omap_hwmod *oh)
 {
-       struct omap_hwmod_ocp_if *os;
        struct clk *c;
        int i;
        int ret = 0;
@@ -440,15 +438,18 @@ static int _init_interface_clks(struct omap_hwmod *oh)
        if (oh->slaves_cnt == 0)
                return 0;
 
-       for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) {
+       for (i = 0; i < oh->slaves_cnt; i++) {
+               struct omap_hwmod_ocp_if *os = oh->slaves[i];
+
                if (!os->clk)
                        continue;
 
                c = omap_clk_get_by_name(os->clk);
-               WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get "
-                    "interface_clk %s\n", oh->name, os->clk);
-               if (IS_ERR(c))
+               if (!c) {
+                       pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n",
+                                  oh->name, os->clk);
                        ret = -EINVAL;
+               }
                os->_clk = c;
        }
 
@@ -471,10 +472,11 @@ static int _init_opt_clks(struct omap_hwmod *oh)
 
        for (i = oh->opt_clks_cnt, oc = oh->opt_clks; i > 0; i--, oc++) {
                c = omap_clk_get_by_name(oc->clk);
-               WARN(IS_ERR(c), "omap_hwmod: %s: cannot clk_get opt_clk "
-                    "%s\n", oh->name, oc->clk);
-               if (IS_ERR(c))
+               if (!c) {
+                       pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n",
+                                  oh->name, oc->clk);
                        ret = -EINVAL;
+               }
                oc->_clk = c;
        }
 
@@ -490,19 +492,19 @@ static int _init_opt_clks(struct omap_hwmod *oh)
  */
 static int _enable_clocks(struct omap_hwmod *oh)
 {
-       struct omap_hwmod_ocp_if *os;
        int i;
 
        pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name);
 
-       if (oh->_clk && !IS_ERR(oh->_clk))
+       if (oh->_clk)
                clk_enable(oh->_clk);
 
        if (oh->slaves_cnt > 0) {
-               for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) {
+               for (i = 0; i < oh->slaves_cnt; i++) {
+                       struct omap_hwmod_ocp_if *os = oh->slaves[i];
                        struct clk *c = os->_clk;
 
-                       if (c && !IS_ERR(c) && (os->flags & OCPIF_SWSUP_IDLE))
+                       if (c && (os->flags & OCPIF_SWSUP_IDLE))
                                clk_enable(c);
                }
        }
@@ -520,19 +522,19 @@ static int _enable_clocks(struct omap_hwmod *oh)
  */
 static int _disable_clocks(struct omap_hwmod *oh)
 {
-       struct omap_hwmod_ocp_if *os;
        int i;
 
        pr_debug("omap_hwmod: %s: disabling clocks\n", oh->name);
 
-       if (oh->_clk && !IS_ERR(oh->_clk))
+       if (oh->_clk)
                clk_disable(oh->_clk);
 
        if (oh->slaves_cnt > 0) {
-               for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) {
+               for (i = 0; i < oh->slaves_cnt; i++) {
+                       struct omap_hwmod_ocp_if *os = oh->slaves[i];
                        struct clk *c = os->_clk;
 
-                       if (c && !IS_ERR(c) && (os->flags & OCPIF_SWSUP_IDLE))
+                       if (c && (os->flags & OCPIF_SWSUP_IDLE))
                                clk_disable(c);
                }
        }
@@ -551,14 +553,15 @@ static int _disable_clocks(struct omap_hwmod *oh)
  */
 static int _find_mpu_port_index(struct omap_hwmod *oh)
 {
-       struct omap_hwmod_ocp_if *os;
        int i;
        int found = 0;
 
        if (!oh || oh->slaves_cnt == 0)
                return -EINVAL;
 
-       for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) {
+       for (i = 0; i < oh->slaves_cnt; i++) {
+               struct omap_hwmod_ocp_if *os = oh->slaves[i];
+
                if (os->user & OCP_USER_MPU) {
                        found = 1;
                        break;
@@ -593,7 +596,7 @@ static void __iomem *_find_mpu_rt_base(struct omap_hwmod *oh, u8 index)
        if (!oh || oh->slaves_cnt == 0)
                return NULL;
 
-       os = *oh->slaves + index;
+       os = oh->slaves[index];
 
        for (i = 0, mem = os->addr; i < os->addr_cnt; i++, mem++) {
                if (mem->flags & ADDR_TYPE_RT) {
@@ -761,6 +764,7 @@ static struct omap_hwmod *_lookup(const char *name)
 /**
  * _init_clocks - clk_get() all clocks associated with this hwmod
  * @oh: struct omap_hwmod *
+ * @data: not used; pass NULL
  *
  * Called by omap_hwmod_late_init() (after omap2_clk_init()).
  * Resolves all clock names embedded in the hwmod.  Must be called
@@ -768,7 +772,7 @@ static struct omap_hwmod *_lookup(const char *name)
  * has not yet been registered or if the clocks have already been
  * initialized, 0 on success, or a non-zero error on failure.
  */
-static int _init_clocks(struct omap_hwmod *oh)
+static int _init_clocks(struct omap_hwmod *oh, void *data)
 {
        int ret = 0;
 
@@ -781,9 +785,10 @@ static int _init_clocks(struct omap_hwmod *oh)
        ret |= _init_interface_clks(oh);
        ret |= _init_opt_clks(oh);
 
-       oh->_state = _HWMOD_STATE_CLKS_INITED;
+       if (!ret)
+               oh->_state = _HWMOD_STATE_CLKS_INITED;
 
-       return ret;
+       return 0;
 }
 
 /**
@@ -806,9 +811,9 @@ static int _wait_target_ready(struct omap_hwmod *oh)
        if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
                return 0;
 
-       os = *oh->slaves + oh->_mpu_port_index;
+       os = oh->slaves[oh->_mpu_port_index];
 
-       if (!(os->flags & OCPIF_HAS_IDLEST))
+       if (oh->flags & HWMOD_NO_IDLEST)
                return 0;
 
        /* XXX check module SIDLEMODE */
@@ -819,11 +824,8 @@ static int _wait_target_ready(struct omap_hwmod *oh)
                ret = omap2_cm_wait_module_ready(oh->prcm.omap2.module_offs,
                                                 oh->prcm.omap2.idlest_reg_id,
                                                 oh->prcm.omap2.idlest_idle_bit);
-#if 0
        } else if (cpu_is_omap44xx()) {
-               ret = omap4_cm_wait_module_ready(oh->prcm.omap4.module_offs,
-                                                oh->prcm.omap4.device_offs);
-#endif
+               ret = omap4_cm_wait_module_ready(oh->prcm.omap4.clkctrl_reg);
        } else {
                BUG();
        };
@@ -885,7 +887,7 @@ static int _reset(struct omap_hwmod *oh)
 }
 
 /**
- * _enable - enable an omap_hwmod
+ * _omap_hwmod_enable - enable an omap_hwmod
  * @oh: struct omap_hwmod *
  *
  * Enables an omap_hwmod @oh such that the MPU can access the hwmod's
@@ -893,7 +895,7 @@ static int _reset(struct omap_hwmod *oh)
  * Returns -EINVAL if the hwmod is in the wrong state or passes along
  * the return value of _wait_target_ready().
  */
-static int _enable(struct omap_hwmod *oh)
+int _omap_hwmod_enable(struct omap_hwmod *oh)
 {
        int r;
 
@@ -912,16 +914,21 @@ static int _enable(struct omap_hwmod *oh)
        _add_initiator_dep(oh, mpu_oh);
        _enable_clocks(oh);
 
-       if (oh->class->sysc) {
-               if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
-                       _update_sysc_cache(oh);
-               _sysc_enable(oh);
-       }
-
        r = _wait_target_ready(oh);
-       if (!r)
+       if (!r) {
                oh->_state = _HWMOD_STATE_ENABLED;
 
+               /* Access the sysconfig only if the target is ready */
+               if (oh->class->sysc) {
+                       if (!(oh->_int_flags & _HWMOD_SYSCONFIG_LOADED))
+                               _update_sysc_cache(oh);
+                       _sysc_enable(oh);
+               }
+       } else {
+               pr_debug("omap_hwmod: %s: _wait_target_ready: %d\n",
+                        oh->name, r);
+       }
+
        return r;
 }
 
@@ -933,7 +940,7 @@ static int _enable(struct omap_hwmod *oh)
  * no further work.  Returns -EINVAL if the hwmod is in the wrong
  * state or returns 0.
  */
-static int _idle(struct omap_hwmod *oh)
+int _omap_hwmod_idle(struct omap_hwmod *oh)
 {
        if (oh->_state != _HWMOD_STATE_ENABLED) {
                WARN(1, "omap_hwmod: %s: idle state can only be entered from "
@@ -990,26 +997,32 @@ static int _shutdown(struct omap_hwmod *oh)
 /**
  * _setup - do initial configuration of omap_hwmod
  * @oh: struct omap_hwmod *
+ * @skip_setup_idle_p: do not idle hwmods at the end of the fn if 1
  *
  * Writes the CLOCKACTIVITY bits @clockact to the hwmod @oh
- * OCP_SYSCONFIG register.  Must be called with omap_hwmod_mutex
- * held.  Returns -EINVAL if the hwmod is in the wrong state or returns
- * 0.
+ * OCP_SYSCONFIG register.  Must be called with omap_hwmod_mutex held.
+ * @skip_setup_idle is intended to be used on a system that will not
+ * call omap_hwmod_enable() to enable devices (e.g., a system without
+ * PM runtime).  Returns -EINVAL if the hwmod is in the wrong state or
+ * returns 0.
  */
-static int _setup(struct omap_hwmod *oh)
+static int _setup(struct omap_hwmod *oh, void *data)
 {
-       struct omap_hwmod_ocp_if *os;
-       int i;
+       int i, r;
+       u8 skip_setup_idle;
 
-       if (!oh)
+       if (!oh || !data)
                return -EINVAL;
 
+       skip_setup_idle = *(u8 *)data;
+
        /* Set iclk autoidle mode */
        if (oh->slaves_cnt > 0) {
-               for (i = 0, os = *oh->slaves; i < oh->slaves_cnt; i++, os++) {
+               for (i = 0; i < oh->slaves_cnt; i++) {
+                       struct omap_hwmod_ocp_if *os = oh->slaves[i];
                        struct clk *c = os->_clk;
 
-                       if (!c || IS_ERR(c))
+                       if (!c)
                                continue;
 
                        if (os->flags & OCPIF_SWSUP_IDLE) {
@@ -1023,14 +1036,19 @@ static int _setup(struct omap_hwmod *oh)
 
        oh->_state = _HWMOD_STATE_INITIALIZED;
 
-       _enable(oh);
+       r = _omap_hwmod_enable(oh);
+       if (r) {
+               pr_warning("omap_hwmod: %s: cannot be enabled (%d)\n",
+                          oh->name, oh->_state);
+               return 0;
+       }
 
        if (!(oh->flags & HWMOD_INIT_NO_RESET)) {
                /*
                 * XXX Do the OCP_SYSCONFIG bits need to be
                 * reprogrammed after a reset?  If not, then this can
                 * be removed.  If they do, then probably the
-                * _enable() function should be split to avoid the
+                * _omap_hwmod_enable() function should be split to avoid the
                 * rewrite of the OCP_SYSCONFIG register.
                 */
                if (oh->class->sysc) {
@@ -1039,8 +1057,8 @@ static int _setup(struct omap_hwmod *oh)
                }
        }
 
-       if (!(oh->flags & HWMOD_INIT_NO_IDLE))
-               _idle(oh);
+       if (!(oh->flags & HWMOD_INIT_NO_IDLE) && !skip_setup_idle)
+               _omap_hwmod_idle(oh);
 
        return 0;
 }
@@ -1051,14 +1069,29 @@ static int _setup(struct omap_hwmod *oh)
 
 u32 omap_hwmod_readl(struct omap_hwmod *oh, u16 reg_offs)
 {
-       return __raw_readl(oh->_rt_va + reg_offs);
+       return __raw_readl(oh->_mpu_rt_va + reg_offs);
 }
 
 void omap_hwmod_writel(u32 v, struct omap_hwmod *oh, u16 reg_offs)
 {
-       __raw_writel(v, oh->_rt_va + reg_offs);
+       __raw_writel(v, oh->_mpu_rt_va + reg_offs);
 }
 
+/**
+ * omap_hwmod_set_slave_idlemode - set the hwmod's OCP slave idlemode
+ * @oh: struct omap_hwmod *
+ * @idlemode: SIDLEMODE field bits (shifted to bit 0)
+ *
+ * Sets the IP block's OCP slave idlemode in hardware, and updates our
+ * local copy.  Intended to be used by drivers that have some erratum
+ * that requires direct manipulation of the SIDLEMODE bits.  Returns
+ * -EINVAL if @oh is null, or passes along the return value from
+ * _set_slave_idlemode().
+ *
+ * XXX Does this function have any current users?  If not, we should
+ * remove it; it is better to let the rest of the hwmod code handle this.
+ * Any users of this function should be scrutinized carefully.
+ */
 int omap_hwmod_set_slave_idlemode(struct omap_hwmod *oh, u8 idlemode)
 {
        u32 v;
@@ -1113,7 +1146,7 @@ int omap_hwmod_register(struct omap_hwmod *oh)
        ms_id = _find_mpu_port_index(oh);
        if (!IS_ERR_VALUE(ms_id)) {
                oh->_mpu_port_index = ms_id;
-               oh->_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index);
+               oh->_mpu_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index);
        } else {
                oh->_int_flags |= _HWMOD_NO_MPU_PORT;
        }
@@ -1153,6 +1186,7 @@ struct omap_hwmod *omap_hwmod_lookup(const char *name)
 /**
  * omap_hwmod_for_each - call function for each registered omap_hwmod
  * @fn: pointer to a callback function
+ * @data: void * data to pass to callback function
  *
  * Call @fn for each registered omap_hwmod, passing @data to each
  * function.  @fn must return 0 for success or any other value for
@@ -1161,7 +1195,8 @@ struct omap_hwmod *omap_hwmod_lookup(const char *name)
  * caller of omap_hwmod_for_each().  @fn is called with
  * omap_hwmod_for_each() held.
  */
-int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh))
+int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh, void *data),
+                       void *data)
 {
        struct omap_hwmod *temp_oh;
        int ret;
@@ -1171,7 +1206,7 @@ int omap_hwmod_for_each(int (*fn)(struct omap_hwmod *oh))
 
        mutex_lock(&omap_hwmod_mutex);
        list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
-               ret = (*fn)(temp_oh);
+               ret = (*fn)(temp_oh, data);
                if (ret)
                        break;
        }
@@ -1218,24 +1253,28 @@ int omap_hwmod_init(struct omap_hwmod **ohs)
 
 /**
  * omap_hwmod_late_init - do some post-clock framework initialization
+ * @skip_setup_idle: if 1, do not idle hwmods in _setup()
  *
  * Must be called after omap2_clk_init().  Resolves the struct clk names
  * to struct clk pointers for each registered omap_hwmod.  Also calls
  * _setup() on each hwmod.  Returns 0.
  */
-int omap_hwmod_late_init(void)
+int omap_hwmod_late_init(u8 skip_setup_idle)
 {
        int r;
 
        /* XXX check return value */
-       r = omap_hwmod_for_each(_init_clocks);
+       r = omap_hwmod_for_each(_init_clocks, NULL);
        WARN(r, "omap_hwmod: omap_hwmod_late_init(): _init_clocks failed\n");
 
        mpu_oh = omap_hwmod_lookup(MPU_INITIATOR_NAME);
        WARN(!mpu_oh, "omap_hwmod: could not find MPU initiator hwmod %s\n",
             MPU_INITIATOR_NAME);
 
-       omap_hwmod_for_each(_setup);
+       if (skip_setup_idle)
+               pr_debug("omap_hwmod: will leave hwmods enabled during setup\n");
+
+       omap_hwmod_for_each(_setup, &skip_setup_idle);
 
        return 0;
 }
@@ -1259,7 +1298,7 @@ int omap_hwmod_unregister(struct omap_hwmod *oh)
        pr_debug("omap_hwmod: %s: unregistering\n", oh->name);
 
        mutex_lock(&omap_hwmod_mutex);
-       iounmap(oh->_rt_va);
+       iounmap(oh->_mpu_rt_va);
        list_del(&oh->node);
        mutex_unlock(&omap_hwmod_mutex);
 
@@ -1281,12 +1320,13 @@ int omap_hwmod_enable(struct omap_hwmod *oh)
                return -EINVAL;
 
        mutex_lock(&omap_hwmod_mutex);
-       r = _enable(oh);
+       r = _omap_hwmod_enable(oh);
        mutex_unlock(&omap_hwmod_mutex);
 
        return r;
 }
 
+
 /**
  * omap_hwmod_idle - idle an omap_hwmod
  * @oh: struct omap_hwmod *
@@ -1300,7 +1340,7 @@ int omap_hwmod_idle(struct omap_hwmod *oh)
                return -EINVAL;
 
        mutex_lock(&omap_hwmod_mutex);
-       _idle(oh);
+       _omap_hwmod_idle(oh);
        mutex_unlock(&omap_hwmod_mutex);
 
        return 0;
@@ -1402,7 +1442,7 @@ int omap_hwmod_reset(struct omap_hwmod *oh)
        mutex_lock(&omap_hwmod_mutex);
        r = _reset(oh);
        if (!r)
-               r = _enable(oh);
+               r = _omap_hwmod_enable(oh);
        mutex_unlock(&omap_hwmod_mutex);
 
        return r;
@@ -1431,7 +1471,7 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh)
        ret = oh->mpu_irqs_cnt + oh->sdma_chs_cnt;
 
        for (i = 0; i < oh->slaves_cnt; i++)
-               ret += (*oh->slaves + i)->addr_cnt;
+               ret += oh->slaves[i]->addr_cnt;
 
        return ret;
 }
@@ -1472,7 +1512,7 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)
        for (i = 0; i < oh->slaves_cnt; i++) {
                struct omap_hwmod_ocp_if *os;
 
-               os = *oh->slaves + i;
+               os = oh->slaves[i];
 
                for (j = 0; j < os->addr_cnt; j++) {
                        (res + r)->start = (os->addr + j)->pa_start;
@@ -1518,6 +1558,29 @@ struct powerdomain *omap_hwmod_get_pwrdm(struct omap_hwmod *oh)
 
 }
 
+/**
+ * omap_hwmod_get_mpu_rt_va - return the module's base address (for the MPU)
+ * @oh: struct omap_hwmod *
+ *
+ * Returns the virtual address corresponding to the beginning of the
+ * module's register target, in the address range that is intended to
+ * be used by the MPU.  Returns the virtual address upon success or NULL
+ * upon error.
+ */
+void __iomem *omap_hwmod_get_mpu_rt_va(struct omap_hwmod *oh)
+{
+       if (!oh)
+               return NULL;
+
+       if (oh->_int_flags & _HWMOD_NO_MPU_PORT)
+               return NULL;
+
+       if (oh->_state == _HWMOD_STATE_UNKNOWN)
+               return NULL;
+
+       return oh->_mpu_rt_va;
+}
+
 /**
  * omap_hwmod_add_initiator_dep - add sleepdep from @init_oh to @oh
  * @oh: struct omap_hwmod *
This page took 0.044123 seconds and 5 git commands to generate.