From ac5c16887008fc69b3bfee810f97c429df532021 Mon Sep 17 00:00:00 2001 From: Antony Pavlov Date: Mon, 3 Dec 2012 00:42:24 +0400 Subject: clk: add always enabled clocks Current barebox clk framework allow disable any clock and there is no means to prevent that. But there are the clocks that can't be disabled by software at all. This patch allow registration of a clock immune to clk_disable(). Signed-off-by: Antony Pavlov Signed-off-by: Sascha Hauer --- include/linux/clk.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include') diff --git a/include/linux/clk.h b/include/linux/clk.h index 00588bff05..1030b50c63 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -179,8 +179,11 @@ struct clk { int num_parents; struct clk **parents; + unsigned long flags; }; +#define CLK_ALWAYS_ENABLED (1 << 0) + struct clk *clk_fixed(const char *name, int rate); struct clk *clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width); -- cgit v1.2.3 From 802d901450b7a180ca55c0679feb8d1f5141cc72 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 6 Dec 2012 12:42:50 +0100 Subject: clk: Add clk table based divider support For easy support of table based dividers. Signed-off-by: Sascha Hauer --- drivers/clk/Makefile | 2 +- drivers/clk/clk-divider-table.c | 119 ++++++++++++++++++++++++++++++++++++++++ include/linux/clk.h | 10 ++++ 3 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 drivers/clk/clk-divider-table.c (limited to 'include') diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 3cc7163829..656b8594b1 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -1,3 +1,3 @@ obj-$(CONFIG_COMMON_CLK) += clk.o clk-fixed.o clk-divider.o clk-fixed-factor.o \ - clk-mux.o clk-gate.o + clk-mux.o clk-gate.o clk-divider-table.o obj-$(CONFIG_CLKDEV_LOOKUP) += clkdev.o diff --git a/drivers/clk/clk-divider-table.c b/drivers/clk/clk-divider-table.c new file mode 100644 index 0000000000..204e24d576 --- /dev/null +++ b/drivers/clk/clk-divider-table.c @@ -0,0 +1,119 @@ +/* + * clk-divider-table.c - generic barebox clock support. Based on Linux clk support + * + * Copyright (c) 2012 Sascha Hauer , Pengutronix + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ +#include +#include +#include +#include +#include + +struct clk_divider_table { + struct clk clk; + u8 shift; + u8 width; + void __iomem *reg; + const char *parent; + const struct clk_div_table *table; + int table_size; + int max_div_index; +}; + +static int clk_divider_set_rate(struct clk *clk, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_divider_table *div = + container_of(clk, struct clk_divider_table, clk); + unsigned int val; + int i, div_index = -1; + unsigned long best = 0; + + if (rate > parent_rate) + rate = parent_rate; + if (rate < parent_rate / div->table[div->max_div_index].div) + rate = parent_rate / div->table[div->max_div_index].div; + + for (i = 0; i < div->table_size; i++) { + unsigned long now = parent_rate / div->table[i].div; + + if (now <= rate && now >= best) { + best = now; + div_index = i; + } + } + + val = readl(div->reg); + val &= ~(((1 << div->width) - 1) << div->shift); + val |= div_index << div->shift; + writel(val, div->reg); + + return 0; +} + +static unsigned long clk_divider_recalc_rate(struct clk *clk, + unsigned long parent_rate) +{ + struct clk_divider_table *div = + container_of(clk, struct clk_divider_table, clk); + unsigned int val; + + val = readl(div->reg) >> div->shift; + val &= (1 << div->width) - 1; + + if (val >= div->table_size) + return 0; + + return parent_rate / div->table[val].div; +} + +struct clk_ops clk_divider_table_ops = { + .set_rate = clk_divider_set_rate, + .recalc_rate = clk_divider_recalc_rate, +}; + +struct clk *clk_divider_table(const char *name, + const char *parent, void __iomem *reg, u8 shift, u8 width, + const struct clk_div_table *table) +{ + struct clk_divider_table *div = xzalloc(sizeof(*div)); + const struct clk_div_table *clkt; + int ret, max_div = 0; + + div->shift = shift; + div->reg = reg; + div->width = width; + div->parent = parent; + div->clk.ops = &clk_divider_table_ops; + div->clk.name = name; + div->clk.parent_names = &div->parent; + div->clk.num_parents = 1; + div->table = table; + + for (clkt = div->table; clkt->div; clkt++) { + if (clkt->div > max_div) { + max_div = clkt->div; + div->max_div_index = div->table_size; + } + div->table_size++; + } + + ret = clk_register(&div->clk); + if (ret) { + free(div); + return ERR_PTR(ret); + } + + return &div->clk; +} diff --git a/include/linux/clk.h b/include/linux/clk.h index 00588bff05..91574f23d0 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -181,9 +181,19 @@ struct clk { struct clk **parents; }; +#define CLK_ALWAYS_ENABLED (1 << 0) + +struct clk_div_table { + unsigned int val; + unsigned int div; +}; + struct clk *clk_fixed(const char *name, int rate); struct clk *clk_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width); +struct clk *clk_divider_table(const char *name, + const char *parent, void __iomem *reg, u8 shift, u8 width, + const struct clk_div_table *table); struct clk *clk_fixed_factor(const char *name, const char *parent, unsigned int mult, unsigned int div); struct clk *clk_mux(const char *name, void __iomem *reg, -- cgit v1.2.3