diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 418 |
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; } |