diff options
Diffstat (limited to 'arch/arm/mach-omap2/omap_hwmod.c')
-rw-r--r-- | arch/arm/mach-omap2/omap_hwmod.c | 569 |
1 files changed, 124 insertions, 445 deletions
diff --git a/arch/arm/mach-omap2/omap_hwmod.c b/arch/arm/mach-omap2/omap_hwmod.c index 2dbd63239c54..104256a5f0f7 100644 --- a/arch/arm/mach-omap2/omap_hwmod.c +++ b/arch/arm/mach-omap2/omap_hwmod.c @@ -994,6 +994,34 @@ static int _enable_clocks(struct omap_hwmod *oh) } /** + * _omap4_clkctrl_managed_by_clkfwk - true if clkctrl managed by clock framework + * @oh: struct omap_hwmod * + */ +static bool _omap4_clkctrl_managed_by_clkfwk(struct omap_hwmod *oh) +{ + if (oh->prcm.omap4.flags & HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK) + return true; + + return false; +} + +/** + * _omap4_has_clkctrl_clock - returns true if a module has clkctrl clock + * @oh: struct omap_hwmod * + */ +static bool _omap4_has_clkctrl_clock(struct omap_hwmod *oh) +{ + if (oh->prcm.omap4.clkctrl_offs) + return true; + + if (!oh->prcm.omap4.clkctrl_offs && + oh->prcm.omap4.flags & HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET) + return true; + + return false; +} + +/** * _disable_clocks - disable hwmod main clock and interface clocks * @oh: struct omap_hwmod * * @@ -1030,7 +1058,8 @@ static int _disable_clocks(struct omap_hwmod *oh) */ static void _omap4_enable_module(struct omap_hwmod *oh) { - if (!oh->clkdm || !oh->prcm.omap4.modulemode) + if (!oh->clkdm || !oh->prcm.omap4.modulemode || + _omap4_clkctrl_managed_by_clkfwk(oh)) return; pr_debug("omap_hwmod: %s: %s: %d\n", @@ -1061,8 +1090,10 @@ static int _omap4_wait_target_disable(struct omap_hwmod *oh) if (oh->flags & HWMOD_NO_IDLEST) return 0; - if (!oh->prcm.omap4.clkctrl_offs && - !(oh->prcm.omap4.flags & HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET)) + if (_omap4_clkctrl_managed_by_clkfwk(oh)) + return 0; + + if (!_omap4_has_clkctrl_clock(oh)) return 0; return omap_cm_wait_module_idle(oh->clkdm->prcm_partition, @@ -1071,215 +1102,6 @@ static int _omap4_wait_target_disable(struct omap_hwmod *oh) } /** - * _count_mpu_irqs - count the number of MPU IRQ lines associated with @oh - * @oh: struct omap_hwmod *oh - * - * Count and return the number of MPU IRQs associated with the hwmod - * @oh. Used to allocate struct resource data. Returns 0 if @oh is - * NULL. - */ -static int _count_mpu_irqs(struct omap_hwmod *oh) -{ - struct omap_hwmod_irq_info *ohii; - int i = 0; - - if (!oh || !oh->mpu_irqs) - return 0; - - do { - ohii = &oh->mpu_irqs[i++]; - } while (ohii->irq != -1); - - return i-1; -} - -/** - * _count_sdma_reqs - count the number of SDMA request lines associated with @oh - * @oh: struct omap_hwmod *oh - * - * Count and return the number of SDMA request lines associated with - * the hwmod @oh. Used to allocate struct resource data. Returns 0 - * if @oh is NULL. - */ -static int _count_sdma_reqs(struct omap_hwmod *oh) -{ - struct omap_hwmod_dma_info *ohdi; - int i = 0; - - if (!oh || !oh->sdma_reqs) - return 0; - - do { - ohdi = &oh->sdma_reqs[i++]; - } while (ohdi->dma_req != -1); - - return i-1; -} - -/** - * _count_ocp_if_addr_spaces - count the number of address space entries for @oh - * @oh: struct omap_hwmod *oh - * - * Count and return the number of address space ranges associated with - * the hwmod @oh. Used to allocate struct resource data. Returns 0 - * if @oh is NULL. - */ -static int _count_ocp_if_addr_spaces(struct omap_hwmod_ocp_if *os) -{ - struct omap_hwmod_addr_space *mem; - int i = 0; - - if (!os || !os->addr) - return 0; - - do { - mem = &os->addr[i++]; - } while (mem->pa_start != mem->pa_end); - - return i-1; -} - -/** - * _get_mpu_irq_by_name - fetch MPU interrupt line number by name - * @oh: struct omap_hwmod * to operate on - * @name: pointer to the name of the MPU interrupt number to fetch (optional) - * @irq: pointer to an unsigned int to store the MPU IRQ number to - * - * Retrieve a MPU hardware IRQ line number named by @name associated - * with the IP block pointed to by @oh. The IRQ number will be filled - * into the address pointed to by @dma. When @name is non-null, the - * IRQ line number associated with the named entry will be returned. - * If @name is null, the first matching entry will be returned. Data - * order is not meaningful in hwmod data, so callers are strongly - * encouraged to use a non-null @name whenever possible to avoid - * unpredictable effects if hwmod data is later added that causes data - * ordering to change. Returns 0 upon success or a negative error - * code upon error. - */ -static int _get_mpu_irq_by_name(struct omap_hwmod *oh, const char *name, - unsigned int *irq) -{ - int i; - bool found = false; - - if (!oh->mpu_irqs) - return -ENOENT; - - i = 0; - while (oh->mpu_irqs[i].irq != -1) { - if (name == oh->mpu_irqs[i].name || - !strcmp(name, oh->mpu_irqs[i].name)) { - found = true; - break; - } - i++; - } - - if (!found) - return -ENOENT; - - *irq = oh->mpu_irqs[i].irq; - - return 0; -} - -/** - * _get_sdma_req_by_name - fetch SDMA request line ID by name - * @oh: struct omap_hwmod * to operate on - * @name: pointer to the name of the SDMA request line to fetch (optional) - * @dma: pointer to an unsigned int to store the request line ID to - * - * Retrieve an SDMA request line ID named by @name on the IP block - * pointed to by @oh. The ID will be filled into the address pointed - * to by @dma. When @name is non-null, the request line ID associated - * with the named entry will be returned. If @name is null, the first - * matching entry will be returned. Data order is not meaningful in - * hwmod data, so callers are strongly encouraged to use a non-null - * @name whenever possible to avoid unpredictable effects if hwmod - * data is later added that causes data ordering to change. Returns 0 - * upon success or a negative error code upon error. - */ -static int _get_sdma_req_by_name(struct omap_hwmod *oh, const char *name, - unsigned int *dma) -{ - int i; - bool found = false; - - if (!oh->sdma_reqs) - return -ENOENT; - - i = 0; - while (oh->sdma_reqs[i].dma_req != -1) { - if (name == oh->sdma_reqs[i].name || - !strcmp(name, oh->sdma_reqs[i].name)) { - found = true; - break; - } - i++; - } - - if (!found) - return -ENOENT; - - *dma = oh->sdma_reqs[i].dma_req; - - return 0; -} - -/** - * _get_addr_space_by_name - fetch address space start & end by name - * @oh: struct omap_hwmod * to operate on - * @name: pointer to the name of the address space to fetch (optional) - * @pa_start: pointer to a u32 to store the starting address to - * @pa_end: pointer to a u32 to store the ending address to - * - * Retrieve address space start and end addresses for the IP block - * pointed to by @oh. The data will be filled into the addresses - * pointed to by @pa_start and @pa_end. When @name is non-null, the - * address space data associated with the named entry will be - * returned. If @name is null, the first matching entry will be - * returned. Data order is not meaningful in hwmod data, so callers - * are strongly encouraged to use a non-null @name whenever possible - * to avoid unpredictable effects if hwmod data is later added that - * causes data ordering to change. Returns 0 upon success or a - * negative error code upon error. - */ -static int _get_addr_space_by_name(struct omap_hwmod *oh, const char *name, - u32 *pa_start, u32 *pa_end) -{ - int j; - struct omap_hwmod_ocp_if *os; - bool found = false; - - list_for_each_entry(os, &oh->slave_ports, node) { - - if (!os->addr) - return -ENOENT; - - j = 0; - while (os->addr[j].pa_start != os->addr[j].pa_end) { - if (name == os->addr[j].name || - !strcmp(name, os->addr[j].name)) { - found = true; - break; - } - j++; - } - - if (found) - break; - } - - if (!found) - return -ENOENT; - - *pa_start = os->addr[j].pa_start; - *pa_end = os->addr[j].pa_end; - - return 0; -} - -/** * _save_mpu_port_index - find and save the index to @oh's MPU port * @oh: struct omap_hwmod * * @@ -1330,32 +1152,6 @@ static struct omap_hwmod_ocp_if *_find_mpu_rt_port(struct omap_hwmod *oh) }; /** - * _find_mpu_rt_addr_space - return MPU register target address space for @oh - * @oh: struct omap_hwmod * - * - * Returns a pointer to the struct omap_hwmod_addr_space record representing - * the register target MPU address space; or returns NULL upon error. - */ -static struct omap_hwmod_addr_space * __init _find_mpu_rt_addr_space(struct omap_hwmod *oh) -{ - struct omap_hwmod_ocp_if *os; - struct omap_hwmod_addr_space *mem; - int found = 0, i = 0; - - os = _find_mpu_rt_port(oh); - if (!os || !os->addr) - return NULL; - - do { - mem = &os->addr[i++]; - if (mem->flags & ADDR_TYPE_RT) - found = 1; - } while (!found && mem->pa_start != mem->pa_end); - - return (found) ? mem : NULL; -} - -/** * _enable_sysc - try to bring a module out of idle via OCP_SYSCONFIG * @oh: struct omap_hwmod * * @@ -1847,7 +1643,8 @@ static int _omap4_disable_module(struct omap_hwmod *oh) { int v; - if (!oh->clkdm || !oh->prcm.omap4.modulemode) + if (!oh->clkdm || !oh->prcm.omap4.modulemode || + _omap4_clkctrl_managed_by_clkfwk(oh)) return -EINVAL; /* @@ -2362,6 +2159,75 @@ static int of_dev_hwmod_lookup(struct device_node *np, } /** + * omap_hwmod_parse_module_range - map module IO range from device tree + * @oh: struct omap_hwmod * + * @np: struct device_node * + * + * Parse the device tree range an interconnect target module provides + * for it's child device IP blocks. This way we can support the old + * "ti,hwmods" property with just dts data without a need for platform + * data for IO resources. And we don't need all the child IP device + * nodes available in the dts. + */ +int omap_hwmod_parse_module_range(struct omap_hwmod *oh, + struct device_node *np, + struct resource *res) +{ + struct property *prop; + const __be32 *ranges; + const char *name; + u32 nr_addr, nr_size; + u64 base, size; + int len, error; + + if (!res) + return -EINVAL; + + ranges = of_get_property(np, "ranges", &len); + if (!ranges) + return -ENOENT; + + len /= sizeof(*ranges); + + if (len < 3) + return -EINVAL; + + of_property_for_each_string(np, "compatible", prop, name) + if (!strncmp("ti,sysc-", name, 8)) + break; + + if (!name) + return -ENOENT; + + error = of_property_read_u32(np, "#address-cells", &nr_addr); + if (error) + return -ENOENT; + + error = of_property_read_u32(np, "#size-cells", &nr_size); + if (error) + return -ENOENT; + + if (nr_addr != 1 || nr_size != 1) { + pr_err("%s: invalid range for %s->%s\n", __func__, + oh->name, np->name); + return -EINVAL; + } + + ranges++; + base = of_translate_address(np, ranges++); + size = be32_to_cpup(ranges); + + pr_debug("omap_hwmod: %s %s at 0x%llx size 0x%llx\n", + oh->name, np->name, base, size); + + res->start = base; + res->end = base + size - 1; + res->flags = IORESOURCE_MEM; + + return 0; +} + +/** * _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) @@ -2381,8 +2247,9 @@ static int of_dev_hwmod_lookup(struct device_node *np, 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 resource res; + int error; if (!oh) return -EINVAL; @@ -2397,28 +2264,22 @@ static int __init _init_mpu_rt_base(struct omap_hwmod *oh, void *data, if (oh->_int_flags & _HWMOD_NO_MPU_PORT) return -ENXIO; - mem = _find_mpu_rt_addr_space(oh); - if (!mem) { - pr_debug("omap_hwmod: %s: no MPU register target found\n", - oh->name); + if (!np) { + pr_err("omap_hwmod: %s: no dt node\n", oh->name); + return -ENXIO; + } - /* Extract the IO space from device tree blob */ - if (!np) { - pr_err("omap_hwmod: %s: no dt node\n", oh->name); - return -ENXIO; - } + /* Do we have a dts range for the interconnect target module? */ + error = omap_hwmod_parse_module_range(oh, np, &res); + if (!error) + va_start = ioremap(res.start, resource_size(&res)); + /* No ranges, rely on device reg entry */ + if (!va_start) 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) { - if (mem) - pr_err("omap_hwmod: %s: Could not ioremap\n", oh->name); - else - pr_err("omap_hwmod: %s: Missing dt reg%i for %pOF\n", - oh->name, index, np); + pr_err("omap_hwmod: %s: Missing dt reg%i for %pOF\n", + oh->name, index, np); return -ENXIO; } @@ -2829,8 +2690,10 @@ static int _omap4_wait_target_ready(struct omap_hwmod *oh) if (!_find_mpu_rt_port(oh)) return 0; - if (!oh->prcm.omap4.clkctrl_offs && - !(oh->prcm.omap4.flags & HWMOD_OMAP4_ZERO_CLKCTRL_OFFSET)) + if (_omap4_clkctrl_managed_by_clkfwk(oh)) + return 0; + + if (!_omap4_has_clkctrl_clock(oh)) return 0; /* XXX check module SIDLEMODE, hardreset status */ @@ -2986,8 +2849,7 @@ static int _omap4_disable_direct_prcm(struct omap_hwmod *oh) if (!oh) return -EINVAL; - oh->prcm.omap4.clkctrl_offs = 0; - oh->prcm.omap4.modulemode = 0; + oh->prcm.omap4.flags |= HWMOD_OMAP4_CLKFWK_CLKCTR_CLOCK; return 0; } @@ -3322,189 +3184,6 @@ int omap_hwmod_shutdown(struct omap_hwmod *oh) */ /** - * omap_hwmod_count_resources - count number of struct resources needed by hwmod - * @oh: struct omap_hwmod * - * @flags: Type of resources to include when counting (IRQ/DMA/MEM) - * - * Count the number of struct resource array elements necessary to - * contain omap_hwmod @oh resources. Intended to be called by code - * that registers omap_devices. Intended to be used to determine the - * size of a dynamically-allocated struct resource array, before - * calling omap_hwmod_fill_resources(). Returns the number of struct - * resource array elements needed. - * - * XXX This code is not optimized. It could attempt to merge adjacent - * resource IDs. - * - */ -int omap_hwmod_count_resources(struct omap_hwmod *oh, unsigned long flags) -{ - int ret = 0; - - if (flags & IORESOURCE_IRQ) - ret += _count_mpu_irqs(oh); - - if (flags & IORESOURCE_DMA) - ret += _count_sdma_reqs(oh); - - if (flags & IORESOURCE_MEM) { - struct omap_hwmod_ocp_if *os; - - list_for_each_entry(os, &oh->slave_ports, node) - ret += _count_ocp_if_addr_spaces(os); - } - - return ret; -} - -/** - * omap_hwmod_fill_resources - fill struct resource array with hwmod data - * @oh: struct omap_hwmod * - * @res: pointer to the first element of an array of struct resource to fill - * - * Fill the struct resource array @res with resource data from the - * omap_hwmod @oh. Intended to be called by code that registers - * omap_devices. See also omap_hwmod_count_resources(). Returns the - * number of array elements filled. - */ -int omap_hwmod_fill_resources(struct omap_hwmod *oh, struct resource *res) -{ - struct omap_hwmod_ocp_if *os; - int i, j, mpu_irqs_cnt, sdma_reqs_cnt, addr_cnt; - int r = 0; - - /* For each IRQ, DMA, memory area, fill in array.*/ - - mpu_irqs_cnt = _count_mpu_irqs(oh); - for (i = 0; i < mpu_irqs_cnt; i++) { - unsigned int irq; - - if (oh->xlate_irq) - irq = oh->xlate_irq((oh->mpu_irqs + i)->irq); - else - irq = (oh->mpu_irqs + i)->irq; - (res + r)->name = (oh->mpu_irqs + i)->name; - (res + r)->start = irq; - (res + r)->end = irq; - (res + r)->flags = IORESOURCE_IRQ; - r++; - } - - sdma_reqs_cnt = _count_sdma_reqs(oh); - for (i = 0; i < sdma_reqs_cnt; i++) { - (res + r)->name = (oh->sdma_reqs + i)->name; - (res + r)->start = (oh->sdma_reqs + i)->dma_req; - (res + r)->end = (oh->sdma_reqs + i)->dma_req; - (res + r)->flags = IORESOURCE_DMA; - r++; - } - - list_for_each_entry(os, &oh->slave_ports, node) { - addr_cnt = _count_ocp_if_addr_spaces(os); - - for (j = 0; j < addr_cnt; j++) { - (res + r)->name = (os->addr + j)->name; - (res + r)->start = (os->addr + j)->pa_start; - (res + r)->end = (os->addr + j)->pa_end; - (res + r)->flags = IORESOURCE_MEM; - r++; - } - } - - return r; -} - -/** - * omap_hwmod_fill_dma_resources - fill struct resource array with dma data - * @oh: struct omap_hwmod * - * @res: pointer to the array of struct resource to fill - * - * Fill the struct resource array @res with dma resource data from the - * omap_hwmod @oh. Intended to be called by code that registers - * omap_devices. See also omap_hwmod_count_resources(). Returns the - * number of array elements filled. - */ -int omap_hwmod_fill_dma_resources(struct omap_hwmod *oh, struct resource *res) -{ - int i, sdma_reqs_cnt; - int r = 0; - - sdma_reqs_cnt = _count_sdma_reqs(oh); - for (i = 0; i < sdma_reqs_cnt; i++) { - (res + r)->name = (oh->sdma_reqs + i)->name; - (res + r)->start = (oh->sdma_reqs + i)->dma_req; - (res + r)->end = (oh->sdma_reqs + i)->dma_req; - (res + r)->flags = IORESOURCE_DMA; - r++; - } - - return r; -} - -/** - * omap_hwmod_get_resource_byname - fetch IP block integration data by name - * @oh: struct omap_hwmod * to operate on - * @type: one of the IORESOURCE_* constants from include/linux/ioport.h - * @name: pointer to the name of the data to fetch (optional) - * @rsrc: pointer to a struct resource, allocated by the caller - * - * Retrieve MPU IRQ, SDMA request line, or address space start/end - * data for the IP block pointed to by @oh. The data will be filled - * into a struct resource record pointed to by @rsrc. The struct - * resource must be allocated by the caller. When @name is non-null, - * the data associated with the matching entry in the IRQ/SDMA/address - * space hwmod data arrays will be returned. If @name is null, the - * first array entry will be returned. Data order is not meaningful - * in hwmod data, so callers are strongly encouraged to use a non-null - * @name whenever possible to avoid unpredictable effects if hwmod - * data is later added that causes data ordering to change. This - * function is only intended for use by OMAP core code. Device - * drivers should not call this function - the appropriate bus-related - * data accessor functions should be used instead. Returns 0 upon - * success or a negative error code upon error. - */ -int omap_hwmod_get_resource_byname(struct omap_hwmod *oh, unsigned int type, - const char *name, struct resource *rsrc) -{ - int r; - unsigned int irq, dma; - u32 pa_start, pa_end; - - if (!oh || !rsrc) - return -EINVAL; - - if (type == IORESOURCE_IRQ) { - r = _get_mpu_irq_by_name(oh, name, &irq); - if (r) - return r; - - rsrc->start = irq; - rsrc->end = irq; - } else if (type == IORESOURCE_DMA) { - r = _get_sdma_req_by_name(oh, name, &dma); - if (r) - return r; - - rsrc->start = dma; - rsrc->end = dma; - } else if (type == IORESOURCE_MEM) { - r = _get_addr_space_by_name(oh, name, &pa_start, &pa_end); - if (r) - return r; - - rsrc->start = pa_start; - rsrc->end = pa_end; - } else { - return -EINVAL; - } - - rsrc->flags = type; - rsrc->name = name; - - return 0; -} - -/** * omap_hwmod_get_pwrdm - return pointer to this module's main powerdomain * @oh: struct omap_hwmod * * |