diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
| -rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 224 | 
1 files changed, 177 insertions, 47 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index d9ee0ff094d..6c074f37cdd 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -72,7 +72,7 @@   *            | (../mach-omap2/omap_hwmod*)   |   *            +-------------------------------+   *            | OMAP clock/PRCM/register fns  | - *            | (__raw_{read,write}l, clk*)   | + *            | ({read,write}l_relaxed, clk*) |   *            +-------------------------------+   *   * Device drivers should not contain any OMAP-specific code or data in @@ -399,7 +399,7 @@ static int _set_clockactivity(struct omap_hwmod *oh, u8 clockact, u32 *v)  }  /** - * _set_softreset: set OCP_SYSCONFIG.CLOCKACTIVITY bits in @v + * _set_softreset: set OCP_SYSCONFIG.SOFTRESET bit in @v   * @oh: struct omap_hwmod *   * @v: pointer to register contents to modify   * @@ -427,6 +427,36 @@ static int _set_softreset(struct omap_hwmod *oh, u32 *v)  }  /** + * _clear_softreset: clear OCP_SYSCONFIG.SOFTRESET bit in @v + * @oh: struct omap_hwmod * + * @v: pointer to register contents to modify + * + * Clear the SOFTRESET bit in @v for hwmod @oh.  Returns -EINVAL upon + * error or 0 upon success. + */ +static int _clear_softreset(struct omap_hwmod *oh, u32 *v) +{ +	u32 softrst_mask; + +	if (!oh->class->sysc || +	    !(oh->class->sysc->sysc_flags & SYSC_HAS_SOFTRESET)) +		return -EINVAL; + +	if (!oh->class->sysc->sysc_fields) { +		WARN(1, +		     "omap_hwmod: %s: sysc_fields absent for sysconfig class\n", +		     oh->name); +		return -EINVAL; +	} + +	softrst_mask = (0x1 << oh->class->sysc->sysc_fields->srst_shift); + +	*v &= ~softrst_mask; + +	return 0; +} + +/**   * _wait_softreset_complete - wait for an OCP softreset to complete   * @oh: struct omap_hwmod * to wait on   * @@ -656,6 +686,8 @@ static struct clockdomain *_get_clkdm(struct omap_hwmod *oh)  	if (oh->clkdm) {  		return oh->clkdm;  	} else if (oh->_clk) { +		if (__clk_get_flags(oh->_clk) & CLK_IS_BASIC) +			return NULL;  		clk = to_clk_hw_omap(__clk_get_hw(oh->_clk));  		return  clk->clkdm;  	} @@ -785,6 +817,7 @@ static int _init_interface_clks(struct omap_hwmod *oh)  			pr_warning("omap_hwmod: %s: cannot clk_get interface_clk %s\n",  				   oh->name, os->clk);  			ret = -EINVAL; +			continue;  		}  		os->_clk = c;  		/* @@ -821,6 +854,7 @@ static int _init_opt_clks(struct omap_hwmod *oh)  			pr_warning("omap_hwmod: %s: cannot clk_get opt_clk %s\n",  				   oh->name, oc->clk);  			ret = -EINVAL; +			continue;  		}  		oc->_clk = c;  		/* @@ -1544,7 +1578,7 @@ static int _init_clkdm(struct omap_hwmod *oh)  	if (!oh->clkdm) {  		pr_warning("omap_hwmod: %s: could not associate to clkdm %s\n",  			oh->name, oh->clkdm_name); -		return -EINVAL; +		return 0;  	}  	pr_debug("omap_hwmod: %s: associated to clkdm %s\n", @@ -1911,25 +1945,33 @@ static int _ocp_softreset(struct omap_hwmod *oh)  	ret = _set_softreset(oh, &v);  	if (ret)  		goto dis_opt_clks; +  	_write_sysconfig(v, oh);  	if (oh->class->sysc->srst_udelay)  		udelay(oh->class->sysc->srst_udelay);  	c = _wait_softreset_complete(oh); -	if (c == MAX_MODULE_SOFTRESET_WAIT) +	if (c == MAX_MODULE_SOFTRESET_WAIT) {  		pr_warning("omap_hwmod: %s: softreset failed (waited %d usec)\n",  			   oh->name, MAX_MODULE_SOFTRESET_WAIT); -	else +		ret = -ETIMEDOUT; +		goto dis_opt_clks; +	} else {  		pr_debug("omap_hwmod: %s: softreset in %d usec\n", oh->name, c); +	} + +	ret = _clear_softreset(oh, &v); +	if (ret) +		goto dis_opt_clks; + +	_write_sysconfig(v, oh);  	/*  	 * XXX add _HWMOD_STATE_WEDGED for modules that don't come back from  	 * _wait_target_ready() or _reset()  	 */ -	ret = (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 0; -  dis_opt_clks:  	if (oh->flags & HWMOD_CONTROL_OPT_CLKS_IN_RESET)  		_disable_optional_clocks(oh); @@ -2326,56 +2368,102 @@ static int _shutdown(struct omap_hwmod *oh)  	return 0;  } +static int of_dev_find_hwmod(struct device_node *np, +			     struct omap_hwmod *oh) +{ +	int count, i, res; +	const char *p; + +	count = of_property_count_strings(np, "ti,hwmods"); +	if (count < 1) +		return -ENODEV; + +	for (i = 0; i < count; i++) { +		res = of_property_read_string_index(np, "ti,hwmods", +						    i, &p); +		if (res) +			continue; +		if (!strcmp(p, oh->name)) { +			pr_debug("omap_hwmod: dt %s[%i] uses hwmod %s\n", +				 np->name, i, oh->name); +			return i; +		} +	} + +	return -ENODEV; +} +  /**   * of_dev_hwmod_lookup - look up needed hwmod from dt blob   * @np: struct device_node *   * @oh: struct omap_hwmod * + * @index: index of the entry found + * @found: struct device_node * found or NULL   *   * Parse the dt blob and find out needed hwmod. Recursive function is   * implemented to take care hierarchical dt blob parsing. - * Return: The device node on success or NULL on failure. + * Return: Returns 0 on success, -ENODEV when not found.   */ -static struct device_node *of_dev_hwmod_lookup(struct device_node *np, -						struct omap_hwmod *oh) +static int of_dev_hwmod_lookup(struct device_node *np, +			       struct omap_hwmod *oh, +			       int *index, +			       struct device_node **found)  { -	struct device_node *np0 = NULL, *np1 = NULL; -	const char *p; +	struct device_node *np0 = NULL; +	int res; + +	res = of_dev_find_hwmod(np, oh); +	if (res >= 0) { +		*found = np; +		*index = res; +		return 0; +	}  	for_each_child_of_node(np, np0) { -		if (of_find_property(np0, "ti,hwmods", NULL)) { -			p = of_get_property(np0, "ti,hwmods", NULL); -			if (!strcmp(p, oh->name)) -				return np0; -			np1 = of_dev_hwmod_lookup(np0, oh); -			if (np1) -				return np1; +		struct device_node *fc; +		int i; + +		res = of_dev_hwmod_lookup(np0, oh, &i, &fc); +		if (res == 0) { +			*found = fc; +			*index = i; +			return 0;  		}  	} -	return NULL; + +	*found = NULL; +	*index = 0; + +	return -ENODEV;  }  /**   * _init_mpu_rt_base - populate the virtual address for a hwmod   * @oh: struct omap_hwmod * to locate the virtual address + * @data: (unused, caller should pass NULL) + * @index: index of the reg entry iospace in device tree + * @np: struct device_node * of the IP block's device node in the DT data   *   * Cache the virtual address used by the MPU to access this IP block's   * registers.  This address is needed early so the OCP registers that   * are part of the device's address space can be ioremapped properly. - * No return value. + * + * Returns 0 on success, -EINVAL if an invalid hwmod is passed, and + * -ENXIO on absent or invalid register target address space.   */ -static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data) +static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, +				    int index, struct device_node *np)  {  	struct omap_hwmod_addr_space *mem;  	void __iomem *va_start = NULL; -	struct device_node *np;  	if (!oh) -		return; +		return -EINVAL;  	_save_mpu_port_index(oh);  	if (oh->_int_flags & _HWMOD_NO_MPU_PORT) -		return; +		return -ENXIO;  	mem = _find_mpu_rt_addr_space(oh);  	if (!mem) { @@ -2383,25 +2471,28 @@ static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data)  			 oh->name);  		/* Extract the IO space from device tree blob */ -		if (!of_have_populated_dt()) -			return; +		if (!np) +			return -ENXIO; -		np = of_dev_hwmod_lookup(of_find_node_by_name(NULL, "ocp"), oh); -		if (np) -			va_start = of_iomap(np, oh->mpu_rt_idx); +		va_start = of_iomap(np, index + oh->mpu_rt_idx);  	} else {  		va_start = ioremap(mem->pa_start, mem->pa_end - mem->pa_start);  	}  	if (!va_start) { -		pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); -		return; +		if (mem) +			pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); +		else +			pr_err("omap_hwmod: %s: Missing dt reg%i for %s\n", +			       oh->name, index, np->full_name); +		return -ENXIO;  	}  	pr_debug("omap_hwmod: %s: MPU register target at va %p\n",  		 oh->name, va_start);  	oh->_mpu_rt_va = va_start; +	return 0;  }  /** @@ -2414,18 +2505,40 @@ static void __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data)   * registered at this point.  This is the first of two phases for   * hwmod initialization.  Code called here does not touch any hardware   * registers, it simply prepares internal data structures.  Returns 0 - * upon success or if the hwmod isn't registered, or -EINVAL upon - * failure. + * upon success or if the hwmod isn't registered or if the hwmod's + * address space is not defined, or -EINVAL upon failure.   */  static int __init _init(struct omap_hwmod *oh, void *data)  { -	int r; +	int r, index; +	struct device_node *np = NULL;  	if (oh->_state != _HWMOD_STATE_REGISTERED)  		return 0; -	if (oh->class->sysc) -		_init_mpu_rt_base(oh, NULL); +	if (of_have_populated_dt()) { +		struct device_node *bus; + +		bus = of_find_node_by_name(NULL, "ocp"); +		if (!bus) +			return -ENODEV; + +		r = of_dev_hwmod_lookup(bus, oh, &index, &np); +		if (r) +			pr_debug("omap_hwmod: %s missing dt data\n", oh->name); +		else if (np && index) +			pr_warn("omap_hwmod: %s using broken dt data from %s\n", +				oh->name, np->name); +	} + +	if (oh->class->sysc) { +		r = _init_mpu_rt_base(oh, NULL, index, np); +		if (r < 0) { +			WARN(1, "omap_hwmod: %s: doesn't have mpu register target base\n", +			     oh->name); +			return 0; +		} +	}  	r = _init_clocks(oh, NULL);  	if (r < 0) { @@ -2433,6 +2546,13 @@ static int __init _init(struct omap_hwmod *oh, void *data)  		return -EINVAL;  	} +	if (np) { +		if (of_find_property(np, "ti,no-reset-on-init", NULL)) +			oh->flags |= HWMOD_INIT_NO_RESET; +		if (of_find_property(np, "ti,no-idle-on-init", NULL)) +			oh->flags |= HWMOD_INIT_NO_IDLE; +	} +  	oh->_state = _HWMOD_STATE_INITIALIZED;  	return 0; @@ -2676,9 +2796,7 @@ static int __init _alloc_links(struct omap_hwmod_link **ml,  	sz = sizeof(struct omap_hwmod_link) * LINKS_PER_OCP_IF;  	*sl = NULL; -	*ml = alloc_bootmem(sz); - -	memset(*ml, 0, sz); +	*ml = memblock_virt_alloc(sz, 0);  	*sl = (void *)(*ml) + sizeof(struct omap_hwmod_link); @@ -2797,9 +2915,7 @@ static int __init _alloc_linkspace(struct omap_hwmod_ocp_if **ois)  	pr_debug("omap_hwmod: %s: allocating %d byte linkspace (%d links)\n",  		 __func__, sz, max_ls); -	linkspace = alloc_bootmem(sz); - -	memset(linkspace, 0, sz); +	linkspace = memblock_virt_alloc(sz, 0);  	return 0;  } @@ -3114,17 +3230,17 @@ static int _am33xx_is_hardreset_asserted(struct omap_hwmod *oh,  u32 omap_hwmod_read(struct omap_hwmod *oh, u16 reg_offs)  {  	if (oh->flags & HWMOD_16BIT_REG) -		return __raw_readw(oh->_mpu_rt_va + reg_offs); +		return readw_relaxed(oh->_mpu_rt_va + reg_offs);  	else -		return __raw_readl(oh->_mpu_rt_va + reg_offs); +		return readl_relaxed(oh->_mpu_rt_va + reg_offs);  }  void omap_hwmod_write(u32 v, struct omap_hwmod *oh, u16 reg_offs)  {  	if (oh->flags & HWMOD_16BIT_REG) -		__raw_writew(v, oh->_mpu_rt_va + reg_offs); +		writew_relaxed(v, oh->_mpu_rt_va + reg_offs);  	else -		__raw_writel(v, oh->_mpu_rt_va + reg_offs); +		writel_relaxed(v, oh->_mpu_rt_va + reg_offs);  }  /** @@ -3150,6 +3266,11 @@ int omap_hwmod_softreset(struct omap_hwmod *oh)  		goto error;  	_write_sysconfig(v, oh); +	ret = _clear_softreset(oh, &v); +	if (ret) +		goto error; +	_write_sysconfig(v, oh); +  error:  	return ret;  } @@ -4115,6 +4236,7 @@ void __init omap_hwmod_init(void)  		soc_ops.assert_hardreset = _omap2_assert_hardreset;  		soc_ops.deassert_hardreset = _omap2_deassert_hardreset;  		soc_ops.is_hardreset_asserted = _omap2_is_hardreset_asserted; +		soc_ops.init_clkdm = _init_clkdm;  	} else if (cpu_is_omap44xx() || soc_is_omap54xx() || soc_is_dra7xx()) {  		soc_ops.enable_module = _omap4_enable_module;  		soc_ops.disable_module = _omap4_disable_module; @@ -4125,6 +4247,14 @@ void __init omap_hwmod_init(void)  		soc_ops.init_clkdm = _init_clkdm;  		soc_ops.update_context_lost = _omap4_update_context_lost;  		soc_ops.get_context_lost = _omap4_get_context_lost; +	} else if (soc_is_am43xx()) { +		soc_ops.enable_module = _omap4_enable_module; +		soc_ops.disable_module = _omap4_disable_module; +		soc_ops.wait_target_ready = _omap4_wait_target_ready; +		soc_ops.assert_hardreset = _am33xx_assert_hardreset; +		soc_ops.deassert_hardreset = _am33xx_deassert_hardreset; +		soc_ops.is_hardreset_asserted = _am33xx_is_hardreset_asserted; +		soc_ops.init_clkdm = _init_clkdm;  	} else if (soc_is_am33xx()) {  		soc_ops.enable_module = _am33xx_enable_module;  		soc_ops.disable_module = _am33xx_disable_module;  | 
