diff options
Diffstat (limited to 'drivers/clk/ti/clk.c')
| -rw-r--r-- | drivers/clk/ti/clk.c | 167 | 
1 files changed, 167 insertions, 0 deletions
diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c new file mode 100644 index 00000000000..b1a6f7144f3 --- /dev/null +++ b/drivers/clk/ti/clk.c @@ -0,0 +1,167 @@ +/* + * TI clock support + * + * Copyright (C) 2013 Texas Instruments, Inc. + * + * Tero Kristo <t-kristo@ti.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/clk-provider.h> +#include <linux/clkdev.h> +#include <linux/clk/ti.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/list.h> + +#undef pr_fmt +#define pr_fmt(fmt) "%s: " fmt, __func__ + +static int ti_dt_clk_memmap_index; +struct ti_clk_ll_ops *ti_clk_ll_ops; + +/** + * ti_dt_clocks_register - register DT alias clocks during boot + * @oclks: list of clocks to register + * + * Register alias or non-standard DT clock entries during boot. By + * default, DT clocks are found based on their node name. If any + * additional con-id / dev-id -> clock mapping is required, use this + * function to list these. + */ +void __init ti_dt_clocks_register(struct ti_dt_clk oclks[]) +{ +	struct ti_dt_clk *c; +	struct device_node *node; +	struct clk *clk; +	struct of_phandle_args clkspec; + +	for (c = oclks; c->node_name != NULL; c++) { +		node = of_find_node_by_name(NULL, c->node_name); +		clkspec.np = node; +		clk = of_clk_get_from_provider(&clkspec); + +		if (!IS_ERR(clk)) { +			c->lk.clk = clk; +			clkdev_add(&c->lk); +		} else { +			pr_warn("failed to lookup clock node %s\n", +				c->node_name); +		} +	} +} + +struct clk_init_item { +	struct device_node *node; +	struct clk_hw *hw; +	ti_of_clk_init_cb_t func; +	struct list_head link; +}; + +static LIST_HEAD(retry_list); + +/** + * ti_clk_retry_init - retries a failed clock init at later phase + * @node: device not for the clock + * @hw: partially initialized clk_hw struct for the clock + * @func: init function to be called for the clock + * + * Adds a failed clock init to the retry list. The retry list is parsed + * once all the other clocks have been initialized. + */ +int __init ti_clk_retry_init(struct device_node *node, struct clk_hw *hw, +			      ti_of_clk_init_cb_t func) +{ +	struct clk_init_item *retry; + +	pr_debug("%s: adding to retry list...\n", node->name); +	retry = kzalloc(sizeof(*retry), GFP_KERNEL); +	if (!retry) +		return -ENOMEM; + +	retry->node = node; +	retry->func = func; +	retry->hw = hw; +	list_add(&retry->link, &retry_list); + +	return 0; +} + +/** + * ti_clk_get_reg_addr - get register address for a clock register + * @node: device node for the clock + * @index: register index from the clock node + * + * Builds clock register address from device tree information. This + * is a struct of type clk_omap_reg. + */ +void __iomem *ti_clk_get_reg_addr(struct device_node *node, int index) +{ +	struct clk_omap_reg *reg; +	u32 val; +	u32 tmp; + +	reg = (struct clk_omap_reg *)&tmp; +	reg->index = ti_dt_clk_memmap_index; + +	if (of_property_read_u32_index(node, "reg", index, &val)) { +		pr_err("%s must have reg[%d]!\n", node->name, index); +		return NULL; +	} + +	reg->offset = val; + +	return (void __iomem *)tmp; +} + +/** + * ti_dt_clk_init_provider - init master clock provider + * @parent: master node + * @index: internal index for clk_reg_ops + * + * Initializes a master clock IP block and its child clock nodes. + * Regmap is provided for accessing the register space for the + * IP block and all the clocks under it. + */ +void ti_dt_clk_init_provider(struct device_node *parent, int index) +{ +	const struct of_device_id *match; +	struct device_node *np; +	struct device_node *clocks; +	of_clk_init_cb_t clk_init_cb; +	struct clk_init_item *retry; +	struct clk_init_item *tmp; + +	ti_dt_clk_memmap_index = index; + +	/* get clocks for this parent */ +	clocks = of_get_child_by_name(parent, "clocks"); +	if (!clocks) { +		pr_err("%s missing 'clocks' child node.\n", parent->name); +		return; +	} + +	for_each_child_of_node(clocks, np) { +		match = of_match_node(&__clk_of_table, np); +		if (!match) +			continue; +		clk_init_cb = (of_clk_init_cb_t)match->data; +		pr_debug("%s: initializing: %s\n", __func__, np->name); +		clk_init_cb(np); +	} + +	list_for_each_entry_safe(retry, tmp, &retry_list, link) { +		pr_debug("retry-init: %s\n", retry->node->name); +		retry->func(retry->hw, retry->node); +		list_del(&retry->link); +		kfree(retry); +	} +}  | 
