diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
| -rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 376 | 
1 files changed, 160 insertions, 216 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 0da4f2ea76c4..3b47ded5fa0c 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -138,10 +138,10 @@  #include <linux/mutex.h>  #include <linux/spinlock.h>  #include <linux/slab.h> -#include <linux/bootmem.h>  #include <linux/cpu.h>  #include <linux/of.h>  #include <linux/of_address.h> +#include <linux/bootmem.h>  #include <asm/system_misc.h> @@ -183,6 +183,24 @@  #define MOD_CLK_MAX_NAME_LEN		32  /** + * struct clkctrl_provider - clkctrl provider mapping data + * @addr: base address for the provider + * @offset: base offset for the provider + * @clkdm: base clockdomain for provider + * @node: device node associated with the provider + * @link: list link + */ +struct clkctrl_provider { +	u32			addr; +	u16			offset; +	struct clockdomain	*clkdm; +	struct device_node	*node; +	struct list_head	link; +}; + +static LIST_HEAD(clkctrl_providers); + +/**   * struct omap_hwmod_soc_ops - fn ptrs for some SoC-specific operations   * @enable_module: function to enable a module (via MODULEMODE)   * @disable_module: function to disable a module (via MODULEMODE) @@ -205,6 +223,8 @@ struct omap_hwmod_soc_ops {  	void (*update_context_lost)(struct omap_hwmod *oh);  	int (*get_context_lost)(struct omap_hwmod *oh);  	int (*disable_direct_prcm)(struct omap_hwmod *oh); +	u32 (*xlate_clkctrl)(struct omap_hwmod *oh, +			     struct clkctrl_provider *provider);  };  /* soc_ops: adapts the omap_hwmod code to the currently-booted SoC */ @@ -216,49 +236,12 @@ static LIST_HEAD(omap_hwmod_list);  /* mpu_oh: used to add/remove MPU initiator from sleepdep list */  static struct omap_hwmod *mpu_oh; -/* - * linkspace: ptr to a buffer that struct omap_hwmod_link records are - * allocated from - used to reduce the number of small memory - * allocations, which has a significant impact on performance - */ -static struct omap_hwmod_link *linkspace; - -/* - * free_ls, max_ls: array indexes into linkspace; representing the - * next free struct omap_hwmod_link index, and the maximum number of - * struct omap_hwmod_link records allocated (respectively) - */ -static unsigned short free_ls, max_ls, ls_supp; -  /* inited: set to true once the hwmod code is initialized */  static bool inited;  /* Private functions */  /** - * _fetch_next_ocp_if - return the next OCP interface in a list - * @p: ptr to a ptr to the list_head inside the ocp_if to return - * @i: pointer to the index of the element pointed to by @p in the list - * - * Return a pointer to the struct omap_hwmod_ocp_if record - * containing the struct list_head pointed to by @p, and increment - * @p such that a future call to this routine will return the next - * record. - */ -static struct omap_hwmod_ocp_if *_fetch_next_ocp_if(struct list_head **p, -						    int *i) -{ -	struct omap_hwmod_ocp_if *oi; - -	oi = list_entry(*p, struct omap_hwmod_link, node)->ocp_if; -	*p = (*p)->next; - -	*i = *i + 1; - -	return oi; -} - -/**   * _update_sysc_cache - return the module OCP_SYSCONFIG register, keep copy   * @oh: struct omap_hwmod *   * @@ -728,6 +711,103 @@ static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)  	return clkdm_del_sleepdep(clkdm, init_clkdm);  } +static const struct of_device_id ti_clkctrl_match_table[] __initconst = { +	{ .compatible = "ti,clkctrl" }, +	{ } +}; + +static int _match_clkdm(struct clockdomain *clkdm, void *user) +{ +	struct clkctrl_provider *provider = user; + +	if (clkdm_xlate_address(clkdm) == provider->addr) { +		pr_debug("%s: Matched clkdm %s for addr %x (%s)\n", __func__, +			 clkdm->name, provider->addr, +			 provider->node->parent->name); +		provider->clkdm = clkdm; + +		return -1; +	} + +	return 0; +} + +static int _setup_clkctrl_provider(struct device_node *np) +{ +	const __be32 *addrp; +	struct clkctrl_provider *provider; + +	provider = memblock_virt_alloc(sizeof(*provider), 0); +	if (!provider) +		return -ENOMEM; + +	addrp = of_get_address(np, 0, NULL, NULL); +	provider->addr = (u32)of_translate_address(np, addrp); +	provider->offset = provider->addr & 0xff; +	provider->addr &= ~0xff; +	provider->node = np; + +	clkdm_for_each(_match_clkdm, provider); + +	if (!provider->clkdm) { +		pr_err("%s: nothing matched for node %s (%x)\n", +		       __func__, np->parent->name, provider->addr); +		memblock_free_early(__pa(provider), sizeof(*provider)); +		return -EINVAL; +	} + +	list_add(&provider->link, &clkctrl_providers); + +	return 0; +} + +static int _init_clkctrl_providers(void) +{ +	struct device_node *np; +	int ret = 0; + +	for_each_matching_node(np, ti_clkctrl_match_table) { +		ret = _setup_clkctrl_provider(np); +		if (ret) +			break; +	} + +	return ret; +} + +static u32 _omap4_xlate_clkctrl(struct omap_hwmod *oh, +				struct clkctrl_provider *provider) +{ +	return oh->prcm.omap4.clkctrl_offs - +	       provider->offset - provider->clkdm->clkdm_offs; +} + +static struct clk *_lookup_clkctrl_clk(struct omap_hwmod *oh) +{ +	struct clkctrl_provider *provider; +	struct clk *clk; + +	if (!soc_ops.xlate_clkctrl) +		return NULL; + +	list_for_each_entry(provider, &clkctrl_providers, link) { +		if (provider->clkdm == oh->clkdm) { +			struct of_phandle_args clkspec; + +			clkspec.np = provider->node; +			clkspec.args_count = 2; +			clkspec.args[0] = soc_ops.xlate_clkctrl(oh, provider); +			clkspec.args[1] = 0; + +			clk = of_clk_get_from_provider(&clkspec); + +			return clk; +		} +	} + +	return NULL; +} +  /**   * _init_main_clk - get a struct clk * for the the hwmod's main functional clk   * @oh: struct omap_hwmod * @@ -739,22 +819,16 @@ static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)  static int _init_main_clk(struct omap_hwmod *oh)  {  	int ret = 0; -	char name[MOD_CLK_MAX_NAME_LEN]; -	struct clk *clk; -	static const char modck[] = "_mod_ck"; +	struct clk *clk = NULL; -	if (strlen(oh->name) >= MOD_CLK_MAX_NAME_LEN - strlen(modck)) -		pr_warn("%s: warning: cropping name for %s\n", __func__, -			oh->name); +	clk = _lookup_clkctrl_clk(oh); -	strlcpy(name, oh->name, MOD_CLK_MAX_NAME_LEN - strlen(modck)); -	strlcat(name, modck, MOD_CLK_MAX_NAME_LEN); - -	clk = clk_get(NULL, name); -	if (!IS_ERR(clk)) { +	if (!IS_ERR_OR_NULL(clk)) { +		pr_debug("%s: mapped main_clk %s for %s\n", __func__, +			 __clk_get_name(clk), oh->name); +		oh->main_clk = __clk_get_name(clk);  		oh->_clk = clk;  		soc_ops.disable_direct_prcm(oh); -		oh->main_clk = kstrdup(name, GFP_KERNEL);  	} else {  		if (!oh->main_clk)  			return 0; @@ -794,15 +868,10 @@ static int _init_main_clk(struct omap_hwmod *oh)  static int _init_interface_clks(struct omap_hwmod *oh)  {  	struct omap_hwmod_ocp_if *os; -	struct list_head *p;  	struct clk *c; -	int i = 0;  	int ret = 0; -	p = oh->slave_ports.next; - -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (!os->clk)  			continue; @@ -905,19 +974,13 @@ static void _disable_optional_clocks(struct omap_hwmod *oh)  static int _enable_clocks(struct omap_hwmod *oh)  {  	struct omap_hwmod_ocp_if *os; -	struct list_head *p; -	int i = 0;  	pr_debug("omap_hwmod: %s: enabling clocks\n", oh->name);  	if (oh->_clk)  		clk_enable(oh->_clk); -	p = oh->slave_ports.next; - -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); - +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE))  			clk_enable(os->_clk);  	} @@ -939,19 +1002,13 @@ static int _enable_clocks(struct omap_hwmod *oh)  static int _disable_clocks(struct omap_hwmod *oh)  {  	struct omap_hwmod_ocp_if *os; -	struct list_head *p; -	int i = 0;  	pr_debug("omap_hwmod: %s: disabling clocks\n", oh->name);  	if (oh->_clk)  		clk_disable(oh->_clk); -	p = oh->slave_ports.next; - -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); - +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (os->_clk && (os->flags & OCPIF_SWSUP_IDLE))  			clk_disable(os->_clk);  	} @@ -1190,16 +1247,11 @@ static int _get_sdma_req_by_name(struct omap_hwmod *oh, const char *name,  static int _get_addr_space_by_name(struct omap_hwmod *oh, const char *name,  				   u32 *pa_start, u32 *pa_end)  { -	int i, j; +	int j;  	struct omap_hwmod_ocp_if *os; -	struct list_head *p = NULL;  	bool found = false; -	p = oh->slave_ports.next; - -	i = 0; -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (!os->addr)  			return -ENOENT; @@ -1239,18 +1291,13 @@ static int _get_addr_space_by_name(struct omap_hwmod *oh, const char *name,  static void __init _save_mpu_port_index(struct omap_hwmod *oh)  {  	struct omap_hwmod_ocp_if *os = NULL; -	struct list_head *p; -	int i = 0;  	if (!oh)  		return;  	oh->_int_flags |= _HWMOD_NO_MPU_PORT; -	p = oh->slave_ports.next; - -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (os->user & OCP_USER_MPU) {  			oh->_mpu_port = os;  			oh->_int_flags &= ~_HWMOD_NO_MPU_PORT; @@ -1393,7 +1440,7 @@ static void _enable_sysc(struct omap_hwmod *oh)  	 */  	if ((oh->flags & HWMOD_SET_DEFAULT_CLOCKACT) &&  	    (sf & SYSC_HAS_CLOCKACTIVITY)) -		_set_clockactivity(oh, oh->class->sysc->clockact, &v); +		_set_clockactivity(oh, CLOCKACT_TEST_ICLK, &v);  	_write_sysconfig(v, oh); @@ -1547,13 +1594,13 @@ static int _init_clkdm(struct omap_hwmod *oh)   * _init_clocks - clk_get() all clocks associated with this hwmod. Retrieve as   * well the clockdomain.   * @oh: struct omap_hwmod * - * @data: not used; pass NULL + * @np: device_node mapped to this hwmod   *   * Called by omap_hwmod_setup_*() (after omap2_clk_init()).   * Resolves all clock names embedded in the hwmod.  Returns 0 on   * success, or a negative error code on failure.   */ -static int _init_clocks(struct omap_hwmod *oh, void *data) +static int _init_clocks(struct omap_hwmod *oh, struct device_node *np)  {  	int ret = 0; @@ -2092,7 +2139,7 @@ static int _enable(struct omap_hwmod *oh)  	r = (soc_ops.wait_target_ready) ? soc_ops.wait_target_ready(oh) :  		-EINVAL; -	if (oh->clkdm) +	if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO))  		clkdm_allow_idle(oh->clkdm);  	if (!r) { @@ -2149,7 +2196,12 @@ static int _idle(struct omap_hwmod *oh)  		_idle_sysc(oh);  	_del_initiator_dep(oh, mpu_oh); -	if (oh->clkdm) +	/* +	 * If HWMOD_CLKDM_NOAUTO is set then we don't +	 * deny idle the clkdm again since idle was already denied +	 * in _enable() +	 */ +	if (oh->clkdm && !(oh->flags & HWMOD_CLKDM_NOAUTO))  		clkdm_deny_idle(oh->clkdm);  	if (oh->flags & HWMOD_BLOCK_WFI) @@ -2394,24 +2446,21 @@ static int __init _init(struct omap_hwmod *oh, void *data)  {  	int r, index;  	struct device_node *np = NULL; +	struct device_node *bus;  	if (oh->_state != _HWMOD_STATE_REGISTERED)  		return 0; -	if (of_have_populated_dt()) { -		struct device_node *bus; - -		bus = of_find_node_by_name(NULL, "ocp"); -		if (!bus) -			return -ENODEV; +	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); -	} +	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);  	r = _init_mpu_rt_base(oh, NULL, index, np);  	if (r < 0) { @@ -2420,7 +2469,7 @@ static int __init _init(struct omap_hwmod *oh, void *data)  		return 0;  	} -	r = _init_clocks(oh, NULL); +	r = _init_clocks(oh, np);  	if (r < 0) {  		WARN(1, "omap_hwmod: %s: couldn't init clocks\n", oh->name);  		return -EINVAL; @@ -2451,15 +2500,11 @@ static int __init _init(struct omap_hwmod *oh, void *data)  static void __init _setup_iclk_autoidle(struct omap_hwmod *oh)  {  	struct omap_hwmod_ocp_if *os; -	struct list_head *p; -	int i = 0; +  	if (oh->_state != _HWMOD_STATE_INITIALIZED)  		return; -	p = oh->slave_ports.next; - -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); +	list_for_each_entry(os, &oh->slave_ports, node) {  		if (!os->_clk)  			continue; @@ -2657,7 +2702,6 @@ static int __init _register(struct omap_hwmod *oh)  	list_add_tail(&oh->node, &omap_hwmod_list); -	INIT_LIST_HEAD(&oh->master_ports);  	INIT_LIST_HEAD(&oh->slave_ports);  	spin_lock_init(&oh->_lock);  	lockdep_set_class(&oh->_lock, &oh->hwmod_key); @@ -2675,49 +2719,10 @@ static int __init _register(struct omap_hwmod *oh)  }  /** - * _alloc_links - return allocated memory for hwmod links - * @ml: pointer to a struct omap_hwmod_link * for the master link - * @sl: pointer to a struct omap_hwmod_link * for the slave link - * - * Return pointers to two struct omap_hwmod_link records, via the - * addresses pointed to by @ml and @sl.  Will first attempt to return - * memory allocated as part of a large initial block, but if that has - * been exhausted, will allocate memory itself.  Since ideally this - * second allocation path will never occur, the number of these - * 'supplemental' allocations will be logged when debugging is - * enabled.  Returns 0. - */ -static int __init _alloc_links(struct omap_hwmod_link **ml, -			       struct omap_hwmod_link **sl) -{ -	unsigned int sz; - -	if ((free_ls + LINKS_PER_OCP_IF) <= max_ls) { -		*ml = &linkspace[free_ls++]; -		*sl = &linkspace[free_ls++]; -		return 0; -	} - -	sz = sizeof(struct omap_hwmod_link) * LINKS_PER_OCP_IF; - -	*sl = NULL; -	*ml = memblock_virt_alloc(sz, 0); - -	*sl = (void *)(*ml) + sizeof(struct omap_hwmod_link); - -	ls_supp++; -	pr_debug("omap_hwmod: supplemental link allocations needed: %d\n", -		 ls_supp * LINKS_PER_OCP_IF); - -	return 0; -}; - -/**   * _add_link - add an interconnect between two IP blocks   * @oi: pointer to a struct omap_hwmod_ocp_if record   * - * Add struct omap_hwmod_link records connecting the master IP block - * specified in @oi->master to @oi, and connecting the slave IP block + * Add struct omap_hwmod_link records connecting the slave IP block   * specified in @oi->slave to @oi.  This code is assumed to run before   * preemption or SMP has been enabled, thus avoiding the need for   * locking in this code.  Changes to this assumption will require @@ -2725,19 +2730,10 @@ static int __init _alloc_links(struct omap_hwmod_link **ml,   */  static int __init _add_link(struct omap_hwmod_ocp_if *oi)  { -	struct omap_hwmod_link *ml, *sl; -  	pr_debug("omap_hwmod: %s -> %s: adding link\n", oi->master->name,  		 oi->slave->name); -	_alloc_links(&ml, &sl); - -	ml->ocp_if = oi; -	list_add(&ml->node, &oi->master->master_ports); -	oi->master->masters_cnt++; - -	sl->ocp_if = oi; -	list_add(&sl->node, &oi->slave->slave_ports); +	list_add(&oi->node, &oi->slave->slave_ports);  	oi->slave->slaves_cnt++;  	return 0; @@ -2784,45 +2780,6 @@ static int __init _register_link(struct omap_hwmod_ocp_if *oi)  	return 0;  } -/** - * _alloc_linkspace - allocate large block of hwmod links - * @ois: pointer to an array of struct omap_hwmod_ocp_if records to count - * - * Allocate a large block of struct omap_hwmod_link records.  This - * improves boot time significantly by avoiding the need to allocate - * individual records one by one.  If the number of records to - * allocate in the block hasn't been manually specified, this function - * will count the number of struct omap_hwmod_ocp_if records in @ois - * and use that to determine the allocation size.  For SoC families - * that require multiple list registrations, such as OMAP3xxx, this - * estimation process isn't optimal, so manual estimation is advised - * in those cases.  Returns -EEXIST if the allocation has already occurred - * or 0 upon success. - */ -static int __init _alloc_linkspace(struct omap_hwmod_ocp_if **ois) -{ -	unsigned int i = 0; -	unsigned int sz; - -	if (linkspace) { -		WARN(1, "linkspace already allocated\n"); -		return -EEXIST; -	} - -	if (max_ls == 0) -		while (ois[i++]) -			max_ls += LINKS_PER_OCP_IF; - -	sz = sizeof(struct omap_hwmod_link) * max_ls; - -	pr_debug("omap_hwmod: %s: allocating %d byte linkspace (%d links)\n", -		 __func__, sz, max_ls); - -	linkspace = memblock_virt_alloc(sz, 0); - -	return 0; -} -  /* Static functions intended only for use in soc_ops field function pointers */  /** @@ -3180,13 +3137,6 @@ int __init omap_hwmod_register_links(struct omap_hwmod_ocp_if **ois)  	if (ois[0] == NULL) /* Empty list */  		return 0; -	if (!linkspace) { -		if (_alloc_linkspace(ois)) { -			pr_err("omap_hwmod: could not allocate link space\n"); -			return -ENOMEM; -		} -	} -  	i = 0;  	do {  		r = _register_link(ois[i]); @@ -3398,14 +3348,10 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh, unsigned long flags)  		ret += _count_sdma_reqs(oh);  	if (flags & IORESOURCE_MEM) { -		int i = 0;  		struct omap_hwmod_ocp_if *os; -		struct list_head *p = oh->slave_ports.next; -		while (i < oh->slaves_cnt) { -			os = _fetch_next_ocp_if(&p, &i); +		list_for_each_entry(os, &oh->slave_ports, node)  			ret += _count_ocp_if_addr_spaces(os); -		}  	}  	return ret; @@ -3424,7 +3370,6 @@ int omap_hwmod_count_resources(struct omap_hwmod *oh, unsigned long flags)  int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)  {  	struct omap_hwmod_ocp_if *os; -	struct list_head *p;  	int i, j, mpu_irqs_cnt, sdma_reqs_cnt, addr_cnt;  	int r = 0; @@ -3454,11 +3399,7 @@ int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res)  		r++;  	} -	p = oh->slave_ports.next; - -	i = 0; -	while (i < oh->slaves_cnt) { -		os = _fetch_next_ocp_if(&p, &i); +	list_for_each_entry(os, &oh->slave_ports, node) {  		addr_cnt = _count_ocp_if_addr_spaces(os);  		for (j = 0; j < addr_cnt; j++) { @@ -3890,6 +3831,7 @@ void __init omap_hwmod_init(void)  		soc_ops.update_context_lost = _omap4_update_context_lost;  		soc_ops.get_context_lost = _omap4_get_context_lost;  		soc_ops.disable_direct_prcm = _omap4_disable_direct_prcm; +		soc_ops.xlate_clkctrl = _omap4_xlate_clkctrl;  	} else if (cpu_is_ti814x() || cpu_is_ti816x() || soc_is_am33xx() ||  		   soc_is_am43xx()) {  		soc_ops.enable_module = _omap4_enable_module; @@ -3904,6 +3846,8 @@ void __init omap_hwmod_init(void)  		WARN(1, "omap_hwmod: unknown SoC type\n");  	} +	_init_clkctrl_providers(); +  	inited = true;  }  |