|
@@ -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,311 @@ int __init omap_hwmod_setup_one(const char *oh_name)
|
|
|
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 0;
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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_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;
|
|
|
+
|
|
|
+ if (oh->class->sysc)
|
|
|
+ return 0;
|
|
|
+
|
|
|
+ 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
|
|
|
*
|