summaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/omap_hwmod.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
-rw-r--r--arch/arm/mach-omap2/omap_hwmod.c418
1 files changed, 417 insertions, 1 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c
index 34156eca8e23..e7d23e200ecc 100644
--- a/arch/arm/mach-omap2/omap_hwmod.c
+++ b/arch/arm/mach-omap2/omap_hwmod.c
@@ -145,6 +145,8 @@
#include <linux/platform_data/ti-sysc.h>
+#include <dt-bindings/bus/ti-sysc.h>
+
#include <asm/system_misc.h>
#include "clock.h"
@@ -2498,7 +2500,7 @@ static void __init _setup_postsetup(struct omap_hwmod *oh)
* affects the IP block hardware, or system integration hardware
* associated with the IP block. Returns 0.
*/
-static int __init _setup(struct omap_hwmod *oh, void *data)
+static int _setup(struct omap_hwmod *oh, void *data)
{
if (oh->_state != _HWMOD_STATE_INITIALIZED)
return 0;
@@ -3060,6 +3062,414 @@ int __init omap_hwmod_setup_one(const char *oh_name)
return 0;
}
+static void omap_hwmod_check_one(struct device *dev,
+ const char *name, s8 v1, u8 v2)
+{
+ if (v1 < 0)
+ return;
+
+ if (v1 != v2)
+ dev_warn(dev, "%s %d != %d\n", name, v1, v2);
+}
+
+/**
+ * omap_hwmod_check_sysc - check sysc against platform sysc
+ * @dev: struct device
+ * @data: module data
+ * @sysc_fields: new sysc configuration
+ */
+static int omap_hwmod_check_sysc(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ struct sysc_regbits *sysc_fields)
+{
+ const struct sysc_regbits *regbits = data->cap->regbits;
+
+ omap_hwmod_check_one(dev, "dmadisable_shift",
+ regbits->dmadisable_shift,
+ sysc_fields->dmadisable_shift);
+ omap_hwmod_check_one(dev, "midle_shift",
+ regbits->midle_shift,
+ sysc_fields->midle_shift);
+ omap_hwmod_check_one(dev, "sidle_shift",
+ regbits->sidle_shift,
+ sysc_fields->sidle_shift);
+ omap_hwmod_check_one(dev, "clkact_shift",
+ regbits->clkact_shift,
+ sysc_fields->clkact_shift);
+ omap_hwmod_check_one(dev, "enwkup_shift",
+ regbits->enwkup_shift,
+ sysc_fields->enwkup_shift);
+ omap_hwmod_check_one(dev, "srst_shift",
+ regbits->srst_shift,
+ sysc_fields->srst_shift);
+ omap_hwmod_check_one(dev, "autoidle_shift",
+ regbits->autoidle_shift,
+ sysc_fields->autoidle_shift);
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_init_regbits - init sysconfig specific register bits
+ * @dev: struct device
+ * @data: module data
+ * @sysc_fields: new sysc configuration
+ */
+static int omap_hwmod_init_regbits(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ struct sysc_regbits **sysc_fields)
+{
+ *sysc_fields = NULL;
+
+ switch (data->cap->type) {
+ case TI_SYSC_OMAP2:
+ case TI_SYSC_OMAP2_TIMER:
+ *sysc_fields = &omap_hwmod_sysc_type1;
+ break;
+ case TI_SYSC_OMAP3_SHAM:
+ *sysc_fields = &omap3_sham_sysc_fields;
+ break;
+ case TI_SYSC_OMAP3_AES:
+ *sysc_fields = &omap3xxx_aes_sysc_fields;
+ break;
+ case TI_SYSC_OMAP4:
+ case TI_SYSC_OMAP4_TIMER:
+ *sysc_fields = &omap_hwmod_sysc_type2;
+ break;
+ case TI_SYSC_OMAP4_SIMPLE:
+ *sysc_fields = &omap_hwmod_sysc_type3;
+ break;
+ case TI_SYSC_OMAP34XX_SR:
+ *sysc_fields = &omap34xx_sr_sysc_fields;
+ break;
+ case TI_SYSC_OMAP36XX_SR:
+ *sysc_fields = &omap36xx_sr_sysc_fields;
+ break;
+ case TI_SYSC_OMAP4_SR:
+ *sysc_fields = &omap36xx_sr_sysc_fields;
+ break;
+ case TI_SYSC_OMAP4_MCASP:
+ *sysc_fields = &omap_hwmod_sysc_type_mcasp;
+ break;
+ case TI_SYSC_OMAP4_USB_HOST_FS:
+ *sysc_fields = &omap_hwmod_sysc_type_usb_host_fs;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return omap_hwmod_check_sysc(dev, data, *sysc_fields);
+}
+
+/**
+ * omap_hwmod_init_reg_offs - initialize sysconfig register offsets
+ * @dev: struct device
+ * @data: module data
+ * @rev_offs: revision register offset
+ * @sysc_offs: sysc register offset
+ * @syss_offs: syss register offset
+ */
+int omap_hwmod_init_reg_offs(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ u32 *rev_offs, u32 *sysc_offs, u32 *syss_offs)
+{
+ *rev_offs = 0;
+ *sysc_offs = 0;
+ *syss_offs = 0;
+
+ if (data->offsets[SYSC_REVISION] > 0)
+ *rev_offs = data->offsets[SYSC_REVISION];
+
+ if (data->offsets[SYSC_SYSCONFIG] > 0)
+ *sysc_offs = data->offsets[SYSC_SYSCONFIG];
+
+ if (data->offsets[SYSC_SYSSTATUS] > 0)
+ *syss_offs = data->offsets[SYSC_SYSSTATUS];
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_init_sysc_flags - initialize sysconfig features
+ * @dev: struct device
+ * @data: module data
+ * @sysc_flags: module configuration
+ */
+int omap_hwmod_init_sysc_flags(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ u32 *sysc_flags)
+{
+ *sysc_flags = 0;
+
+ switch (data->cap->type) {
+ case TI_SYSC_OMAP2:
+ case TI_SYSC_OMAP2_TIMER:
+ /* See SYSC_OMAP2_* in include/dt-bindings/bus/ti-sysc.h */
+ if (data->cfg->sysc_val & SYSC_OMAP2_CLOCKACTIVITY)
+ *sysc_flags |= SYSC_HAS_CLOCKACTIVITY;
+ if (data->cfg->sysc_val & SYSC_OMAP2_EMUFREE)
+ *sysc_flags |= SYSC_HAS_EMUFREE;
+ if (data->cfg->sysc_val & SYSC_OMAP2_ENAWAKEUP)
+ *sysc_flags |= SYSC_HAS_ENAWAKEUP;
+ if (data->cfg->sysc_val & SYSC_OMAP2_SOFTRESET)
+ *sysc_flags |= SYSC_HAS_SOFTRESET;
+ if (data->cfg->sysc_val & SYSC_OMAP2_AUTOIDLE)
+ *sysc_flags |= SYSC_HAS_AUTOIDLE;
+ break;
+ case TI_SYSC_OMAP4:
+ case TI_SYSC_OMAP4_TIMER:
+ /* See SYSC_OMAP4_* in include/dt-bindings/bus/ti-sysc.h */
+ if (data->cfg->sysc_val & SYSC_OMAP4_DMADISABLE)
+ *sysc_flags |= SYSC_HAS_DMADISABLE;
+ if (data->cfg->sysc_val & SYSC_OMAP4_FREEEMU)
+ *sysc_flags |= SYSC_HAS_EMUFREE;
+ if (data->cfg->sysc_val & SYSC_OMAP4_SOFTRESET)
+ *sysc_flags |= SYSC_HAS_SOFTRESET;
+ break;
+ case TI_SYSC_OMAP34XX_SR:
+ case TI_SYSC_OMAP36XX_SR:
+ /* See SYSC_OMAP3_SR_* in include/dt-bindings/bus/ti-sysc.h */
+ if (data->cfg->sysc_val & SYSC_OMAP3_SR_ENAWAKEUP)
+ *sysc_flags |= SYSC_HAS_ENAWAKEUP;
+ break;
+ default:
+ if (data->cap->regbits->emufree_shift >= 0)
+ *sysc_flags |= SYSC_HAS_EMUFREE;
+ if (data->cap->regbits->enwkup_shift >= 0)
+ *sysc_flags |= SYSC_HAS_ENAWAKEUP;
+ if (data->cap->regbits->srst_shift >= 0)
+ *sysc_flags |= SYSC_HAS_SOFTRESET;
+ if (data->cap->regbits->autoidle_shift >= 0)
+ *sysc_flags |= SYSC_HAS_AUTOIDLE;
+ break;
+ }
+
+ if (data->cap->regbits->midle_shift >= 0 &&
+ data->cfg->midlemodes)
+ *sysc_flags |= SYSC_HAS_MIDLEMODE;
+
+ if (data->cap->regbits->sidle_shift >= 0 &&
+ data->cfg->sidlemodes)
+ *sysc_flags |= SYSC_HAS_SIDLEMODE;
+
+ if (data->cfg->quirks & SYSC_QUIRK_UNCACHED)
+ *sysc_flags |= SYSC_NO_CACHE;
+ if (data->cfg->quirks & SYSC_QUIRK_RESET_STATUS)
+ *sysc_flags |= SYSC_HAS_RESET_STATUS;
+
+ if (data->cfg->syss_mask & 1)
+ *sysc_flags |= SYSS_HAS_RESET_STATUS;
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_init_idlemodes - initialize module idle modes
+ * @dev: struct device
+ * @data: module data
+ * @idlemodes: module supported idle modes
+ */
+int omap_hwmod_init_idlemodes(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ u32 *idlemodes)
+{
+ *idlemodes = 0;
+
+ if (data->cfg->midlemodes & BIT(SYSC_IDLE_FORCE))
+ *idlemodes |= MSTANDBY_FORCE;
+ if (data->cfg->midlemodes & BIT(SYSC_IDLE_NO))
+ *idlemodes |= MSTANDBY_NO;
+ if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART))
+ *idlemodes |= MSTANDBY_SMART;
+ if (data->cfg->midlemodes & BIT(SYSC_IDLE_SMART_WKUP))
+ *idlemodes |= MSTANDBY_SMART_WKUP;
+
+ if (data->cfg->sidlemodes & BIT(SYSC_IDLE_FORCE))
+ *idlemodes |= SIDLE_FORCE;
+ if (data->cfg->sidlemodes & BIT(SYSC_IDLE_NO))
+ *idlemodes |= SIDLE_NO;
+ if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART))
+ *idlemodes |= SIDLE_SMART;
+ if (data->cfg->sidlemodes & BIT(SYSC_IDLE_SMART_WKUP))
+ *idlemodes |= SIDLE_SMART_WKUP;
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_check_module - check new module against platform data
+ * @dev: struct device
+ * @oh: module
+ * @data: new module data
+ * @sysc_fields: sysc register bits
+ * @rev_offs: revision register offset
+ * @sysc_offs: sysconfig register offset
+ * @syss_offs: sysstatus register offset
+ * @sysc_flags: sysc specific flags
+ * @idlemodes: sysc supported idlemodes
+ */
+static int omap_hwmod_check_module(struct device *dev,
+ struct omap_hwmod *oh,
+ const struct ti_sysc_module_data *data,
+ struct sysc_regbits *sysc_fields,
+ u32 rev_offs, u32 sysc_offs,
+ u32 syss_offs, u32 sysc_flags,
+ u32 idlemodes)
+{
+ if (!oh->class->sysc)
+ return -ENODEV;
+
+ if (sysc_fields != oh->class->sysc->sysc_fields)
+ dev_warn(dev, "sysc_fields %p != %p\n", sysc_fields,
+ oh->class->sysc->sysc_fields);
+
+ if (rev_offs != oh->class->sysc->rev_offs)
+ dev_warn(dev, "rev_offs %08x != %08x\n", rev_offs,
+ oh->class->sysc->rev_offs);
+ if (sysc_offs != oh->class->sysc->sysc_offs)
+ dev_warn(dev, "sysc_offs %08x != %08x\n", sysc_offs,
+ oh->class->sysc->sysc_offs);
+ if (syss_offs != oh->class->sysc->syss_offs)
+ dev_warn(dev, "syss_offs %08x != %08x\n", syss_offs,
+ oh->class->sysc->syss_offs);
+
+ if (sysc_flags != oh->class->sysc->sysc_flags)
+ dev_warn(dev, "sysc_flags %08x != %08x\n", sysc_flags,
+ oh->class->sysc->sysc_flags);
+
+ if (idlemodes != oh->class->sysc->idlemodes)
+ dev_warn(dev, "idlemodes %08x != %08x\n", idlemodes,
+ oh->class->sysc->idlemodes);
+
+ if (data->cfg->srst_udelay != oh->class->sysc->srst_udelay)
+ dev_warn(dev, "srst_udelay %i != %i\n",
+ data->cfg->srst_udelay,
+ oh->class->sysc->srst_udelay);
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_allocate_module - allocate new module
+ * @dev: struct device
+ * @oh: module
+ * @sysc_fields: sysc register bits
+ * @rev_offs: revision register offset
+ * @sysc_offs: sysconfig register offset
+ * @syss_offs: sysstatus register offset
+ * @sysc_flags: sysc specific flags
+ * @idlemodes: sysc supported idlemodes
+ *
+ * Note that the allocations here cannot use devm as ti-sysc can rebind.
+ */
+int omap_hwmod_allocate_module(struct device *dev, struct omap_hwmod *oh,
+ const struct ti_sysc_module_data *data,
+ struct sysc_regbits *sysc_fields,
+ u32 rev_offs, u32 sysc_offs, u32 syss_offs,
+ u32 sysc_flags, u32 idlemodes)
+{
+ struct omap_hwmod_class_sysconfig *sysc;
+ struct omap_hwmod_class *class;
+ void __iomem *regs = NULL;
+ unsigned long flags;
+
+ sysc = kzalloc(sizeof(*sysc), GFP_KERNEL);
+ if (!sysc)
+ return -ENOMEM;
+
+ sysc->sysc_fields = sysc_fields;
+ sysc->rev_offs = rev_offs;
+ sysc->sysc_offs = sysc_offs;
+ sysc->syss_offs = syss_offs;
+ sysc->sysc_flags = sysc_flags;
+ sysc->idlemodes = idlemodes;
+ sysc->srst_udelay = data->cfg->srst_udelay;
+
+ if (!oh->_mpu_rt_va) {
+ regs = ioremap(data->module_pa,
+ data->module_size);
+ if (!regs)
+ return -ENOMEM;
+ }
+
+ /*
+ * We need new oh->class as the other devices in the same class
+ * may not yet have ioremapped their registers.
+ */
+ class = kmemdup(oh->class, sizeof(*oh->class), GFP_KERNEL);
+ if (!class)
+ return -ENOMEM;
+
+ class->sysc = sysc;
+
+ spin_lock_irqsave(&oh->_lock, flags);
+ if (regs)
+ oh->_mpu_rt_va = regs;
+ oh->class = class;
+ oh->_state = _HWMOD_STATE_INITIALIZED;
+ _setup(oh, NULL);
+ spin_unlock_irqrestore(&oh->_lock, flags);
+
+ return 0;
+}
+
+/**
+ * omap_hwmod_init_module - initialize new module
+ * @dev: struct device
+ * @data: module data
+ * @cookie: cookie for the caller to use for later calls
+ */
+int omap_hwmod_init_module(struct device *dev,
+ const struct ti_sysc_module_data *data,
+ struct ti_sysc_cookie *cookie)
+{
+ struct omap_hwmod *oh;
+ struct sysc_regbits *sysc_fields;
+ u32 rev_offs, sysc_offs, syss_offs, sysc_flags, idlemodes;
+ int error;
+
+ if (!dev || !data)
+ return -EINVAL;
+
+ oh = _lookup(data->name);
+ if (!oh)
+ return -ENODEV;
+
+ cookie->data = oh;
+
+ error = omap_hwmod_init_regbits(dev, data, &sysc_fields);
+ if (error)
+ return error;
+
+ error = omap_hwmod_init_reg_offs(dev, data, &rev_offs,
+ &sysc_offs, &syss_offs);
+ if (error)
+ return error;
+
+ error = omap_hwmod_init_sysc_flags(dev, data, &sysc_flags);
+ if (error)
+ return error;
+
+ error = omap_hwmod_init_idlemodes(dev, data, &idlemodes);
+ if (error)
+ return error;
+
+ if (data->cfg->quirks & SYSC_QUIRK_NO_IDLE_ON_INIT)
+ oh->flags |= HWMOD_INIT_NO_IDLE;
+ if (data->cfg->quirks & SYSC_QUIRK_NO_RESET_ON_INIT)
+ oh->flags |= HWMOD_INIT_NO_RESET;
+
+ error = omap_hwmod_check_module(dev, oh, data, sysc_fields,
+ rev_offs, sysc_offs, syss_offs,
+ sysc_flags, idlemodes);
+ if (!error)
+ return error;
+
+ return omap_hwmod_allocate_module(dev, oh, data, sysc_fields,
+ rev_offs, sysc_offs, syss_offs,
+ sysc_flags, idlemodes);
+}
+
/**
* omap_hwmod_setup_earlycon_flags - set up flags for early console
*
@@ -3082,6 +3492,12 @@ static void __init omap_hwmod_setup_earlycon_flags(void)
if (np) {
uart = of_get_property(np, "ti,hwmods", NULL);
oh = omap_hwmod_lookup(uart);
+ if (!oh) {
+ uart = of_get_property(np->parent,
+ "ti,hwmods",
+ NULL);
+ oh = omap_hwmod_lookup(uart);
+ }
if (oh)
oh->flags |= DEBUG_OMAPUART_FLAGS;
}