From 8c87970543b17adfbd829ec1541c72a7da518acf Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:04:56 -0800 Subject: ARM: OMAP2+: Add functions to allocate module data from device tree We can have ti-sysc driver manage the interconnect target module via platform data callback functions to hwmod code. This allows initializing and idling the devices using dts data instead of the legacy static data for interconnect target modules. Let's add functions to configure the module sysconfig data with platform callbacks from ti-sysc driver. Cc: Paul Walmsley Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod.c | 309 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 308 insertions(+), 1 deletion(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 124f9af34a15..20895fcadbf5 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -145,6 +145,8 @@ #include +#include + #include #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 * -- cgit From a88443068572aaa79048bc19c741e0edae17faa7 Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Fri, 23 Feb 2018 08:59:23 -0800 Subject: ARM: OMAP2+: Add checks for device tree based sysconfig data We can check the device tree based sysconfig data against the existing platform data to make sure we're not introducing regressions. Then at some point after the sysconfig data comes from device tree, we can just drop the related platform data. Cc: Paul Walmsley Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod.c | 109 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 3 deletions(-) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 20895fcadbf5..fca22f7ac0e3 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -3062,6 +3062,53 @@ 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 @@ -3111,7 +3158,7 @@ static int omap_hwmod_init_regbits(struct device *dev, return -EINVAL; } - return 0; + return omap_hwmod_check_sysc(dev, data, *sysc_fields); } /** @@ -3249,6 +3296,59 @@ int omap_hwmod_init_idlemodes(struct device *dev, 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 @@ -3359,8 +3459,11 @@ int omap_hwmod_init_module(struct device *dev, if (data->cfg->quirks & SYSC_QUIRK_NO_RESET_ON_INIT) oh->flags |= HWMOD_INIT_NO_RESET; - if (oh->class->sysc) - return 0; + 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, -- cgit From 15618256224769621b2c2300f66ad9513135ffaa Mon Sep 17 00:00:00 2001 From: Tony Lindgren Date: Thu, 22 Feb 2018 14:04:25 -0800 Subject: ARM: OMAP2+: Try to parse earlycon from parent too With ti-sysc driver the "ti,hwmods" property will be moved to the interconnect target module instead of the child device. To keep earlycon working, we need to match against the interconnect target module in the ti-sysc case. Cc: Paul Walmsley Signed-off-by: Tony Lindgren --- arch/arm/mach-omap2/omap_hwmod.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'arch/arm/mach-omap2/omap_hwmod.c') diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index fca22f7ac0e3..2d554a48eb61 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -3492,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; } -- cgit