mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
9
drivers/clk/shmobile/Makefile
Normal file
9
drivers/clk/shmobile/Makefile
Normal file
|
@ -0,0 +1,9 @@
|
|||
obj-$(CONFIG_ARCH_EMEV2) += clk-emev2.o
|
||||
obj-$(CONFIG_ARCH_R7S72100) += clk-rz.o
|
||||
obj-$(CONFIG_ARCH_R8A7740) += clk-r8a7740.o
|
||||
obj-$(CONFIG_ARCH_R8A7779) += clk-r8a7779.o
|
||||
obj-$(CONFIG_ARCH_R8A7790) += clk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_R8A7791) += clk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_R8A7794) += clk-rcar-gen2.o
|
||||
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-div6.o
|
||||
obj-$(CONFIG_ARCH_SHMOBILE_MULTI) += clk-mstp.o
|
185
drivers/clk/shmobile/clk-div6.c
Normal file
185
drivers/clk/shmobile/clk-div6.c
Normal file
|
@ -0,0 +1,185 @@
|
|||
/*
|
||||
* r8a7790 Common Clock Framework support
|
||||
*
|
||||
* Copyright (C) 2013 Renesas Solutions Corp.
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
#define CPG_DIV6_CKSTP BIT(8)
|
||||
#define CPG_DIV6_DIV(d) ((d) & 0x3f)
|
||||
#define CPG_DIV6_DIV_MASK 0x3f
|
||||
|
||||
/**
|
||||
* struct div6_clock - CPG 6 bit divider clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @reg: IO-remapped register
|
||||
* @div: divisor value (1-64)
|
||||
*/
|
||||
struct div6_clock {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
unsigned int div;
|
||||
};
|
||||
|
||||
#define to_div6_clock(_hw) container_of(_hw, struct div6_clock, hw)
|
||||
|
||||
static int cpg_div6_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void cpg_div6_clock_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
/* DIV6 clocks require the divisor field to be non-zero when stopping
|
||||
* the clock.
|
||||
*/
|
||||
clk_writel(CPG_DIV6_CKSTP | CPG_DIV6_DIV(CPG_DIV6_DIV_MASK),
|
||||
clock->reg);
|
||||
}
|
||||
|
||||
static int cpg_div6_clock_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
|
||||
return !(clk_readl(clock->reg) & CPG_DIV6_CKSTP);
|
||||
}
|
||||
|
||||
static unsigned long cpg_div6_clock_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
unsigned int div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
|
||||
|
||||
return parent_rate / div;
|
||||
}
|
||||
|
||||
static unsigned int cpg_div6_clock_calc_div(unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
unsigned int div;
|
||||
|
||||
div = DIV_ROUND_CLOSEST(parent_rate, rate);
|
||||
return clamp_t(unsigned int, div, 1, 64);
|
||||
}
|
||||
|
||||
static long cpg_div6_clock_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned int div = cpg_div6_clock_calc_div(rate, *parent_rate);
|
||||
|
||||
return *parent_rate / div;
|
||||
}
|
||||
|
||||
static int cpg_div6_clock_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct div6_clock *clock = to_div6_clock(hw);
|
||||
unsigned int div = cpg_div6_clock_calc_div(rate, parent_rate);
|
||||
|
||||
clock->div = div;
|
||||
|
||||
/* Only program the new divisor if the clock isn't stopped. */
|
||||
if (!(clk_readl(clock->reg) & CPG_DIV6_CKSTP))
|
||||
clk_writel(CPG_DIV6_DIV(clock->div - 1), clock->reg);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_div6_clock_ops = {
|
||||
.enable = cpg_div6_clock_enable,
|
||||
.disable = cpg_div6_clock_disable,
|
||||
.is_enabled = cpg_div6_clock_is_enabled,
|
||||
.recalc_rate = cpg_div6_clock_recalc_rate,
|
||||
.round_rate = cpg_div6_clock_round_rate,
|
||||
.set_rate = cpg_div6_clock_set_rate,
|
||||
};
|
||||
|
||||
static void __init cpg_div6_clock_init(struct device_node *np)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct div6_clock *clock;
|
||||
const char *parent_name;
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
int ret;
|
||||
|
||||
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
|
||||
if (!clock) {
|
||||
pr_err("%s: failed to allocate %s DIV6 clock\n",
|
||||
__func__, np->name);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Remap the clock register and read the divisor. Disabling the
|
||||
* clock overwrites the divisor, so we need to cache its value for the
|
||||
* enable operation.
|
||||
*/
|
||||
clock->reg = of_iomap(np, 0);
|
||||
if (clock->reg == NULL) {
|
||||
pr_err("%s: failed to map %s DIV6 clock register\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
clock->div = (clk_readl(clock->reg) & CPG_DIV6_DIV_MASK) + 1;
|
||||
|
||||
/* Parse the DT properties. */
|
||||
ret = of_property_read_string(np, "clock-output-names", &name);
|
||||
if (ret < 0) {
|
||||
pr_err("%s: failed to get %s DIV6 clock output name\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (parent_name == NULL) {
|
||||
pr_err("%s: failed to get %s DIV6 clock parent name\n",
|
||||
__func__, np->name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* Register the clock. */
|
||||
init.name = name;
|
||||
init.ops = &cpg_div6_clock_ops;
|
||||
init.flags = CLK_IS_BASIC;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clock->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &clock->hw);
|
||||
if (IS_ERR(clk)) {
|
||||
pr_err("%s: failed to register %s DIV6 clock (%ld)\n",
|
||||
__func__, np->name, PTR_ERR(clk));
|
||||
goto error;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
|
||||
return;
|
||||
|
||||
error:
|
||||
if (clock->reg)
|
||||
iounmap(clock->reg);
|
||||
kfree(clock);
|
||||
}
|
||||
CLK_OF_DECLARE(cpg_div6_clk, "renesas,cpg-div6-clock", cpg_div6_clock_init);
|
104
drivers/clk/shmobile/clk-emev2.c
Normal file
104
drivers/clk/shmobile/clk-emev2.c
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* EMMA Mobile EV2 common clock framework support
|
||||
*
|
||||
* Copyright (C) 2013 Takashi Yoshii <takashi.yoshii.ze@renesas.com>
|
||||
* Copyright (C) 2012 Magnus Damm
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
|
||||
/* EMEV2 SMU registers */
|
||||
#define USIAU0_RSTCTRL 0x094
|
||||
#define USIBU1_RSTCTRL 0x0ac
|
||||
#define USIBU2_RSTCTRL 0x0b0
|
||||
#define USIBU3_RSTCTRL 0x0b4
|
||||
#define STI_RSTCTRL 0x124
|
||||
#define STI_CLKSEL 0x688
|
||||
|
||||
static DEFINE_SPINLOCK(lock);
|
||||
|
||||
/* not pretty, but hey */
|
||||
void __iomem *smu_base;
|
||||
|
||||
static void __init emev2_smu_write(unsigned long value, int offs)
|
||||
{
|
||||
BUG_ON(!smu_base || (offs >= PAGE_SIZE));
|
||||
writel_relaxed(value, smu_base + offs);
|
||||
}
|
||||
|
||||
static const struct of_device_id smu_id[] __initconst = {
|
||||
{ .compatible = "renesas,emev2-smu", },
|
||||
{},
|
||||
};
|
||||
|
||||
static void __init emev2_smu_init(void)
|
||||
{
|
||||
struct device_node *np;
|
||||
|
||||
np = of_find_matching_node(NULL, smu_id);
|
||||
BUG_ON(!np);
|
||||
smu_base = of_iomap(np, 0);
|
||||
BUG_ON(!smu_base);
|
||||
of_node_put(np);
|
||||
|
||||
/* setup STI timer to run on 32.768 kHz and deassert reset */
|
||||
emev2_smu_write(0, STI_CLKSEL);
|
||||
emev2_smu_write(1, STI_RSTCTRL);
|
||||
|
||||
/* deassert reset for UART0->UART3 */
|
||||
emev2_smu_write(2, USIAU0_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU1_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU2_RSTCTRL);
|
||||
emev2_smu_write(2, USIBU3_RSTCTRL);
|
||||
}
|
||||
|
||||
static void __init emev2_smu_clkdiv_init(struct device_node *np)
|
||||
{
|
||||
u32 reg[2];
|
||||
struct clk *clk;
|
||||
const char *parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2)))
|
||||
return;
|
||||
if (!smu_base)
|
||||
emev2_smu_init();
|
||||
clk = clk_register_divider(NULL, np->name, parent_name, 0,
|
||||
smu_base + reg[0], reg[1], 8, 0, &lock);
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, np->name, NULL);
|
||||
pr_debug("## %s %s %p\n", __func__, np->name, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(emev2_smu_clkdiv, "renesas,emev2-smu-clkdiv",
|
||||
emev2_smu_clkdiv_init);
|
||||
|
||||
static void __init emev2_smu_gclk_init(struct device_node *np)
|
||||
{
|
||||
u32 reg[2];
|
||||
struct clk *clk;
|
||||
const char *parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (WARN_ON(of_property_read_u32_array(np, "reg", reg, 2)))
|
||||
return;
|
||||
if (!smu_base)
|
||||
emev2_smu_init();
|
||||
clk = clk_register_gate(NULL, np->name, parent_name, 0,
|
||||
smu_base + reg[0], reg[1], 0, &lock);
|
||||
of_clk_add_provider(np, of_clk_src_simple_get, clk);
|
||||
clk_register_clkdev(clk, np->name, NULL);
|
||||
pr_debug("## %s %s %p\n", __func__, np->name, clk);
|
||||
}
|
||||
CLK_OF_DECLARE(emev2_smu_gclk, "renesas,emev2-smu-gclk", emev2_smu_gclk_init);
|
238
drivers/clk/shmobile/clk-mstp.c
Normal file
238
drivers/clk/shmobile/clk-mstp.c
Normal file
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* R-Car MSTP clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/*
|
||||
* MSTP clocks. We can't use standard gate clocks as we need to poll on the
|
||||
* status register when enabling the clock.
|
||||
*/
|
||||
|
||||
#define MSTP_MAX_CLOCKS 32
|
||||
|
||||
/**
|
||||
* struct mstp_clock_group - MSTP gating clocks group
|
||||
*
|
||||
* @data: clocks in this group
|
||||
* @smstpcr: module stop control register
|
||||
* @mstpsr: module stop status register (optional)
|
||||
* @lock: protects writes to SMSTPCR
|
||||
*/
|
||||
struct mstp_clock_group {
|
||||
struct clk_onecell_data data;
|
||||
void __iomem *smstpcr;
|
||||
void __iomem *mstpsr;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct mstp_clock - MSTP gating clock
|
||||
* @hw: handle between common and hardware-specific interfaces
|
||||
* @bit_index: control bit index
|
||||
* @group: MSTP clocks group
|
||||
*/
|
||||
struct mstp_clock {
|
||||
struct clk_hw hw;
|
||||
u32 bit_index;
|
||||
struct mstp_clock_group *group;
|
||||
};
|
||||
|
||||
#define to_mstp_clock(_hw) container_of(_hw, struct mstp_clock, hw)
|
||||
|
||||
static int cpg_mstp_clock_endisable(struct clk_hw *hw, bool enable)
|
||||
{
|
||||
struct mstp_clock *clock = to_mstp_clock(hw);
|
||||
struct mstp_clock_group *group = clock->group;
|
||||
u32 bitmask = BIT(clock->bit_index);
|
||||
unsigned long flags;
|
||||
unsigned int i;
|
||||
u32 value;
|
||||
|
||||
spin_lock_irqsave(&group->lock, flags);
|
||||
|
||||
value = clk_readl(group->smstpcr);
|
||||
if (enable)
|
||||
value &= ~bitmask;
|
||||
else
|
||||
value |= bitmask;
|
||||
clk_writel(value, group->smstpcr);
|
||||
|
||||
spin_unlock_irqrestore(&group->lock, flags);
|
||||
|
||||
if (!enable || !group->mstpsr)
|
||||
return 0;
|
||||
|
||||
for (i = 1000; i > 0; --i) {
|
||||
if (!(clk_readl(group->mstpsr) & bitmask))
|
||||
break;
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
if (!i) {
|
||||
pr_err("%s: failed to enable %p[%d]\n", __func__,
|
||||
group->smstpcr, clock->bit_index);
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cpg_mstp_clock_enable(struct clk_hw *hw)
|
||||
{
|
||||
return cpg_mstp_clock_endisable(hw, true);
|
||||
}
|
||||
|
||||
static void cpg_mstp_clock_disable(struct clk_hw *hw)
|
||||
{
|
||||
cpg_mstp_clock_endisable(hw, false);
|
||||
}
|
||||
|
||||
static int cpg_mstp_clock_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct mstp_clock *clock = to_mstp_clock(hw);
|
||||
struct mstp_clock_group *group = clock->group;
|
||||
u32 value;
|
||||
|
||||
if (group->mstpsr)
|
||||
value = clk_readl(group->mstpsr);
|
||||
else
|
||||
value = clk_readl(group->smstpcr);
|
||||
|
||||
return !(value & BIT(clock->bit_index));
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_mstp_clock_ops = {
|
||||
.enable = cpg_mstp_clock_enable,
|
||||
.disable = cpg_mstp_clock_disable,
|
||||
.is_enabled = cpg_mstp_clock_is_enabled,
|
||||
};
|
||||
|
||||
static struct clk * __init
|
||||
cpg_mstp_clock_register(const char *name, const char *parent_name,
|
||||
unsigned int index, struct mstp_clock_group *group)
|
||||
{
|
||||
struct clk_init_data init;
|
||||
struct mstp_clock *clock;
|
||||
struct clk *clk;
|
||||
|
||||
clock = kzalloc(sizeof(*clock), GFP_KERNEL);
|
||||
if (!clock) {
|
||||
pr_err("%s: failed to allocate MSTP clock.\n", __func__);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
init.name = name;
|
||||
init.ops = &cpg_mstp_clock_ops;
|
||||
init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
clock->bit_index = index;
|
||||
clock->group = group;
|
||||
clock->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &clock->hw);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
kfree(clock);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
static void __init cpg_mstp_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct mstp_clock_group *group;
|
||||
const char *idxname;
|
||||
struct clk **clks;
|
||||
unsigned int i;
|
||||
|
||||
group = kzalloc(sizeof(*group), GFP_KERNEL);
|
||||
clks = kmalloc(MSTP_MAX_CLOCKS * sizeof(*clks), GFP_KERNEL);
|
||||
if (group == NULL || clks == NULL) {
|
||||
kfree(group);
|
||||
kfree(clks);
|
||||
pr_err("%s: failed to allocate group\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&group->lock);
|
||||
group->data.clks = clks;
|
||||
|
||||
group->smstpcr = of_iomap(np, 0);
|
||||
group->mstpsr = of_iomap(np, 1);
|
||||
|
||||
if (group->smstpcr == NULL) {
|
||||
pr_err("%s: failed to remap SMSTPCR\n", __func__);
|
||||
kfree(group);
|
||||
kfree(clks);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < MSTP_MAX_CLOCKS; ++i)
|
||||
clks[i] = ERR_PTR(-ENOENT);
|
||||
|
||||
if (of_find_property(np, "clock-indices", &i))
|
||||
idxname = "clock-indices";
|
||||
else
|
||||
idxname = "renesas,clock-indices";
|
||||
|
||||
for (i = 0; i < MSTP_MAX_CLOCKS; ++i) {
|
||||
const char *parent_name;
|
||||
const char *name;
|
||||
u32 clkidx;
|
||||
int ret;
|
||||
|
||||
/* Skip clocks with no name. */
|
||||
ret = of_property_read_string_index(np, "clock-output-names",
|
||||
i, &name);
|
||||
if (ret < 0 || strlen(name) == 0)
|
||||
continue;
|
||||
|
||||
parent_name = of_clk_get_parent_name(np, i);
|
||||
ret = of_property_read_u32_index(np, idxname, i, &clkidx);
|
||||
if (parent_name == NULL || ret < 0)
|
||||
break;
|
||||
|
||||
if (clkidx >= MSTP_MAX_CLOCKS) {
|
||||
pr_err("%s: invalid clock %s %s index %u)\n",
|
||||
__func__, np->name, name, clkidx);
|
||||
continue;
|
||||
}
|
||||
|
||||
clks[clkidx] = cpg_mstp_clock_register(name, parent_name,
|
||||
clkidx, group);
|
||||
if (!IS_ERR(clks[clkidx])) {
|
||||
group->data.clk_num = max(group->data.clk_num,
|
||||
clkidx + 1);
|
||||
/*
|
||||
* Register a clkdev to let board code retrieve the
|
||||
* clock by name and register aliases for non-DT
|
||||
* devices.
|
||||
*
|
||||
* FIXME: Remove this when all devices that require a
|
||||
* clock will be instantiated from DT.
|
||||
*/
|
||||
clk_register_clkdev(clks[clkidx], name, NULL);
|
||||
} else {
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clks[clkidx]));
|
||||
}
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &group->data);
|
||||
}
|
||||
CLK_OF_DECLARE(cpg_mstp_clks, "renesas,cpg-mstp-clocks", cpg_mstp_clocks_init);
|
199
drivers/clk/shmobile/clk-r8a7740.c
Normal file
199
drivers/clk/shmobile/clk-r8a7740.c
Normal file
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* r8a7740 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2014 Ulrich Hecht
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/shmobile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct r8a7740_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_FRQCRA 0x00
|
||||
#define CPG_FRQCRB 0x04
|
||||
#define CPG_PLLC2CR 0x2c
|
||||
#define CPG_USBCKCR 0x8c
|
||||
#define CPG_FRQCRC 0xe0
|
||||
|
||||
#define CLK_ENABLE_ON_INIT BIT(0)
|
||||
|
||||
struct div4_clk {
|
||||
const char *name;
|
||||
unsigned int reg;
|
||||
unsigned int shift;
|
||||
int flags;
|
||||
};
|
||||
|
||||
static struct div4_clk div4_clks[] = {
|
||||
{ "i", CPG_FRQCRA, 20, CLK_ENABLE_ON_INIT },
|
||||
{ "zg", CPG_FRQCRA, 16, CLK_ENABLE_ON_INIT },
|
||||
{ "b", CPG_FRQCRA, 8, CLK_ENABLE_ON_INIT },
|
||||
{ "m1", CPG_FRQCRA, 4, CLK_ENABLE_ON_INIT },
|
||||
{ "hp", CPG_FRQCRB, 4, 0 },
|
||||
{ "hpp", CPG_FRQCRC, 20, 0 },
|
||||
{ "usbp", CPG_FRQCRC, 16, 0 },
|
||||
{ "s", CPG_FRQCRC, 12, 0 },
|
||||
{ "zb", CPG_FRQCRC, 8, 0 },
|
||||
{ "m3", CPG_FRQCRC, 4, 0 },
|
||||
{ "cp", CPG_FRQCRC, 0, 0 },
|
||||
{ NULL, 0, 0, 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table div4_div_table[] = {
|
||||
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 }, { 4, 8 }, { 5, 12 },
|
||||
{ 6, 16 }, { 7, 18 }, { 8, 24 }, { 9, 32 }, { 10, 36 }, { 11, 48 },
|
||||
{ 13, 72 }, { 14, 96 }, { 0, 0 }
|
||||
};
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg,
|
||||
const char *name)
|
||||
{
|
||||
const struct clk_div_table *table = NULL;
|
||||
const char *parent_name;
|
||||
unsigned int shift, reg;
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "r")) {
|
||||
switch (cpg_mode & (BIT(2) | BIT(1))) {
|
||||
case BIT(1) | BIT(2):
|
||||
/* extal1 */
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = 2048;
|
||||
break;
|
||||
case BIT(2):
|
||||
/* extal1 */
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = 1024;
|
||||
break;
|
||||
default:
|
||||
/* extalr */
|
||||
parent_name = of_clk_get_parent_name(np, 2);
|
||||
break;
|
||||
}
|
||||
} else if (!strcmp(name, "system")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
if (cpg_mode & BIT(1))
|
||||
div = 2;
|
||||
} else if (!strcmp(name, "pllc0")) {
|
||||
/* PLLC0/1 are configurable multiplier clocks. Register them as
|
||||
* fixed factor clocks for now as there's no generic multiplier
|
||||
* clock implementation and we currently have no need to change
|
||||
* the multiplier value.
|
||||
*/
|
||||
u32 value = clk_readl(cpg->reg + CPG_FRQCRC);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x7f) + 1;
|
||||
} else if (!strcmp(name, "pllc1")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_FRQCRA);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x7f) + 1;
|
||||
div = 2;
|
||||
} else if (!strcmp(name, "pllc2")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_PLLC2CR);
|
||||
parent_name = "system";
|
||||
mult = ((value >> 24) & 0x3f) + 1;
|
||||
} else if (!strcmp(name, "usb24s")) {
|
||||
u32 value = clk_readl(cpg->reg + CPG_USBCKCR);
|
||||
if (value & BIT(7))
|
||||
/* extal2 */
|
||||
parent_name = of_clk_get_parent_name(np, 1);
|
||||
else
|
||||
parent_name = "system";
|
||||
if (!(value & BIT(6)))
|
||||
div = 2;
|
||||
} else {
|
||||
struct div4_clk *c;
|
||||
for (c = div4_clks; c->name; c++) {
|
||||
if (!strcmp(name, c->name)) {
|
||||
parent_name = "pllc1";
|
||||
table = div4_div_table;
|
||||
reg = c->reg;
|
||||
shift = c->shift;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!c->name)
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!table) {
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0,
|
||||
mult, div);
|
||||
} else {
|
||||
return clk_register_divider_table(NULL, name, parent_name, 0,
|
||||
cpg->reg + reg, shift, 4, 0,
|
||||
table, &cpg->lock);
|
||||
}
|
||||
}
|
||||
|
||||
static void __init r8a7740_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct r8a7740_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned int i;
|
||||
int num_clks;
|
||||
|
||||
if (of_property_read_u32(np, "renesas,mode", &cpg_mode))
|
||||
pr_warn("%s: missing renesas,mode property\n", __func__);
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (num_clks < 0) {
|
||||
pr_err("%s: failed to count clocks\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
|
||||
if (cpg == NULL || clks == NULL) {
|
||||
/* We're leaking memory on purpose, there's no point in cleaning
|
||||
* up as the system won't boot anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&cpg->lock);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
cpg->reg = of_iomap(np, 0);
|
||||
if (WARN_ON(cpg->reg == NULL))
|
||||
return;
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i,
|
||||
&name);
|
||||
|
||||
clk = r8a7740_cpg_register_clock(np, cpg, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(r8a7740_cpg_clks, "renesas,r8a7740-cpg-clocks",
|
||||
r8a7740_cpg_clocks_init);
|
180
drivers/clk/shmobile/clk-r8a7779.c
Normal file
180
drivers/clk/shmobile/clk-r8a7779.c
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* r8a7779 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013, 2014 Horms Solutions Ltd.
|
||||
*
|
||||
* Contact: Simon Horman <horms@verge.net.au>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/shmobile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#include <dt-bindings/clock/r8a7779-clock.h>
|
||||
|
||||
#define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1)
|
||||
|
||||
struct r8a7779_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CPG Clock Data
|
||||
*/
|
||||
|
||||
/*
|
||||
* MD1 = 1 MD1 = 0
|
||||
* (PLLA = 1500) (PLLA = 1600)
|
||||
* (MHz) (MHz)
|
||||
*------------------------------------------------+--------------------
|
||||
* clkz 1000 (2/3) 800 (1/2)
|
||||
* clkzs 250 (1/6) 200 (1/8)
|
||||
* clki 750 (1/2) 800 (1/2)
|
||||
* clks 250 (1/6) 200 (1/8)
|
||||
* clks1 125 (1/12) 100 (1/16)
|
||||
* clks3 187.5 (1/8) 200 (1/8)
|
||||
* clks4 93.7 (1/16) 100 (1/16)
|
||||
* clkp 62.5 (1/24) 50 (1/32)
|
||||
* clkg 62.5 (1/24) 66.6 (1/24)
|
||||
* clkb, CLKOUT
|
||||
* (MD2 = 0) 62.5 (1/24) 66.6 (1/24)
|
||||
* (MD2 = 1) 41.6 (1/36) 50 (1/32)
|
||||
*/
|
||||
|
||||
#define CPG_CLK_CONFIG_INDEX(md) (((md) & (BIT(2)|BIT(1))) >> 1)
|
||||
|
||||
struct cpg_clk_config {
|
||||
unsigned int z_mult;
|
||||
unsigned int z_div;
|
||||
unsigned int zs_and_s_div;
|
||||
unsigned int s1_div;
|
||||
unsigned int p_div;
|
||||
unsigned int b_and_out_div;
|
||||
};
|
||||
|
||||
static const struct cpg_clk_config cpg_clk_configs[4] __initconst = {
|
||||
{ 1, 2, 8, 16, 32, 24 },
|
||||
{ 2, 3, 6, 12, 24, 24 },
|
||||
{ 1, 2, 8, 16, 32, 32 },
|
||||
{ 2, 3, 6, 12, 24, 36 },
|
||||
};
|
||||
|
||||
/*
|
||||
* MD PLLA Ratio
|
||||
* 12 11
|
||||
*------------------------
|
||||
* 0 0 x42
|
||||
* 0 1 x48
|
||||
* 1 0 x56
|
||||
* 1 1 x64
|
||||
*/
|
||||
|
||||
#define CPG_PLLA_MULT_INDEX(md) (((md) & (BIT(12)|BIT(11))) >> 11)
|
||||
|
||||
static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 };
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg,
|
||||
const struct cpg_clk_config *config,
|
||||
unsigned int plla_mult, const char *name)
|
||||
{
|
||||
const char *parent_name = "plla";
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "plla")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
mult = plla_mult;
|
||||
} else if (!strcmp(name, "z")) {
|
||||
div = config->z_div;
|
||||
mult = config->z_mult;
|
||||
} else if (!strcmp(name, "zs") || !strcmp(name, "s")) {
|
||||
div = config->zs_and_s_div;
|
||||
} else if (!strcmp(name, "s1")) {
|
||||
div = config->s1_div;
|
||||
} else if (!strcmp(name, "p")) {
|
||||
div = config->p_div;
|
||||
} else if (!strcmp(name, "b") || !strcmp(name, "out")) {
|
||||
div = config->b_and_out_div;
|
||||
} else {
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, div);
|
||||
}
|
||||
|
||||
static void __init r8a7779_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
const struct cpg_clk_config *config;
|
||||
struct r8a7779_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned int i, plla_mult;
|
||||
int num_clks;
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (num_clks < 0) {
|
||||
pr_err("%s: failed to count clocks\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(CPG_NUM_CLOCKS * sizeof(*clks), GFP_KERNEL);
|
||||
if (cpg == NULL || clks == NULL) {
|
||||
/* We're leaking memory on purpose, there's no point in cleaning
|
||||
* up as the system won't boot anyway.
|
||||
*/
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&cpg->lock);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(cpg_mode)];
|
||||
plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(cpg_mode)];
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i,
|
||||
&name);
|
||||
|
||||
clk = r8a7779_cpg_register_clock(np, cpg, config,
|
||||
plla_mult, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(r8a7779_cpg_clks, "renesas,r8a7779-cpg-clocks",
|
||||
r8a7779_cpg_clocks_init);
|
||||
|
||||
void __init r8a7779_clocks_init(u32 mode)
|
||||
{
|
||||
cpg_mode = mode;
|
||||
|
||||
of_clk_init(NULL);
|
||||
}
|
339
drivers/clk/shmobile/clk-rcar-gen2.c
Normal file
339
drivers/clk/shmobile/clk-rcar-gen2.c
Normal file
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
* rcar_gen2 Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
*
|
||||
* Contact: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/clkdev.h>
|
||||
#include <linux/clk/shmobile.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
struct rcar_gen2_cpg {
|
||||
struct clk_onecell_data data;
|
||||
spinlock_t lock;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_FRQCRB 0x00000004
|
||||
#define CPG_FRQCRB_KICK BIT(31)
|
||||
#define CPG_SDCKCR 0x00000074
|
||||
#define CPG_PLL0CR 0x000000d8
|
||||
#define CPG_FRQCRC 0x000000e0
|
||||
#define CPG_FRQCRC_ZFC_MASK (0x1f << 8)
|
||||
#define CPG_FRQCRC_ZFC_SHIFT 8
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Z Clock
|
||||
*
|
||||
* Traits of this clock:
|
||||
* prepare - clk_prepare only ensures that parents are prepared
|
||||
* enable - clk_enable only ensures that parents are enabled
|
||||
* rate - rate is adjustable. clk->rate = parent->rate * mult / 32
|
||||
* parent - fixed parent. No clk_set_parent support
|
||||
*/
|
||||
|
||||
struct cpg_z_clk {
|
||||
struct clk_hw hw;
|
||||
void __iomem *reg;
|
||||
void __iomem *kick_reg;
|
||||
};
|
||||
|
||||
#define to_z_clk(_hw) container_of(_hw, struct cpg_z_clk, hw)
|
||||
|
||||
static unsigned long cpg_z_clk_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cpg_z_clk *zclk = to_z_clk(hw);
|
||||
unsigned int mult;
|
||||
unsigned int val;
|
||||
|
||||
val = (clk_readl(zclk->reg) & CPG_FRQCRC_ZFC_MASK)
|
||||
>> CPG_FRQCRC_ZFC_SHIFT;
|
||||
mult = 32 - val;
|
||||
|
||||
return div_u64((u64)parent_rate * mult, 32);
|
||||
}
|
||||
|
||||
static long cpg_z_clk_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *parent_rate)
|
||||
{
|
||||
unsigned long prate = *parent_rate;
|
||||
unsigned int mult;
|
||||
|
||||
if (!prate)
|
||||
prate = 1;
|
||||
|
||||
mult = div_u64((u64)rate * 32, prate);
|
||||
mult = clamp(mult, 1U, 32U);
|
||||
|
||||
return *parent_rate / 32 * mult;
|
||||
}
|
||||
|
||||
static int cpg_z_clk_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
struct cpg_z_clk *zclk = to_z_clk(hw);
|
||||
unsigned int mult;
|
||||
u32 val, kick;
|
||||
unsigned int i;
|
||||
|
||||
mult = div_u64((u64)rate * 32, parent_rate);
|
||||
mult = clamp(mult, 1U, 32U);
|
||||
|
||||
if (clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK)
|
||||
return -EBUSY;
|
||||
|
||||
val = clk_readl(zclk->reg);
|
||||
val &= ~CPG_FRQCRC_ZFC_MASK;
|
||||
val |= (32 - mult) << CPG_FRQCRC_ZFC_SHIFT;
|
||||
clk_writel(val, zclk->reg);
|
||||
|
||||
/*
|
||||
* Set KICK bit in FRQCRB to update hardware setting and wait for
|
||||
* clock change completion.
|
||||
*/
|
||||
kick = clk_readl(zclk->kick_reg);
|
||||
kick |= CPG_FRQCRB_KICK;
|
||||
clk_writel(kick, zclk->kick_reg);
|
||||
|
||||
/*
|
||||
* Note: There is no HW information about the worst case latency.
|
||||
*
|
||||
* Using experimental measurements, it seems that no more than
|
||||
* ~10 iterations are needed, independently of the CPU rate.
|
||||
* Since this value might be dependant of external xtal rate, pll1
|
||||
* rate or even the other emulation clocks rate, use 1000 as a
|
||||
* "super" safe value.
|
||||
*/
|
||||
for (i = 1000; i; i--) {
|
||||
if (!(clk_readl(zclk->kick_reg) & CPG_FRQCRB_KICK))
|
||||
return 0;
|
||||
|
||||
cpu_relax();
|
||||
}
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
static const struct clk_ops cpg_z_clk_ops = {
|
||||
.recalc_rate = cpg_z_clk_recalc_rate,
|
||||
.round_rate = cpg_z_clk_round_rate,
|
||||
.set_rate = cpg_z_clk_set_rate,
|
||||
};
|
||||
|
||||
static struct clk * __init cpg_z_clk_register(struct rcar_gen2_cpg *cpg)
|
||||
{
|
||||
static const char *parent_name = "pll0";
|
||||
struct clk_init_data init;
|
||||
struct cpg_z_clk *zclk;
|
||||
struct clk *clk;
|
||||
|
||||
zclk = kzalloc(sizeof(*zclk), GFP_KERNEL);
|
||||
if (!zclk)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
init.name = "z";
|
||||
init.ops = &cpg_z_clk_ops;
|
||||
init.flags = 0;
|
||||
init.parent_names = &parent_name;
|
||||
init.num_parents = 1;
|
||||
|
||||
zclk->reg = cpg->reg + CPG_FRQCRC;
|
||||
zclk->kick_reg = cpg->reg + CPG_FRQCRB;
|
||||
zclk->hw.init = &init;
|
||||
|
||||
clk = clk_register(NULL, &zclk->hw);
|
||||
if (IS_ERR(clk))
|
||||
kfree(zclk);
|
||||
|
||||
return clk;
|
||||
}
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* CPG Clock Data
|
||||
*/
|
||||
|
||||
/*
|
||||
* MD EXTAL PLL0 PLL1 PLL3
|
||||
* 14 13 19 (MHz) *1 *1
|
||||
*---------------------------------------------------
|
||||
* 0 0 0 15 x 1 x172/2 x208/2 x106
|
||||
* 0 0 1 15 x 1 x172/2 x208/2 x88
|
||||
* 0 1 0 20 x 1 x130/2 x156/2 x80
|
||||
* 0 1 1 20 x 1 x130/2 x156/2 x66
|
||||
* 1 0 0 26 / 2 x200/2 x240/2 x122
|
||||
* 1 0 1 26 / 2 x200/2 x240/2 x102
|
||||
* 1 1 0 30 / 2 x172/2 x208/2 x106
|
||||
* 1 1 1 30 / 2 x172/2 x208/2 x88
|
||||
*
|
||||
* *1 : Table 7.6 indicates VCO ouput (PLLx = VCO/2)
|
||||
*/
|
||||
#define CPG_PLL_CONFIG_INDEX(md) ((((md) & BIT(14)) >> 12) | \
|
||||
(((md) & BIT(13)) >> 12) | \
|
||||
(((md) & BIT(19)) >> 19))
|
||||
struct cpg_pll_config {
|
||||
unsigned int extal_div;
|
||||
unsigned int pll1_mult;
|
||||
unsigned int pll3_mult;
|
||||
};
|
||||
|
||||
static const struct cpg_pll_config cpg_pll_configs[8] __initconst = {
|
||||
{ 1, 208, 106 }, { 1, 208, 88 }, { 1, 156, 80 }, { 1, 156, 66 },
|
||||
{ 2, 240, 122 }, { 2, 240, 102 }, { 2, 208, 106 }, { 2, 208, 88 },
|
||||
};
|
||||
|
||||
/* SDHI divisors */
|
||||
static const struct clk_div_table cpg_sdh_div_table[] = {
|
||||
{ 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 },
|
||||
{ 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
|
||||
{ 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 },
|
||||
};
|
||||
|
||||
static const struct clk_div_table cpg_sd01_div_table[] = {
|
||||
{ 4, 8 },
|
||||
{ 5, 12 }, { 6, 16 }, { 7, 18 }, { 8, 24 },
|
||||
{ 10, 36 }, { 11, 48 }, { 12, 10 }, { 0, 0 },
|
||||
};
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static u32 cpg_mode __initdata;
|
||||
|
||||
static struct clk * __init
|
||||
rcar_gen2_cpg_register_clock(struct device_node *np, struct rcar_gen2_cpg *cpg,
|
||||
const struct cpg_pll_config *config,
|
||||
const char *name)
|
||||
{
|
||||
const struct clk_div_table *table = NULL;
|
||||
const char *parent_name;
|
||||
unsigned int shift;
|
||||
unsigned int mult = 1;
|
||||
unsigned int div = 1;
|
||||
|
||||
if (!strcmp(name, "main")) {
|
||||
parent_name = of_clk_get_parent_name(np, 0);
|
||||
div = config->extal_div;
|
||||
} else if (!strcmp(name, "pll0")) {
|
||||
/* PLL0 is a configurable multiplier clock. Register it as a
|
||||
* fixed factor clock for now as there's no generic multiplier
|
||||
* clock implementation and we currently have no need to change
|
||||
* the multiplier value.
|
||||
*/
|
||||
u32 value = clk_readl(cpg->reg + CPG_PLL0CR);
|
||||
parent_name = "main";
|
||||
mult = ((value >> 24) & ((1 << 7) - 1)) + 1;
|
||||
} else if (!strcmp(name, "pll1")) {
|
||||
parent_name = "main";
|
||||
mult = config->pll1_mult / 2;
|
||||
} else if (!strcmp(name, "pll3")) {
|
||||
parent_name = "main";
|
||||
mult = config->pll3_mult;
|
||||
} else if (!strcmp(name, "lb")) {
|
||||
parent_name = "pll1";
|
||||
div = cpg_mode & BIT(18) ? 36 : 24;
|
||||
} else if (!strcmp(name, "qspi")) {
|
||||
parent_name = "pll1_div2";
|
||||
div = (cpg_mode & (BIT(3) | BIT(2) | BIT(1))) == BIT(2)
|
||||
? 8 : 10;
|
||||
} else if (!strcmp(name, "sdh")) {
|
||||
parent_name = "pll1";
|
||||
table = cpg_sdh_div_table;
|
||||
shift = 8;
|
||||
} else if (!strcmp(name, "sd0")) {
|
||||
parent_name = "pll1";
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 4;
|
||||
} else if (!strcmp(name, "sd1")) {
|
||||
parent_name = "pll1";
|
||||
table = cpg_sd01_div_table;
|
||||
shift = 0;
|
||||
} else if (!strcmp(name, "z")) {
|
||||
return cpg_z_clk_register(cpg);
|
||||
} else {
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (!table)
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0,
|
||||
mult, div);
|
||||
else
|
||||
return clk_register_divider_table(NULL, name, parent_name, 0,
|
||||
cpg->reg + CPG_SDCKCR, shift,
|
||||
4, 0, table, &cpg->lock);
|
||||
}
|
||||
|
||||
static void __init rcar_gen2_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
const struct cpg_pll_config *config;
|
||||
struct rcar_gen2_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned int i;
|
||||
int num_clks;
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (num_clks < 0) {
|
||||
pr_err("%s: failed to count clocks\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
|
||||
if (cpg == NULL || clks == NULL) {
|
||||
/* We're leaking memory on purpose, there's no point in cleaning
|
||||
* up as the system won't boot anyway.
|
||||
*/
|
||||
pr_err("%s: failed to allocate cpg\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_init(&cpg->lock);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
cpg->reg = of_iomap(np, 0);
|
||||
if (WARN_ON(cpg->reg == NULL))
|
||||
return;
|
||||
|
||||
config = &cpg_pll_configs[CPG_PLL_CONFIG_INDEX(cpg_mode)];
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i,
|
||||
&name);
|
||||
|
||||
clk = rcar_gen2_cpg_register_clock(np, cpg, config, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(rcar_gen2_cpg_clks, "renesas,rcar-gen2-cpg-clocks",
|
||||
rcar_gen2_cpg_clocks_init);
|
||||
|
||||
void __init rcar_gen2_clocks_init(u32 mode)
|
||||
{
|
||||
cpg_mode = mode;
|
||||
|
||||
of_clk_init(NULL);
|
||||
}
|
103
drivers/clk/shmobile/clk-rz.c
Normal file
103
drivers/clk/shmobile/clk-rz.c
Normal file
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* rz Core CPG Clocks
|
||||
*
|
||||
* Copyright (C) 2013 Ideas On Board SPRL
|
||||
* Copyright (C) 2014 Wolfram Sang, Sang Engineering <wsa@sang-engineering.com>
|
||||
*
|
||||
* 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; version 2 of the License.
|
||||
*/
|
||||
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct rz_cpg {
|
||||
struct clk_onecell_data data;
|
||||
void __iomem *reg;
|
||||
};
|
||||
|
||||
#define CPG_FRQCR 0x10
|
||||
#define CPG_FRQCR2 0x14
|
||||
|
||||
/* -----------------------------------------------------------------------------
|
||||
* Initialization
|
||||
*/
|
||||
|
||||
static struct clk * __init
|
||||
rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name)
|
||||
{
|
||||
u32 val;
|
||||
unsigned mult;
|
||||
static const unsigned frqcr_tab[4] = { 3, 2, 0, 1 };
|
||||
|
||||
if (strcmp(name, "pll") == 0) {
|
||||
/* FIXME: cpg_mode should be read from GPIO. But no GPIO support yet */
|
||||
unsigned cpg_mode = 0; /* hardcoded to EXTAL for now */
|
||||
const char *parent_name = of_clk_get_parent_name(np, cpg_mode);
|
||||
|
||||
mult = cpg_mode ? (32 / 4) : 30;
|
||||
|
||||
return clk_register_fixed_factor(NULL, name, parent_name, 0, mult, 1);
|
||||
}
|
||||
|
||||
/* If mapping regs failed, skip non-pll clocks. System will boot anyhow */
|
||||
if (!cpg->reg)
|
||||
return ERR_PTR(-ENXIO);
|
||||
|
||||
/* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3)
|
||||
* and the constraint that always g <= i. To get the rz platform started,
|
||||
* let them run at fixed current speed and implement the details later.
|
||||
*/
|
||||
if (strcmp(name, "i") == 0)
|
||||
val = (clk_readl(cpg->reg + CPG_FRQCR) >> 8) & 3;
|
||||
else if (strcmp(name, "g") == 0)
|
||||
val = clk_readl(cpg->reg + CPG_FRQCR2) & 3;
|
||||
else
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mult = frqcr_tab[val];
|
||||
return clk_register_fixed_factor(NULL, name, "pll", 0, mult, 3);
|
||||
}
|
||||
|
||||
static void __init rz_cpg_clocks_init(struct device_node *np)
|
||||
{
|
||||
struct rz_cpg *cpg;
|
||||
struct clk **clks;
|
||||
unsigned i;
|
||||
int num_clks;
|
||||
|
||||
num_clks = of_property_count_strings(np, "clock-output-names");
|
||||
if (WARN(num_clks <= 0, "can't count CPG clocks\n"))
|
||||
return;
|
||||
|
||||
cpg = kzalloc(sizeof(*cpg), GFP_KERNEL);
|
||||
clks = kzalloc(num_clks * sizeof(*clks), GFP_KERNEL);
|
||||
BUG_ON(!cpg || !clks);
|
||||
|
||||
cpg->data.clks = clks;
|
||||
cpg->data.clk_num = num_clks;
|
||||
|
||||
cpg->reg = of_iomap(np, 0);
|
||||
|
||||
for (i = 0; i < num_clks; ++i) {
|
||||
const char *name;
|
||||
struct clk *clk;
|
||||
|
||||
of_property_read_string_index(np, "clock-output-names", i, &name);
|
||||
|
||||
clk = rz_cpg_register_clock(np, cpg, name);
|
||||
if (IS_ERR(clk))
|
||||
pr_err("%s: failed to register %s %s clock (%ld)\n",
|
||||
__func__, np->name, name, PTR_ERR(clk));
|
||||
else
|
||||
cpg->data.clks[i] = clk;
|
||||
}
|
||||
|
||||
of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data);
|
||||
}
|
||||
CLK_OF_DECLARE(rz_cpg_clks, "renesas,rz-cpg-clocks", rz_cpg_clocks_init);
|
Loading…
Add table
Add a link
Reference in a new issue