Merge branch 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus

Pull MIPS updates from Ralf Baechle:

 - Improvements to the tlb_dump code
 - KVM fixes
 - Add support for appended DTB
 - Minor improvements to the R12000 support
 - Minor improvements to the R12000 support
 - Various platform improvments for BCM47xx
 - The usual pile of minor cleanups
 - A number of BPF fixes and improvments
 - Some improvments to the support for R3000 and DECstations
 - Some improvments to the ATH79 platform support
 - A major patchset for the JZ4740 SOC adding support for the CI20 platform
 - Add support for the Pistachio SOC
 - Minor BMIPS/BCM63xx platform support improvments.
 - Avoid "SYNC 0" as memory barrier when unlocking spinlocks
 - Add support for the XWR-1750 board.
 - Paul's __cpuinit/__cpuinitdata cleanups.
 - New Malta CPU board support large memory so enable ZONE_DMA32.

* 'upstream' of git://git.linux-mips.org/pub/scm/ralf/upstream-linus: (131 commits)
  MIPS: spinlock: Adjust arch_spin_lock back-off time
  MIPS: asmmacro: Ensure 64-bit FP registers are used with MSA
  MIPS: BCM47xx: Simplify handling SPROM revisions
  MIPS: Cobalt Don't use module_init in non-modular MTD registration.
  MIPS: BCM47xx: Move NVRAM driver to the drivers/firmware/
  MIPS: use for_each_sg()
  MIPS: BCM47xx: Don't select BCMA_HOST_PCI
  MIPS: BCM47xx: Add helper variable for storing NVRAM length
  MIPS: IRQ/IP27: Move IRQ allocation API to platform code.
  MIPS: Replace smp_mb with release barrier function in unlocks.
  MIPS: i8259: DT support
  MIPS: Malta: Basic DT plumbing
  MIPS: include errno.h for ENODEV in mips-cm.h
  MIPS: Define GCR_GIC_STATUS register fields
  MIPS: BPF: Introduce BPF ASM helpers
  MIPS: BPF: Use BPF register names to describe the ABI
  MIPS: BPF: Move register definition to the BPF header
  MIPS: net: BPF: Replace RSIZE with SZREG
  MIPS: BPF: Free up some callee-saved registers
  MIPS: Xtalk: Update xwidget.h with known Xtalk device numbers
  ...
This commit is contained in:
Linus Torvalds 2015-06-27 12:44:34 -07:00
commit 78c10e556e
281 changed files with 6271 additions and 2321 deletions

View file

@ -24,7 +24,7 @@ obj-$(CONFIG_COMMON_CLK_CDCE706) += clk-cdce706.o
obj-$(CONFIG_ARCH_CLPS711X) += clk-clps711x.o
obj-$(CONFIG_ARCH_EFM32) += clk-efm32gg.o
obj-$(CONFIG_ARCH_HIGHBANK) += clk-highbank.o
obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o
obj-$(CONFIG_MACH_LOONGSON32) += clk-ls1x.o
obj-$(CONFIG_COMMON_CLK_MAX_GEN) += clk-max-gen.o
obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o
obj-$(CONFIG_COMMON_CLK_MAX77802) += clk-max77802.o
@ -51,6 +51,7 @@ obj-$(CONFIG_ARCH_HI3xxx) += hisilicon/
obj-$(CONFIG_ARCH_HIP04) += hisilicon/
obj-$(CONFIG_ARCH_HIX5HD2) += hisilicon/
obj-$(CONFIG_ARCH_MXC) += imx/
obj-$(CONFIG_MACH_INGENIC) += ingenic/
obj-$(CONFIG_COMMON_CLK_KEYSTONE) += keystone/
ifeq ($(CONFIG_COMMON_CLK), y)
obj-$(CONFIG_ARCH_MMP) += mmp/

View file

@ -0,0 +1,3 @@
obj-y += cgu.o
obj-$(CONFIG_MACH_JZ4740) += jz4740-cgu.o
obj-$(CONFIG_MACH_JZ4780) += jz4780-cgu.o

711
drivers/clk/ingenic/cgu.c Normal file
View file

@ -0,0 +1,711 @@
/*
* Ingenic SoC CGU driver
*
* Copyright (c) 2013-2015 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.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; 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 <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/clkdev.h>
#include <linux/delay.h>
#include <linux/math64.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "cgu.h"
#define MHZ (1000 * 1000)
/**
* ingenic_cgu_gate_get() - get the value of clock gate register bit
* @cgu: reference to the CGU whose registers should be read
* @info: info struct describing the gate bit
*
* Retrieves the state of the clock gate bit described by info. The
* caller must hold cgu->lock.
*
* Return: true if the gate bit is set, else false.
*/
static inline bool
ingenic_cgu_gate_get(struct ingenic_cgu *cgu,
const struct ingenic_cgu_gate_info *info)
{
return readl(cgu->base + info->reg) & BIT(info->bit);
}
/**
* ingenic_cgu_gate_set() - set the value of clock gate register bit
* @cgu: reference to the CGU whose registers should be modified
* @info: info struct describing the gate bit
* @val: non-zero to gate a clock, otherwise zero
*
* Sets the given gate bit in order to gate or ungate a clock.
*
* The caller must hold cgu->lock.
*/
static inline void
ingenic_cgu_gate_set(struct ingenic_cgu *cgu,
const struct ingenic_cgu_gate_info *info, bool val)
{
u32 clkgr = readl(cgu->base + info->reg);
if (val)
clkgr |= BIT(info->bit);
else
clkgr &= ~BIT(info->bit);
writel(clkgr, cgu->base + info->reg);
}
/*
* PLL operations
*/
static unsigned long
ingenic_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
const struct ingenic_cgu_pll_info *pll_info;
unsigned m, n, od_enc, od;
bool bypass, enable;
unsigned long flags;
u32 ctl;
clk_info = &cgu->clock_info[ingenic_clk->idx];
BUG_ON(clk_info->type != CGU_CLK_PLL);
pll_info = &clk_info->pll;
spin_lock_irqsave(&cgu->lock, flags);
ctl = readl(cgu->base + pll_info->reg);
spin_unlock_irqrestore(&cgu->lock, flags);
m = (ctl >> pll_info->m_shift) & GENMASK(pll_info->m_bits - 1, 0);
m += pll_info->m_offset;
n = (ctl >> pll_info->n_shift) & GENMASK(pll_info->n_bits - 1, 0);
n += pll_info->n_offset;
od_enc = ctl >> pll_info->od_shift;
od_enc &= GENMASK(pll_info->od_bits - 1, 0);
bypass = !!(ctl & BIT(pll_info->bypass_bit));
enable = !!(ctl & BIT(pll_info->enable_bit));
if (bypass)
return parent_rate;
if (!enable)
return 0;
for (od = 0; od < pll_info->od_max; od++) {
if (pll_info->od_encoding[od] == od_enc)
break;
}
BUG_ON(od == pll_info->od_max);
od++;
return div_u64((u64)parent_rate * m, n * od);
}
static unsigned long
ingenic_pll_calc(const struct ingenic_cgu_clk_info *clk_info,
unsigned long rate, unsigned long parent_rate,
unsigned *pm, unsigned *pn, unsigned *pod)
{
const struct ingenic_cgu_pll_info *pll_info;
unsigned m, n, od;
pll_info = &clk_info->pll;
od = 1;
/*
* The frequency after the input divider must be between 10 and 50 MHz.
* The highest divider yields the best resolution.
*/
n = parent_rate / (10 * MHZ);
n = min_t(unsigned, n, 1 << clk_info->pll.n_bits);
n = max_t(unsigned, n, pll_info->n_offset);
m = (rate / MHZ) * od * n / (parent_rate / MHZ);
m = min_t(unsigned, m, 1 << clk_info->pll.m_bits);
m = max_t(unsigned, m, pll_info->m_offset);
if (pm)
*pm = m;
if (pn)
*pn = n;
if (pod)
*pod = od;
return div_u64((u64)parent_rate * m, n * od);
}
static long
ingenic_pll_round_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long *prate)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
clk_info = &cgu->clock_info[ingenic_clk->idx];
BUG_ON(clk_info->type != CGU_CLK_PLL);
return ingenic_pll_calc(clk_info, req_rate, *prate, NULL, NULL, NULL);
}
static int
ingenic_pll_set_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long parent_rate)
{
const unsigned timeout = 100;
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
const struct ingenic_cgu_pll_info *pll_info;
unsigned long rate, flags;
unsigned m, n, od, i;
u32 ctl;
clk_info = &cgu->clock_info[ingenic_clk->idx];
BUG_ON(clk_info->type != CGU_CLK_PLL);
pll_info = &clk_info->pll;
rate = ingenic_pll_calc(clk_info, req_rate, parent_rate,
&m, &n, &od);
if (rate != req_rate)
pr_info("ingenic-cgu: request '%s' rate %luHz, actual %luHz\n",
clk_info->name, req_rate, rate);
spin_lock_irqsave(&cgu->lock, flags);
ctl = readl(cgu->base + pll_info->reg);
ctl &= ~(GENMASK(pll_info->m_bits - 1, 0) << pll_info->m_shift);
ctl |= (m - pll_info->m_offset) << pll_info->m_shift;
ctl &= ~(GENMASK(pll_info->n_bits - 1, 0) << pll_info->n_shift);
ctl |= (n - pll_info->n_offset) << pll_info->n_shift;
ctl &= ~(GENMASK(pll_info->od_bits - 1, 0) << pll_info->od_shift);
ctl |= pll_info->od_encoding[od - 1] << pll_info->od_shift;
ctl &= ~BIT(pll_info->bypass_bit);
ctl |= BIT(pll_info->enable_bit);
writel(ctl, cgu->base + pll_info->reg);
/* wait for the PLL to stabilise */
for (i = 0; i < timeout; i++) {
ctl = readl(cgu->base + pll_info->reg);
if (ctl & BIT(pll_info->stable_bit))
break;
mdelay(1);
}
spin_unlock_irqrestore(&cgu->lock, flags);
if (i == timeout)
return -EBUSY;
return 0;
}
static const struct clk_ops ingenic_pll_ops = {
.recalc_rate = ingenic_pll_recalc_rate,
.round_rate = ingenic_pll_round_rate,
.set_rate = ingenic_pll_set_rate,
};
/*
* Operations for all non-PLL clocks
*/
static u8 ingenic_clk_get_parent(struct clk_hw *hw)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
u32 reg;
u8 i, hw_idx, idx = 0;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_MUX) {
reg = readl(cgu->base + clk_info->mux.reg);
hw_idx = (reg >> clk_info->mux.shift) &
GENMASK(clk_info->mux.bits - 1, 0);
/*
* Convert the hardware index to the parent index by skipping
* over any -1's in the parents array.
*/
for (i = 0; i < hw_idx; i++) {
if (clk_info->parents[i] != -1)
idx++;
}
}
return idx;
}
static int ingenic_clk_set_parent(struct clk_hw *hw, u8 idx)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
unsigned long flags;
u8 curr_idx, hw_idx, num_poss;
u32 reg, mask;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_MUX) {
/*
* Convert the parent index to the hardware index by adding
* 1 for any -1 in the parents array preceding the given
* index. That is, we want the index of idx'th entry in
* clk_info->parents which does not equal -1.
*/
hw_idx = curr_idx = 0;
num_poss = 1 << clk_info->mux.bits;
for (; hw_idx < num_poss; hw_idx++) {
if (clk_info->parents[hw_idx] == -1)
continue;
if (curr_idx == idx)
break;
curr_idx++;
}
/* idx should always be a valid parent */
BUG_ON(curr_idx != idx);
mask = GENMASK(clk_info->mux.bits - 1, 0);
mask <<= clk_info->mux.shift;
spin_lock_irqsave(&cgu->lock, flags);
/* write the register */
reg = readl(cgu->base + clk_info->mux.reg);
reg &= ~mask;
reg |= hw_idx << clk_info->mux.shift;
writel(reg, cgu->base + clk_info->mux.reg);
spin_unlock_irqrestore(&cgu->lock, flags);
return 0;
}
return idx ? -EINVAL : 0;
}
static unsigned long
ingenic_clk_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
unsigned long rate = parent_rate;
u32 div_reg, div;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_DIV) {
div_reg = readl(cgu->base + clk_info->div.reg);
div = (div_reg >> clk_info->div.shift) &
GENMASK(clk_info->div.bits - 1, 0);
div += 1;
rate /= div;
}
return rate;
}
static unsigned
ingenic_clk_calc_div(const struct ingenic_cgu_clk_info *clk_info,
unsigned long parent_rate, unsigned long req_rate)
{
unsigned div;
/* calculate the divide */
div = DIV_ROUND_UP(parent_rate, req_rate);
/* and impose hardware constraints */
div = min_t(unsigned, div, 1 << clk_info->div.bits);
div = max_t(unsigned, div, 1);
return div;
}
static long
ingenic_clk_round_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long *parent_rate)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
long rate = *parent_rate;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_DIV)
rate /= ingenic_clk_calc_div(clk_info, *parent_rate, req_rate);
else if (clk_info->type & CGU_CLK_FIXDIV)
rate /= clk_info->fixdiv.div;
return rate;
}
static int
ingenic_clk_set_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long parent_rate)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
const unsigned timeout = 100;
unsigned long rate, flags;
unsigned div, i;
u32 reg, mask;
int ret = 0;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_DIV) {
div = ingenic_clk_calc_div(clk_info, parent_rate, req_rate);
rate = parent_rate / div;
if (rate != req_rate)
return -EINVAL;
spin_lock_irqsave(&cgu->lock, flags);
reg = readl(cgu->base + clk_info->div.reg);
/* update the divide */
mask = GENMASK(clk_info->div.bits - 1, 0);
reg &= ~(mask << clk_info->div.shift);
reg |= (div - 1) << clk_info->div.shift;
/* clear the stop bit */
if (clk_info->div.stop_bit != -1)
reg &= ~BIT(clk_info->div.stop_bit);
/* set the change enable bit */
if (clk_info->div.ce_bit != -1)
reg |= BIT(clk_info->div.ce_bit);
/* update the hardware */
writel(reg, cgu->base + clk_info->div.reg);
/* wait for the change to take effect */
if (clk_info->div.busy_bit != -1) {
for (i = 0; i < timeout; i++) {
reg = readl(cgu->base + clk_info->div.reg);
if (!(reg & BIT(clk_info->div.busy_bit)))
break;
mdelay(1);
}
if (i == timeout)
ret = -EBUSY;
}
spin_unlock_irqrestore(&cgu->lock, flags);
return ret;
}
return -EINVAL;
}
static int ingenic_clk_enable(struct clk_hw *hw)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
unsigned long flags;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_GATE) {
/* ungate the clock */
spin_lock_irqsave(&cgu->lock, flags);
ingenic_cgu_gate_set(cgu, &clk_info->gate, false);
spin_unlock_irqrestore(&cgu->lock, flags);
}
return 0;
}
static void ingenic_clk_disable(struct clk_hw *hw)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
unsigned long flags;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_GATE) {
/* gate the clock */
spin_lock_irqsave(&cgu->lock, flags);
ingenic_cgu_gate_set(cgu, &clk_info->gate, true);
spin_unlock_irqrestore(&cgu->lock, flags);
}
}
static int ingenic_clk_is_enabled(struct clk_hw *hw)
{
struct ingenic_clk *ingenic_clk = to_ingenic_clk(hw);
struct ingenic_cgu *cgu = ingenic_clk->cgu;
const struct ingenic_cgu_clk_info *clk_info;
unsigned long flags;
int enabled = 1;
clk_info = &cgu->clock_info[ingenic_clk->idx];
if (clk_info->type & CGU_CLK_GATE) {
spin_lock_irqsave(&cgu->lock, flags);
enabled = !ingenic_cgu_gate_get(cgu, &clk_info->gate);
spin_unlock_irqrestore(&cgu->lock, flags);
}
return enabled;
}
static const struct clk_ops ingenic_clk_ops = {
.get_parent = ingenic_clk_get_parent,
.set_parent = ingenic_clk_set_parent,
.recalc_rate = ingenic_clk_recalc_rate,
.round_rate = ingenic_clk_round_rate,
.set_rate = ingenic_clk_set_rate,
.enable = ingenic_clk_enable,
.disable = ingenic_clk_disable,
.is_enabled = ingenic_clk_is_enabled,
};
/*
* Setup functions.
*/
static int ingenic_register_clock(struct ingenic_cgu *cgu, unsigned idx)
{
const struct ingenic_cgu_clk_info *clk_info = &cgu->clock_info[idx];
struct clk_init_data clk_init;
struct ingenic_clk *ingenic_clk = NULL;
struct clk *clk, *parent;
const char *parent_names[4];
unsigned caps, i, num_possible;
int err = -EINVAL;
BUILD_BUG_ON(ARRAY_SIZE(clk_info->parents) > ARRAY_SIZE(parent_names));
if (clk_info->type == CGU_CLK_EXT) {
clk = of_clk_get_by_name(cgu->np, clk_info->name);
if (IS_ERR(clk)) {
pr_err("%s: no external clock '%s' provided\n",
__func__, clk_info->name);
err = -ENODEV;
goto out;
}
err = clk_register_clkdev(clk, clk_info->name, NULL);
if (err) {
clk_put(clk);
goto out;
}
cgu->clocks.clks[idx] = clk;
return 0;
}
if (!clk_info->type) {
pr_err("%s: no clock type specified for '%s'\n", __func__,
clk_info->name);
goto out;
}
ingenic_clk = kzalloc(sizeof(*ingenic_clk), GFP_KERNEL);
if (!ingenic_clk) {
err = -ENOMEM;
goto out;
}
ingenic_clk->hw.init = &clk_init;
ingenic_clk->cgu = cgu;
ingenic_clk->idx = idx;
clk_init.name = clk_info->name;
clk_init.flags = 0;
clk_init.parent_names = parent_names;
caps = clk_info->type;
if (caps & (CGU_CLK_MUX | CGU_CLK_CUSTOM)) {
clk_init.num_parents = 0;
if (caps & CGU_CLK_MUX)
num_possible = 1 << clk_info->mux.bits;
else
num_possible = ARRAY_SIZE(clk_info->parents);
for (i = 0; i < num_possible; i++) {
if (clk_info->parents[i] == -1)
continue;
parent = cgu->clocks.clks[clk_info->parents[i]];
parent_names[clk_init.num_parents] =
__clk_get_name(parent);
clk_init.num_parents++;
}
BUG_ON(!clk_init.num_parents);
BUG_ON(clk_init.num_parents > ARRAY_SIZE(parent_names));
} else {
BUG_ON(clk_info->parents[0] == -1);
clk_init.num_parents = 1;
parent = cgu->clocks.clks[clk_info->parents[0]];
parent_names[0] = __clk_get_name(parent);
}
if (caps & CGU_CLK_CUSTOM) {
clk_init.ops = clk_info->custom.clk_ops;
caps &= ~CGU_CLK_CUSTOM;
if (caps) {
pr_err("%s: custom clock may not be combined with type 0x%x\n",
__func__, caps);
goto out;
}
} else if (caps & CGU_CLK_PLL) {
clk_init.ops = &ingenic_pll_ops;
caps &= ~CGU_CLK_PLL;
if (caps) {
pr_err("%s: PLL may not be combined with type 0x%x\n",
__func__, caps);
goto out;
}
} else {
clk_init.ops = &ingenic_clk_ops;
}
/* nothing to do for gates or fixed dividers */
caps &= ~(CGU_CLK_GATE | CGU_CLK_FIXDIV);
if (caps & CGU_CLK_MUX) {
if (!(caps & CGU_CLK_MUX_GLITCHFREE))
clk_init.flags |= CLK_SET_PARENT_GATE;
caps &= ~(CGU_CLK_MUX | CGU_CLK_MUX_GLITCHFREE);
}
if (caps & CGU_CLK_DIV) {
caps &= ~CGU_CLK_DIV;
} else {
/* pass rate changes to the parent clock */
clk_init.flags |= CLK_SET_RATE_PARENT;
}
if (caps) {
pr_err("%s: unknown clock type 0x%x\n", __func__, caps);
goto out;
}
clk = clk_register(NULL, &ingenic_clk->hw);
if (IS_ERR(clk)) {
pr_err("%s: failed to register clock '%s'\n", __func__,
clk_info->name);
err = PTR_ERR(clk);
goto out;
}
err = clk_register_clkdev(clk, clk_info->name, NULL);
if (err)
goto out;
cgu->clocks.clks[idx] = clk;
out:
if (err)
kfree(ingenic_clk);
return err;
}
struct ingenic_cgu *
ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info,
unsigned num_clocks, struct device_node *np)
{
struct ingenic_cgu *cgu;
cgu = kzalloc(sizeof(*cgu), GFP_KERNEL);
if (!cgu)
goto err_out;
cgu->base = of_iomap(np, 0);
if (!cgu->base) {
pr_err("%s: failed to map CGU registers\n", __func__);
goto err_out_free;
}
cgu->np = np;
cgu->clock_info = clock_info;
cgu->clocks.clk_num = num_clocks;
spin_lock_init(&cgu->lock);
return cgu;
err_out_free:
kfree(cgu);
err_out:
return NULL;
}
int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu)
{
unsigned i;
int err;
cgu->clocks.clks = kcalloc(cgu->clocks.clk_num, sizeof(struct clk *),
GFP_KERNEL);
if (!cgu->clocks.clks) {
err = -ENOMEM;
goto err_out;
}
for (i = 0; i < cgu->clocks.clk_num; i++) {
err = ingenic_register_clock(cgu, i);
if (err)
goto err_out_unregister;
}
err = of_clk_add_provider(cgu->np, of_clk_src_onecell_get,
&cgu->clocks);
if (err)
goto err_out_unregister;
return 0;
err_out_unregister:
for (i = 0; i < cgu->clocks.clk_num; i++) {
if (!cgu->clocks.clks[i])
continue;
if (cgu->clock_info[i].type & CGU_CLK_EXT)
clk_put(cgu->clocks.clks[i]);
else
clk_unregister(cgu->clocks.clks[i]);
}
kfree(cgu->clocks.clks);
err_out:
return err;
}

223
drivers/clk/ingenic/cgu.h Normal file
View file

@ -0,0 +1,223 @@
/*
* Ingenic SoC CGU driver
*
* Copyright (c) 2013-2015 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.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; 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.
*/
#ifndef __DRIVERS_CLK_INGENIC_CGU_H__
#define __DRIVERS_CLK_INGENIC_CGU_H__
#include <linux/bitops.h>
#include <linux/of.h>
#include <linux/spinlock.h>
/**
* struct ingenic_cgu_pll_info - information about a PLL
* @reg: the offset of the PLL's control register within the CGU
* @m_shift: the number of bits to shift the multiplier value by (ie. the
* index of the lowest bit of the multiplier value in the PLL's
* control register)
* @m_bits: the size of the multiplier field in bits
* @m_offset: the multiplier value which encodes to 0 in the PLL's control
* register
* @n_shift: the number of bits to shift the divider value by (ie. the
* index of the lowest bit of the divider value in the PLL's
* control register)
* @n_bits: the size of the divider field in bits
* @n_offset: the divider value which encodes to 0 in the PLL's control
* register
* @od_shift: the number of bits to shift the post-VCO divider value by (ie.
* the index of the lowest bit of the post-VCO divider value in
* the PLL's control register)
* @od_bits: the size of the post-VCO divider field in bits
* @od_max: the maximum post-VCO divider value
* @od_encoding: a pointer to an array mapping post-VCO divider values to
* their encoded values in the PLL control register, or -1 for
* unsupported values
* @bypass_bit: the index of the bypass bit in the PLL control register
* @enable_bit: the index of the enable bit in the PLL control register
* @stable_bit: the index of the stable bit in the PLL control register
*/
struct ingenic_cgu_pll_info {
unsigned reg;
const s8 *od_encoding;
u8 m_shift, m_bits, m_offset;
u8 n_shift, n_bits, n_offset;
u8 od_shift, od_bits, od_max;
u8 bypass_bit;
u8 enable_bit;
u8 stable_bit;
};
/**
* struct ingenic_cgu_mux_info - information about a clock mux
* @reg: offset of the mux control register within the CGU
* @shift: number of bits to shift the mux value by (ie. the index of
* the lowest bit of the mux value within its control register)
* @bits: the size of the mux value in bits
*/
struct ingenic_cgu_mux_info {
unsigned reg;
u8 shift;
u8 bits;
};
/**
* struct ingenic_cgu_div_info - information about a divider
* @reg: offset of the divider control register within the CGU
* @shift: number of bits to shift the divide value by (ie. the index of
* the lowest bit of the divide value within its control register)
* @bits: the size of the divide value in bits
* @ce_bit: the index of the change enable bit within reg, or -1 if there
* isn't one
* @busy_bit: the index of the busy bit within reg, or -1 if there isn't one
* @stop_bit: the index of the stop bit within reg, or -1 if there isn't one
*/
struct ingenic_cgu_div_info {
unsigned reg;
u8 shift;
u8 bits;
s8 ce_bit;
s8 busy_bit;
s8 stop_bit;
};
/**
* struct ingenic_cgu_fixdiv_info - information about a fixed divider
* @div: the divider applied to the parent clock
*/
struct ingenic_cgu_fixdiv_info {
unsigned div;
};
/**
* struct ingenic_cgu_gate_info - information about a clock gate
* @reg: offset of the gate control register within the CGU
* @bit: offset of the bit in the register that controls the gate
*/
struct ingenic_cgu_gate_info {
unsigned reg;
u8 bit;
};
/**
* struct ingenic_cgu_custom_info - information about a custom (SoC) clock
* @clk_ops: custom clock operation callbacks
*/
struct ingenic_cgu_custom_info {
struct clk_ops *clk_ops;
};
/**
* struct ingenic_cgu_clk_info - information about a clock
* @name: name of the clock
* @type: a bitmask formed from CGU_CLK_* values
* @parents: an array of the indices of potential parents of this clock
* within the clock_info array of the CGU, or -1 in entries
* which correspond to no valid parent
* @pll: information valid if type includes CGU_CLK_PLL
* @gate: information valid if type includes CGU_CLK_GATE
* @mux: information valid if type includes CGU_CLK_MUX
* @div: information valid if type includes CGU_CLK_DIV
* @fixdiv: information valid if type includes CGU_CLK_FIXDIV
* @custom: information valid if type includes CGU_CLK_CUSTOM
*/
struct ingenic_cgu_clk_info {
const char *name;
enum {
CGU_CLK_NONE = 0,
CGU_CLK_EXT = BIT(0),
CGU_CLK_PLL = BIT(1),
CGU_CLK_GATE = BIT(2),
CGU_CLK_MUX = BIT(3),
CGU_CLK_MUX_GLITCHFREE = BIT(4),
CGU_CLK_DIV = BIT(5),
CGU_CLK_FIXDIV = BIT(6),
CGU_CLK_CUSTOM = BIT(7),
} type;
int parents[4];
union {
struct ingenic_cgu_pll_info pll;
struct {
struct ingenic_cgu_gate_info gate;
struct ingenic_cgu_mux_info mux;
struct ingenic_cgu_div_info div;
struct ingenic_cgu_fixdiv_info fixdiv;
};
struct ingenic_cgu_custom_info custom;
};
};
/**
* struct ingenic_cgu - data about the CGU
* @np: the device tree node that caused the CGU to be probed
* @base: the ioremap'ed base address of the CGU registers
* @clock_info: an array containing information about implemented clocks
* @clocks: used to provide clocks to DT, allows lookup of struct clk*
* @lock: lock to be held whilst manipulating CGU registers
*/
struct ingenic_cgu {
struct device_node *np;
void __iomem *base;
const struct ingenic_cgu_clk_info *clock_info;
struct clk_onecell_data clocks;
spinlock_t lock;
};
/**
* struct ingenic_clk - private data for a clock
* @hw: see Documentation/clk.txt
* @cgu: a pointer to the CGU data
* @idx: the index of this clock in cgu->clock_info
*/
struct ingenic_clk {
struct clk_hw hw;
struct ingenic_cgu *cgu;
unsigned idx;
};
#define to_ingenic_clk(_hw) container_of(_hw, struct ingenic_clk, hw)
/**
* ingenic_cgu_new() - create a new CGU instance
* @clock_info: an array of clock information structures describing the clocks
* which are implemented by the CGU
* @num_clocks: the number of entries in clock_info
* @np: the device tree node which causes this CGU to be probed
*
* Return: a pointer to the CGU instance if initialisation is successful,
* otherwise NULL.
*/
struct ingenic_cgu *
ingenic_cgu_new(const struct ingenic_cgu_clk_info *clock_info,
unsigned num_clocks, struct device_node *np);
/**
* ingenic_cgu_register_clocks() - Registers the clocks
* @cgu: pointer to cgu data
*
* Register the clocks described by the CGU with the common clock framework.
*
* Return: 0 on success or -errno if unsuccesful.
*/
int ingenic_cgu_register_clocks(struct ingenic_cgu *cgu);
#endif /* __DRIVERS_CLK_INGENIC_CGU_H__ */

View file

@ -0,0 +1,303 @@
/*
* Ingenic JZ4740 SoC CGU driver
*
* Copyright (c) 2015 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.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; 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 <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <dt-bindings/clock/jz4740-cgu.h>
#include <asm/mach-jz4740/clock.h>
#include "cgu.h"
/* CGU register offsets */
#define CGU_REG_CPCCR 0x00
#define CGU_REG_LCR 0x04
#define CGU_REG_CPPCR 0x10
#define CGU_REG_CLKGR 0x20
#define CGU_REG_SCR 0x24
#define CGU_REG_I2SCDR 0x60
#define CGU_REG_LPCDR 0x64
#define CGU_REG_MSCCDR 0x68
#define CGU_REG_UHCCDR 0x6c
#define CGU_REG_SSICDR 0x74
/* bits within a PLL control register */
#define PLLCTL_M_SHIFT 23
#define PLLCTL_M_MASK (0x1ff << PLLCTL_M_SHIFT)
#define PLLCTL_N_SHIFT 18
#define PLLCTL_N_MASK (0x1f << PLLCTL_N_SHIFT)
#define PLLCTL_OD_SHIFT 16
#define PLLCTL_OD_MASK (0x3 << PLLCTL_OD_SHIFT)
#define PLLCTL_STABLE (1 << 10)
#define PLLCTL_BYPASS (1 << 9)
#define PLLCTL_ENABLE (1 << 8)
/* bits within the LCR register */
#define LCR_SLEEP (1 << 0)
/* bits within the CLKGR register */
#define CLKGR_UDC (1 << 11)
static struct ingenic_cgu *cgu;
static const s8 pll_od_encoding[4] = {
0x0, 0x1, -1, 0x3,
};
static const struct ingenic_cgu_clk_info jz4740_cgu_clocks[] = {
/* External clocks */
[JZ4740_CLK_EXT] = { "ext", CGU_CLK_EXT },
[JZ4740_CLK_RTC] = { "rtc", CGU_CLK_EXT },
[JZ4740_CLK_PLL] = {
"pll", CGU_CLK_PLL,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.pll = {
.reg = CGU_REG_CPPCR,
.m_shift = 23,
.m_bits = 9,
.m_offset = 2,
.n_shift = 18,
.n_bits = 5,
.n_offset = 2,
.od_shift = 16,
.od_bits = 2,
.od_max = 4,
.od_encoding = pll_od_encoding,
.stable_bit = 10,
.bypass_bit = 9,
.enable_bit = 8,
},
},
/* Muxes & dividers */
[JZ4740_CLK_PLL_HALF] = {
"pll half", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 21, 1, -1, -1, -1 },
},
[JZ4740_CLK_CCLK] = {
"cclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 0, 4, 22, -1, -1 },
},
[JZ4740_CLK_HCLK] = {
"hclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 4, 4, 22, -1, -1 },
},
[JZ4740_CLK_PCLK] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 8, 4, 22, -1, -1 },
},
[JZ4740_CLK_MCLK] = {
"mclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 12, 4, 22, -1, -1 },
},
[JZ4740_CLK_LCD] = {
"lcd", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
.div = { CGU_REG_CPCCR, 16, 5, 22, -1, -1 },
.gate = { CGU_REG_CLKGR, 10 },
},
[JZ4740_CLK_LCD_PCLK] = {
"lcd_pclk", CGU_CLK_DIV,
.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
.div = { CGU_REG_LPCDR, 0, 11, -1, -1, -1 },
},
[JZ4740_CLK_I2S] = {
"i2s", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
.mux = { CGU_REG_CPCCR, 31, 1 },
.div = { CGU_REG_I2SCDR, 0, 8, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 6 },
},
[JZ4740_CLK_SPI] = {
"spi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL, -1, -1 },
.mux = { CGU_REG_SSICDR, 31, 1 },
.div = { CGU_REG_SSICDR, 0, 4, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 4 },
},
[JZ4740_CLK_MMC] = {
"mmc", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
.div = { CGU_REG_MSCCDR, 0, 5, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 7 },
},
[JZ4740_CLK_UHC] = {
"uhc", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4740_CLK_PLL_HALF, -1, -1, -1 },
.div = { CGU_REG_UHCCDR, 0, 4, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 14 },
},
[JZ4740_CLK_UDC] = {
"udc", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4740_CLK_EXT, JZ4740_CLK_PLL_HALF, -1, -1 },
.mux = { CGU_REG_CPCCR, 29, 1 },
.div = { CGU_REG_CPCCR, 23, 6, -1, -1, -1 },
.gate = { CGU_REG_SCR, 6 },
},
/* Gate-only clocks */
[JZ4740_CLK_UART0] = {
"uart0", CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 0 },
},
[JZ4740_CLK_UART1] = {
"uart1", CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 15 },
},
[JZ4740_CLK_DMA] = {
"dma", CGU_CLK_GATE,
.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 12 },
},
[JZ4740_CLK_IPU] = {
"ipu", CGU_CLK_GATE,
.parents = { JZ4740_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 13 },
},
[JZ4740_CLK_ADC] = {
"adc", CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 8 },
},
[JZ4740_CLK_I2C] = {
"i2c", CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 3 },
},
[JZ4740_CLK_AIC] = {
"aic", CGU_CLK_GATE,
.parents = { JZ4740_CLK_EXT, -1, -1, -1 },
.gate = { CGU_REG_CLKGR, 5 },
},
};
static void __init jz4740_cgu_init(struct device_node *np)
{
int retval;
cgu = ingenic_cgu_new(jz4740_cgu_clocks,
ARRAY_SIZE(jz4740_cgu_clocks), np);
if (!cgu) {
pr_err("%s: failed to initialise CGU\n", __func__);
return;
}
retval = ingenic_cgu_register_clocks(cgu);
if (retval)
pr_err("%s: failed to register CGU Clocks\n", __func__);
}
CLK_OF_DECLARE(jz4740_cgu, "ingenic,jz4740-cgu", jz4740_cgu_init);
void jz4740_clock_set_wait_mode(enum jz4740_wait_mode mode)
{
uint32_t lcr = readl(cgu->base + CGU_REG_LCR);
switch (mode) {
case JZ4740_WAIT_MODE_IDLE:
lcr &= ~LCR_SLEEP;
break;
case JZ4740_WAIT_MODE_SLEEP:
lcr |= LCR_SLEEP;
break;
}
writel(lcr, cgu->base + CGU_REG_LCR);
}
void jz4740_clock_udc_disable_auto_suspend(void)
{
uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR);
clkgr &= ~CLKGR_UDC;
writel(clkgr, cgu->base + CGU_REG_CLKGR);
}
EXPORT_SYMBOL_GPL(jz4740_clock_udc_disable_auto_suspend);
void jz4740_clock_udc_enable_auto_suspend(void)
{
uint32_t clkgr = readl(cgu->base + CGU_REG_CLKGR);
clkgr |= CLKGR_UDC;
writel(clkgr, cgu->base + CGU_REG_CLKGR);
}
EXPORT_SYMBOL_GPL(jz4740_clock_udc_enable_auto_suspend);
#define JZ_CLOCK_GATE_UART0 BIT(0)
#define JZ_CLOCK_GATE_TCU BIT(1)
#define JZ_CLOCK_GATE_DMAC BIT(12)
void jz4740_clock_suspend(void)
{
uint32_t clkgr, cppcr;
clkgr = readl(cgu->base + CGU_REG_CLKGR);
clkgr |= JZ_CLOCK_GATE_TCU | JZ_CLOCK_GATE_DMAC | JZ_CLOCK_GATE_UART0;
writel(clkgr, cgu->base + CGU_REG_CLKGR);
cppcr = readl(cgu->base + CGU_REG_CPPCR);
cppcr &= ~BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit);
writel(cppcr, cgu->base + CGU_REG_CPPCR);
}
void jz4740_clock_resume(void)
{
uint32_t clkgr, cppcr, stable;
cppcr = readl(cgu->base + CGU_REG_CPPCR);
cppcr |= BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.enable_bit);
writel(cppcr, cgu->base + CGU_REG_CPPCR);
stable = BIT(jz4740_cgu_clocks[JZ4740_CLK_PLL].pll.stable_bit);
do {
cppcr = readl(cgu->base + CGU_REG_CPPCR);
} while (!(cppcr & stable));
clkgr = readl(cgu->base + CGU_REG_CLKGR);
clkgr &= ~JZ_CLOCK_GATE_TCU;
clkgr &= ~JZ_CLOCK_GATE_DMAC;
clkgr &= ~JZ_CLOCK_GATE_UART0;
writel(clkgr, cgu->base + CGU_REG_CLKGR);
}

View file

@ -0,0 +1,733 @@
/*
* Ingenic JZ4780 SoC CGU driver
*
* Copyright (c) 2013-2015 Imagination Technologies
* Author: Paul Burton <paul.burton@imgtec.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; 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 <linux/clk-provider.h>
#include <linux/delay.h>
#include <linux/of.h>
#include <dt-bindings/clock/jz4780-cgu.h>
#include "cgu.h"
/* CGU register offsets */
#define CGU_REG_CLOCKCONTROL 0x00
#define CGU_REG_PLLCONTROL 0x0c
#define CGU_REG_APLL 0x10
#define CGU_REG_MPLL 0x14
#define CGU_REG_EPLL 0x18
#define CGU_REG_VPLL 0x1c
#define CGU_REG_CLKGR0 0x20
#define CGU_REG_OPCR 0x24
#define CGU_REG_CLKGR1 0x28
#define CGU_REG_DDRCDR 0x2c
#define CGU_REG_VPUCDR 0x30
#define CGU_REG_USBPCR 0x3c
#define CGU_REG_USBRDT 0x40
#define CGU_REG_USBVBFIL 0x44
#define CGU_REG_USBPCR1 0x48
#define CGU_REG_LP0CDR 0x54
#define CGU_REG_I2SCDR 0x60
#define CGU_REG_LP1CDR 0x64
#define CGU_REG_MSC0CDR 0x68
#define CGU_REG_UHCCDR 0x6c
#define CGU_REG_SSICDR 0x74
#define CGU_REG_CIMCDR 0x7c
#define CGU_REG_PCMCDR 0x84
#define CGU_REG_GPUCDR 0x88
#define CGU_REG_HDMICDR 0x8c
#define CGU_REG_MSC1CDR 0xa4
#define CGU_REG_MSC2CDR 0xa8
#define CGU_REG_BCHCDR 0xac
#define CGU_REG_CLOCKSTATUS 0xd4
/* bits within the OPCR register */
#define OPCR_SPENDN0 (1 << 7)
#define OPCR_SPENDN1 (1 << 6)
/* bits within the USBPCR register */
#define USBPCR_USB_MODE BIT(31)
#define USBPCR_IDPULLUP_MASK (0x3 << 28)
#define USBPCR_COMMONONN BIT(25)
#define USBPCR_VBUSVLDEXT BIT(24)
#define USBPCR_VBUSVLDEXTSEL BIT(23)
#define USBPCR_POR BIT(22)
#define USBPCR_OTG_DISABLE BIT(20)
#define USBPCR_COMPDISTUNE_MASK (0x7 << 17)
#define USBPCR_OTGTUNE_MASK (0x7 << 14)
#define USBPCR_SQRXTUNE_MASK (0x7 << 11)
#define USBPCR_TXFSLSTUNE_MASK (0xf << 7)
#define USBPCR_TXPREEMPHTUNE BIT(6)
#define USBPCR_TXHSXVTUNE_MASK (0x3 << 4)
#define USBPCR_TXVREFTUNE_MASK 0xf
/* bits within the USBPCR1 register */
#define USBPCR1_REFCLKSEL_SHIFT 26
#define USBPCR1_REFCLKSEL_MASK (0x3 << USBPCR1_REFCLKSEL_SHIFT)
#define USBPCR1_REFCLKSEL_CORE (0x2 << USBPCR1_REFCLKSEL_SHIFT)
#define USBPCR1_REFCLKDIV_SHIFT 24
#define USBPCR1_REFCLKDIV_MASK (0x3 << USBPCR1_REFCLKDIV_SHIFT)
#define USBPCR1_REFCLKDIV_19_2 (0x3 << USBPCR1_REFCLKDIV_SHIFT)
#define USBPCR1_REFCLKDIV_48 (0x2 << USBPCR1_REFCLKDIV_SHIFT)
#define USBPCR1_REFCLKDIV_24 (0x1 << USBPCR1_REFCLKDIV_SHIFT)
#define USBPCR1_REFCLKDIV_12 (0x0 << USBPCR1_REFCLKDIV_SHIFT)
#define USBPCR1_USB_SEL BIT(28)
#define USBPCR1_WORD_IF0 BIT(19)
#define USBPCR1_WORD_IF1 BIT(18)
/* bits within the USBRDT register */
#define USBRDT_VBFIL_LD_EN BIT(25)
#define USBRDT_USBRDT_MASK 0x7fffff
/* bits within the USBVBFIL register */
#define USBVBFIL_IDDIGFIL_SHIFT 16
#define USBVBFIL_IDDIGFIL_MASK (0xffff << USBVBFIL_IDDIGFIL_SHIFT)
#define USBVBFIL_USBVBFIL_MASK (0xffff)
static struct ingenic_cgu *cgu;
static u8 jz4780_otg_phy_get_parent(struct clk_hw *hw)
{
/* we only use CLKCORE, revisit if that ever changes */
return 0;
}
static int jz4780_otg_phy_set_parent(struct clk_hw *hw, u8 idx)
{
unsigned long flags;
u32 usbpcr1;
if (idx > 0)
return -EINVAL;
spin_lock_irqsave(&cgu->lock, flags);
usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1);
usbpcr1 &= ~USBPCR1_REFCLKSEL_MASK;
/* we only use CLKCORE */
usbpcr1 |= USBPCR1_REFCLKSEL_CORE;
writel(usbpcr1, cgu->base + CGU_REG_USBPCR1);
spin_unlock_irqrestore(&cgu->lock, flags);
return 0;
}
static unsigned long jz4780_otg_phy_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
u32 usbpcr1;
unsigned refclk_div;
usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1);
refclk_div = usbpcr1 & USBPCR1_REFCLKDIV_MASK;
switch (refclk_div) {
case USBPCR1_REFCLKDIV_12:
return 12000000;
case USBPCR1_REFCLKDIV_24:
return 24000000;
case USBPCR1_REFCLKDIV_48:
return 48000000;
case USBPCR1_REFCLKDIV_19_2:
return 19200000;
}
BUG();
return parent_rate;
}
static long jz4780_otg_phy_round_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long *parent_rate)
{
if (req_rate < 15600000)
return 12000000;
if (req_rate < 21600000)
return 19200000;
if (req_rate < 36000000)
return 24000000;
return 48000000;
}
static int jz4780_otg_phy_set_rate(struct clk_hw *hw, unsigned long req_rate,
unsigned long parent_rate)
{
unsigned long flags;
u32 usbpcr1, div_bits;
switch (req_rate) {
case 12000000:
div_bits = USBPCR1_REFCLKDIV_12;
break;
case 19200000:
div_bits = USBPCR1_REFCLKDIV_19_2;
break;
case 24000000:
div_bits = USBPCR1_REFCLKDIV_24;
break;
case 48000000:
div_bits = USBPCR1_REFCLKDIV_48;
break;
default:
return -EINVAL;
}
spin_lock_irqsave(&cgu->lock, flags);
usbpcr1 = readl(cgu->base + CGU_REG_USBPCR1);
usbpcr1 &= ~USBPCR1_REFCLKDIV_MASK;
usbpcr1 |= div_bits;
writel(usbpcr1, cgu->base + CGU_REG_USBPCR1);
spin_unlock_irqrestore(&cgu->lock, flags);
return 0;
}
static struct clk_ops jz4780_otg_phy_ops = {
.get_parent = jz4780_otg_phy_get_parent,
.set_parent = jz4780_otg_phy_set_parent,
.recalc_rate = jz4780_otg_phy_recalc_rate,
.round_rate = jz4780_otg_phy_round_rate,
.set_rate = jz4780_otg_phy_set_rate,
};
static const s8 pll_od_encoding[16] = {
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7,
0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf,
};
static const struct ingenic_cgu_clk_info jz4780_cgu_clocks[] = {
/* External clocks */
[JZ4780_CLK_EXCLK] = { "ext", CGU_CLK_EXT },
[JZ4780_CLK_RTCLK] = { "rtc", CGU_CLK_EXT },
/* PLLs */
#define DEF_PLL(name) { \
.reg = CGU_REG_ ## name, \
.m_shift = 19, \
.m_bits = 13, \
.m_offset = 1, \
.n_shift = 13, \
.n_bits = 6, \
.n_offset = 1, \
.od_shift = 9, \
.od_bits = 4, \
.od_max = 16, \
.od_encoding = pll_od_encoding, \
.stable_bit = 6, \
.bypass_bit = 1, \
.enable_bit = 0, \
}
[JZ4780_CLK_APLL] = {
"apll", CGU_CLK_PLL,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.pll = DEF_PLL(APLL),
},
[JZ4780_CLK_MPLL] = {
"mpll", CGU_CLK_PLL,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.pll = DEF_PLL(MPLL),
},
[JZ4780_CLK_EPLL] = {
"epll", CGU_CLK_PLL,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.pll = DEF_PLL(EPLL),
},
[JZ4780_CLK_VPLL] = {
"vpll", CGU_CLK_PLL,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.pll = DEF_PLL(VPLL),
},
#undef DEF_PLL
/* Custom (SoC-specific) OTG PHY */
[JZ4780_CLK_OTGPHY] = {
"otg_phy", CGU_CLK_CUSTOM,
.parents = { -1, -1, JZ4780_CLK_EXCLK, -1 },
.custom = { &jz4780_otg_phy_ops },
},
/* Muxes & dividers */
[JZ4780_CLK_SCLKA] = {
"sclk_a", CGU_CLK_MUX,
.parents = { -1, JZ4780_CLK_APLL, JZ4780_CLK_EXCLK,
JZ4780_CLK_RTCLK },
.mux = { CGU_REG_CLOCKCONTROL, 30, 2 },
},
[JZ4780_CLK_CPUMUX] = {
"cpumux", CGU_CLK_MUX,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL },
.mux = { CGU_REG_CLOCKCONTROL, 28, 2 },
},
[JZ4780_CLK_CPU] = {
"cpu", CGU_CLK_DIV,
.parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 },
.div = { CGU_REG_CLOCKCONTROL, 0, 4, 22, -1, -1 },
},
[JZ4780_CLK_L2CACHE] = {
"l2cache", CGU_CLK_DIV,
.parents = { JZ4780_CLK_CPUMUX, -1, -1, -1 },
.div = { CGU_REG_CLOCKCONTROL, 4, 4, -1, -1, -1 },
},
[JZ4780_CLK_AHB0] = {
"ahb0", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL },
.mux = { CGU_REG_CLOCKCONTROL, 26, 2 },
.div = { CGU_REG_CLOCKCONTROL, 8, 4, 21, -1, -1 },
},
[JZ4780_CLK_AHB2PMUX] = {
"ahb2_apb_mux", CGU_CLK_MUX,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_RTCLK },
.mux = { CGU_REG_CLOCKCONTROL, 24, 2 },
},
[JZ4780_CLK_AHB2] = {
"ahb2", CGU_CLK_DIV,
.parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 },
.div = { CGU_REG_CLOCKCONTROL, 12, 4, 20, -1, -1 },
},
[JZ4780_CLK_PCLK] = {
"pclk", CGU_CLK_DIV,
.parents = { JZ4780_CLK_AHB2PMUX, -1, -1, -1 },
.div = { CGU_REG_CLOCKCONTROL, 16, 4, 20, -1, -1 },
},
[JZ4780_CLK_DDR] = {
"ddr", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 },
.mux = { CGU_REG_DDRCDR, 30, 2 },
.div = { CGU_REG_DDRCDR, 0, 4, 29, 28, 27 },
},
[JZ4780_CLK_VPU] = {
"vpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL, -1 },
.mux = { CGU_REG_VPUCDR, 30, 2 },
.div = { CGU_REG_VPUCDR, 0, 4, 29, 28, 27 },
.gate = { CGU_REG_CLKGR1, 2 },
},
[JZ4780_CLK_I2SPLL] = {
"i2s_pll", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_EPLL, -1, -1 },
.mux = { CGU_REG_I2SCDR, 30, 1 },
.div = { CGU_REG_I2SCDR, 0, 8, 29, 28, 27 },
},
[JZ4780_CLK_I2S] = {
"i2s", CGU_CLK_MUX,
.parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_I2SPLL, -1, -1 },
.mux = { CGU_REG_I2SCDR, 31, 1 },
},
[JZ4780_CLK_LCD0PIXCLK] = {
"lcd0pixclk", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_VPLL, -1 },
.mux = { CGU_REG_LP0CDR, 30, 2 },
.div = { CGU_REG_LP0CDR, 0, 8, 28, 27, 26 },
},
[JZ4780_CLK_LCD1PIXCLK] = {
"lcd1pixclk", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_VPLL, -1 },
.mux = { CGU_REG_LP1CDR, 30, 2 },
.div = { CGU_REG_LP1CDR, 0, 8, 28, 27, 26 },
},
[JZ4780_CLK_MSCMUX] = {
"msc_mux", CGU_CLK_MUX,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1 },
.mux = { CGU_REG_MSC0CDR, 30, 2 },
},
[JZ4780_CLK_MSC0] = {
"msc0", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 },
.div = { CGU_REG_MSC0CDR, 0, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR0, 3 },
},
[JZ4780_CLK_MSC1] = {
"msc1", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 },
.div = { CGU_REG_MSC1CDR, 0, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR0, 11 },
},
[JZ4780_CLK_MSC2] = {
"msc2", CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_MSCMUX, -1, -1, -1 },
.div = { CGU_REG_MSC2CDR, 0, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR0, 12 },
},
[JZ4780_CLK_UHC] = {
"uhc", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL, JZ4780_CLK_OTGPHY },
.mux = { CGU_REG_UHCCDR, 30, 2 },
.div = { CGU_REG_UHCCDR, 0, 8, 29, 28, 27 },
.gate = { CGU_REG_CLKGR0, 24 },
},
[JZ4780_CLK_SSIPLL] = {
"ssi_pll", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 },
.mux = { CGU_REG_SSICDR, 30, 1 },
.div = { CGU_REG_SSICDR, 0, 8, 29, 28, 27 },
},
[JZ4780_CLK_SSI] = {
"ssi", CGU_CLK_MUX,
.parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_SSIPLL, -1, -1 },
.mux = { CGU_REG_SSICDR, 31, 1 },
},
[JZ4780_CLK_CIMMCLK] = {
"cim_mclk", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL, -1, -1 },
.mux = { CGU_REG_CIMCDR, 31, 1 },
.div = { CGU_REG_CIMCDR, 0, 8, 30, 29, 28 },
},
[JZ4780_CLK_PCMPLL] = {
"pcm_pll", CGU_CLK_MUX | CGU_CLK_DIV,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL, JZ4780_CLK_VPLL },
.mux = { CGU_REG_PCMCDR, 29, 2 },
.div = { CGU_REG_PCMCDR, 0, 8, 28, 27, 26 },
},
[JZ4780_CLK_PCM] = {
"pcm", CGU_CLK_MUX | CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, JZ4780_CLK_PCMPLL, -1, -1 },
.mux = { CGU_REG_PCMCDR, 31, 1 },
.gate = { CGU_REG_CLKGR1, 3 },
},
[JZ4780_CLK_GPU] = {
"gpu", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL },
.mux = { CGU_REG_GPUCDR, 30, 2 },
.div = { CGU_REG_GPUCDR, 0, 4, 29, 28, 27 },
.gate = { CGU_REG_CLKGR1, 4 },
},
[JZ4780_CLK_HDMI] = {
"hdmi", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_VPLL, -1 },
.mux = { CGU_REG_HDMICDR, 30, 2 },
.div = { CGU_REG_HDMICDR, 0, 8, 29, 28, 26 },
.gate = { CGU_REG_CLKGR1, 9 },
},
[JZ4780_CLK_BCH] = {
"bch", CGU_CLK_MUX | CGU_CLK_DIV | CGU_CLK_GATE,
.parents = { -1, JZ4780_CLK_SCLKA, JZ4780_CLK_MPLL,
JZ4780_CLK_EPLL },
.mux = { CGU_REG_BCHCDR, 30, 2 },
.div = { CGU_REG_BCHCDR, 0, 4, 29, 28, 27 },
.gate = { CGU_REG_CLKGR0, 1 },
},
/* Gate-only clocks */
[JZ4780_CLK_NEMC] = {
"nemc", CGU_CLK_GATE,
.parents = { JZ4780_CLK_AHB2, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 0 },
},
[JZ4780_CLK_OTG0] = {
"otg0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 2 },
},
[JZ4780_CLK_SSI0] = {
"ssi0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_SSI, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 4 },
},
[JZ4780_CLK_SMB0] = {
"smb0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 5 },
},
[JZ4780_CLK_SMB1] = {
"smb1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 6 },
},
[JZ4780_CLK_SCC] = {
"scc", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 7 },
},
[JZ4780_CLK_AIC] = {
"aic", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 8 },
},
[JZ4780_CLK_TSSI0] = {
"tssi0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 9 },
},
[JZ4780_CLK_OWI] = {
"owi", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 10 },
},
[JZ4780_CLK_KBC] = {
"kbc", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 13 },
},
[JZ4780_CLK_SADC] = {
"sadc", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 14 },
},
[JZ4780_CLK_UART0] = {
"uart0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 15 },
},
[JZ4780_CLK_UART1] = {
"uart1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 16 },
},
[JZ4780_CLK_UART2] = {
"uart2", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 17 },
},
[JZ4780_CLK_UART3] = {
"uart3", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 18 },
},
[JZ4780_CLK_SSI1] = {
"ssi1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_SSI, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 19 },
},
[JZ4780_CLK_SSI2] = {
"ssi2", CGU_CLK_GATE,
.parents = { JZ4780_CLK_SSI, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 20 },
},
[JZ4780_CLK_PDMA] = {
"pdma", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 21 },
},
[JZ4780_CLK_GPS] = {
"gps", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 22 },
},
[JZ4780_CLK_MAC] = {
"mac", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 23 },
},
[JZ4780_CLK_SMB2] = {
"smb2", CGU_CLK_GATE,
.parents = { JZ4780_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 24 },
},
[JZ4780_CLK_CIM] = {
"cim", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 26 },
},
[JZ4780_CLK_LCD] = {
"lcd", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 28 },
},
[JZ4780_CLK_TVE] = {
"tve", CGU_CLK_GATE,
.parents = { JZ4780_CLK_LCD, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 27 },
},
[JZ4780_CLK_IPU] = {
"ipu", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 29 },
},
[JZ4780_CLK_DDR0] = {
"ddr0", CGU_CLK_GATE,
.parents = { JZ4780_CLK_DDR, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 30 },
},
[JZ4780_CLK_DDR1] = {
"ddr1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_DDR, -1, -1, -1 },
.gate = { CGU_REG_CLKGR0, 31 },
},
[JZ4780_CLK_SMB3] = {
"smb3", CGU_CLK_GATE,
.parents = { JZ4780_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 0 },
},
[JZ4780_CLK_TSSI1] = {
"tssi1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 1 },
},
[JZ4780_CLK_COMPRESS] = {
"compress", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 5 },
},
[JZ4780_CLK_AIC1] = {
"aic1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 6 },
},
[JZ4780_CLK_GPVLC] = {
"gpvlc", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 7 },
},
[JZ4780_CLK_OTG1] = {
"otg1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 8 },
},
[JZ4780_CLK_UART4] = {
"uart4", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 10 },
},
[JZ4780_CLK_AHBMON] = {
"ahb_mon", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 11 },
},
[JZ4780_CLK_SMB4] = {
"smb4", CGU_CLK_GATE,
.parents = { JZ4780_CLK_PCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 12 },
},
[JZ4780_CLK_DES] = {
"des", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 13 },
},
[JZ4780_CLK_X2D] = {
"x2d", CGU_CLK_GATE,
.parents = { JZ4780_CLK_EXCLK, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 14 },
},
[JZ4780_CLK_CORE1] = {
"core1", CGU_CLK_GATE,
.parents = { JZ4780_CLK_CPU, -1, -1, -1 },
.gate = { CGU_REG_CLKGR1, 15 },
},
};
static void __init jz4780_cgu_init(struct device_node *np)
{
int retval;
cgu = ingenic_cgu_new(jz4780_cgu_clocks,
ARRAY_SIZE(jz4780_cgu_clocks), np);
if (!cgu) {
pr_err("%s: failed to initialise CGU\n", __func__);
return;
}
retval = ingenic_cgu_register_clocks(cgu);
if (retval) {
pr_err("%s: failed to register CGU Clocks\n", __func__);
return;
}
}
CLK_OF_DECLARE(jz4780_cgu, "ingenic,jz4780-cgu", jz4780_cgu_init);