at v2.6.34 178 lines 3.7 kB view raw
1/* 2 * arch/arm/common/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/slab.h> 22 23#include <asm/clkdev.h> 24#include <mach/clkdev.h> 25 26static LIST_HEAD(clocks); 27static DEFINE_MUTEX(clocks_mutex); 28 29/* 30 * Find the correct struct clk for the device and connection ID. 31 * We do slightly fuzzy matching here: 32 * An entry with a NULL ID is assumed to be a wildcard. 33 * If an entry has a device ID, it must match 34 * If an entry has a connection ID, it must match 35 * Then we take the most specific entry - with the following 36 * order of precedence: dev+con > dev only > con only. 37 */ 38static struct clk *clk_find(const char *dev_id, const char *con_id) 39{ 40 struct clk_lookup *p; 41 struct clk *clk = NULL; 42 int match, best = 0; 43 44 list_for_each_entry(p, &clocks, node) { 45 match = 0; 46 if (p->dev_id) { 47 if (!dev_id || strcmp(p->dev_id, dev_id)) 48 continue; 49 match += 2; 50 } 51 if (p->con_id) { 52 if (!con_id || strcmp(p->con_id, con_id)) 53 continue; 54 match += 1; 55 } 56 if (match == 0) 57 continue; 58 59 if (match > best) { 60 clk = p->clk; 61 best = match; 62 } 63 } 64 return clk; 65} 66 67struct clk *clk_get_sys(const char *dev_id, const char *con_id) 68{ 69 struct clk *clk; 70 71 mutex_lock(&clocks_mutex); 72 clk = clk_find(dev_id, con_id); 73 if (clk && !__clk_get(clk)) 74 clk = NULL; 75 mutex_unlock(&clocks_mutex); 76 77 return clk ? clk : ERR_PTR(-ENOENT); 78} 79EXPORT_SYMBOL(clk_get_sys); 80 81struct clk *clk_get(struct device *dev, const char *con_id) 82{ 83 const char *dev_id = dev ? dev_name(dev) : NULL; 84 85 return clk_get_sys(dev_id, con_id); 86} 87EXPORT_SYMBOL(clk_get); 88 89void clk_put(struct clk *clk) 90{ 91 __clk_put(clk); 92} 93EXPORT_SYMBOL(clk_put); 94 95void clkdev_add(struct clk_lookup *cl) 96{ 97 mutex_lock(&clocks_mutex); 98 list_add_tail(&cl->node, &clocks); 99 mutex_unlock(&clocks_mutex); 100} 101EXPORT_SYMBOL(clkdev_add); 102 103void __init clkdev_add_table(struct clk_lookup *cl, size_t num) 104{ 105 mutex_lock(&clocks_mutex); 106 while (num--) { 107 list_add_tail(&cl->node, &clocks); 108 cl++; 109 } 110 mutex_unlock(&clocks_mutex); 111} 112 113#define MAX_DEV_ID 20 114#define MAX_CON_ID 16 115 116struct clk_lookup_alloc { 117 struct clk_lookup cl; 118 char dev_id[MAX_DEV_ID]; 119 char con_id[MAX_CON_ID]; 120}; 121 122struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, 123 const char *dev_fmt, ...) 124{ 125 struct clk_lookup_alloc *cla; 126 127 cla = kzalloc(sizeof(*cla), GFP_KERNEL); 128 if (!cla) 129 return NULL; 130 131 cla->cl.clk = clk; 132 if (con_id) { 133 strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); 134 cla->cl.con_id = cla->con_id; 135 } 136 137 if (dev_fmt) { 138 va_list ap; 139 140 va_start(ap, dev_fmt); 141 vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); 142 cla->cl.dev_id = cla->dev_id; 143 va_end(ap); 144 } 145 146 return &cla->cl; 147} 148EXPORT_SYMBOL(clkdev_alloc); 149 150int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, 151 struct device *dev) 152{ 153 struct clk *r = clk_get(dev, id); 154 struct clk_lookup *l; 155 156 if (IS_ERR(r)) 157 return PTR_ERR(r); 158 159 l = clkdev_alloc(r, alias, alias_dev_name); 160 clk_put(r); 161 if (!l) 162 return -ENODEV; 163 clkdev_add(l); 164 return 0; 165} 166EXPORT_SYMBOL(clk_add_alias); 167 168/* 169 * clkdev_drop - remove a clock dynamically allocated 170 */ 171void clkdev_drop(struct clk_lookup *cl) 172{ 173 mutex_lock(&clocks_mutex); 174 list_del(&cl->node); 175 mutex_unlock(&clocks_mutex); 176 kfree(cl); 177} 178EXPORT_SYMBOL(clkdev_drop);