|
@@ -46,3 +46,178 @@
|
|
|
* Drivers won't call hwmod functions directly. That is done by the
|
|
|
* omap_device code, and in rare occasions, by custom integration code
|
|
|
* in arch/arm/ *omap*. The omap_device code includes functions to
|
|
|
+ * build a struct platform_device using omap_hwmod data, and that is
|
|
|
+ * currently how hwmod data is communicated to drivers and to the
|
|
|
+ * Linux driver model. Most drivers will call omap_hwmod functions only
|
|
|
+ * indirectly, via pm_runtime*() functions.
|
|
|
+ *
|
|
|
+ * From a layering perspective, here is where the OMAP hwmod code
|
|
|
+ * fits into the kernel software stack:
|
|
|
+ *
|
|
|
+ * +-------------------------------+
|
|
|
+ * | Device driver code |
|
|
|
+ * | (e.g., drivers/) |
|
|
|
+ * +-------------------------------+
|
|
|
+ * | Linux driver model |
|
|
|
+ * | (platform_device / |
|
|
|
+ * | platform_driver data/code) |
|
|
|
+ * +-------------------------------+
|
|
|
+ * | OMAP core-driver integration |
|
|
|
+ * |(arch/arm/mach-omap2/devices.c)|
|
|
|
+ * +-------------------------------+
|
|
|
+ * | omap_device code |
|
|
|
+ * | (../plat-omap/omap_device.c) |
|
|
|
+ * +-------------------------------+
|
|
|
+ * ----> | omap_hwmod code/data | <-----
|
|
|
+ * | (../mach-omap2/omap_hwmod*) |
|
|
|
+ * +-------------------------------+
|
|
|
+ * | OMAP clock/PRCM/register fns |
|
|
|
+ * | (__raw_{read,write}l, clk*) |
|
|
|
+ * +-------------------------------+
|
|
|
+ *
|
|
|
+ * Device drivers should not contain any OMAP-specific code or data in
|
|
|
+ * them. They should only contain code to operate the IP block that
|
|
|
+ * the driver is responsible for. This is because these IP blocks can
|
|
|
+ * also appear in other SoCs, either from TI (such as DaVinci) or from
|
|
|
+ * other manufacturers; and drivers should be reusable across other
|
|
|
+ * platforms.
|
|
|
+ *
|
|
|
+ * The OMAP hwmod code also will attempt to reset and idle all on-chip
|
|
|
+ * devices upon boot. The goal here is for the kernel to be
|
|
|
+ * completely self-reliant and independent from bootloaders. This is
|
|
|
+ * to ensure a repeatable configuration, both to ensure consistent
|
|
|
+ * runtime behavior, and to make it easier for others to reproduce
|
|
|
+ * bugs.
|
|
|
+ *
|
|
|
+ * OMAP module activity states
|
|
|
+ * ---------------------------
|
|
|
+ * The hwmod code considers modules to be in one of several activity
|
|
|
+ * states. IP blocks start out in an UNKNOWN state, then once they
|
|
|
+ * are registered via the hwmod code, proceed to the REGISTERED state.
|
|
|
+ * Once their clock names are resolved to clock pointers, the module
|
|
|
+ * enters the CLKS_INITED state; and finally, once the module has been
|
|
|
+ * reset and the integration registers programmed, the INITIALIZED state
|
|
|
+ * is entered. The hwmod code will then place the module into either
|
|
|
+ * the IDLE state to save power, or in the case of a critical system
|
|
|
+ * module, the ENABLED state.
|
|
|
+ *
|
|
|
+ * OMAP core integration code can then call omap_hwmod*() functions
|
|
|
+ * directly to move the module between the IDLE, ENABLED, and DISABLED
|
|
|
+ * states, as needed. This is done during both the PM idle loop, and
|
|
|
+ * in the OMAP core integration code's implementation of the PM runtime
|
|
|
+ * functions.
|
|
|
+ *
|
|
|
+ * References
|
|
|
+ * ----------
|
|
|
+ * This is a partial list.
|
|
|
+ * - OMAP2420 Multimedia Processor Silicon Revision 2.1.1, 2.2 (SWPU064)
|
|
|
+ * - OMAP2430 Multimedia Device POP Silicon Revision 2.1 (SWPU090)
|
|
|
+ * - OMAP34xx Multimedia Device Silicon Revision 3.1 (SWPU108)
|
|
|
+ * - OMAP4430 Multimedia Device Silicon Revision 1.0 (SWPU140)
|
|
|
+ * - Open Core Protocol Specification 2.2
|
|
|
+ *
|
|
|
+ * To do:
|
|
|
+ * - handle IO mapping
|
|
|
+ * - bus throughput & module latency measurement code
|
|
|
+ *
|
|
|
+ * XXX add tests at the beginning of each function to ensure the hwmod is
|
|
|
+ * in the appropriate state
|
|
|
+ * XXX error return values should be checked to ensure that they are
|
|
|
+ * appropriate
|
|
|
+ */
|
|
|
+#undef DEBUG
|
|
|
+
|
|
|
+#include <linux/kernel.h>
|
|
|
+#include <linux/errno.h>
|
|
|
+#include <linux/io.h>
|
|
|
+#include <linux/clk-provider.h>
|
|
|
+#include <linux/delay.h>
|
|
|
+#include <linux/err.h>
|
|
|
+#include <linux/list.h>
|
|
|
+#include <linux/mutex.h>
|
|
|
+#include <linux/spinlock.h>
|
|
|
+#include <linux/slab.h>
|
|
|
+#include <linux/bootmem.h>
|
|
|
+
|
|
|
+#include "clock.h"
|
|
|
+#include "omap_hwmod.h"
|
|
|
+
|
|
|
+#include "soc.h"
|
|
|
+#include "common.h"
|
|
|
+#include "clockdomain.h"
|
|
|
+#include "powerdomain.h"
|
|
|
+#include "cm2xxx.h"
|
|
|
+#include "cm3xxx.h"
|
|
|
+#include "cminst44xx.h"
|
|
|
+#include "cm33xx.h"
|
|
|
+#include "prm.h"
|
|
|
+#include "prm3xxx.h"
|
|
|
+#include "prm44xx.h"
|
|
|
+#include "prm33xx.h"
|
|
|
+#include "prminst44xx.h"
|
|
|
+#include "mux.h"
|
|
|
+#include "pm.h"
|
|
|
+
|
|
|
+/* Name of the OMAP hwmod for the MPU */
|
|
|
+#define MPU_INITIATOR_NAME "mpu"
|
|
|
+
|
|
|
+/*
|
|
|
+ * Number of struct omap_hwmod_link records per struct
|
|
|
+ * omap_hwmod_ocp_if record (master->slave and slave->master)
|
|
|
+ */
|
|
|
+#define LINKS_PER_OCP_IF 2
|
|
|
+
|
|
|
+/**
|
|
|
+ * struct omap_hwmod_soc_ops - fn ptrs for some SoC-specific operations
|
|
|
+ * @enable_module: function to enable a module (via MODULEMODE)
|
|
|
+ * @disable_module: function to disable a module (via MODULEMODE)
|
|
|
+ *
|
|
|
+ * XXX Eventually this functionality will be hidden inside the PRM/CM
|
|
|
+ * device drivers. Until then, this should avoid huge blocks of cpu_is_*()
|
|
|
+ * conditionals in this code.
|
|
|
+ */
|
|
|
+struct omap_hwmod_soc_ops {
|
|
|
+ void (*enable_module)(struct omap_hwmod *oh);
|
|
|
+ int (*disable_module)(struct omap_hwmod *oh);
|
|
|
+ int (*wait_target_ready)(struct omap_hwmod *oh);
|
|
|
+ int (*assert_hardreset)(struct omap_hwmod *oh,
|
|
|
+ struct omap_hwmod_rst_info *ohri);
|
|
|
+ int (*deassert_hardreset)(struct omap_hwmod *oh,
|
|
|
+ struct omap_hwmod_rst_info *ohri);
|
|
|
+ int (*is_hardreset_asserted)(struct omap_hwmod *oh,
|
|
|
+ struct omap_hwmod_rst_info *ohri);
|
|
|
+ int (*init_clkdm)(struct omap_hwmod *oh);
|
|
|
+ void (*update_context_lost)(struct omap_hwmod *oh);
|
|
|
+ int (*get_context_lost)(struct omap_hwmod *oh);
|
|
|
+};
|
|
|
+
|
|
|
+/* soc_ops: adapts the omap_hwmod code to the currently-booted SoC */
|
|
|
+static struct omap_hwmod_soc_ops soc_ops;
|
|
|
+
|
|
|
+/* omap_hwmod_list contains all registered struct omap_hwmods */
|
|
|
+static LIST_HEAD(omap_hwmod_list);
|
|
|
+
|
|
|
+/* mpu_oh: used to add/remove MPU initiator from sleepdep list */
|
|
|
+static struct omap_hwmod *mpu_oh;
|
|
|
+
|
|
|
+/* io_chain_lock: used to serialize reconfigurations of the I/O chain */
|
|
|
+static DEFINE_SPINLOCK(io_chain_lock);
|
|
|
+
|
|
|
+/*
|
|
|
+ * linkspace: ptr to a buffer that struct omap_hwmod_link records are
|
|
|
+ * allocated from - used to reduce the number of small memory
|
|
|
+ * allocations, which has a significant impact on performance
|
|
|
+ */
|
|
|
+static struct omap_hwmod_link *linkspace;
|
|
|
+
|
|
|
+/*
|
|
|
+ * free_ls, max_ls: array indexes into linkspace; representing the
|
|
|
+ * next free struct omap_hwmod_link index, and the maximum number of
|
|
|
+ * struct omap_hwmod_link records allocated (respectively)
|
|
|
+ */
|
|
|
+static unsigned short free_ls, max_ls, ls_supp;
|
|
|
+
|
|
|
+/* inited: set to true once the hwmod code is initialized */
|
|
|
+static bool inited;
|
|
|
+
|
|
|
+/* Private functions */
|