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 57 if (match > best) { 58 clk = p->clk; 59 if (match != 3) 60 best = match; 61 else 62 break; 63 } 64 } 65 return clk; 66} 67 68struct clk *clk_get_sys(const char *dev_id, const char *con_id) 69{ 70 struct clk *clk; 71 72 mutex_lock(&clocks_mutex); 73 clk = clk_find(dev_id, con_id); 74 if (clk && !__clk_get(clk)) 75 clk = NULL; 76 mutex_unlock(&clocks_mutex); 77 78 return clk ? clk : ERR_PTR(-ENOENT); 79} 80EXPORT_SYMBOL(clk_get_sys); 81 82struct clk *clk_get(struct device *dev, const char *con_id) 83{ 84 const char *dev_id = dev ? dev_name(dev) : NULL; 85 86 return clk_get_sys(dev_id, con_id); 87} 88EXPORT_SYMBOL(clk_get); 89 90void clk_put(struct clk *clk) 91{ 92 __clk_put(clk); 93} 94EXPORT_SYMBOL(clk_put); 95 96void clkdev_add(struct clk_lookup *cl) 97{ 98 mutex_lock(&clocks_mutex); 99 list_add_tail(&cl->node, &clocks); 100 mutex_unlock(&clocks_mutex); 101} 102EXPORT_SYMBOL(clkdev_add); 103 104void __init clkdev_add_table(struct clk_lookup *cl, size_t num) 105{ 106 mutex_lock(&clocks_mutex); 107 while (num--) { 108 list_add_tail(&cl->node, &clocks); 109 cl++; 110 } 111 mutex_unlock(&clocks_mutex); 112} 113 114#define MAX_DEV_ID 20 115#define MAX_CON_ID 16 116 117struct clk_lookup_alloc { 118 struct clk_lookup cl; 119 char dev_id[MAX_DEV_ID]; 120 char con_id[MAX_CON_ID]; 121}; 122 123struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, 124 const char *dev_fmt, ...) 125{ 126 struct clk_lookup_alloc *cla; 127 128 cla = kzalloc(sizeof(*cla), GFP_KERNEL); 129 if (!cla) 130 return NULL; 131 132 cla->cl.clk = clk; 133 if (con_id) { 134 strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); 135 cla->cl.con_id = cla->con_id; 136 } 137 138 if (dev_fmt) { 139 va_list ap; 140 141 va_start(ap, dev_fmt); 142 vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); 143 cla->cl.dev_id = cla->dev_id; 144 va_end(ap); 145 } 146 147 return &cla->cl; 148} 149EXPORT_SYMBOL(clkdev_alloc); 150 151int clk_add_alias(const char *alias, const char *alias_dev_name, char *id, 152 struct device *dev) 153{ 154 struct clk *r = clk_get(dev, id); 155 struct clk_lookup *l; 156 157 if (IS_ERR(r)) 158 return PTR_ERR(r); 159 160 l = clkdev_alloc(r, alias, alias_dev_name); 161 clk_put(r); 162 if (!l) 163 return -ENODEV; 164 clkdev_add(l); 165 return 0; 166} 167EXPORT_SYMBOL(clk_add_alias); 168 169/* 170 * clkdev_drop - remove a clock dynamically allocated 171 */ 172void clkdev_drop(struct clk_lookup *cl) 173{ 174 mutex_lock(&clocks_mutex); 175 list_del(&cl->node); 176 mutex_unlock(&clocks_mutex); 177 kfree(cl); 178} 179EXPORT_SYMBOL(clkdev_drop); 180