Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 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); } } |