From: "Heiko Stübner" <heiko@sntech.de>
To: Tomasz Figa <tomasz.figa@gmail.com>
Cc: Arnd Bergmann <arnd@arndb.de>, Tomasz Figa <t.figa@samsung.com>,
Thomas Abraham <thomas.abraham@linaro.org>,
Kukjin Kim <kgene.kim@samsung.com>,
Tushar Behera <tushar.behera@linaro.org>,
Deepak Saxena <dsaxena@linaro.org>,
linux-arm-kernel@lists.infradead.org,
linux-samsung-soc@vger.kernel.org,
Olof Johansson <olof@lixom.net>
Subject: Re: [PATCH 00/23] RFC: exynos multiplatform support
Date: Wed, 6 Mar 2013 23:14:56 +0100 [thread overview]
Message-ID: <201303062314.57183.heiko@sntech.de> (raw)
In-Reply-To: <1781901.cQ9rKScpd8@flatron>
Am Dienstag, 5. März 2013, 23:48:45 schrieb Tomasz Figa:
> On Tuesday 05 of March 2013 19:19:02 Arnd Bergmann wrote:
> > On Tuesday 05 March 2013, Tomasz Figa wrote:
> > > > With this patch set, we can build mach-exynos as part
> > > > of a multiplatform kernel, with the following caveats:
> > > >
> > > > * Only DT based boards are supported
> > >
> > > As far as I'm aware, there are plans to drop non-DT Exynos support.
> > > Kgene should know more on this.
> >
> > Yes, that was my understanding as well. It might not actually be too
> > hard to get multiplatform working with the ATAGS based board files
> > (we do that on some of the other platforms), but there is probably
> > no reason to try hard.
> >
> > > > * Moving to common-clk breaks things including cpufreq
> > > >
> > > > and others. Thomas is working on a patch for this
> > >
> > > We have several patches internally for fixing things up to run
> > > correclty with common-clk. I'll see if we can post some patches.
> >
> > Ok, excellent.
> >
> > > > * We disable the gpio implementation, which also breaks
> > > >
> > > > stuff, but Thomas has a patch already
> > >
> > > The legacy GPIO code is needed only for non-DT case. DT-case uses the
> > > new pinctrl-samsung driver, which is (AFAIK) multiplatform-aware.
> >
> > Please have a closer look at the "ARM: exynos: work around missing gpio
> > code on multiplatform" patch, I think there a few files I had to touch
> > that actually rely on the legacy gpio code:
> >
> > * the pm-gpio.c file
> > * the s3c_i2c0_cfg_gpio function
> > * the eint irqchip
> >
> > If none of these are needed for DT-based systems, we should probably
> > build that code conditionally based on the CONFIG_EXYNOS_ATAGS symbol
> > I introduced.
>
> Yes, none of them are needed for DT-based systems.
>
> > > > * sparsemem support is not available on multiplatform
> > >
> > > There was some discussion some time ago whether we really need
> > > sparsemem on Exynos. If I remember correctly, it turned out that we
> > > don't. So this is not really an issue.
> >
> > Ok, good.
> >
> > > > Arnd Bergmann (23):
> > > > ARM: exynos: introduce EXYNOS_ATAGS symbol
> > > > irqchip: exynos: remove dependency on mach/irqs.h
> > > > tty: serial/samsung: prepare for common clock API
> > > > tty: serial/samsung: make register definitions global
> > > > tty: serial/samsung: fix modular build
> > > > ARM: exynos: move debug-macro.S to include/debug/
> > > > i2c: s3c2410: make header file local
> > > > mmc: sdhci-s3c: remove platform dependencies
> > > > usb: exynos: do not include plat/usb-phy.h
> > > > [media] exynos: remove unnecessary header inclusions
> > > > video/exynos: remove unnecessary header inclusions
> > > > thermal/exynos: remove unnecessary header inclusions
> > > > mtd: onenand/samsung: make regs-onenand.h file local
> > > > rtc: s3c: make header file local
> > > > spi: s3c64xx: move to generic dmaengine API
> > > > pwm: samsung: repair the worst MMIO abuses
> > >
> > > I'm currently working (in my free time) on a series of cleanup patches
> > > sanitizing Samsung PWM code for S3C64xx and S5PV210 DT (and
> > > multiplatform) support.
> >
> > Ah, nice.
> >
> > > On those platforms it is a bit more complex case as there are two
> > > blocks of code that access the same hardware - samsung-time (using
> > > two PWM channels for clocksource and clock events) and pwm-samsung.
> > >
> > > Hopefully, I will have some patches ready soon.
> >
> > How are you planning to solve this? Do you want to have a combined
> > driver that registers both a clocksource and a pwm?
>
> Let's start with a quick introduction to the s3c-pwm hardware. Each
> channel has its own timer value, compare and reload value registers, so
> they don't need any extra locking. However there are additional shared
> configuration registers, containing things such as start and reload bits
> for all channels, prescaler and main divisor values, etc. Those registers
> needs synchronization.
>
> Now there are several possible approaches:
>
> 1) A brute force one - two separate drivers, based on the fact that the
> clocksource driver will be used only on uniprocessor systems, so
> a simple _irqsave when accessing a shared register in both will
> guarantee correct synchronization.
>
> 2) Two separate drivers with some synchronized shared code accessing
> registers (_start, _stop, _set_reload, _set_prescaler, _set_divisor,
> etc.; all using a shared spinlock).
>
> 3) Single driver registering PWM channels, clocksource and clock event
> device. This does not seem like a bad idea, since the whole code for
> configuration of the PWM block would reside in one location and there
> would be no redundancy. However there is a question where such driver
> should be placed - drivers/clocksource, drivers/pwm, or maybe somewhere
> else?
>
> Personally I wanted to go with first option, which would require least
> amount of changes to existing code, at a cost of some code duplication
> (but some PWM code is duplicated already).
Do you also want to include the prescaler and divider handling (that is
currently sitting pwm-clock.c in plat-samsung) into this new driver?
Because for my current common-clk work I ported the pwm-clock stuff to the
common-clk-fw to keep the timer running. Unfinished code below. The pwm and
time drivers do not use the TCFG registers at the beginning of the block.
Either way is fine with me, I just want to prevent me doing more cleanups
there, when the right way is thru your new driver :-)
Heiko
unfinished but working clk-pwm.c:
/*
* Copyright (c) 2007 Simtec Electronics
* Copyright (c) 2007, 2008 Ben Dooks
* Ben Dooks <ben-linux@fluff.org>
* Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
*
* 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.
*
* Common Clock Framework support for Samsung pwm clocks
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <linux/io.h>
#include <plat/cpu.h>
#include <mach/map.h>
#include "clk.h"
/* Each of the timers 0 through 5 go through the following
* clock tree, with the inputs depending on the timers.
*
* pclk ---- [ prescaler 0 ] -+---> timer 0
* +---> timer 1
*
* pclk ---- [ prescaler 1 ] -+---> timer 2
* +---> timer 3
* \---> timer 4
*
* Which are fed into the timers as so:
*
* prescaled 0 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 0
* tclk 0 ------------------------------/
*
* prescaled 0 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 1
* tclk 0 ------------------------------/
*
*
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 2
* tclk 1 ------------------------------/
*
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 3
* tclk 1 ------------------------------/
*
* prescaled 1 ---- [ div 2,4,8, 16 ] --\
* [mux] -> timer 4
* tclk 1 ------------------------------/
*
* Since the mux and the divider are tied together in the
* same register space, it is impossible to set the parent
* and the rate at the same time. To avoid this, we add an
* intermediate 'prescaled-and-divided' clock to select
* as the parent for the timer input clock called tdiv.
*
* prescaled clk --> pwm-tdiv ---\
* [ mux ] --> timer X
* tclk -------------------------/
*/
enum pwm_clks {
none,
tclk0, tclk1, tdiv0, tdiv1, tdiv2, tdiv3, tdiv4,
tin0, tin1, tin2, tin3, tin4,
nr_clks,
};
/* the soc types */
enum supported_socs {
S3C24XX,
S3C64XX, /* also S5PC100 */
S5P64XX,
};
/* clock controller register offsets */
#define TCFG0 0
#define TCFG1 0x4
static DEFINE_SPINLOCK(lock);
static int current_soc;
static void __iomem *reg_base;
static struct clk **clk_table;
#ifdef CONFIG_OF
static struct clk_onecell_data clk_data;
#endif
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump reg_dump[2] = {
{ .offset = TCFG0 },
{ .offset = TCFG1 },
};
static int samsung_clk_pwm_suspend(void)
{
reg_dump[0].value = readl_relaxed(reg_base + reg_dump[0].offset);
reg_dump[1].value = readl_relaxed(reg_base + reg_dump[1].offset);
return 0;
}
static void samsung_clk_pwm_resume(void)
{
writel_relaxed(reg_dump[0].value, reg_base + reg_dump[0].offset);
writel_relaxed(reg_dump[1].value, reg_base + reg_dump[1].offset);
}
static struct syscore_ops samsung_clk_pwm_syscore_ops = {
.suspend = samsung_clk_pwm_suspend,
.resume = samsung_clk_pwm_resume,
};
#endif /* CONFIG_PM_SLEEP */
#define S3C2410_TCFG1_MUX_TCLK (4 << 0)
#define S3C64XX_TCFG1_MUX_TCLK (5 << 0)
/**
* pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
* @tcfg: The timer TCFG1 register bits shifted down to 0.
*
* Return true if the given configuration from TCFG1 is a TCLK instead
* any of the TDIV clocks.
*/
static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
{
if (current_soc == S3C24XX)
return tcfg == S3C2410_TCFG1_MUX_TCLK;
else if (current_soc == S3C64XX)
return tcfg >= S3C64XX_TCFG1_MUX_TCLK;
else if (current_soc == S5P64XX)
return 0;
else
return tcfg == S3C64XX_TCFG1_MUX_TCLK;
}
struct clk_tdiv {
struct clk_divider divider;
const struct clk_ops *ops;
void __iomem *reg;
unsigned int divisor;
};
static inline struct clk_tdiv *to_clk_tdiv(struct clk_hw *hw)
{
struct clk_divider *divider = container_of(hw, struct clk_divider, hw);
return container_of(divider, struct clk_tdiv, divider);
}
static unsigned long clk_tdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
unsigned long tcfg1 = readl_relaxed(tdiv->reg);
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
if (pwm_cfg_src_is_tclk(tcfg1))
return parent_rate / tdiv->divisor;
else
return tdiv->ops->recalc_rate(&tdiv->divider.hw, parent_rate);
}
static long clk_tdiv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
return tdiv->ops->round_rate(&tdiv->divider.hw, rate, parent_rate);
}
static int clk_tdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
unsigned long tcfg1 = readl_relaxed(tdiv->reg);
unsigned long divisor;
int ret = 0;
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
rate = tdiv->ops->round_rate(&tdiv->divider.hw, rate, &parent_rate);
divisor = parent_rate / rate;
if (divisor > 16)
return -EINVAL;
tdiv->divisor = divisor;
/* Update the current MUX settings if we are currently
* selected as the clock source for this clock. */
if (!pwm_cfg_src_is_tclk(tcfg1))
ret = tdiv->ops->set_rate(&tdiv->divider.hw, rate, parent_rate);
return ret;
}
static struct clk_ops clk_tdiv_ops = {
.recalc_rate = clk_tdiv_recalc_rate,
.round_rate = clk_tdiv_round_rate,
.set_rate = clk_tdiv_set_rate,
};
static struct clk *samsung_clk_register_tdiv(const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift,
u8 clk_divider_flags, const struct clk_div_table *table)
{
unsigned long tcfg1;
const struct clk_div_table *clkt;
struct clk_tdiv *tdiv;
struct clk *clk;
struct clk_init_data init;
tdiv = kzalloc(sizeof(struct clk_tdiv), GFP_KERNEL);
if (!tdiv)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_tdiv_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_divider assignments */
tdiv->divider.reg = reg;
tdiv->divider.shift = shift;
tdiv->divider.width = 4;
tdiv->divider.flags = clk_divider_flags;
tdiv->divider.lock = &lock;
tdiv->divider.hw.init = &init;
tdiv->divider.table = table;
tdiv->ops = &clk_divider_ops;
tcfg1 = readl_relaxed(reg);
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
tdiv->reg = reg;
tdiv->divisor = 1;
for (clkt = table; clkt->div; clkt++)
if (clkt->val == tcfg1)
tdiv->divisor = clkt->div;
clk = clk_register(NULL, &tdiv->divider.hw);
if (IS_ERR(clk))
kfree(tdiv);
return clk;
}
struct clk_tin {
struct clk_hw hw;
void __iomem *reg;
u8 shift;
u8 width;
spinlock_t *lock;
};
#define to_clk_tin(_hw) container_of(_hw, struct clk_tin, hw)
static u8 clk_tin_get_parent(struct clk_hw *hw)
{
struct clk_tin *tin = to_clk_tin(hw);
unsigned long tcfg1 = readl_relaxed(tin->reg);
tcfg1 >>= tin->shift;
tcfg1 &= ((1 << (tin->width)) - 1);
/* assume tclk is parent 0 and tdiv is parent 1 */
return pwm_cfg_src_is_tclk(tcfg1) ? 0 : 1;
}
static int clk_tin_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_tin *tin = to_clk_tin(hw);
struct clk *tdiv;
struct clk *prescaler;
unsigned long tcfg1;
unsigned long flags = 0;
unsigned long div;
int bits;
switch (index) {
case 0:
if (current_soc == S3C24XX)
bits = S3C2410_TCFG1_MUX_TCLK << tin->shift;
else if (current_soc == S5P64XX)
bits = 0;
else
bits = S3C64XX_TCFG1_MUX_TCLK << tin->shift;
break;
case 1:
tdiv = clk_get(NULL, hw->init->parent_names[0]);
prescaler = clk_get_parent(tdiv);
div = clk_get_rate(prescaler) / clk_get_rate(tdiv);
bits = (current_soc == S3C24XX) ? (ilog2(div) - 1) : ilog2(div);
bits &= ((1 << (tin->width)) - 1);
bits <<= tin->shift;
clk_put(tdiv);
break;
default:
return -EINVAL;
}
spin_lock_irqsave(tin->lock, flags);
tcfg1 = readl_relaxed(tin->reg);
tcfg1 &= ~(((1 << tin->width) - 1) << tin->shift);
tcfg1 |= bits;
writel_relaxed(tcfg1, tin->reg);
spin_unlock_irqrestore(tin->lock, flags);
return 0;
}
static const struct clk_ops clk_tin_ops = {
.get_parent = clk_tin_get_parent,
.set_parent = clk_tin_set_parent,
};
static struct clk *samsung_clk_register_tin(const char *name,
const char **parent_names, u8 num_parents,
void __iomem *reg, u8 shift, u8 width)
{
struct clk_tin *tin;
struct clk *clk;
struct clk_init_data init;
/* allocate the mux */
tin = kzalloc(sizeof(struct clk_tin), GFP_KERNEL);
if (!tin) {
pr_err("%s: could not allocate tin clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_tin_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
/* struct clk_mux assignments */
tin->reg = reg;
tin->shift = shift;
tin->width = width;
tin->lock = &lock;
tin->hw.init = &init;
clk = clk_register(NULL, &tin->hw);
if (IS_ERR(clk))
kfree(tin);
return clk;
}
PNAME(tin0_p) = { "pwm-tclk0", "pwm-tdiv0" };
PNAME(tin1_p) = { "pwm-tclk0", "pwm-tdiv1" };
PNAME(tin2_p) = { "pwm-tclk1", "pwm-tdiv2" };
PNAME(tin3_p) = { "pwm-tclk1", "pwm-tdiv3" };
PNAME(tin4_p) = { "pwm-tclk1", "pwm-tdiv4" };
static struct clk_div_table tdiv_s3c24xx_d[] = {
{ .val = 0, .div = 2 },
{ .val = 1, .div = 4 },
{ .val = 2, .div = 8 },
{ .val = 3, .div = 16 },
{ .div = 0 },
};
struct samsung_div_clock pwm_s3c24xx_tdiv_dividers[] __initdata = {
DIV_T(tdiv0, "pwm-tdiv0", "pwm-scaler0", TCFG1, 0, 4, tdiv_s3c24xx_d),
DIV_T(tdiv1, "pwm-tdiv1", "pwm-scaler0", TCFG1, 4, 4, tdiv_s3c24xx_d),
DIV_T(tdiv2, "pwm-tdiv2", "pwm-scaler1", TCFG1, 8, 4, tdiv_s3c24xx_d),
DIV_T(tdiv3, "pwm-tdiv3", "pwm-scaler1", TCFG1, 12, 4, tdiv_s3c24xx_d),
DIV_T(tdiv4, "pwm-tdiv4", "pwm-scaler1", TCFG1, 16, 4, tdiv_s3c24xx_d),
};
static struct clk_div_table tdiv_s3c64xx_d[] = {
{ .val = 0, .div = 1 },
{ .val = 1, .div = 2 },
{ .val = 2, .div = 4 },
{ .val = 3, .div = 8 },
{ .val = 4, .div = 16 },
{ .div = 0 },
};
struct samsung_div_clock pwm_s3c64xx_tdiv_dividers[] __initdata = {
DIV_T(tdiv0, "pwm-tdiv0", "pwm-scaler0", TCFG1, 0, 4, tdiv_s3c64xx_d),
DIV_T(tdiv1, "pwm-tdiv1", "pwm-scaler0", TCFG1, 4, 4, tdiv_s3c64xx_d),
DIV_T(tdiv2, "pwm-tdiv2", "pwm-scaler1", TCFG1, 8, 4, tdiv_s3c64xx_d),
DIV_T(tdiv3, "pwm-tdiv3", "pwm-scaler1", TCFG1, 12, 4, tdiv_s3c64xx_d),
DIV_T(tdiv4, "pwm-tdiv4", "pwm-scaler1", TCFG1, 16, 4, tdiv_s3c64xx_d),
};
struct samsung_mux_clock pwm_tin[] __initdata = {
MUX(tin0, "pwm-tin0", tin0_p, TCFG1, 0, 4),
MUX(tin1, "pwm-tin1", tin1_p, TCFG1, 4, 4),
MUX(tin2, "pwm-tin2", tin2_p, TCFG1, 8, 4),
MUX(tin3, "pwm-tin3", tin3_p, TCFG1, 12, 4),
MUX(tin4, "pwm-tin4", tin4_p, TCFG1, 16, 4),
};
struct samsung_clock_alias pwm_aliases[] __initdata = {
ALIAS(tdiv0, "s3c24xx-pwm.0", "pwm-tdiv"),
ALIAS(tdiv1, "s3c24xx-pwm.1", "pwm-tdiv"),
ALIAS(tdiv2, "s3c24xx-pwm.2", "pwm-tdiv"),
ALIAS(tdiv3, "s3c24xx-pwm.3", "pwm-tdiv"),
ALIAS(tdiv4, "s3c24xx-pwm.4", "pwm-tdiv"),
ALIAS(tin0, "s3c24xx-pwm.0", "pwm-tin"),
ALIAS(tin1, "s3c24xx-pwm.1", "pwm-tin"),
ALIAS(tin2, "s3c24xx-pwm.2", "pwm-tin"),
ALIAS(tin3, "s3c24xx-pwm.3", "pwm-tin"),
ALIAS(tin4, "s3c24xx-pwm.4", "pwm-tin"),
};
#ifdef CONFIG_OF
static struct of_device_id pwm_clk_ids[] __initdata = {
{ .compatible = "samsung,s3c24xx-clock-pwm",
.data = (void *)S3C24XX, },
{ .compatible = "samsung,s3c64xx-clock-pwm",
.data = (void *)S3C64XX, },
{ .compatible = "samsung,s5p64xx-clock-pwm",
.data = (void *)S5P64XX, },
{ },
};
#endif
void __init samsung_pwm_clk_init(struct device_node *np)
{
struct clk *clk;
struct samsung_div_clock *tdiv_list;
struct samsung_mux_clock *tin_list;
struct samsung_clock_alias *alias_list;
unsigned int idx;
int ret;
if (np) {
const struct of_device_id *match;
match = of_match_node(pwm_clk_ids, np);
current_soc = (u32)match->data;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
} else {
reg_base = S3C_VA_TIMER;
if (soc_is_s3c24xx())
current_soc = S3C24XX;
else if (soc_is_s3c64xx() || soc_is_s5pc100())
current_soc = S3C64XX;
else if (soc_is_s5p6440() || soc_is_s5p6450())
current_soc = S5P64XX;
else
panic("%s: unable to determine soc\n", __func__);
}
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
panic("could not allocate clock lookup table\n");
#ifdef CONFIG_OF
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
#endif
#ifdef CONFIG_PM_SLEEP
register_syscore_ops(&samsung_clk_pwm_syscore_ops);
#endif
clk = clk_register_fixed_rate(NULL, "pwm-tclk0", NULL, CLK_IS_ROOT, 0);
clk = clk_register_fixed_rate(NULL, "pwm-tclk1", NULL, CLK_IS_ROOT, 0);
clk = clk_register_divider(NULL, "pwm-scaler0", "pwm", 0, reg_base + TCFG0, 0, 8, 0, &lock);
clk = clk_register_divider(NULL, "pwm-scaler1", "pwm", 0, reg_base + TCFG0, 8, 8, 0, &lock);
tdiv_list = (current_soc == S3C24XX) ? pwm_s3c24xx_tdiv_dividers
: pwm_s3c64xx_tdiv_dividers;
for (idx = 0; idx < 5; idx++, tdiv_list++) {
clk = samsung_clk_register_tdiv(tdiv_list->name, tdiv_list->parent_name,
tdiv_list->flags, reg_base + tdiv_list->offset,
tdiv_list->shift, tdiv_list->div_flags, tdiv_list->table);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
tdiv_list->name);
continue;
}
if (clk_table && tdiv_list->id)
clk_table[tdiv_list->id] = clk;
}
tin_list = pwm_tin;
for (idx = 0; idx < 5; idx++, tin_list++) {
clk = samsung_clk_register_tin(tin_list->name,
tin_list->parent_names, tin_list->num_parents,
reg_base + tin_list->offset, tin_list->shift,
tin_list->width);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
tin_list->name);
continue;
}
if (clk_table && tin_list->id)
clk_table[tin_list->id] = clk;
}
alias_list = pwm_aliases;
for (idx = 0; idx < ARRAY_SIZE(pwm_aliases); idx++, alias_list++) {
if (!alias_list->id) {
pr_err("%s: clock id missing for index %d\n", __func__,
idx);
continue;
}
clk = clk_table[alias_list->id];
if (!clk) {
pr_err("%s: failed to find clock %d\n", __func__,
alias_list->id);
continue;
}
ret = clk_register_clkdev(clk, alias_list->alias,
alias_list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, alias_list->alias);
}
}
WARNING: multiple messages have this Message-ID (diff)
From: heiko@sntech.de (Heiko Stübner)
To: linux-arm-kernel@lists.infradead.org
Subject: [PATCH 00/23] RFC: exynos multiplatform support
Date: Wed, 6 Mar 2013 23:14:56 +0100 [thread overview]
Message-ID: <201303062314.57183.heiko@sntech.de> (raw)
In-Reply-To: <1781901.cQ9rKScpd8@flatron>
Am Dienstag, 5. M?rz 2013, 23:48:45 schrieb Tomasz Figa:
> On Tuesday 05 of March 2013 19:19:02 Arnd Bergmann wrote:
> > On Tuesday 05 March 2013, Tomasz Figa wrote:
> > > > With this patch set, we can build mach-exynos as part
> > > > of a multiplatform kernel, with the following caveats:
> > > >
> > > > * Only DT based boards are supported
> > >
> > > As far as I'm aware, there are plans to drop non-DT Exynos support.
> > > Kgene should know more on this.
> >
> > Yes, that was my understanding as well. It might not actually be too
> > hard to get multiplatform working with the ATAGS based board files
> > (we do that on some of the other platforms), but there is probably
> > no reason to try hard.
> >
> > > > * Moving to common-clk breaks things including cpufreq
> > > >
> > > > and others. Thomas is working on a patch for this
> > >
> > > We have several patches internally for fixing things up to run
> > > correclty with common-clk. I'll see if we can post some patches.
> >
> > Ok, excellent.
> >
> > > > * We disable the gpio implementation, which also breaks
> > > >
> > > > stuff, but Thomas has a patch already
> > >
> > > The legacy GPIO code is needed only for non-DT case. DT-case uses the
> > > new pinctrl-samsung driver, which is (AFAIK) multiplatform-aware.
> >
> > Please have a closer look at the "ARM: exynos: work around missing gpio
> > code on multiplatform" patch, I think there a few files I had to touch
> > that actually rely on the legacy gpio code:
> >
> > * the pm-gpio.c file
> > * the s3c_i2c0_cfg_gpio function
> > * the eint irqchip
> >
> > If none of these are needed for DT-based systems, we should probably
> > build that code conditionally based on the CONFIG_EXYNOS_ATAGS symbol
> > I introduced.
>
> Yes, none of them are needed for DT-based systems.
>
> > > > * sparsemem support is not available on multiplatform
> > >
> > > There was some discussion some time ago whether we really need
> > > sparsemem on Exynos. If I remember correctly, it turned out that we
> > > don't. So this is not really an issue.
> >
> > Ok, good.
> >
> > > > Arnd Bergmann (23):
> > > > ARM: exynos: introduce EXYNOS_ATAGS symbol
> > > > irqchip: exynos: remove dependency on mach/irqs.h
> > > > tty: serial/samsung: prepare for common clock API
> > > > tty: serial/samsung: make register definitions global
> > > > tty: serial/samsung: fix modular build
> > > > ARM: exynos: move debug-macro.S to include/debug/
> > > > i2c: s3c2410: make header file local
> > > > mmc: sdhci-s3c: remove platform dependencies
> > > > usb: exynos: do not include plat/usb-phy.h
> > > > [media] exynos: remove unnecessary header inclusions
> > > > video/exynos: remove unnecessary header inclusions
> > > > thermal/exynos: remove unnecessary header inclusions
> > > > mtd: onenand/samsung: make regs-onenand.h file local
> > > > rtc: s3c: make header file local
> > > > spi: s3c64xx: move to generic dmaengine API
> > > > pwm: samsung: repair the worst MMIO abuses
> > >
> > > I'm currently working (in my free time) on a series of cleanup patches
> > > sanitizing Samsung PWM code for S3C64xx and S5PV210 DT (and
> > > multiplatform) support.
> >
> > Ah, nice.
> >
> > > On those platforms it is a bit more complex case as there are two
> > > blocks of code that access the same hardware - samsung-time (using
> > > two PWM channels for clocksource and clock events) and pwm-samsung.
> > >
> > > Hopefully, I will have some patches ready soon.
> >
> > How are you planning to solve this? Do you want to have a combined
> > driver that registers both a clocksource and a pwm?
>
> Let's start with a quick introduction to the s3c-pwm hardware. Each
> channel has its own timer value, compare and reload value registers, so
> they don't need any extra locking. However there are additional shared
> configuration registers, containing things such as start and reload bits
> for all channels, prescaler and main divisor values, etc. Those registers
> needs synchronization.
>
> Now there are several possible approaches:
>
> 1) A brute force one - two separate drivers, based on the fact that the
> clocksource driver will be used only on uniprocessor systems, so
> a simple _irqsave when accessing a shared register in both will
> guarantee correct synchronization.
>
> 2) Two separate drivers with some synchronized shared code accessing
> registers (_start, _stop, _set_reload, _set_prescaler, _set_divisor,
> etc.; all using a shared spinlock).
>
> 3) Single driver registering PWM channels, clocksource and clock event
> device. This does not seem like a bad idea, since the whole code for
> configuration of the PWM block would reside in one location and there
> would be no redundancy. However there is a question where such driver
> should be placed - drivers/clocksource, drivers/pwm, or maybe somewhere
> else?
>
> Personally I wanted to go with first option, which would require least
> amount of changes to existing code, at a cost of some code duplication
> (but some PWM code is duplicated already).
Do you also want to include the prescaler and divider handling (that is
currently sitting pwm-clock.c in plat-samsung) into this new driver?
Because for my current common-clk work I ported the pwm-clock stuff to the
common-clk-fw to keep the timer running. Unfinished code below. The pwm and
time drivers do not use the TCFG registers at the beginning of the block.
Either way is fine with me, I just want to prevent me doing more cleanups
there, when the right way is thru your new driver :-)
Heiko
unfinished but working clk-pwm.c:
/*
* Copyright (c) 2007 Simtec Electronics
* Copyright (c) 2007, 2008 Ben Dooks
* Ben Dooks <ben-linux@fluff.org>
* Copyright (c) 2013 Heiko Stuebner <heiko@sntech.de>
*
* 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.
*
* Common Clock Framework support for Samsung pwm clocks
*/
#include <linux/clk.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <linux/io.h>
#include <plat/cpu.h>
#include <mach/map.h>
#include "clk.h"
/* Each of the timers 0 through 5 go through the following
* clock tree, with the inputs depending on the timers.
*
* pclk ---- [ prescaler 0 ] -+---> timer 0
* +---> timer 1
*
* pclk ---- [ prescaler 1 ] -+---> timer 2
* +---> timer 3
* \---> timer 4
*
* Which are fed into the timers as so:
*
* prescaled 0 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 0
* tclk 0 ------------------------------/
*
* prescaled 0 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 1
* tclk 0 ------------------------------/
*
*
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 2
* tclk 1 ------------------------------/
*
* prescaled 1 ---- [ div 2,4,8,16 ] ---\
* [mux] -> timer 3
* tclk 1 ------------------------------/
*
* prescaled 1 ---- [ div 2,4,8, 16 ] --\
* [mux] -> timer 4
* tclk 1 ------------------------------/
*
* Since the mux and the divider are tied together in the
* same register space, it is impossible to set the parent
* and the rate at the same time. To avoid this, we add an
* intermediate 'prescaled-and-divided' clock to select
* as the parent for the timer input clock called tdiv.
*
* prescaled clk --> pwm-tdiv ---\
* [ mux ] --> timer X
* tclk -------------------------/
*/
enum pwm_clks {
none,
tclk0, tclk1, tdiv0, tdiv1, tdiv2, tdiv3, tdiv4,
tin0, tin1, tin2, tin3, tin4,
nr_clks,
};
/* the soc types */
enum supported_socs {
S3C24XX,
S3C64XX, /* also S5PC100 */
S5P64XX,
};
/* clock controller register offsets */
#define TCFG0 0
#define TCFG1 0x4
static DEFINE_SPINLOCK(lock);
static int current_soc;
static void __iomem *reg_base;
static struct clk **clk_table;
#ifdef CONFIG_OF
static struct clk_onecell_data clk_data;
#endif
#ifdef CONFIG_PM_SLEEP
static struct samsung_clk_reg_dump reg_dump[2] = {
{ .offset = TCFG0 },
{ .offset = TCFG1 },
};
static int samsung_clk_pwm_suspend(void)
{
reg_dump[0].value = readl_relaxed(reg_base + reg_dump[0].offset);
reg_dump[1].value = readl_relaxed(reg_base + reg_dump[1].offset);
return 0;
}
static void samsung_clk_pwm_resume(void)
{
writel_relaxed(reg_dump[0].value, reg_base + reg_dump[0].offset);
writel_relaxed(reg_dump[1].value, reg_base + reg_dump[1].offset);
}
static struct syscore_ops samsung_clk_pwm_syscore_ops = {
.suspend = samsung_clk_pwm_suspend,
.resume = samsung_clk_pwm_resume,
};
#endif /* CONFIG_PM_SLEEP */
#define S3C2410_TCFG1_MUX_TCLK (4 << 0)
#define S3C64XX_TCFG1_MUX_TCLK (5 << 0)
/**
* pwm_cfg_src_is_tclk() - return whether the given mux config is a tclk
* @tcfg: The timer TCFG1 register bits shifted down to 0.
*
* Return true if the given configuration from TCFG1 is a TCLK instead
* any of the TDIV clocks.
*/
static inline int pwm_cfg_src_is_tclk(unsigned long tcfg)
{
if (current_soc == S3C24XX)
return tcfg == S3C2410_TCFG1_MUX_TCLK;
else if (current_soc == S3C64XX)
return tcfg >= S3C64XX_TCFG1_MUX_TCLK;
else if (current_soc == S5P64XX)
return 0;
else
return tcfg == S3C64XX_TCFG1_MUX_TCLK;
}
struct clk_tdiv {
struct clk_divider divider;
const struct clk_ops *ops;
void __iomem *reg;
unsigned int divisor;
};
static inline struct clk_tdiv *to_clk_tdiv(struct clk_hw *hw)
{
struct clk_divider *divider = container_of(hw, struct clk_divider, hw);
return container_of(divider, struct clk_tdiv, divider);
}
static unsigned long clk_tdiv_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
unsigned long tcfg1 = readl_relaxed(tdiv->reg);
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
if (pwm_cfg_src_is_tclk(tcfg1))
return parent_rate / tdiv->divisor;
else
return tdiv->ops->recalc_rate(&tdiv->divider.hw, parent_rate);
}
static long clk_tdiv_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
return tdiv->ops->round_rate(&tdiv->divider.hw, rate, parent_rate);
}
static int clk_tdiv_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct clk_tdiv *tdiv = to_clk_tdiv(hw);
unsigned long tcfg1 = readl_relaxed(tdiv->reg);
unsigned long divisor;
int ret = 0;
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
rate = tdiv->ops->round_rate(&tdiv->divider.hw, rate, &parent_rate);
divisor = parent_rate / rate;
if (divisor > 16)
return -EINVAL;
tdiv->divisor = divisor;
/* Update the current MUX settings if we are currently
* selected as the clock source for this clock. */
if (!pwm_cfg_src_is_tclk(tcfg1))
ret = tdiv->ops->set_rate(&tdiv->divider.hw, rate, parent_rate);
return ret;
}
static struct clk_ops clk_tdiv_ops = {
.recalc_rate = clk_tdiv_recalc_rate,
.round_rate = clk_tdiv_round_rate,
.set_rate = clk_tdiv_set_rate,
};
static struct clk *samsung_clk_register_tdiv(const char *name,
const char *parent_name, unsigned long flags,
void __iomem *reg, u8 shift,
u8 clk_divider_flags, const struct clk_div_table *table)
{
unsigned long tcfg1;
const struct clk_div_table *clkt;
struct clk_tdiv *tdiv;
struct clk *clk;
struct clk_init_data init;
tdiv = kzalloc(sizeof(struct clk_tdiv), GFP_KERNEL);
if (!tdiv)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &clk_tdiv_ops;
init.flags = flags;
init.parent_names = (parent_name ? &parent_name: NULL);
init.num_parents = (parent_name ? 1 : 0);
/* struct clk_divider assignments */
tdiv->divider.reg = reg;
tdiv->divider.shift = shift;
tdiv->divider.width = 4;
tdiv->divider.flags = clk_divider_flags;
tdiv->divider.lock = &lock;
tdiv->divider.hw.init = &init;
tdiv->divider.table = table;
tdiv->ops = &clk_divider_ops;
tcfg1 = readl_relaxed(reg);
tcfg1 >>= tdiv->divider.shift;
tcfg1 &= ((1 << (tdiv->divider.width)) - 1);
tdiv->reg = reg;
tdiv->divisor = 1;
for (clkt = table; clkt->div; clkt++)
if (clkt->val == tcfg1)
tdiv->divisor = clkt->div;
clk = clk_register(NULL, &tdiv->divider.hw);
if (IS_ERR(clk))
kfree(tdiv);
return clk;
}
struct clk_tin {
struct clk_hw hw;
void __iomem *reg;
u8 shift;
u8 width;
spinlock_t *lock;
};
#define to_clk_tin(_hw) container_of(_hw, struct clk_tin, hw)
static u8 clk_tin_get_parent(struct clk_hw *hw)
{
struct clk_tin *tin = to_clk_tin(hw);
unsigned long tcfg1 = readl_relaxed(tin->reg);
tcfg1 >>= tin->shift;
tcfg1 &= ((1 << (tin->width)) - 1);
/* assume tclk is parent 0 and tdiv is parent 1 */
return pwm_cfg_src_is_tclk(tcfg1) ? 0 : 1;
}
static int clk_tin_set_parent(struct clk_hw *hw, u8 index)
{
struct clk_tin *tin = to_clk_tin(hw);
struct clk *tdiv;
struct clk *prescaler;
unsigned long tcfg1;
unsigned long flags = 0;
unsigned long div;
int bits;
switch (index) {
case 0:
if (current_soc == S3C24XX)
bits = S3C2410_TCFG1_MUX_TCLK << tin->shift;
else if (current_soc == S5P64XX)
bits = 0;
else
bits = S3C64XX_TCFG1_MUX_TCLK << tin->shift;
break;
case 1:
tdiv = clk_get(NULL, hw->init->parent_names[0]);
prescaler = clk_get_parent(tdiv);
div = clk_get_rate(prescaler) / clk_get_rate(tdiv);
bits = (current_soc == S3C24XX) ? (ilog2(div) - 1) : ilog2(div);
bits &= ((1 << (tin->width)) - 1);
bits <<= tin->shift;
clk_put(tdiv);
break;
default:
return -EINVAL;
}
spin_lock_irqsave(tin->lock, flags);
tcfg1 = readl_relaxed(tin->reg);
tcfg1 &= ~(((1 << tin->width) - 1) << tin->shift);
tcfg1 |= bits;
writel_relaxed(tcfg1, tin->reg);
spin_unlock_irqrestore(tin->lock, flags);
return 0;
}
static const struct clk_ops clk_tin_ops = {
.get_parent = clk_tin_get_parent,
.set_parent = clk_tin_set_parent,
};
static struct clk *samsung_clk_register_tin(const char *name,
const char **parent_names, u8 num_parents,
void __iomem *reg, u8 shift, u8 width)
{
struct clk_tin *tin;
struct clk *clk;
struct clk_init_data init;
/* allocate the mux */
tin = kzalloc(sizeof(struct clk_tin), GFP_KERNEL);
if (!tin) {
pr_err("%s: could not allocate tin clk\n", __func__);
return ERR_PTR(-ENOMEM);
}
init.name = name;
init.ops = &clk_tin_ops;
init.parent_names = parent_names;
init.num_parents = num_parents;
/* struct clk_mux assignments */
tin->reg = reg;
tin->shift = shift;
tin->width = width;
tin->lock = &lock;
tin->hw.init = &init;
clk = clk_register(NULL, &tin->hw);
if (IS_ERR(clk))
kfree(tin);
return clk;
}
PNAME(tin0_p) = { "pwm-tclk0", "pwm-tdiv0" };
PNAME(tin1_p) = { "pwm-tclk0", "pwm-tdiv1" };
PNAME(tin2_p) = { "pwm-tclk1", "pwm-tdiv2" };
PNAME(tin3_p) = { "pwm-tclk1", "pwm-tdiv3" };
PNAME(tin4_p) = { "pwm-tclk1", "pwm-tdiv4" };
static struct clk_div_table tdiv_s3c24xx_d[] = {
{ .val = 0, .div = 2 },
{ .val = 1, .div = 4 },
{ .val = 2, .div = 8 },
{ .val = 3, .div = 16 },
{ .div = 0 },
};
struct samsung_div_clock pwm_s3c24xx_tdiv_dividers[] __initdata = {
DIV_T(tdiv0, "pwm-tdiv0", "pwm-scaler0", TCFG1, 0, 4, tdiv_s3c24xx_d),
DIV_T(tdiv1, "pwm-tdiv1", "pwm-scaler0", TCFG1, 4, 4, tdiv_s3c24xx_d),
DIV_T(tdiv2, "pwm-tdiv2", "pwm-scaler1", TCFG1, 8, 4, tdiv_s3c24xx_d),
DIV_T(tdiv3, "pwm-tdiv3", "pwm-scaler1", TCFG1, 12, 4, tdiv_s3c24xx_d),
DIV_T(tdiv4, "pwm-tdiv4", "pwm-scaler1", TCFG1, 16, 4, tdiv_s3c24xx_d),
};
static struct clk_div_table tdiv_s3c64xx_d[] = {
{ .val = 0, .div = 1 },
{ .val = 1, .div = 2 },
{ .val = 2, .div = 4 },
{ .val = 3, .div = 8 },
{ .val = 4, .div = 16 },
{ .div = 0 },
};
struct samsung_div_clock pwm_s3c64xx_tdiv_dividers[] __initdata = {
DIV_T(tdiv0, "pwm-tdiv0", "pwm-scaler0", TCFG1, 0, 4, tdiv_s3c64xx_d),
DIV_T(tdiv1, "pwm-tdiv1", "pwm-scaler0", TCFG1, 4, 4, tdiv_s3c64xx_d),
DIV_T(tdiv2, "pwm-tdiv2", "pwm-scaler1", TCFG1, 8, 4, tdiv_s3c64xx_d),
DIV_T(tdiv3, "pwm-tdiv3", "pwm-scaler1", TCFG1, 12, 4, tdiv_s3c64xx_d),
DIV_T(tdiv4, "pwm-tdiv4", "pwm-scaler1", TCFG1, 16, 4, tdiv_s3c64xx_d),
};
struct samsung_mux_clock pwm_tin[] __initdata = {
MUX(tin0, "pwm-tin0", tin0_p, TCFG1, 0, 4),
MUX(tin1, "pwm-tin1", tin1_p, TCFG1, 4, 4),
MUX(tin2, "pwm-tin2", tin2_p, TCFG1, 8, 4),
MUX(tin3, "pwm-tin3", tin3_p, TCFG1, 12, 4),
MUX(tin4, "pwm-tin4", tin4_p, TCFG1, 16, 4),
};
struct samsung_clock_alias pwm_aliases[] __initdata = {
ALIAS(tdiv0, "s3c24xx-pwm.0", "pwm-tdiv"),
ALIAS(tdiv1, "s3c24xx-pwm.1", "pwm-tdiv"),
ALIAS(tdiv2, "s3c24xx-pwm.2", "pwm-tdiv"),
ALIAS(tdiv3, "s3c24xx-pwm.3", "pwm-tdiv"),
ALIAS(tdiv4, "s3c24xx-pwm.4", "pwm-tdiv"),
ALIAS(tin0, "s3c24xx-pwm.0", "pwm-tin"),
ALIAS(tin1, "s3c24xx-pwm.1", "pwm-tin"),
ALIAS(tin2, "s3c24xx-pwm.2", "pwm-tin"),
ALIAS(tin3, "s3c24xx-pwm.3", "pwm-tin"),
ALIAS(tin4, "s3c24xx-pwm.4", "pwm-tin"),
};
#ifdef CONFIG_OF
static struct of_device_id pwm_clk_ids[] __initdata = {
{ .compatible = "samsung,s3c24xx-clock-pwm",
.data = (void *)S3C24XX, },
{ .compatible = "samsung,s3c64xx-clock-pwm",
.data = (void *)S3C64XX, },
{ .compatible = "samsung,s5p64xx-clock-pwm",
.data = (void *)S5P64XX, },
{ },
};
#endif
void __init samsung_pwm_clk_init(struct device_node *np)
{
struct clk *clk;
struct samsung_div_clock *tdiv_list;
struct samsung_mux_clock *tin_list;
struct samsung_clock_alias *alias_list;
unsigned int idx;
int ret;
if (np) {
const struct of_device_id *match;
match = of_match_node(pwm_clk_ids, np);
current_soc = (u32)match->data;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
} else {
reg_base = S3C_VA_TIMER;
if (soc_is_s3c24xx())
current_soc = S3C24XX;
else if (soc_is_s3c64xx() || soc_is_s5pc100())
current_soc = S3C64XX;
else if (soc_is_s5p6440() || soc_is_s5p6450())
current_soc = S5P64XX;
else
panic("%s: unable to determine soc\n", __func__);
}
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
panic("could not allocate clock lookup table\n");
#ifdef CONFIG_OF
clk_data.clks = clk_table;
clk_data.clk_num = nr_clks;
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
#endif
#ifdef CONFIG_PM_SLEEP
register_syscore_ops(&samsung_clk_pwm_syscore_ops);
#endif
clk = clk_register_fixed_rate(NULL, "pwm-tclk0", NULL, CLK_IS_ROOT, 0);
clk = clk_register_fixed_rate(NULL, "pwm-tclk1", NULL, CLK_IS_ROOT, 0);
clk = clk_register_divider(NULL, "pwm-scaler0", "pwm", 0, reg_base + TCFG0, 0, 8, 0, &lock);
clk = clk_register_divider(NULL, "pwm-scaler1", "pwm", 0, reg_base + TCFG0, 8, 8, 0, &lock);
tdiv_list = (current_soc == S3C24XX) ? pwm_s3c24xx_tdiv_dividers
: pwm_s3c64xx_tdiv_dividers;
for (idx = 0; idx < 5; idx++, tdiv_list++) {
clk = samsung_clk_register_tdiv(tdiv_list->name, tdiv_list->parent_name,
tdiv_list->flags, reg_base + tdiv_list->offset,
tdiv_list->shift, tdiv_list->div_flags, tdiv_list->table);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
tdiv_list->name);
continue;
}
if (clk_table && tdiv_list->id)
clk_table[tdiv_list->id] = clk;
}
tin_list = pwm_tin;
for (idx = 0; idx < 5; idx++, tin_list++) {
clk = samsung_clk_register_tin(tin_list->name,
tin_list->parent_names, tin_list->num_parents,
reg_base + tin_list->offset, tin_list->shift,
tin_list->width);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock %s\n", __func__,
tin_list->name);
continue;
}
if (clk_table && tin_list->id)
clk_table[tin_list->id] = clk;
}
alias_list = pwm_aliases;
for (idx = 0; idx < ARRAY_SIZE(pwm_aliases); idx++, alias_list++) {
if (!alias_list->id) {
pr_err("%s: clock id missing for index %d\n", __func__,
idx);
continue;
}
clk = clk_table[alias_list->id];
if (!clk) {
pr_err("%s: failed to find clock %d\n", __func__,
alias_list->id);
continue;
}
ret = clk_register_clkdev(clk, alias_list->alias,
alias_list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, alias_list->alias);
}
}
next prev parent reply other threads:[~2013-03-06 22:15 UTC|newest]
Thread overview: 99+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-03-05 17:42 [PATCH 00/23] RFC: exynos multiplatform support Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 01/23] ARM: exynos: introduce EXYNOS_ATAGS symbol Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 02/23] irqchip: exynos: remove dependency on mach/irqs.h Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 03/23] tty: serial/samsung: prepare for common clock API Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 04/23] tty: serial/samsung: make register definitions global Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 05/23] tty: serial/samsung: fix modular build Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 06/23] ARM: exynos: move debug-macro.S to include/debug/ Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 07/23] i2c: s3c2410: make header file local Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-04-02 20:36 ` Heiko Stübner
2013-04-02 20:36 ` Heiko Stübner
2013-03-05 17:42 ` [PATCH 08/23] mmc: sdhci-s3c: remove platform dependencies Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 09/23] usb: exynos: do not include plat/usb-phy.h Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 10/23] [media] exynos: remove unnecessary header inclusions Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-04-02 13:08 ` Sylwester Nawrocki
2013-04-02 13:08 ` Sylwester Nawrocki
2013-04-02 13:17 ` Sylwester Nawrocki
2013-03-05 17:42 ` [PATCH 11/23] video/exynos: " Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 12/23] thermal/exynos: " Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 13/23] mtd: onenand/samsung: make regs-onenand.h file local Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 14/23] rtc: s3c: make header " Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 15/23] spi: s3c64xx: move to generic dmaengine API Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-06 9:14 ` Padma Venkat
2013-03-06 9:14 ` Padma Venkat
2013-03-05 17:42 ` [PATCH 16/23] pwm: samsung: repair the worst MMIO abuses Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 17/23] ASoC: samsung: move plat/ headers to local directory Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 18/23] ASoC: samsung: convert to dmaengine API Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-06 8:58 ` Padma Venkat
2013-03-06 8:58 ` Padma Venkat
2013-03-06 12:01 ` Arnd Bergmann
2013-03-06 12:01 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 19/23] ASoC: samsung: use irq resource for idma Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 20/23] ARM: exynos: prepare for sparse IRQ Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 21/23] ARM: exynos: hack to disable private clock code Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 22/23] ARM: exynos: work around missing gpio code on multiplatform Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 17:42 ` [PATCH 23/23] ARM: exynos: experimental multiplatform support Arnd Bergmann
2013-03-05 17:42 ` Arnd Bergmann
2013-03-05 18:27 ` [PATCH 00/23] RFC: exynos " Tony Lindgren
2013-03-05 18:27 ` Tony Lindgren
2013-03-05 18:28 ` Tomasz Figa
2013-03-05 18:28 ` Tomasz Figa
2013-03-05 19:19 ` Arnd Bergmann
2013-03-05 19:19 ` Arnd Bergmann
2013-03-05 22:48 ` Tomasz Figa
2013-03-05 22:48 ` Tomasz Figa
2013-03-06 10:50 ` Arnd Bergmann
2013-03-06 10:50 ` Arnd Bergmann
2013-03-06 12:34 ` Thierry Reding
2013-03-06 12:34 ` Thierry Reding
2013-03-06 22:57 ` Tomasz Figa
2013-03-06 22:57 ` Tomasz Figa
2013-03-07 3:02 ` Arnd Bergmann
2013-03-07 3:02 ` Arnd Bergmann
2013-03-07 7:22 ` Thierry Reding
2013-03-07 7:22 ` Thierry Reding
2013-03-08 0:40 ` Tomasz Figa
2013-03-08 0:40 ` Tomasz Figa
2013-03-08 12:52 ` Arnd Bergmann
2013-03-08 12:52 ` Arnd Bergmann
2013-03-06 22:14 ` Heiko Stübner [this message]
2013-03-06 22:14 ` Heiko Stübner
2013-03-06 22:55 ` Tomasz Figa
2013-03-06 22:55 ` Tomasz Figa
2013-03-05 20:50 ` Heiko Stübner
2013-03-05 20:50 ` Heiko Stübner
2013-03-05 21:24 ` Arnd Bergmann
2013-03-05 21:24 ` Arnd Bergmann
2013-03-05 21:54 ` Arnd Bergmann
2013-03-05 21:54 ` Arnd Bergmann
2013-03-05 22:12 ` Tomasz Figa
2013-03-05 22:12 ` Tomasz Figa
2013-03-05 22:21 ` Arnd Bergmann
2013-03-05 22:21 ` Arnd Bergmann
2013-03-05 22:25 ` Heiko Stübner
2013-03-05 22:25 ` Heiko Stübner
2013-03-05 22:43 ` Arnd Bergmann
2013-03-05 22:43 ` Arnd Bergmann
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=201303062314.57183.heiko@sntech.de \
--to=heiko@sntech.de \
--cc=arnd@arndb.de \
--cc=dsaxena@linaro.org \
--cc=kgene.kim@samsung.com \
--cc=linux-arm-kernel@lists.infradead.org \
--cc=linux-samsung-soc@vger.kernel.org \
--cc=olof@lixom.net \
--cc=t.figa@samsung.com \
--cc=thomas.abraham@linaro.org \
--cc=tomasz.figa@gmail.com \
--cc=tushar.behera@linaro.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.