1 /* 2 * drivers/clk/clkdev.c 3 * 4 * Copyright (C) 2008 Russell King. 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * Helper for the clk API to assist looking up a struct clk. 11 */ 12 #include <linux/module.h> 13 #include <linux/kernel.h> 14 #include <linux/device.h> 15 #include <linux/list.h> 16 #include <linux/errno.h> 17 #include <linux/err.h> 18 #include <linux/string.h> 19 #include <linux/mutex.h> 20 #include <linux/clk.h> 21 #include <linux/clkdev.h> 22 23 static LIST_HEAD(clocks); 24 static DEFINE_MUTEX(clocks_mutex); 25 26 /* 27 * Find the correct struct clk for the device and connection ID. 28 * We do slightly fuzzy matching here: 29 * An entry with a NULL ID is assumed to be a wildcard. 30 * If an entry has a device ID, it must match 31 * If an entry has a connection ID, it must match 32 * Then we take the most specific entry - with the following 33 * order of precedence: dev+con > dev only > con only. 34 */ 35 static struct clk *clk_find(const char *dev_id, const char *con_id) 36 { 37 struct clk_lookup *p; 38 struct clk *clk = NULL; 39 int match, best = 0; 40 41 list_for_each_entry(p, &clocks, node) { 42 match = 0; 43 if (p->dev_id) { 44 if (!dev_id || strcmp(p->dev_id, dev_id)) 45 continue; 46 match += 2; 47 } 48 if (p->con_id) { 49 if (!con_id || strcmp(p->con_id, con_id)) 50 continue; 51 match += 1; 52 } 53 54 if (match > best) { 55 clk = p->clk; 56 if (match != 3) 57 best = match; 58 else 59 break; 60 } 61 } 62 return clk; 63 } 64 65 struct clk *clk_get_sys(const char *dev_id, const char *con_id) 66 { 67 struct clk *clk; 68 69 mutex_lock(&clocks_mutex); 70 clk = clk_find(dev_id, con_id); 71 if (clk && !__clk_get(clk)) 72 clk = NULL; 73 mutex_unlock(&clocks_mutex); 74 75 return clk ? clk : ERR_PTR(-ENOENT); 76 } 77 EXPORT_SYMBOL(clk_get_sys); 78 79 struct clk *clk_get(struct device *dev, const char *con_id) 80 { 81 const char *dev_id = dev ? dev_name(dev) : NULL; 82 83 return clk_get_sys(dev_id, con_id); 84 } 85 EXPORT_SYMBOL(clk_get); 86 87 void clk_put(struct clk *clk) 88 { 89 __clk_put(clk); 90 } 91 EXPORT_SYMBOL(clk_put); 92 93 void clkdev_add(struct clk_lookup *cl) 94 { 95 mutex_lock(&clocks_mutex); 96 list_add_tail(&cl->node, &clocks); 97 mutex_unlock(&clocks_mutex); 98 } 99 EXPORT_SYMBOL(clkdev_add); 100 101 void __init clkdev_add_table(struct clk_lookup *cl, size_t num) 102 { 103 mutex_lock(&clocks_mutex); 104 while (num--) { 105 list_add_tail(&cl->node, &clocks); 106 cl++; 107 } 108 mutex_unlock(&clocks_mutex); 109 } 110 111 #define MAX_DEV_ID 20 112 #define MAX_CON_ID 16 113 114 struct clk_lookup_alloc { 115 struct clk_lookup cl; 116 char dev_id[MAX_DEV_ID]; 117 char con_id[MAX_CON_ID]; 118 }; 119 120 struct clk_lookup * __init_refok 121 clkdev_alloc(struct clk *clk, const char *con_id, const char *dev_fmt, ...) 122 { 123 struct clk_lookup_alloc *cla; 124 125 cla = __clkdev_alloc(sizeof(*cla)); 126 if (!cla) 127 return NULL; 128 129 cla->cl.clk = clk; 130 if (con_id) { 131 strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); 132 cla->cl.con_id = cla->con_id; 133 } 134 135 if (dev_fmt) { 136 va_list ap; 137 138 va_start(ap, dev_fmt); 139 vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); 140 cla->cl.dev_id = cla->dev_id; 141 va_end(ap); 142 } 143 144 return &cla->cl; 145 } 146 EXPORT_SYMBOL(clkdev_alloc); 147 148 int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, 149 struct device *dev) 150 { 151 struct clk *r = clk_get(dev, id); 152 struct clk_lookup *l; 153 154 if (IS_ERR(r)) 155 return PTR_ERR(r); 156 157 l = clkdev_alloc(r, alias, alias_dev_name); 158 clk_put(r); 159 if (!l) 160 return -ENODEV; 161 clkdev_add(l); 162 return 0; 163 } 164 EXPORT_SYMBOL(clk_add_alias); 165 166 /* 167 * clkdev_drop - remove a clock dynamically allocated 168 */ 169 void clkdev_drop(struct clk_lookup *cl) 170 { 171 mutex_lock(&clocks_mutex); 172 list_del(&cl->node); 173 mutex_unlock(&clocks_mutex); 174 kfree(cl); 175 } 176 EXPORT_SYMBOL(clkdev_drop); 177