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;  			}  | 
