Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
# SPDX-License-Identifier: GPL-2.0-only
obj-y += berlin2-avpll.o berlin2-pll.o berlin2-div.o
obj-$(CONFIG_MACH_BERLIN_BG2) += bg2.o
obj-$(CONFIG_MACH_BERLIN_BG2CD) += bg2.o
obj-$(CONFIG_MACH_BERLIN_BG2Q) += bg2q.o
@@ -0,0 +1,382 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include "berlin2-avpll.h"
/*
* Berlin2 SoCs comprise up to two PLLs called AVPLL built upon a
* VCO with 8 channels each, channel 8 is the odd-one-out and does
* not provide mul/div.
*
* Unfortunately, its registers are not named but just numbered. To
* get in at least some kind of structure, we split each AVPLL into
* the VCOs and each channel into separate clock drivers.
*
* Also, here and there the VCO registers are a bit different with
* respect to bit shifts. Make sure to add a comment for those.
*/
#define NUM_CHANNELS 8
#define AVPLL_CTRL(x) ((x) * 0x4)
#define VCO_CTRL0 AVPLL_CTRL(0)
/* BG2/BG2CDs VCO_B has an additional shift of 4 for its VCO_CTRL0 reg */
#define VCO_RESET BIT(0)
#define VCO_POWERUP BIT(1)
#define VCO_INTERPOL_SHIFT 2
#define VCO_INTERPOL_MASK (0xf << VCO_INTERPOL_SHIFT)
#define VCO_REG1V45_SEL_SHIFT 6
#define VCO_REG1V45_SEL(x) ((x) << VCO_REG1V45_SEL_SHIFT)
#define VCO_REG1V45_SEL_1V40 VCO_REG1V45_SEL(0)
#define VCO_REG1V45_SEL_1V45 VCO_REG1V45_SEL(1)
#define VCO_REG1V45_SEL_1V50 VCO_REG1V45_SEL(2)
#define VCO_REG1V45_SEL_1V55 VCO_REG1V45_SEL(3)
#define VCO_REG1V45_SEL_MASK VCO_REG1V45_SEL(3)
#define VCO_REG0V9_SEL_SHIFT 8
#define VCO_REG0V9_SEL_MASK (0xf << VCO_REG0V9_SEL_SHIFT)
#define VCO_VTHCAL_SHIFT 12
#define VCO_VTHCAL(x) ((x) << VCO_VTHCAL_SHIFT)
#define VCO_VTHCAL_0V90 VCO_VTHCAL(0)
#define VCO_VTHCAL_0V95 VCO_VTHCAL(1)
#define VCO_VTHCAL_1V00 VCO_VTHCAL(2)
#define VCO_VTHCAL_1V05 VCO_VTHCAL(3)
#define VCO_VTHCAL_MASK VCO_VTHCAL(3)
#define VCO_KVCOEXT_SHIFT 14
#define VCO_KVCOEXT_MASK (0x3 << VCO_KVCOEXT_SHIFT)
#define VCO_KVCOEXT_ENABLE BIT(17)
#define VCO_V2IEXT_SHIFT 18
#define VCO_V2IEXT_MASK (0xf << VCO_V2IEXT_SHIFT)
#define VCO_V2IEXT_ENABLE BIT(22)
#define VCO_SPEED_SHIFT 23
#define VCO_SPEED(x) ((x) << VCO_SPEED_SHIFT)
#define VCO_SPEED_1G08_1G21 VCO_SPEED(0)
#define VCO_SPEED_1G21_1G40 VCO_SPEED(1)
#define VCO_SPEED_1G40_1G61 VCO_SPEED(2)
#define VCO_SPEED_1G61_1G86 VCO_SPEED(3)
#define VCO_SPEED_1G86_2G00 VCO_SPEED(4)
#define VCO_SPEED_2G00_2G22 VCO_SPEED(5)
#define VCO_SPEED_2G22 VCO_SPEED(6)
#define VCO_SPEED_MASK VCO_SPEED(0x7)
#define VCO_CLKDET_ENABLE BIT(26)
#define VCO_CTRL1 AVPLL_CTRL(1)
#define VCO_REFDIV_SHIFT 0
#define VCO_REFDIV(x) ((x) << VCO_REFDIV_SHIFT)
#define VCO_REFDIV_1 VCO_REFDIV(0)
#define VCO_REFDIV_2 VCO_REFDIV(1)
#define VCO_REFDIV_4 VCO_REFDIV(2)
#define VCO_REFDIV_3 VCO_REFDIV(3)
#define VCO_REFDIV_MASK VCO_REFDIV(0x3f)
#define VCO_FBDIV_SHIFT 6
#define VCO_FBDIV(x) ((x) << VCO_FBDIV_SHIFT)
#define VCO_FBDIV_MASK VCO_FBDIV(0xff)
#define VCO_ICP_SHIFT 14
/* PLL Charge Pump Current = 10uA * (x + 1) */
#define VCO_ICP(x) ((x) << VCO_ICP_SHIFT)
#define VCO_ICP_MASK VCO_ICP(0xf)
#define VCO_LOAD_CAP BIT(18)
#define VCO_CALIBRATION_START BIT(19)
#define VCO_FREQOFFSETn(x) AVPLL_CTRL(3 + (x))
#define VCO_FREQOFFSET_MASK 0x7ffff
#define VCO_CTRL10 AVPLL_CTRL(10)
#define VCO_POWERUP_CH1 BIT(20)
#define VCO_CTRL11 AVPLL_CTRL(11)
#define VCO_CTRL12 AVPLL_CTRL(12)
#define VCO_CTRL13 AVPLL_CTRL(13)
#define VCO_CTRL14 AVPLL_CTRL(14)
#define VCO_CTRL15 AVPLL_CTRL(15)
#define VCO_SYNC1n(x) AVPLL_CTRL(15 + (x))
#define VCO_SYNC1_MASK 0x1ffff
#define VCO_SYNC2n(x) AVPLL_CTRL(23 + (x))
#define VCO_SYNC2_MASK 0x1ffff
#define VCO_CTRL30 AVPLL_CTRL(30)
#define VCO_DPLL_CH1_ENABLE BIT(17)
struct berlin2_avpll_vco {
struct clk_hw hw;
void __iomem *base;
u8 flags;
};
#define to_avpll_vco(hw) container_of(hw, struct berlin2_avpll_vco, hw)
static int berlin2_avpll_vco_is_enabled(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg >>= 4;
return !!(reg & VCO_POWERUP);
}
static int berlin2_avpll_vco_enable(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg |= VCO_POWERUP << 4;
else
reg |= VCO_POWERUP;
writel_relaxed(reg, vco->base + VCO_CTRL0);
return 0;
}
static void berlin2_avpll_vco_disable(struct clk_hw *hw)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg;
reg = readl_relaxed(vco->base + VCO_CTRL0);
if (vco->flags & BERLIN2_AVPLL_BIT_QUIRK)
reg &= ~(VCO_POWERUP << 4);
else
reg &= ~VCO_POWERUP;
writel_relaxed(reg, vco->base + VCO_CTRL0);
}
static u8 vco_refdiv[] = { 1, 2, 4, 3 };
static unsigned long
berlin2_avpll_vco_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_avpll_vco *vco = to_avpll_vco(hw);
u32 reg, refdiv, fbdiv;
u64 freq = parent_rate;
/* AVPLL VCO frequency: Fvco = (Fref / refdiv) * fbdiv */
reg = readl_relaxed(vco->base + VCO_CTRL1);
refdiv = (reg & VCO_REFDIV_MASK) >> VCO_REFDIV_SHIFT;
refdiv = vco_refdiv[refdiv];
fbdiv = (reg & VCO_FBDIV_MASK) >> VCO_FBDIV_SHIFT;
freq *= fbdiv;
do_div(freq, refdiv);
return (unsigned long)freq;
}
static const struct clk_ops berlin2_avpll_vco_ops = {
.is_enabled = berlin2_avpll_vco_is_enabled,
.enable = berlin2_avpll_vco_enable,
.disable = berlin2_avpll_vco_disable,
.recalc_rate = berlin2_avpll_vco_recalc_rate,
};
int __init berlin2_avpll_vco_register(void __iomem *base,
const char *name, const char *parent_name,
u8 vco_flags, unsigned long flags)
{
struct berlin2_avpll_vco *vco;
struct clk_init_data init;
vco = kzalloc_obj(*vco);
if (!vco)
return -ENOMEM;
vco->base = base;
vco->flags = vco_flags;
vco->hw.init = &init;
init.name = name;
init.ops = &berlin2_avpll_vco_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_hw_register(NULL, &vco->hw);
}
struct berlin2_avpll_channel {
struct clk_hw hw;
void __iomem *base;
u8 flags;
u8 index;
};
#define to_avpll_channel(hw) container_of(hw, struct berlin2_avpll_channel, hw)
static int berlin2_avpll_channel_is_enabled(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
if (ch->index == 7)
return 1;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg &= VCO_POWERUP_CH1 << ch->index;
return !!reg;
}
static int berlin2_avpll_channel_enable(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg |= VCO_POWERUP_CH1 << ch->index;
writel_relaxed(reg, ch->base + VCO_CTRL10);
return 0;
}
static void berlin2_avpll_channel_disable(struct clk_hw *hw)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg;
reg = readl_relaxed(ch->base + VCO_CTRL10);
reg &= ~(VCO_POWERUP_CH1 << ch->index);
writel_relaxed(reg, ch->base + VCO_CTRL10);
}
static const u8 div_hdmi[] = { 1, 2, 4, 6 };
static const u8 div_av1[] = { 1, 2, 5, 5 };
static unsigned long
berlin2_avpll_channel_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_avpll_channel *ch = to_avpll_channel(hw);
u32 reg, div_av2, div_av3, divider = 1;
u64 freq = parent_rate;
reg = readl_relaxed(ch->base + VCO_CTRL30);
if ((reg & (VCO_DPLL_CH1_ENABLE << ch->index)) == 0)
goto skip_div;
/*
* Fch = (Fref * sync2) /
* (sync1 * div_hdmi * div_av1 * div_av2 * div_av3)
*/
reg = readl_relaxed(ch->base + VCO_SYNC1n(ch->index));
/* BG2/BG2CDs SYNC1 reg on AVPLL_B channel 1 is shifted by 4 */
if (ch->flags & BERLIN2_AVPLL_BIT_QUIRK && ch->index == 0)
reg >>= 4;
divider = reg & VCO_SYNC1_MASK;
reg = readl_relaxed(ch->base + VCO_SYNC2n(ch->index));
freq *= reg & VCO_SYNC2_MASK;
/* Channel 8 has no dividers */
if (ch->index == 7)
goto skip_div;
/*
* HDMI divider start at VCO_CTRL11, bit 7; MSB is enable, lower 2 bit
* determine divider.
*/
reg = readl_relaxed(ch->base + VCO_CTRL11) >> 7;
reg = (reg >> (ch->index * 3));
if (reg & BIT(2))
divider *= div_hdmi[reg & 0x3];
/*
* AV1 divider start at VCO_CTRL11, bit 28; MSB is enable, lower 2 bit
* determine divider.
*/
if (ch->index == 0) {
reg = readl_relaxed(ch->base + VCO_CTRL11);
reg >>= 28;
} else {
reg = readl_relaxed(ch->base + VCO_CTRL12);
reg >>= (ch->index-1) * 3;
}
if (reg & BIT(2))
divider *= div_av1[reg & 0x3];
/*
* AV2 divider start at VCO_CTRL12, bit 18; each 7 bits wide,
* zero is not a valid value.
*/
if (ch->index < 2) {
reg = readl_relaxed(ch->base + VCO_CTRL12);
reg >>= 18 + (ch->index * 7);
} else if (ch->index < 7) {
reg = readl_relaxed(ch->base + VCO_CTRL13);
reg >>= (ch->index - 2) * 7;
} else {
reg = readl_relaxed(ch->base + VCO_CTRL14);
}
div_av2 = reg & 0x7f;
if (div_av2)
divider *= div_av2;
/*
* AV3 divider start at VCO_CTRL14, bit 7; each 4 bits wide.
* AV2/AV3 form a fractional divider, where only specific values for AV3
* are allowed. AV3 != 0 divides by AV2/2, AV3=0 is bypass.
*/
if (ch->index < 6) {
reg = readl_relaxed(ch->base + VCO_CTRL14);
reg >>= 7 + (ch->index * 4);
} else {
reg = readl_relaxed(ch->base + VCO_CTRL15);
}
div_av3 = reg & 0xf;
if (div_av2 && div_av3)
freq *= 2;
skip_div:
do_div(freq, divider);
return (unsigned long)freq;
}
static const struct clk_ops berlin2_avpll_channel_ops = {
.is_enabled = berlin2_avpll_channel_is_enabled,
.enable = berlin2_avpll_channel_enable,
.disable = berlin2_avpll_channel_disable,
.recalc_rate = berlin2_avpll_channel_recalc_rate,
};
/*
* Another nice quirk:
* On some production SoCs, AVPLL channels are scrambled with respect
* to the channel numbering in the registers but still referenced by
* their original channel numbers. We deal with it by having a flag
* and a translation table for the index.
*/
static const u8 quirk_index[] __initconst = { 0, 6, 5, 4, 3, 2, 1, 7 };
int __init berlin2_avpll_channel_register(void __iomem *base,
const char *name, u8 index, const char *parent_name,
u8 ch_flags, unsigned long flags)
{
struct berlin2_avpll_channel *ch;
struct clk_init_data init;
ch = kzalloc_obj(*ch);
if (!ch)
return -ENOMEM;
ch->base = base;
if (ch_flags & BERLIN2_AVPLL_SCRAMBLE_QUIRK)
ch->index = quirk_index[index];
else
ch->index = index;
ch->flags = ch_flags;
ch->hw.init = &init;
init.name = name;
init.ops = &berlin2_avpll_channel_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_hw_register(NULL, &ch->hw);
}
@@ -0,0 +1,21 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
#ifndef __BERLIN2_AVPLL_H
#define __BERLIN2_AVPLL_H
#define BERLIN2_AVPLL_BIT_QUIRK BIT(0)
#define BERLIN2_AVPLL_SCRAMBLE_QUIRK BIT(1)
int berlin2_avpll_vco_register(void __iomem *base, const char *name,
const char *parent_name, u8 vco_flags, unsigned long flags);
int berlin2_avpll_channel_register(void __iomem *base, const char *name,
u8 index, const char *parent_name, u8 ch_flags,
unsigned long flags);
#endif /* __BERLIN2_AVPLL_H */
@@ -0,0 +1,256 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*/
#include <linux/bitops.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include "berlin2-div.h"
/*
* Clock dividers in Berlin2 SoCs comprise a complex cell to select
* input pll and divider. The virtual structure as it is used in Marvell
* BSP code can be seen as:
*
* +---+
* pll0 --------------->| 0 | +---+
* +---+ |(B)|--+--------------->| 0 | +---+
* pll1.0 -->| 0 | +-->| 1 | | +--------+ |(E)|----->| 0 | +---+
* pll1.1 -->| 1 | | +---+ +-->|(C) 1:M |-->| 1 | |(F)|-->|(G)|->
* ... -->|(A)|--+ | +--------+ +---+ +-->| 1 | +---+
* ... -->| | +-->|(D) 1:3 |----------+ +---+
* pll1.N -->| N | +---------
* +---+
*
* (A) input pll clock mux controlled by <PllSelect[1:n]>
* (B) input pll bypass mux controlled by <PllSwitch>
* (C) programmable clock divider controlled by <Select[1:n]>
* (D) constant div-by-3 clock divider
* (E) programmable clock divider bypass controlled by <Switch>
* (F) constant div-by-3 clock mux controlled by <D3Switch>
* (G) clock gate controlled by <Enable>
*
* For whatever reason, above control signals come in two flavors:
* - single register dividers with all bits in one register
* - shared register dividers with bits spread over multiple registers
* (including signals for the same cell spread over consecutive registers)
*
* Also, clock gate and pll mux is not available on every div cell, so
* we have to deal with those, too. We reuse common clock composite driver
* for it.
*/
#define PLL_SELECT_MASK 0x7
#define DIV_SELECT_MASK 0x7
struct berlin2_div {
struct clk_hw hw;
void __iomem *base;
struct berlin2_div_map map;
spinlock_t *lock;
};
#define to_berlin2_div(hw) container_of(hw, struct berlin2_div, hw)
static u8 clk_div[] = { 1, 2, 4, 6, 8, 12, 1, 1 };
static int berlin2_div_is_enabled(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg >>= map->gate_shift;
if (div->lock)
spin_unlock(div->lock);
return (reg & 0x1);
}
static int berlin2_div_enable(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg |= BIT(map->gate_shift);
writel_relaxed(reg, div->base + map->gate_offs);
if (div->lock)
spin_unlock(div->lock);
return 0;
}
static void berlin2_div_disable(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
reg = readl_relaxed(div->base + map->gate_offs);
reg &= ~BIT(map->gate_shift);
writel_relaxed(reg, div->base + map->gate_offs);
if (div->lock)
spin_unlock(div->lock);
}
static int berlin2_div_set_parent(struct clk_hw *hw, u8 index)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
if (div->lock)
spin_lock(div->lock);
/* index == 0 is PLL_SWITCH */
reg = readl_relaxed(div->base + map->pll_switch_offs);
if (index == 0)
reg &= ~BIT(map->pll_switch_shift);
else
reg |= BIT(map->pll_switch_shift);
writel_relaxed(reg, div->base + map->pll_switch_offs);
/* index > 0 is PLL_SELECT */
if (index > 0) {
reg = readl_relaxed(div->base + map->pll_select_offs);
reg &= ~(PLL_SELECT_MASK << map->pll_select_shift);
reg |= (index - 1) << map->pll_select_shift;
writel_relaxed(reg, div->base + map->pll_select_offs);
}
if (div->lock)
spin_unlock(div->lock);
return 0;
}
static u8 berlin2_div_get_parent(struct clk_hw *hw)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 reg;
u8 index = 0;
if (div->lock)
spin_lock(div->lock);
/* PLL_SWITCH == 0 is index 0 */
reg = readl_relaxed(div->base + map->pll_switch_offs);
reg &= BIT(map->pll_switch_shift);
if (reg) {
reg = readl_relaxed(div->base + map->pll_select_offs);
reg >>= map->pll_select_shift;
reg &= PLL_SELECT_MASK;
index = 1 + reg;
}
if (div->lock)
spin_unlock(div->lock);
return index;
}
static unsigned long berlin2_div_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct berlin2_div *div = to_berlin2_div(hw);
struct berlin2_div_map *map = &div->map;
u32 divsw, div3sw, divider = 1;
if (div->lock)
spin_lock(div->lock);
divsw = readl_relaxed(div->base + map->div_switch_offs) &
(1 << map->div_switch_shift);
div3sw = readl_relaxed(div->base + map->div3_switch_offs) &
(1 << map->div3_switch_shift);
/* constant divide-by-3 (dominant) */
if (div3sw != 0) {
divider = 3;
/* divider can be bypassed with DIV_SWITCH == 0 */
} else if (divsw == 0) {
divider = 1;
/* clock divider determined by DIV_SELECT */
} else {
u32 reg;
reg = readl_relaxed(div->base + map->div_select_offs);
reg >>= map->div_select_shift;
reg &= DIV_SELECT_MASK;
divider = clk_div[reg];
}
if (div->lock)
spin_unlock(div->lock);
return parent_rate / divider;
}
static const struct clk_ops berlin2_div_rate_ops = {
.determine_rate = clk_hw_determine_rate_no_reparent,
.recalc_rate = berlin2_div_recalc_rate,
};
static const struct clk_ops berlin2_div_gate_ops = {
.is_enabled = berlin2_div_is_enabled,
.enable = berlin2_div_enable,
.disable = berlin2_div_disable,
};
static const struct clk_ops berlin2_div_mux_ops = {
.set_parent = berlin2_div_set_parent,
.get_parent = berlin2_div_get_parent,
};
struct clk_hw * __init
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
unsigned long flags, spinlock_t *lock)
{
const struct clk_ops *mux_ops = &berlin2_div_mux_ops;
const struct clk_ops *rate_ops = &berlin2_div_rate_ops;
const struct clk_ops *gate_ops = &berlin2_div_gate_ops;
struct berlin2_div *div;
div = kzalloc_obj(*div);
if (!div)
return ERR_PTR(-ENOMEM);
/* copy div_map to allow __initconst */
memcpy(&div->map, map, sizeof(*map));
div->base = base;
div->lock = lock;
if ((div_flags & BERLIN2_DIV_HAS_GATE) == 0)
gate_ops = NULL;
if ((div_flags & BERLIN2_DIV_HAS_MUX) == 0)
mux_ops = NULL;
return clk_hw_register_composite(NULL, name, parent_names, num_parents,
&div->hw, mux_ops, &div->hw, rate_ops,
&div->hw, gate_ops, flags);
}
@@ -0,0 +1,78 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*/
#ifndef __BERLIN2_DIV_H
#define __BERLIN2_DIV_H
struct clk_hw;
#define BERLIN2_DIV_HAS_GATE BIT(0)
#define BERLIN2_DIV_HAS_MUX BIT(1)
#define BERLIN2_PLL_SELECT(_off, _sh) \
.pll_select_offs = _off, \
.pll_select_shift = _sh
#define BERLIN2_PLL_SWITCH(_off, _sh) \
.pll_switch_offs = _off, \
.pll_switch_shift = _sh
#define BERLIN2_DIV_SELECT(_off, _sh) \
.div_select_offs = _off, \
.div_select_shift = _sh
#define BERLIN2_DIV_SWITCH(_off, _sh) \
.div_switch_offs = _off, \
.div_switch_shift = _sh
#define BERLIN2_DIV_D3SWITCH(_off, _sh) \
.div3_switch_offs = _off, \
.div3_switch_shift = _sh
#define BERLIN2_DIV_GATE(_off, _sh) \
.gate_offs = _off, \
.gate_shift = _sh
#define BERLIN2_SINGLE_DIV(_off) \
BERLIN2_DIV_GATE(_off, 0), \
BERLIN2_PLL_SELECT(_off, 1), \
BERLIN2_PLL_SWITCH(_off, 4), \
BERLIN2_DIV_SWITCH(_off, 5), \
BERLIN2_DIV_D3SWITCH(_off, 6), \
BERLIN2_DIV_SELECT(_off, 7)
struct berlin2_div_map {
u16 pll_select_offs;
u16 pll_switch_offs;
u16 div_select_offs;
u16 div_switch_offs;
u16 div3_switch_offs;
u16 gate_offs;
u8 pll_select_shift;
u8 pll_switch_shift;
u8 div_select_shift;
u8 div_switch_shift;
u8 div3_switch_shift;
u8 gate_shift;
};
struct berlin2_div_data {
const char *name;
const u8 *parent_ids;
int num_parents;
unsigned long flags;
struct berlin2_div_map map;
u8 div_flags;
};
struct clk_hw *
berlin2_div_register(const struct berlin2_div_map *map,
void __iomem *base, const char *name, u8 div_flags,
const char **parent_names, int num_parents,
unsigned long flags, spinlock_t *lock);
#endif /* __BERLIN2_DIV_H */
@@ -0,0 +1,99 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*/
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <asm/div64.h>
#include "berlin2-div.h"
#include "berlin2-pll.h"
struct berlin2_pll {
struct clk_hw hw;
void __iomem *base;
struct berlin2_pll_map map;
};
#define to_berlin2_pll(hw) container_of(hw, struct berlin2_pll, hw)
#define SPLL_CTRL0 0x00
#define SPLL_CTRL1 0x04
#define SPLL_CTRL2 0x08
#define SPLL_CTRL3 0x0c
#define SPLL_CTRL4 0x10
#define FBDIV_MASK 0x1ff
#define RFDIV_MASK 0x1f
#define DIVSEL_MASK 0xf
/*
* The output frequency formula for the pll is:
* clkout = fbdiv / refdiv * parent / vcodiv
*/
static unsigned long
berlin2_pll_recalc_rate(struct clk_hw *hw, unsigned long parent_rate)
{
struct berlin2_pll *pll = to_berlin2_pll(hw);
struct berlin2_pll_map *map = &pll->map;
u32 val, fbdiv, rfdiv, vcodivsel, vcodiv;
u64 rate = parent_rate;
val = readl_relaxed(pll->base + SPLL_CTRL0);
fbdiv = (val >> map->fbdiv_shift) & FBDIV_MASK;
rfdiv = (val >> map->rfdiv_shift) & RFDIV_MASK;
if (rfdiv == 0) {
pr_warn("%s has zero rfdiv\n", clk_hw_get_name(hw));
rfdiv = 1;
}
val = readl_relaxed(pll->base + SPLL_CTRL1);
vcodivsel = (val >> map->divsel_shift) & DIVSEL_MASK;
vcodiv = map->vcodiv[vcodivsel];
if (vcodiv == 0) {
pr_warn("%s has zero vcodiv (index %d)\n",
clk_hw_get_name(hw), vcodivsel);
vcodiv = 1;
}
rate *= fbdiv * map->mult;
do_div(rate, rfdiv * vcodiv);
return (unsigned long)rate;
}
static const struct clk_ops berlin2_pll_ops = {
.recalc_rate = berlin2_pll_recalc_rate,
};
int __init
berlin2_pll_register(const struct berlin2_pll_map *map,
void __iomem *base, const char *name,
const char *parent_name, unsigned long flags)
{
struct clk_init_data init;
struct berlin2_pll *pll;
pll = kzalloc_obj(*pll);
if (!pll)
return -ENOMEM;
/* copy pll_map to allow __initconst */
memcpy(&pll->map, map, sizeof(*map));
pll->base = base;
pll->hw.init = &init;
init.name = name;
init.ops = &berlin2_pll_ops;
init.parent_names = &parent_name;
init.num_parents = 1;
init.flags = flags;
return clk_hw_register(NULL, &pll->hw);
}
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*/
#ifndef __BERLIN2_PLL_H
#define __BERLIN2_PLL_H
struct berlin2_pll_map {
const u8 vcodiv[16];
u8 mult;
u8 fbdiv_shift;
u8 rfdiv_shift;
u8 divsel_shift;
};
int berlin2_pll_register(const struct berlin2_pll_map *map,
void __iomem *base, const char *name,
const char *parent_name, unsigned long flags);
#endif /* __BERLIN2_PLL_H */
+687
View File
@@ -0,0 +1,687 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <dt-bindings/clock/berlin2.h>
#include "berlin2-avpll.h"
#include "berlin2-div.h"
#include "berlin2-pll.h"
#include "common.h"
#define REG_PINMUX0 0x0000
#define REG_PINMUX1 0x0004
#define REG_SYSPLLCTL0 0x0014
#define REG_SYSPLLCTL4 0x0024
#define REG_MEMPLLCTL0 0x0028
#define REG_MEMPLLCTL4 0x0038
#define REG_CPUPLLCTL0 0x003c
#define REG_CPUPLLCTL4 0x004c
#define REG_AVPLLCTL0 0x0050
#define REG_AVPLLCTL31 0x00cc
#define REG_AVPLLCTL62 0x0148
#define REG_PLLSTATUS 0x014c
#define REG_CLKENABLE 0x0150
#define REG_CLKSELECT0 0x0154
#define REG_CLKSELECT1 0x0158
#define REG_CLKSELECT2 0x015c
#define REG_CLKSELECT3 0x0160
#define REG_CLKSWITCH0 0x0164
#define REG_CLKSWITCH1 0x0168
#define REG_RESET_TRIGGER 0x0178
#define REG_RESET_STATUS0 0x017c
#define REG_RESET_STATUS1 0x0180
#define REG_SW_GENERIC0 0x0184
#define REG_SW_GENERIC3 0x0190
#define REG_PRODUCTID 0x01cc
#define REG_PRODUCTID_EXT 0x01d0
#define REG_GFX3DCORE_CLKCTL 0x022c
#define REG_GFX3DSYS_CLKCTL 0x0230
#define REG_ARC_CLKCTL 0x0234
#define REG_VIP_CLKCTL 0x0238
#define REG_SDIO0XIN_CLKCTL 0x023c
#define REG_SDIO1XIN_CLKCTL 0x0240
#define REG_GFX3DEXTRA_CLKCTL 0x0244
#define REG_GFX3D_RESET 0x0248
#define REG_GC360_CLKCTL 0x024c
#define REG_SDIO_DLLMST_CLKCTL 0x0250
/*
* BG2/BG2CD SoCs have the following audio/video I/O units:
*
* audiohd: HDMI TX audio
* audio0: 7.1ch TX
* audio1: 2ch TX
* audio2: 2ch RX
* audio3: SPDIF TX
* video0: HDMI video
* video1: Secondary video
* video2: SD auxiliary video
*
* There are no external audio clocks (ACLKI0, ACLKI1) and
* only one external video clock (VCLKI0).
*
* Currently missing bits and pieces:
* - audio_fast_pll is unknown
* - audiohd_pll is unknown
* - video0_pll is unknown
* - audio[023], audiohd parent pll is assumed to be audio_fast_pll
*
*/
#define MAX_CLKS 41
static struct clk_hw_onecell_data *clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
enum {
REFCLK, VIDEO_EXT0,
SYSPLL, MEMPLL, CPUPLL,
AVPLL_A1, AVPLL_A2, AVPLL_A3, AVPLL_A4,
AVPLL_A5, AVPLL_A6, AVPLL_A7, AVPLL_A8,
AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
AUDIO1_PLL, AUDIO_FAST_PLL,
VIDEO0_PLL, VIDEO0_IN,
VIDEO1_PLL, VIDEO1_IN,
VIDEO2_PLL, VIDEO2_IN,
};
static const char *clk_names[] = {
[REFCLK] = "refclk",
[VIDEO_EXT0] = "video_ext0",
[SYSPLL] = "syspll",
[MEMPLL] = "mempll",
[CPUPLL] = "cpupll",
[AVPLL_A1] = "avpll_a1",
[AVPLL_A2] = "avpll_a2",
[AVPLL_A3] = "avpll_a3",
[AVPLL_A4] = "avpll_a4",
[AVPLL_A5] = "avpll_a5",
[AVPLL_A6] = "avpll_a6",
[AVPLL_A7] = "avpll_a7",
[AVPLL_A8] = "avpll_a8",
[AVPLL_B1] = "avpll_b1",
[AVPLL_B2] = "avpll_b2",
[AVPLL_B3] = "avpll_b3",
[AVPLL_B4] = "avpll_b4",
[AVPLL_B5] = "avpll_b5",
[AVPLL_B6] = "avpll_b6",
[AVPLL_B7] = "avpll_b7",
[AVPLL_B8] = "avpll_b8",
[AUDIO1_PLL] = "audio1_pll",
[AUDIO_FAST_PLL] = "audio_fast_pll",
[VIDEO0_PLL] = "video0_pll",
[VIDEO0_IN] = "video0_in",
[VIDEO1_PLL] = "video1_pll",
[VIDEO1_IN] = "video1_in",
[VIDEO2_PLL] = "video2_pll",
[VIDEO2_IN] = "video2_in",
};
static const struct berlin2_pll_map bg2_pll_map __initconst = {
.vcodiv = {10, 15, 20, 25, 30, 40, 50, 60, 80},
.mult = 10,
.fbdiv_shift = 6,
.rfdiv_shift = 1,
.divsel_shift = 7,
};
static const u8 default_parent_ids[] = {
SYSPLL, AVPLL_B4, AVPLL_A5, AVPLL_B6, AVPLL_B7, SYSPLL
};
static const struct berlin2_div_data bg2_divs[] __initconst = {
{
.name = "sys",
.parent_ids = (const u8 []){
SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
},
.num_parents = 6,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "cpu",
.parent_ids = (const u8 []){
CPUPLL, MEMPLL, MEMPLL, MEMPLL, MEMPLL
},
.num_parents = 5,
.map = {
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
},
.div_flags = BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "drmfigo",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 16),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 17),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 20),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "cfg",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 23),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 26),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 29),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 0),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "zsp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 5),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 3),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 6),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "perif",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 9),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 12),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "pcube",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 15),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 18),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vscope",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 21),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 24),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "nfc_ecc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 18),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 27),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 0),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vpp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 3),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 6),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 4),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 5),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 6),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "app",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 9),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 12),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 7),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 8),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 9),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "audio0",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 22),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 17),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 10),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 11),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio2",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 20),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 14),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 15),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio3",
.parent_ids = (const u8 []){ AUDIO_FAST_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 25),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 23),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "audio1",
.parent_ids = (const u8 []){ AUDIO1_PLL },
.num_parents = 1,
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 23),
BERLIN2_DIV_SELECT(REG_CLKSELECT3, 0),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 12),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 13),
},
.div_flags = BERLIN2_DIV_HAS_GATE,
.flags = 0,
},
{
.name = "gfx3d_core",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DCORE_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx3d_sys",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DSYS_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "arc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_ARC_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vip",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_VIP_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio0xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio1xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx3d_extra",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GFX3DEXTRA_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gc360",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_GC360_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio_dllmst",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO_DLLMST_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
};
static const struct berlin2_gate_data bg2_gates[] __initconst = {
{ "geth0", "perif", 7 },
{ "geth1", "perif", 8 },
{ "sata", "perif", 9 },
{ "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
{ "usb0", "perif", 11 },
{ "usb1", "perif", 12 },
{ "pbridge", "perif", 13, CLK_IGNORE_UNUSED },
{ "sdio0", "perif", 14 },
{ "sdio1", "perif", 15 },
{ "nfc", "perif", 17 },
{ "smemc", "perif", 19 },
{ "audiohd", "audiohd_pll", 26 },
{ "video0", "video0_in", 27 },
{ "video1", "video1_in", 28 },
{ "video2", "video2_in", 29 },
};
static void __init berlin2_clock_setup(struct device_node *np)
{
struct device_node *parent_np = of_get_parent(np);
const char *parent_names[9];
struct clk *clk;
struct clk_hw *hw;
struct clk_hw **hws;
u8 avpll_flags = 0;
int n, ret;
clk_data = kzalloc_flex(*clk_data, hws, MAX_CLKS);
if (!clk_data) {
of_node_put(parent_np);
return;
}
clk_data->num = MAX_CLKS;
hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
of_node_put(parent_np);
if (!gbase)
return;
/* overwrite default clock names with DT provided ones */
clk = of_clk_get_by_name(np, clk_names[REFCLK]);
if (!IS_ERR(clk)) {
clk_names[REFCLK] = __clk_get_name(clk);
clk_put(clk);
}
clk = of_clk_get_by_name(np, clk_names[VIDEO_EXT0]);
if (!IS_ERR(clk)) {
clk_names[VIDEO_EXT0] = __clk_get_name(clk);
clk_put(clk);
}
/* simple register PLLs */
ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
if (ret)
goto bg2_fail;
ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_MEMPLLCTL0,
clk_names[MEMPLL], clk_names[REFCLK], 0);
if (ret)
goto bg2_fail;
ret = berlin2_pll_register(&bg2_pll_map, gbase + REG_CPUPLLCTL0,
clk_names[CPUPLL], clk_names[REFCLK], 0);
if (ret)
goto bg2_fail;
if (of_device_is_compatible(np, "marvell,berlin2-global-register"))
avpll_flags |= BERLIN2_AVPLL_SCRAMBLE_QUIRK;
/* audio/video VCOs */
ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL0, "avpll_vcoA",
clk_names[REFCLK], avpll_flags, 0);
if (ret)
goto bg2_fail;
for (n = 0; n < 8; n++) {
ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL0,
clk_names[AVPLL_A1 + n], n, "avpll_vcoA",
avpll_flags, 0);
if (ret)
goto bg2_fail;
}
ret = berlin2_avpll_vco_register(gbase + REG_AVPLLCTL31, "avpll_vcoB",
clk_names[REFCLK], BERLIN2_AVPLL_BIT_QUIRK |
avpll_flags, 0);
if (ret)
goto bg2_fail;
for (n = 0; n < 8; n++) {
ret = berlin2_avpll_channel_register(gbase + REG_AVPLLCTL31,
clk_names[AVPLL_B1 + n], n, "avpll_vcoB",
BERLIN2_AVPLL_BIT_QUIRK | avpll_flags, 0);
if (ret)
goto bg2_fail;
}
/* reference clock bypass switches */
parent_names[0] = clk_names[SYSPLL];
parent_names[1] = clk_names[REFCLK];
hw = clk_hw_register_mux(NULL, "syspll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 0, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
clk_names[SYSPLL] = clk_hw_get_name(hw);
parent_names[0] = clk_names[MEMPLL];
parent_names[1] = clk_names[REFCLK];
hw = clk_hw_register_mux(NULL, "mempll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 1, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
clk_names[MEMPLL] = clk_hw_get_name(hw);
parent_names[0] = clk_names[CPUPLL];
parent_names[1] = clk_names[REFCLK];
hw = clk_hw_register_mux(NULL, "cpupll_byp", parent_names, 2,
0, gbase + REG_CLKSWITCH0, 2, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
clk_names[CPUPLL] = clk_hw_get_name(hw);
/* clock muxes */
parent_names[0] = clk_names[AVPLL_B3];
parent_names[1] = clk_names[AVPLL_A3];
hw = clk_hw_register_mux(NULL, clk_names[AUDIO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT2, 29, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO0_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
hw = clk_hw_register_mux(NULL, clk_names[VIDEO0_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 4, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO1_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 6, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_A2];
parent_names[1] = clk_names[AVPLL_B2];
hw = clk_hw_register_mux(NULL, clk_names[VIDEO1_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 7, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[VIDEO2_PLL];
parent_names[1] = clk_names[VIDEO_EXT0];
hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_IN], parent_names, 2,
0, gbase + REG_CLKSELECT3, 9, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
parent_names[0] = clk_names[AVPLL_B1];
parent_names[1] = clk_names[AVPLL_A5];
hw = clk_hw_register_mux(NULL, clk_names[VIDEO2_PLL], parent_names, 2,
0, gbase + REG_CLKSELECT3, 10, 1, 0, &lock);
if (IS_ERR(hw))
goto bg2_fail;
/* clock divider cells */
for (n = 0; n < ARRAY_SIZE(bg2_divs); n++) {
const struct berlin2_div_data *dd = &bg2_divs[n];
int k;
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
/* clock gate cells */
for (n = 0; n < ARRAY_SIZE(bg2_gates); n++) {
const struct berlin2_gate_data *gd = &bg2_gates[n];
hws[CLKID_GETH0 + n] = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/* twdclk is derived from cpu/3 */
hws[CLKID_TWD] =
clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
if (!IS_ERR(hws[n]))
continue;
pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
goto bg2_fail;
}
/* register clk-provider */
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
return;
bg2_fail:
iounmap(gbase);
}
CLK_OF_DECLARE(berlin2_clk, "marvell,berlin2-clk",
berlin2_clock_setup);
+386
View File
@@ -0,0 +1,386 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
*/
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <dt-bindings/clock/berlin2q.h>
#include "berlin2-div.h"
#include "berlin2-pll.h"
#include "common.h"
#define REG_PINMUX0 0x0018
#define REG_PINMUX5 0x002c
#define REG_SYSPLLCTL0 0x0030
#define REG_SYSPLLCTL4 0x0040
#define REG_CLKENABLE 0x00e8
#define REG_CLKSELECT0 0x00ec
#define REG_CLKSELECT1 0x00f0
#define REG_CLKSELECT2 0x00f4
#define REG_CLKSWITCH0 0x00f8
#define REG_CLKSWITCH1 0x00fc
#define REG_SW_GENERIC0 0x0110
#define REG_SW_GENERIC3 0x011c
#define REG_SDIO0XIN_CLKCTL 0x0158
#define REG_SDIO1XIN_CLKCTL 0x015c
#define MAX_CLKS 28
static struct clk_hw_onecell_data *clk_data;
static DEFINE_SPINLOCK(lock);
static void __iomem *gbase;
static void __iomem *cpupll_base;
enum {
REFCLK,
SYSPLL, CPUPLL,
AVPLL_B1, AVPLL_B2, AVPLL_B3, AVPLL_B4,
AVPLL_B5, AVPLL_B6, AVPLL_B7, AVPLL_B8,
};
static const char *clk_names[] = {
[REFCLK] = "refclk",
[SYSPLL] = "syspll",
[CPUPLL] = "cpupll",
[AVPLL_B1] = "avpll_b1",
[AVPLL_B2] = "avpll_b2",
[AVPLL_B3] = "avpll_b3",
[AVPLL_B4] = "avpll_b4",
[AVPLL_B5] = "avpll_b5",
[AVPLL_B6] = "avpll_b6",
[AVPLL_B7] = "avpll_b7",
[AVPLL_B8] = "avpll_b8",
};
static const struct berlin2_pll_map bg2q_pll_map __initconst = {
.vcodiv = {1, 0, 2, 0, 3, 4, 0, 6, 8},
.mult = 1,
.fbdiv_shift = 7,
.rfdiv_shift = 2,
.divsel_shift = 9,
};
static const u8 default_parent_ids[] = {
SYSPLL, AVPLL_B4, AVPLL_B5, AVPLL_B6, AVPLL_B7, SYSPLL
};
static const struct berlin2_div_data bg2q_divs[] __initconst = {
{
.name = "sys",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 0),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 3),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 4),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 5),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "drmfigo",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 17),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 6),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 7),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 8),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "cfg",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 1),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 12),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 15),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 9),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 10),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 11),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "gfx2d",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 4),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 18),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 21),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 12),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 13),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 14),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "zsp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 6),
BERLIN2_PLL_SELECT(REG_CLKSELECT0, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT0, 27),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 15),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 16),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 17),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "perif",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 7),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 18),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 19),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 20),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = CLK_IGNORE_UNUSED,
},
{
.name = "pcube",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 2),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 6),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 9),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 21),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 22),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 23),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vscope",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 3),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 12),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 15),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 24),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 25),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 26),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "nfc_ecc",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 19),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 18),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 21),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 27),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 28),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH0, 29),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "vpp",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 21),
BERLIN2_PLL_SELECT(REG_CLKSELECT1, 24),
BERLIN2_DIV_SELECT(REG_CLKSELECT1, 27),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH0, 30),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH0, 31),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 0),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "app",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_DIV_GATE(REG_CLKENABLE, 20),
BERLIN2_PLL_SELECT(REG_CLKSELECT2, 0),
BERLIN2_DIV_SELECT(REG_CLKSELECT2, 3),
BERLIN2_PLL_SWITCH(REG_CLKSWITCH1, 1),
BERLIN2_DIV_SWITCH(REG_CLKSWITCH1, 2),
BERLIN2_DIV_D3SWITCH(REG_CLKSWITCH1, 3),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio0xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO0XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
{
.name = "sdio1xin",
.parent_ids = default_parent_ids,
.num_parents = ARRAY_SIZE(default_parent_ids),
.map = {
BERLIN2_SINGLE_DIV(REG_SDIO1XIN_CLKCTL),
},
.div_flags = BERLIN2_DIV_HAS_GATE | BERLIN2_DIV_HAS_MUX,
.flags = 0,
},
};
static const struct berlin2_gate_data bg2q_gates[] __initconst = {
{ "gfx2daxi", "perif", 5 },
{ "geth0", "perif", 8 },
{ "sata", "perif", 9 },
{ "ahbapb", "perif", 10, CLK_IGNORE_UNUSED },
{ "usb0", "perif", 11 },
{ "usb1", "perif", 12 },
{ "usb2", "perif", 13 },
{ "usb3", "perif", 14 },
{ "pbridge", "perif", 15, CLK_IGNORE_UNUSED },
{ "sdio", "perif", 16 },
{ "nfc", "perif", 18 },
{ "pcie", "perif", 22 },
};
static void __init berlin2q_clock_setup(struct device_node *np)
{
struct device_node *parent_np = of_get_parent(np);
const char *parent_names[9];
struct clk *clk;
struct clk_hw **hws;
int n, ret;
clk_data = kzalloc_flex(*clk_data, hws, MAX_CLKS);
if (!clk_data) {
of_node_put(parent_np);
return;
}
clk_data->num = MAX_CLKS;
hws = clk_data->hws;
gbase = of_iomap(parent_np, 0);
if (!gbase) {
of_node_put(parent_np);
pr_err("%pOF: Unable to map global base\n", np);
return;
}
/* BG2Q CPU PLL is not part of global registers */
cpupll_base = of_iomap(parent_np, 1);
of_node_put(parent_np);
if (!cpupll_base) {
pr_err("%pOF: Unable to map cpupll base\n", np);
iounmap(gbase);
return;
}
/* overwrite default clock names with DT provided ones */
clk = of_clk_get_by_name(np, clk_names[REFCLK]);
if (!IS_ERR(clk)) {
clk_names[REFCLK] = __clk_get_name(clk);
clk_put(clk);
}
/* simple register PLLs */
ret = berlin2_pll_register(&bg2q_pll_map, gbase + REG_SYSPLLCTL0,
clk_names[SYSPLL], clk_names[REFCLK], 0);
if (ret)
goto bg2q_fail;
ret = berlin2_pll_register(&bg2q_pll_map, cpupll_base,
clk_names[CPUPLL], clk_names[REFCLK], 0);
if (ret)
goto bg2q_fail;
/* TODO: add BG2Q AVPLL */
/*
* TODO: add reference clock bypass switches:
* memPLLSWBypass, cpuPLLSWBypass, and sysPLLSWBypass
*/
/* clock divider cells */
for (n = 0; n < ARRAY_SIZE(bg2q_divs); n++) {
const struct berlin2_div_data *dd = &bg2q_divs[n];
int k;
for (k = 0; k < dd->num_parents; k++)
parent_names[k] = clk_names[dd->parent_ids[k]];
hws[CLKID_SYS + n] = berlin2_div_register(&dd->map, gbase,
dd->name, dd->div_flags, parent_names,
dd->num_parents, dd->flags, &lock);
}
/* clock gate cells */
for (n = 0; n < ARRAY_SIZE(bg2q_gates); n++) {
const struct berlin2_gate_data *gd = &bg2q_gates[n];
hws[CLKID_GFX2DAXI + n] = clk_hw_register_gate(NULL, gd->name,
gd->parent_name, gd->flags, gbase + REG_CLKENABLE,
gd->bit_idx, 0, &lock);
}
/* cpuclk divider is fixed to 1 */
hws[CLKID_CPU] =
clk_hw_register_fixed_factor(NULL, "cpu", clk_names[CPUPLL],
0, 1, 1);
/* twdclk is derived from cpu/3 */
hws[CLKID_TWD] =
clk_hw_register_fixed_factor(NULL, "twd", "cpu", 0, 1, 3);
/* check for errors on leaf clocks */
for (n = 0; n < MAX_CLKS; n++) {
if (!IS_ERR(hws[n]))
continue;
pr_err("%pOF: Unable to register leaf clock %d\n", np, n);
goto bg2q_fail;
}
/* register clk-provider */
of_clk_add_hw_provider(np, of_clk_hw_onecell_get, clk_data);
return;
bg2q_fail:
iounmap(cpupll_base);
iounmap(gbase);
}
CLK_OF_DECLARE(berlin2q_clk, "marvell,berlin2q-clk",
berlin2q_clock_setup);
+18
View File
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2014 Marvell Technology Group Ltd.
*
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
* Alexandre Belloni <alexandre.belloni@free-electrons.com>
*/
#ifndef __BERLIN2_COMMON_H
#define __BERLIN2_COMMON_H
struct berlin2_gate_data {
const char *name;
const char *parent_name;
u8 bit_idx;
unsigned long flags;
};
#endif /* BERLIN2_COMMON_H */