From mboxrd@z Thu Jan 1 00:00:00 1970 From: Kuo-Jung Su Date: Tue, 1 Apr 2014 16:46:53 +0800 Subject: [U-Boot] [PATCH v12 2/8] arm: add legacy linux clock framework support In-Reply-To: <1396342019-644-1-git-send-email-dantesu@gmail.com> References: <1396342019-644-1-git-send-email-dantesu@gmail.com> Message-ID: <1396342019-644-3-git-send-email-dantesu@gmail.com> List-Id: MIME-Version: 1.0 Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit To: u-boot@lists.denx.de From: Kuo-Jung Su This ports the legacy linux clock framework from linux-3.10 (i.e., no common clock support) Please check the list bellow for details: 1. CONFIG_CLKDEV_LOOKUP - Core clock management APIs - clk_get_sys(), clk_put(), clkdev_add(), clkdev_add_table() clkdev_alloc(), clkdev_drop(), clk_register_clkdev(), clk_register_clkdevs() 2. CONFIG_HAVE_MACH_CLKDEV (CONFIG_HAVE_ARCH_CLKDEV in U-Boot) - Use this to have machine specific structs & defines included in asm/clkdev.h 3. CONFIG_HAVE_CLK_PREPARE - This prepares the clock source for use - clk_prepare(), clk_unprepare() Signed-off-by: Kuo-Jung Su CC: Albert Aribaud CC: Wolfgang Denk --- Changes for v12: - Initial commit arch/arm/include/asm/clkdev.h | 29 ++++++ drivers/Makefile | 1 + drivers/clk/Makefile | 8 ++ drivers/clk/clkdev.c | 207 +++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 181 +++++++++++++++++++++++++++++++++++ include/linux/clkdev.h | 45 +++++++++ 6 files changed, 471 insertions(+) create mode 100644 arch/arm/include/asm/clkdev.h create mode 100644 drivers/clk/Makefile create mode 100644 drivers/clk/clkdev.c create mode 100644 include/linux/clk.h create mode 100644 include/linux/clkdev.h diff --git a/arch/arm/include/asm/clkdev.h b/arch/arm/include/asm/clkdev.h new file mode 100644 index 0000000..2b605aa --- /dev/null +++ b/arch/arm/include/asm/clkdev.h @@ -0,0 +1,29 @@ +/* + * arch/arm/include/asm/clkdev.h + * + * Copyright (C) 2008 Russell King. + * + * 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. + * + * Helper for the clk API to assist looking up a struct clk. + */ +#ifndef __ASM_CLKDEV_H +#define __ASM_CLKDEV_H + +#ifdef CONFIG_HAVE_ARCH_CLKDEV +#include +#else +#define __clk_get(clk) ({ 1; }) +#define __clk_put(clk) do { } while (0) +#endif + +#include + +static inline struct clk_lookup_alloc *__clkdev_alloc(size_t size) +{ + return calloc(1, size); +} + +#endif diff --git a/drivers/Makefile b/drivers/Makefile index 5d03f37..b1aba5e 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_BIOSEMU) += bios_emulator/ obj-y += block/ obj-$(CONFIG_BOOTCOUNT_LIMIT) += bootcount/ +obj-y += clk/ obj-y += crypto/ obj-$(CONFIG_FPGA) += fpga/ obj-y += hwmon/ diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile new file mode 100644 index 0000000..d1479d2 --- /dev/null +++ b/drivers/clk/Makefile @@ -0,0 +1,8 @@ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd at denx.de. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o diff --git a/drivers/clk/clkdev.c b/drivers/clk/clkdev.c new file mode 100644 index 0000000..5f2921e --- /dev/null +++ b/drivers/clk/clkdev.c @@ -0,0 +1,207 @@ +/* + * drivers/clk/clkdev.c + * + * Copyright (C) 2008 Russell King. + * + * 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. + * + * Helper for the clk API to assist looking up a struct clk. + */ +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(clocks); + +/* + * Find the correct struct clk for the device and connection ID. + * We do slightly fuzzy matching here: + * An entry with a NULL ID is assumed to be a wildcard. + * If an entry has a device ID, it must match + * If an entry has a connection ID, it must match + * Then we take the most specific entry - with the following + * order of precedence: dev+con > dev only > con only. + */ +static struct clk_lookup *clk_find(const char *dev_id, const char *con_id) +{ + struct clk_lookup *p, *cl = NULL; + int match, best_found = 0, best_possible = 0; + + if (dev_id) + best_possible += 2; + if (con_id) + best_possible += 1; + + list_for_each_entry(p, &clocks, node) { + match = 0; + if (p->dev_id) { + if (!dev_id || strcmp(p->dev_id, dev_id)) + continue; + match += 2; + } + if (p->con_id) { + if (!con_id || strcmp(p->con_id, con_id)) + continue; + match += 1; + } + + if (match > best_found) { + cl = p; + if (match != best_possible) + best_found = match; + else + break; + } + } + return cl; +} + +struct clk *clk_get_sys(const char *dev_id, const char *con_id) +{ + struct clk_lookup *cl; + + cl = clk_find(dev_id, con_id); + if (cl && !__clk_get(cl->clk)) + cl = NULL; + + return cl ? cl->clk : ERR_PTR(-ENOENT); +} + +void clk_put(struct clk *clk) +{ + __clk_put(clk); +} + +void clkdev_add(struct clk_lookup *cl) +{ + list_add_tail(&cl->node, &clocks); +} + +void clkdev_add_table(struct clk_lookup *cl, size_t num) +{ + while (num--) { + list_add_tail(&cl->node, &clocks); + cl++; + } +} + +#define MAX_DEV_ID 20 +#define MAX_CON_ID 16 + +struct clk_lookup_alloc { + struct clk_lookup cl; + char dev_id[MAX_DEV_ID]; + char con_id[MAX_CON_ID]; +}; + +static struct clk_lookup *vclkdev_alloc(struct clk *clk, const char *con_id, + const char *dev_fmt, va_list ap) +{ + struct clk_lookup_alloc *cla; + + cla = __clkdev_alloc(sizeof(*cla)); + if (!cla) + return NULL; + + cla->cl.clk = clk; + if (con_id) { + strlcpy(cla->con_id, con_id, sizeof(cla->con_id)); + cla->cl.con_id = cla->con_id; + } + + if (dev_fmt) { + vscnprintf(cla->dev_id, sizeof(cla->dev_id), dev_fmt, ap); + cla->cl.dev_id = cla->dev_id; + } + + return &cla->cl; +} + +struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, + const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + va_start(ap, dev_fmt); + cl = vclkdev_alloc(clk, con_id, dev_fmt, ap); + va_end(ap); + + return cl; +} + +/* + * clkdev_drop - remove a clock dynamically allocated + */ +void clkdev_drop(struct clk_lookup *cl) +{ + list_del(&cl->node); + free(cl); +} + +/** + * clk_register_clkdev - register one clock lookup for a struct clk + * @clk: struct clk to associate with all clk_lookups + * @con_id: connection ID string on device + * @dev_id: format string describing device name + * + * con_id or dev_id may be NULL as a wildcard, just as in the rest of + * clkdev. + * + * To make things easier for mass registration, we detect error clks + * from a previous clk_register() call, and return the error code for + * those. This is to permit this function to be called immediately + * after clk_register(). + */ +int clk_register_clkdev(struct clk *clk, const char *con_id, + const char *dev_fmt, ...) +{ + struct clk_lookup *cl; + va_list ap; + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + va_start(ap, dev_fmt); + cl = vclkdev_alloc(clk, con_id, dev_fmt, ap); + va_end(ap); + + if (!cl) + return -ENOMEM; + + clkdev_add(cl); + + return 0; +} + +/** + * clk_register_clkdevs - register a set of clk_lookup for a struct clk + * @clk: struct clk to associate with all clk_lookups + * @cl: array of clk_lookup structures with con_id and dev_id pre-initialized + * @num: number of clk_lookup structures to register + * + * To make things easier for mass registration, we detect error clks + * from a previous clk_register() call, and return the error code for + * those. This is to permit this function to be called immediately + * after clk_register(). + */ +int clk_register_clkdevs(struct clk *clk, struct clk_lookup *cl, size_t num) +{ + unsigned i; + + if (IS_ERR(clk)) + return PTR_ERR(clk); + + for (i = 0; i < num; i++, cl++) { + cl->clk = clk; + clkdev_add(cl); + } + + return 0; +} diff --git a/include/linux/clk.h b/include/linux/clk.h new file mode 100644 index 0000000..5f2ccbb --- /dev/null +++ b/include/linux/clk.h @@ -0,0 +1,181 @@ +/* + * linux/include/linux/clk.h + * + * Copyright (C) 2004 ARM Limited. + * Written by Deep Blue Solutions Limited. + * Copyright (C) 2011-2012 Linaro Ltd + * + * 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. + */ +#ifndef __LINUX_CLK_H +#define __LINUX_CLK_H + +#include +#include + +struct clk; + +/** + * clk_prepare - prepare a clock source + * @clk: clock source + * + * This prepares the clock source for use. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +int clk_prepare(struct clk *clk); +#else +static inline int clk_prepare(struct clk *clk) +{ + return 0; +} +#endif + +/** + * clk_unprepare - undo preparation of a clock source + * @clk: clock source + * + * This undoes a previously prepared clock. The caller must balance + * the number of prepare and unprepare calls. + * + * Must not be called from within atomic context. + */ +#ifdef CONFIG_HAVE_CLK_PREPARE +void clk_unprepare(struct clk *clk); +#else +static inline void clk_unprepare(struct clk *clk) +{ +} +#endif + +/** + * clk_enable - inform the system when the clock source should be running. + * @clk: clock source + * + * If the clock can not be enabled/disabled, this should return success. + * + * May be called from atomic contexts. + * + * Returns success (0) or negative errno. + */ +int clk_enable(struct clk *clk); + +/** + * clk_disable - inform the system when the clock source is no longer required. + * @clk: clock source + * + * Inform the system that a clock source is no longer required by + * a driver and may be shut down. + * + * May be called from atomic contexts. + * + * Implementation detail: if the clock source is shared between + * multiple drivers, clk_enable() calls must be balanced by the + * same number of clk_disable() calls for the clock source to be + * disabled. + */ +void clk_disable(struct clk *clk); + +/** + * clk_get_rate - obtain the current clock rate (in Hz) for a clock source. + * This is only valid once the clock source has been enabled. + * @clk: clock source + */ +unsigned long clk_get_rate(struct clk *clk); + +/** + * clk_put - "free" the clock source + * @clk: clock source + * + * Note: drivers must ensure that all clk_enable calls made on this + * clock source are balanced by clk_disable calls prior to calling + * this function. + * + * clk_put should not be called from within interrupt context. + */ +void clk_put(struct clk *clk); + +/* + * The remaining APIs are optional for machine class support. + */ + + +/** + * clk_round_rate - adjust a rate to the exact rate a clock can provide + * @clk: clock source + * @rate: desired clock rate in Hz + * + * Returns rounded clock rate in Hz, or negative errno. + */ +long clk_round_rate(struct clk *clk, unsigned long rate); + +/** + * clk_set_rate - set the clock rate for a clock source + * @clk: clock source + * @rate: desired clock rate in Hz + * + * Returns success (0) or negative errno. + */ +int clk_set_rate(struct clk *clk, unsigned long rate); + +/** + * clk_set_parent - set the parent clock source for this clock + * @clk: clock source + * @parent: parent clock source + * + * Returns success (0) or negative errno. + */ +int clk_set_parent(struct clk *clk, struct clk *parent); + +/** + * clk_get_parent - get the parent clock source for this clock + * @clk: clock source + * + * Returns struct clk corresponding to parent clock source, or + * valid IS_ERR() condition containing errno. + */ +struct clk *clk_get_parent(struct clk *clk); + +/** + * clk_get_sys - get a clock based upon the device name + * @dev_id: device name + * @con_id: connection ID + * + * Returns a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev_id and @con_id to determine the clock consumer, and + * thereby the clock producer. In contrast to clk_get() this function + * takes the device name instead of the device itself for identification. + * + * Drivers must assume that the clock source is not enabled. + * + * clk_get_sys should not be called from within interrupt context. + */ +struct clk *clk_get_sys(const char *dev_id, const char *con_id); + +/* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ +static inline int clk_prepare_enable(struct clk *clk) +{ + int ret; + + ret = clk_prepare(clk); + if (ret) + return ret; + ret = clk_enable(clk); + if (ret) + clk_unprepare(clk); + + return ret; +} + +/* clk_disable_unprepare helps cases using clk_disable in non-atomic context. */ +static inline void clk_disable_unprepare(struct clk *clk) +{ + clk_disable(clk); + clk_unprepare(clk); +} + +#endif diff --git a/include/linux/clkdev.h b/include/linux/clkdev.h new file mode 100644 index 0000000..78f2ff4 --- /dev/null +++ b/include/linux/clkdev.h @@ -0,0 +1,45 @@ +/* + * include/linux/clkdev.h + * + * Copyright (C) 2008 Russell King. + * + * 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. + * + * Helper for the clk API to assist looking up a struct clk. + */ +#ifndef __CLKDEV_H +#define __CLKDEV_H + +#include +#include + +struct clk; + +struct clk_lookup { + struct list_head node; + const char *dev_id; + const char *con_id; + struct clk *clk; +}; + +#define CLKDEV_INIT(d, n, c) \ + { \ + .dev_id = d, \ + .con_id = n, \ + .clk = c, \ + } + +struct clk_lookup *clkdev_alloc(struct clk *clk, const char *con_id, + const char *dev_fmt, ...); + +void clkdev_add(struct clk_lookup *cl); +void clkdev_drop(struct clk_lookup *cl); + +void clkdev_add_table(struct clk_lookup *, size_t); + +int clk_register_clkdev(struct clk *, const char *, const char *, ...); +int clk_register_clkdevs(struct clk *, struct clk_lookup *, size_t); + +#endif -- 1.7.9.5