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
+114
View File
@@ -0,0 +1,114 @@
# SPDX-License-Identifier: GPL-2.0-only
# Recent Exynos platforms should just select COMMON_CLK_SAMSUNG:
config COMMON_CLK_SAMSUNG
bool "Samsung Exynos clock controller support" if COMPILE_TEST
depends on OF
select S3C64XX_COMMON_CLK if ARM && ARCH_S3C64XX
select S5PV210_COMMON_CLK if ARM && ARCH_S5PV210
select EXYNOS_3250_COMMON_CLK if ARM && SOC_EXYNOS3250
select EXYNOS_4_COMMON_CLK if ARM && ARCH_EXYNOS4
select EXYNOS_5250_COMMON_CLK if ARM && SOC_EXYNOS5250
select EXYNOS_5260_COMMON_CLK if ARM && SOC_EXYNOS5260
select EXYNOS_5410_COMMON_CLK if ARM && SOC_EXYNOS5410
select EXYNOS_5420_COMMON_CLK if ARM && SOC_EXYNOS5420
select EXYNOS_ARM64_COMMON_CLK if ARM64 && ARCH_EXYNOS
select TESLA_FSD_COMMON_CLK if ARM64 && ARCH_TESLA_FSD
config S3C64XX_COMMON_CLK
bool "Samsung S3C64xx clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung S3C64xx SoCs.
Choose Y here only if you build for this SoC.
config S5PV210_COMMON_CLK
bool "Samsung S5Pv210 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung S5Pv210 SoCs.
Choose Y here only if you build for this SoC.
config EXYNOS_3250_COMMON_CLK
bool "Samsung Exynos3250 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos3250 SoCs. Choose Y here only if you build for this SoC.
config EXYNOS_4_COMMON_CLK
bool "Samsung Exynos4 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos4212 and Exynos4412 SoCs. Choose Y here only if you build for
this SoC.
config EXYNOS_5250_COMMON_CLK
bool "Samsung Exynos5250 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos5250 SoCs. Choose Y here only if you build for this SoC.
config EXYNOS_5260_COMMON_CLK
bool "Samsung Exynos5260 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos5260 SoCs. Choose Y here only if you build for this SoC.
config EXYNOS_5410_COMMON_CLK
bool "Samsung Exynos5410 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos5410 SoCs. Choose Y here only if you build for this SoC.
config EXYNOS_5420_COMMON_CLK
bool "Samsung Exynos5420 clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
help
Support for the clock controller present on the Samsung
Exynos5420/Exynos5422/Exynos5800 SoCs. Choose Y here only if you
build for this SoC.
config EXYNOS_ARM64_COMMON_CLK
bool "Samsung Exynos ARMv8-family clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
config EXYNOS_AUDSS_CLK_CON
tristate "Samsung Exynos AUDSS clock controller support"
depends on COMMON_CLK_SAMSUNG
default y if ARCH_EXYNOS
help
Support for the Audio Subsystem CLKCON clock controller present
on some Samsung Exynos SoC variants. Choose M or Y here if you want
to use audio devices such as I2S, PCM, etc.
config EXYNOS_CLKOUT
tristate "Samsung Exynos clock output driver"
depends on COMMON_CLK_SAMSUNG
default y if ARCH_EXYNOS
help
Support for the clock output (XCLKOUT) present on some of Samsung
Exynos SoC variants. Usually the XCLKOUT is used to monitor the
status of the certains clocks from SoC, but it could also be tied to
other devices as an input clock.
config EXYNOS_ACPM_CLK
tristate "Clock driver controlled via ACPM interface"
depends on EXYNOS_ACPM_PROTOCOL
help
This driver provides support for clocks that are controlled by
firmware that implements the ACPM interface.
This driver uses the ACPM interface to interact with the firmware
providing all the clock controlls.
config TESLA_FSD_COMMON_CLK
bool "Tesla FSD clock controller support" if COMPILE_TEST
depends on COMMON_CLK_SAMSUNG
depends on EXYNOS_ARM64_COMMON_CLK
help
Support for the clock controller present on the Tesla FSD SoC.
Choose Y here only if you build for this SoC.
+35
View File
@@ -0,0 +1,35 @@
# SPDX-License-Identifier: GPL-2.0
#
# Samsung Clock specific Makefile
#
obj-$(CONFIG_COMMON_CLK) += clk.o clk-pll.o clk-cpu.o
obj-$(CONFIG_EXYNOS_3250_COMMON_CLK) += clk-exynos3250.o
obj-$(CONFIG_EXYNOS_4_COMMON_CLK) += clk-exynos4.o
obj-$(CONFIG_EXYNOS_4_COMMON_CLK) += clk-exynos4412-isp.o
obj-$(CONFIG_EXYNOS_5250_COMMON_CLK) += clk-exynos5250.o
obj-$(CONFIG_EXYNOS_5250_COMMON_CLK) += clk-exynos5-subcmu.o
obj-$(CONFIG_EXYNOS_5260_COMMON_CLK) += clk-exynos5260.o
obj-$(CONFIG_EXYNOS_5410_COMMON_CLK) += clk-exynos5410.o
obj-$(CONFIG_EXYNOS_5420_COMMON_CLK) += clk-exynos5420.o
obj-$(CONFIG_EXYNOS_5420_COMMON_CLK) += clk-exynos5-subcmu.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-artpec8.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-artpec9.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos5433.o
obj-$(CONFIG_EXYNOS_AUDSS_CLK_CON) += clk-exynos-audss.o
obj-$(CONFIG_EXYNOS_CLKOUT) += clk-exynos-clkout.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos-arm64.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos2200.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos7.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos7870.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos7885.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos850.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos8895.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynos990.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov9.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-exynosautov920.o
obj-$(CONFIG_EXYNOS_ARM64_COMMON_CLK) += clk-gs101.o
obj-$(CONFIG_EXYNOS_ACPM_CLK) += clk-acpm.o
obj-$(CONFIG_S3C64XX_COMMON_CLK) += clk-s3c64xx.o
obj-$(CONFIG_S5PV210_COMMON_CLK) += clk-s5pv210.o clk-s5pv210-audss.o
obj-$(CONFIG_TESLA_FSD_COMMON_CLK) += clk-fsd.o
+185
View File
@@ -0,0 +1,185 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Samsung Exynos ACPM protocol based clock driver.
*
* Copyright 2025 Linaro Ltd.
*/
#include <linux/array_size.h>
#include <linux/clk-provider.h>
#include <linux/container_of.h>
#include <linux/device/devres.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/firmware/samsung/exynos-acpm-protocol.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/types.h>
struct acpm_clk {
u32 id;
struct clk_hw hw;
unsigned int mbox_chan_id;
struct acpm_handle *handle;
};
struct acpm_clk_variant {
const char *name;
};
struct acpm_clk_driver_data {
const struct acpm_clk_variant *clks;
unsigned int nr_clks;
unsigned int mbox_chan_id;
};
#define to_acpm_clk(clk) container_of(clk, struct acpm_clk, hw)
#define ACPM_CLK(cname) \
{ \
.name = cname, \
}
static const struct acpm_clk_variant gs101_acpm_clks[] = {
ACPM_CLK("mif"),
ACPM_CLK("int"),
ACPM_CLK("cpucl0"),
ACPM_CLK("cpucl1"),
ACPM_CLK("cpucl2"),
ACPM_CLK("g3d"),
ACPM_CLK("g3dl2"),
ACPM_CLK("tpu"),
ACPM_CLK("intcam"),
ACPM_CLK("tnr"),
ACPM_CLK("cam"),
ACPM_CLK("mfc"),
ACPM_CLK("disp"),
ACPM_CLK("bo"),
};
static const struct acpm_clk_driver_data acpm_clk_gs101 = {
.clks = gs101_acpm_clks,
.nr_clks = ARRAY_SIZE(gs101_acpm_clks),
.mbox_chan_id = 0,
};
static unsigned long acpm_clk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct acpm_clk *clk = to_acpm_clk(hw);
return clk->handle->ops->dvfs.get_rate(clk->handle, clk->mbox_chan_id,
clk->id);
}
static int acpm_clk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
/*
* We can't figure out what rate it will be, so just return the
* rate back to the caller. acpm_clk_recalc_rate() will be called
* after the rate is set and we'll know what rate the clock is
* running at then.
*/
return 0;
}
static int acpm_clk_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct acpm_clk *clk = to_acpm_clk(hw);
return clk->handle->ops->dvfs.set_rate(clk->handle, clk->mbox_chan_id,
clk->id, rate);
}
static const struct clk_ops acpm_clk_ops = {
.recalc_rate = acpm_clk_recalc_rate,
.determine_rate = acpm_clk_determine_rate,
.set_rate = acpm_clk_set_rate,
};
static int acpm_clk_register(struct device *dev, struct acpm_clk *aclk,
const char *name)
{
struct clk_init_data init = {};
init.name = name;
init.ops = &acpm_clk_ops;
aclk->hw.init = &init;
return devm_clk_hw_register(dev, &aclk->hw);
}
static int acpm_clk_probe(struct platform_device *pdev)
{
struct acpm_handle *acpm_handle;
struct clk_hw_onecell_data *clk_data;
struct clk_hw **hws;
struct device *dev = &pdev->dev;
struct acpm_clk *aclks;
unsigned int mbox_chan_id;
int i, err, count;
acpm_handle = devm_acpm_get_by_node(dev, dev->parent->of_node);
if (IS_ERR(acpm_handle))
return dev_err_probe(dev, PTR_ERR(acpm_handle),
"Failed to get acpm handle\n");
count = acpm_clk_gs101.nr_clks;
mbox_chan_id = acpm_clk_gs101.mbox_chan_id;
clk_data = devm_kzalloc(dev, struct_size(clk_data, hws, count),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = count;
hws = clk_data->hws;
aclks = devm_kcalloc(dev, count, sizeof(*aclks), GFP_KERNEL);
if (!aclks)
return -ENOMEM;
for (i = 0; i < count; i++) {
struct acpm_clk *aclk = &aclks[i];
/*
* The code assumes the clock IDs start from zero,
* are sequential and do not have gaps.
*/
aclk->id = i;
aclk->handle = acpm_handle;
aclk->mbox_chan_id = mbox_chan_id;
hws[i] = &aclk->hw;
err = acpm_clk_register(dev, aclk,
acpm_clk_gs101.clks[i].name);
if (err)
return dev_err_probe(dev, err,
"Failed to register clock\n");
}
return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
clk_data);
}
static const struct platform_device_id acpm_clk_id[] = {
{ "gs101-acpm-clk" },
{}
};
MODULE_DEVICE_TABLE(platform, acpm_clk_id);
static struct platform_driver acpm_clk_driver = {
.driver = {
.name = "acpm-clocks",
},
.probe = acpm_clk_probe,
.id_table = acpm_clk_id,
};
module_platform_driver(acpm_clk_driver);
MODULE_AUTHOR("Tudor Ambarus <tudor.ambarus@linaro.org>");
MODULE_DESCRIPTION("Samsung Exynos ACPM clock driver");
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+727
View File
@@ -0,0 +1,727 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* Copyright (c) 2015 Samsung Electronics Co., Ltd.
* Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
*
* This file contains the utility function to register CPU clock for Samsung
* Exynos platforms. A CPU clock is defined as a clock supplied to a CPU or a
* group of CPUs. The CPU clock is typically derived from a hierarchy of clock
* blocks which includes mux and divider blocks. There are a number of other
* auxiliary clocks supplied to the CPU domain such as the debug blocks and AXI
* clock for CPU domain. The rates of these auxiliary clocks are related to the
* CPU clock rate and this relation is usually specified in the hardware manual
* of the SoC or supplied after the SoC characterization.
*
* The below implementation of the CPU clock allows the rate changes of the CPU
* clock and the corresponding rate changes of the auxiliary clocks of the CPU
* domain. The platform clock driver provides a clock register configuration
* for each configurable rate which is then used to program the clock hardware
* registers to achieve a fast coordinated rate change for all the CPU domain
* clocks.
*
* On a rate change request for the CPU clock, the rate change is propagated
* up to the PLL supplying the clock to the CPU domain clock blocks. While the
* CPU domain PLL is reconfigured, the CPU domain clocks are driven using an
* alternate clock source. If required, the alternate clock source is divided
* down in order to keep the output clock rate within the previous OPP limits.
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include "clk.h"
#include "clk-cpu.h"
struct exynos_cpuclk;
typedef int (*exynos_rate_change_fn_t)(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk);
/**
* struct exynos_cpuclk_regs - Register offsets for CPU related clocks
* @mux_sel: offset of CPU MUX_SEL register (for selecting MUX clock parent)
* @mux_stat: offset of CPU MUX_STAT register (for checking MUX clock status)
* @div_cpu0: offset of CPU DIV0 register (for modifying divider values)
* @div_cpu1: offset of CPU DIV1 register (for modifying divider values)
* @div_stat_cpu0: offset of CPU DIV0_STAT register (for checking DIV status)
* @div_stat_cpu1: offset of CPU DIV1_STAT register (for checking DIV status)
* @mux: offset of MUX register for choosing CPU clock source
* @divs: offsets of DIV registers (ACLK, ATCLK, PCLKDBG and PERIPHCLK)
*/
struct exynos_cpuclk_regs {
u32 mux_sel;
u32 mux_stat;
u32 div_cpu0;
u32 div_cpu1;
u32 div_stat_cpu0;
u32 div_stat_cpu1;
u32 mux;
u32 divs[4];
};
/**
* struct exynos_cpuclk_chip - Chip specific data for CPU clock
* @regs: register offsets for CPU related clocks
* @pre_rate_cb: callback to run before CPU clock rate change
* @post_rate_cb: callback to run after CPU clock rate change
*/
struct exynos_cpuclk_chip {
const struct exynos_cpuclk_regs *regs;
exynos_rate_change_fn_t pre_rate_cb;
exynos_rate_change_fn_t post_rate_cb;
};
/**
* struct exynos_cpuclk - information about clock supplied to a CPU core
* @hw: handle between CCF and CPU clock
* @alt_parent: alternate parent clock to use when switching the speed
* of the primary parent clock
* @base: start address of the CPU clock registers block
* @lock: cpu clock domain register access lock
* @cfg: cpu clock rate configuration data
* @num_cfgs: number of array elements in @cfg array
* @clk_nb: clock notifier registered for changes in clock speed of the
* primary parent clock
* @flags: configuration flags for the CPU clock
* @chip: chip-specific data for the CPU clock
*
* This structure holds information required for programming the CPU clock for
* various clock speeds.
*/
struct exynos_cpuclk {
struct clk_hw hw;
const struct clk_hw *alt_parent;
void __iomem *base;
spinlock_t *lock;
const struct exynos_cpuclk_cfg_data *cfg;
const unsigned long num_cfgs;
struct notifier_block clk_nb;
unsigned long flags;
const struct exynos_cpuclk_chip *chip;
};
/* ---- Common code --------------------------------------------------------- */
/* Divider stabilization time, msec */
#define MAX_STAB_TIME 10
#define MAX_DIV 8
#define DIV_MASK GENMASK(2, 0)
#define DIV_MASK_ALL GENMASK(31, 0)
#define MUX_MASK GENMASK(2, 0)
/*
* Helper function to wait until divider(s) have stabilized after the divider
* value has changed.
*/
static void wait_until_divider_stable(void __iomem *div_reg, unsigned long mask)
{
unsigned long timeout = jiffies + msecs_to_jiffies(MAX_STAB_TIME);
do {
if (!(readl(div_reg) & mask))
return;
} while (time_before(jiffies, timeout));
if (!(readl(div_reg) & mask))
return;
pr_err("%s: timeout in divider stabilization\n", __func__);
}
/*
* Helper function to wait until mux has stabilized after the mux selection
* value was changed.
*/
static void wait_until_mux_stable(void __iomem *mux_reg, u32 mux_pos,
unsigned long mask, unsigned long mux_value)
{
unsigned long timeout = jiffies + msecs_to_jiffies(MAX_STAB_TIME);
do {
if (((readl(mux_reg) >> mux_pos) & mask) == mux_value)
return;
} while (time_before(jiffies, timeout));
if (((readl(mux_reg) >> mux_pos) & mask) == mux_value)
return;
pr_err("%s: re-parenting mux timed-out\n", __func__);
}
/*
* Helper function to set the 'safe' dividers for the CPU clock. The parameters
* div and mask contain the divider value and the register bit mask of the
* dividers to be programmed.
*/
static void exynos_set_safe_div(struct exynos_cpuclk *cpuclk, unsigned long div,
unsigned long mask)
{
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
void __iomem *base = cpuclk->base;
unsigned long div0;
div0 = readl(base + regs->div_cpu0);
div0 = (div0 & ~mask) | (div & mask);
writel(div0, base + regs->div_cpu0);
wait_until_divider_stable(base + regs->div_stat_cpu0, mask);
}
/* ---- Exynos 3/4/5 -------------------------------------------------------- */
#define E4210_DIV0_RATIO0_MASK GENMASK(2, 0)
#define E4210_DIV1_HPM_MASK GENMASK(6, 4)
#define E4210_DIV1_COPY_MASK GENMASK(2, 0)
#define E4210_MUX_HPM_MASK BIT(20)
#define E4210_DIV0_ATB_SHIFT 16
#define E4210_DIV0_ATB_MASK (DIV_MASK << E4210_DIV0_ATB_SHIFT)
static const struct exynos_cpuclk_regs e4210_cpuclk_regs = {
.mux_sel = 0x200,
.mux_stat = 0x400,
.div_cpu0 = 0x500,
.div_cpu1 = 0x504,
.div_stat_cpu0 = 0x600,
.div_stat_cpu1 = 0x604,
};
/* handler for pre-rate change notification from parent clock */
static int exynos_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
void __iomem *base = cpuclk->base;
unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
unsigned long div0, div1 = 0, mux_reg;
unsigned long flags;
/* find out the divider values to use for clock data */
while ((cfg_data->prate * 1000) != ndata->new_rate) {
if (cfg_data->prate == 0)
return -EINVAL;
cfg_data++;
}
spin_lock_irqsave(cpuclk->lock, flags);
/*
* For the selected PLL clock frequency, get the pre-defined divider
* values. If the clock for sclk_hpm is not sourced from apll, then
* the values for DIV_COPY and DIV_HPM dividers need not be set.
*/
div0 = cfg_data->div0;
if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
div1 = cfg_data->div1;
if (readl(base + regs->mux_sel) & E4210_MUX_HPM_MASK)
div1 = readl(base + regs->div_cpu1) &
(E4210_DIV1_HPM_MASK | E4210_DIV1_COPY_MASK);
}
/*
* If the old parent clock speed is less than the clock speed of
* the alternate parent, then it should be ensured that at no point
* the armclk speed is more than the old_prate until the dividers are
* set. Also workaround the issue of the dividers being set to lower
* values before the parent clock speed is set to new lower speed
* (this can result in too high speed of armclk output clocks).
*/
if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
unsigned long alt_div, alt_div_mask = DIV_MASK;
alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
WARN_ON(alt_div >= MAX_DIV);
if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
/*
* In Exynos4210, ATB clock parent is also mout_core. So
* ATB clock also needs to be maintained at safe speed.
*/
alt_div |= E4210_DIV0_ATB_MASK;
alt_div_mask |= E4210_DIV0_ATB_MASK;
}
exynos_set_safe_div(cpuclk, alt_div, alt_div_mask);
div0 |= alt_div;
}
/* select sclk_mpll as the alternate parent */
mux_reg = readl(base + regs->mux_sel);
writel(mux_reg | (1 << 16), base + regs->mux_sel);
wait_until_mux_stable(base + regs->mux_stat, 16, MUX_MASK, 2);
/* alternate parent is active now. set the dividers */
writel(div0, base + regs->div_cpu0);
wait_until_divider_stable(base + regs->div_stat_cpu0, DIV_MASK_ALL);
if (cpuclk->flags & CLK_CPU_HAS_DIV1) {
writel(div1, base + regs->div_cpu1);
wait_until_divider_stable(base + regs->div_stat_cpu1,
DIV_MASK_ALL);
}
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
/* handler for post-rate change notification from parent clock */
static int exynos_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
void __iomem *base = cpuclk->base;
unsigned long div = 0, div_mask = DIV_MASK;
unsigned long mux_reg;
unsigned long flags;
/* find out the divider values to use for clock data */
if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
while ((cfg_data->prate * 1000) != ndata->new_rate) {
if (cfg_data->prate == 0)
return -EINVAL;
cfg_data++;
}
}
spin_lock_irqsave(cpuclk->lock, flags);
/* select mout_apll as the alternate parent */
mux_reg = readl(base + regs->mux_sel);
writel(mux_reg & ~(1 << 16), base + regs->mux_sel);
wait_until_mux_stable(base + regs->mux_stat, 16, MUX_MASK, 1);
if (cpuclk->flags & CLK_CPU_NEEDS_DEBUG_ALT_DIV) {
div |= (cfg_data->div0 & E4210_DIV0_ATB_MASK);
div_mask |= E4210_DIV0_ATB_MASK;
}
exynos_set_safe_div(cpuclk, div, div_mask);
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
/* ---- Exynos5433 ---------------------------------------------------------- */
static const struct exynos_cpuclk_regs e5433_cpuclk_regs = {
.mux_sel = 0x208,
.mux_stat = 0x408,
.div_cpu0 = 0x600,
.div_cpu1 = 0x604,
.div_stat_cpu0 = 0x700,
.div_stat_cpu1 = 0x704,
};
/* handler for pre-rate change notification from parent clock */
static int exynos5433_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
void __iomem *base = cpuclk->base;
unsigned long alt_prate = clk_hw_get_rate(cpuclk->alt_parent);
unsigned long div0, div1 = 0, mux_reg;
unsigned long flags;
/* find out the divider values to use for clock data */
while ((cfg_data->prate * 1000) != ndata->new_rate) {
if (cfg_data->prate == 0)
return -EINVAL;
cfg_data++;
}
spin_lock_irqsave(cpuclk->lock, flags);
/*
* For the selected PLL clock frequency, get the pre-defined divider
* values.
*/
div0 = cfg_data->div0;
div1 = cfg_data->div1;
/*
* If the old parent clock speed is less than the clock speed of
* the alternate parent, then it should be ensured that at no point
* the armclk speed is more than the old_prate until the dividers are
* set. Also workaround the issue of the dividers being set to lower
* values before the parent clock speed is set to new lower speed
* (this can result in too high speed of armclk output clocks).
*/
if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
unsigned long alt_div, alt_div_mask = DIV_MASK;
alt_div = DIV_ROUND_UP(alt_prate, tmp_rate) - 1;
WARN_ON(alt_div >= MAX_DIV);
exynos_set_safe_div(cpuclk, alt_div, alt_div_mask);
div0 |= alt_div;
}
/* select the alternate parent */
mux_reg = readl(base + regs->mux_sel);
writel(mux_reg | 1, base + regs->mux_sel);
wait_until_mux_stable(base + regs->mux_stat, 0, MUX_MASK, 2);
/* alternate parent is active now. set the dividers */
writel(div0, base + regs->div_cpu0);
wait_until_divider_stable(base + regs->div_stat_cpu0, DIV_MASK_ALL);
writel(div1, base + regs->div_cpu1);
wait_until_divider_stable(base + regs->div_stat_cpu1, DIV_MASK_ALL);
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
/* handler for post-rate change notification from parent clock */
static int exynos5433_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
void __iomem *base = cpuclk->base;
unsigned long div = 0, div_mask = DIV_MASK;
unsigned long mux_reg;
unsigned long flags;
spin_lock_irqsave(cpuclk->lock, flags);
/* select apll as the alternate parent */
mux_reg = readl(base + regs->mux_sel);
writel(mux_reg & ~1, base + regs->mux_sel);
wait_until_mux_stable(base + regs->mux_stat, 0, MUX_MASK, 1);
exynos_set_safe_div(cpuclk, div, div_mask);
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
/* ---- Exynos850 ----------------------------------------------------------- */
#define E850_DIV_RATIO_MASK GENMASK(3, 0)
#define E850_BUSY_MASK BIT(16)
/* Max time for divider or mux to stabilize, usec */
#define E850_DIV_MUX_STAB_TIME 100
/* OSCCLK clock rate, Hz */
#define E850_OSCCLK (26 * MHZ)
static const struct exynos_cpuclk_regs e850cl0_cpuclk_regs = {
.mux = 0x100c,
.divs = { 0x1800, 0x1808, 0x180c, 0x1810 },
};
static const struct exynos_cpuclk_regs e850cl1_cpuclk_regs = {
.mux = 0x1000,
.divs = { 0x1800, 0x1808, 0x180c, 0x1810 },
};
/*
* Set alternate parent rate to "rate" value or less.
*
* rate: Desired alt_parent rate, or 0 for max alt_parent rate
*
* Exynos850 doesn't have CPU clock divider in CMU_CPUCLx block (CMUREF divider
* doesn't affect CPU speed). So CPUCLx_SWITCH divider from CMU_TOP is used
* instead to adjust alternate parent speed.
*
* It's possible to use clk_set_max_rate() instead of this function, but it
* would set overly pessimistic rate values to alternate parent.
*/
static int exynos850_alt_parent_set_max_rate(const struct clk_hw *alt_parent,
unsigned long rate)
{
struct clk_hw *clk_div, *clk_divp;
unsigned long divp_rate, div_rate, div;
int ret;
/* Divider from CMU_TOP */
clk_div = clk_hw_get_parent(alt_parent);
if (!clk_div)
return -ENOENT;
/* Divider's parent from CMU_TOP */
clk_divp = clk_hw_get_parent(clk_div);
if (!clk_divp)
return -ENOENT;
/* Divider input rate */
divp_rate = clk_hw_get_rate(clk_divp);
if (!divp_rate)
return -EINVAL;
/* Calculate new alt_parent rate for integer divider value */
if (rate == 0)
div = 1;
else
div = DIV_ROUND_UP(divp_rate, rate);
div_rate = DIV_ROUND_UP(divp_rate, div);
WARN_ON(div >= MAX_DIV);
/* alt_parent will propagate this change up to the divider */
ret = clk_set_rate(alt_parent->clk, div_rate);
if (ret)
return ret;
udelay(E850_DIV_MUX_STAB_TIME);
return 0;
}
/* Handler for pre-rate change notification from parent clock */
static int exynos850_cpuclk_pre_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const unsigned int shifts[4] = { 16, 12, 8, 4 }; /* E850_CPU_DIV0() */
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
const struct exynos_cpuclk_cfg_data *cfg_data = cpuclk->cfg;
const struct clk_hw *alt_parent = cpuclk->alt_parent;
void __iomem *base = cpuclk->base;
unsigned long alt_prate = clk_hw_get_rate(alt_parent);
unsigned long flags;
u32 mux_reg;
size_t i;
int ret;
/* No actions are needed when switching to or from OSCCLK parent */
if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK)
return 0;
/* Find out the divider values to use for clock data */
while ((cfg_data->prate * 1000) != ndata->new_rate) {
if (cfg_data->prate == 0)
return -EINVAL;
cfg_data++;
}
/*
* If the old parent clock speed is less than the clock speed of
* the alternate parent, then it should be ensured that at no point
* the armclk speed is more than the old_prate until the dividers are
* set. Also workaround the issue of the dividers being set to lower
* values before the parent clock speed is set to new lower speed
* (this can result in too high speed of armclk output clocks).
*/
if (alt_prate > ndata->old_rate || ndata->old_rate > ndata->new_rate) {
unsigned long tmp_rate = min(ndata->old_rate, ndata->new_rate);
ret = exynos850_alt_parent_set_max_rate(alt_parent, tmp_rate);
if (ret)
return ret;
}
spin_lock_irqsave(cpuclk->lock, flags);
/* Select the alternate parent */
mux_reg = readl(base + regs->mux);
writel(mux_reg | 1, base + regs->mux);
wait_until_mux_stable(base + regs->mux, 16, 1, 0);
/* Alternate parent is active now. Set the dividers */
for (i = 0; i < ARRAY_SIZE(shifts); ++i) {
unsigned long div = (cfg_data->div0 >> shifts[i]) & 0xf;
u32 val;
val = readl(base + regs->divs[i]);
val = (val & ~E850_DIV_RATIO_MASK) | div;
writel(val, base + regs->divs[i]);
wait_until_divider_stable(base + regs->divs[i], E850_BUSY_MASK);
}
spin_unlock_irqrestore(cpuclk->lock, flags);
return 0;
}
/* Handler for post-rate change notification from parent clock */
static int exynos850_cpuclk_post_rate_change(struct clk_notifier_data *ndata,
struct exynos_cpuclk *cpuclk)
{
const struct exynos_cpuclk_regs * const regs = cpuclk->chip->regs;
const struct clk_hw *alt_parent = cpuclk->alt_parent;
void __iomem *base = cpuclk->base;
unsigned long flags;
u32 mux_reg;
/* No actions are needed when switching to or from OSCCLK parent */
if (ndata->new_rate == E850_OSCCLK || ndata->old_rate == E850_OSCCLK)
return 0;
spin_lock_irqsave(cpuclk->lock, flags);
/* Select main parent (PLL) for mux */
mux_reg = readl(base + regs->mux);
writel(mux_reg & ~1, base + regs->mux);
wait_until_mux_stable(base + regs->mux, 16, 1, 0);
spin_unlock_irqrestore(cpuclk->lock, flags);
/* Set alt_parent rate back to max */
return exynos850_alt_parent_set_max_rate(alt_parent, 0);
}
/* -------------------------------------------------------------------------- */
/* Common round rate callback usable for all types of CPU clocks */
static int exynos_cpuclk_determine_rate(struct clk_hw *hw,
struct clk_rate_request *req)
{
struct clk_hw *parent = clk_hw_get_parent(hw);
req->best_parent_rate = clk_hw_round_rate(parent, req->rate);
req->rate = req->best_parent_rate;
return 0;
}
/* Common recalc rate callback usable for all types of CPU clocks */
static unsigned long exynos_cpuclk_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
/*
* The CPU clock output (armclk) rate is the same as its parent
* rate. Although there exist certain dividers inside the CPU
* clock block that could be used to divide the parent clock,
* the driver does not make use of them currently, except during
* frequency transitions.
*/
return parent_rate;
}
static const struct clk_ops exynos_cpuclk_clk_ops = {
.recalc_rate = exynos_cpuclk_recalc_rate,
.determine_rate = exynos_cpuclk_determine_rate,
};
/*
* This notifier function is called for the pre-rate and post-rate change
* notifications of the parent clock of cpuclk.
*/
static int exynos_cpuclk_notifier_cb(struct notifier_block *nb,
unsigned long event, void *data)
{
struct clk_notifier_data *ndata = data;
struct exynos_cpuclk *cpuclk;
int err = 0;
cpuclk = container_of(nb, struct exynos_cpuclk, clk_nb);
if (event == PRE_RATE_CHANGE)
err = cpuclk->chip->pre_rate_cb(ndata, cpuclk);
else if (event == POST_RATE_CHANGE)
err = cpuclk->chip->post_rate_cb(ndata, cpuclk);
return notifier_from_errno(err);
}
static const struct exynos_cpuclk_chip exynos_clkcpu_chips[] = {
[CPUCLK_LAYOUT_E4210] = {
.regs = &e4210_cpuclk_regs,
.pre_rate_cb = exynos_cpuclk_pre_rate_change,
.post_rate_cb = exynos_cpuclk_post_rate_change,
},
[CPUCLK_LAYOUT_E5433] = {
.regs = &e5433_cpuclk_regs,
.pre_rate_cb = exynos5433_cpuclk_pre_rate_change,
.post_rate_cb = exynos5433_cpuclk_post_rate_change,
},
[CPUCLK_LAYOUT_E850_CL0] = {
.regs = &e850cl0_cpuclk_regs,
.pre_rate_cb = exynos850_cpuclk_pre_rate_change,
.post_rate_cb = exynos850_cpuclk_post_rate_change,
},
[CPUCLK_LAYOUT_E850_CL1] = {
.regs = &e850cl1_cpuclk_regs,
.pre_rate_cb = exynos850_cpuclk_pre_rate_change,
.post_rate_cb = exynos850_cpuclk_post_rate_change,
},
};
/* helper function to register a CPU clock */
static int __init exynos_register_cpu_clock(struct samsung_clk_provider *ctx,
const struct samsung_cpu_clock *clk_data)
{
const struct clk_hw *parent, *alt_parent;
struct clk_hw **hws;
struct exynos_cpuclk *cpuclk;
struct clk_init_data init;
const char *parent_name;
unsigned int num_cfgs;
int ret = 0;
hws = ctx->clk_data.hws;
parent = hws[clk_data->parent_id];
alt_parent = hws[clk_data->alt_parent_id];
if (IS_ERR(parent) || IS_ERR(alt_parent)) {
pr_err("%s: invalid parent clock(s)\n", __func__);
return -EINVAL;
}
cpuclk = kzalloc_obj(*cpuclk);
if (!cpuclk)
return -ENOMEM;
parent_name = clk_hw_get_name(parent);
init.name = clk_data->name;
init.flags = CLK_SET_RATE_PARENT;
init.parent_names = &parent_name;
init.num_parents = 1;
init.ops = &exynos_cpuclk_clk_ops;
cpuclk->alt_parent = alt_parent;
cpuclk->hw.init = &init;
cpuclk->base = ctx->reg_base + clk_data->offset;
cpuclk->lock = &ctx->lock;
cpuclk->flags = clk_data->flags;
cpuclk->clk_nb.notifier_call = exynos_cpuclk_notifier_cb;
cpuclk->chip = &exynos_clkcpu_chips[clk_data->reg_layout];
ret = clk_notifier_register(parent->clk, &cpuclk->clk_nb);
if (ret) {
pr_err("%s: failed to register clock notifier for %s\n",
__func__, clk_data->name);
goto free_cpuclk;
}
/* Find count of configuration rates in cfg */
for (num_cfgs = 0; clk_data->cfg[num_cfgs].prate != 0; )
num_cfgs++;
cpuclk->cfg = kmemdup_array(clk_data->cfg, num_cfgs, sizeof(*cpuclk->cfg),
GFP_KERNEL);
if (!cpuclk->cfg) {
ret = -ENOMEM;
goto unregister_clk_nb;
}
ret = clk_hw_register(NULL, &cpuclk->hw);
if (ret) {
pr_err("%s: could not register cpuclk %s\n", __func__,
clk_data->name);
goto free_cpuclk_data;
}
samsung_clk_add_lookup(ctx, &cpuclk->hw, clk_data->id);
return 0;
free_cpuclk_data:
kfree(cpuclk->cfg);
unregister_clk_nb:
clk_notifier_unregister(parent->clk, &cpuclk->clk_nb);
free_cpuclk:
kfree(cpuclk);
return ret;
}
void __init samsung_clk_register_cpu(struct samsung_clk_provider *ctx,
const struct samsung_cpu_clock *list, unsigned int nr_clk)
{
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++)
exynos_register_cpu_clock(ctx, &list[idx]);
}
+48
View File
@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
*
* Common Clock Framework support for all PLL's in Samsung platforms
*/
#ifndef __SAMSUNG_CLK_CPU_H
#define __SAMSUNG_CLK_CPU_H
/* The CPU clock registers have DIV1 configuration register */
#define CLK_CPU_HAS_DIV1 BIT(0)
/* When ALT parent is active, debug clocks need safe divider values */
#define CLK_CPU_NEEDS_DEBUG_ALT_DIV BIT(1)
/**
* enum exynos_cpuclk_layout - CPU clock registers layout compatibility
* @CPUCLK_LAYOUT_E4210: Exynos4210 compatible layout
* @CPUCLK_LAYOUT_E5433: Exynos5433 compatible layout
* @CPUCLK_LAYOUT_E850_CL0: Exynos850 cluster 0 compatible layout
* @CPUCLK_LAYOUT_E850_CL1: Exynos850 cluster 1 compatible layout
*/
enum exynos_cpuclk_layout {
CPUCLK_LAYOUT_E4210,
CPUCLK_LAYOUT_E5433,
CPUCLK_LAYOUT_E850_CL0,
CPUCLK_LAYOUT_E850_CL1,
};
/**
* struct exynos_cpuclk_cfg_data - config data to setup cpu clocks
* @prate: frequency of the primary parent clock (in KHz)
* @div0: value to be programmed in the div_cpu0 register
* @div1: value to be programmed in the div_cpu1 register
*
* This structure holds the divider configuration data for dividers in the CPU
* clock domain. The parent frequency at which these divider values are valid is
* specified in @prate. The @prate is the frequency of the primary parent clock.
* For CPU clock domains that do not have a DIV1 register, the @div1 member
* value is not used.
*/
struct exynos_cpuclk_cfg_data {
unsigned long prate;
unsigned long div0;
unsigned long div1;
};
#endif /* __SAMSUNG_CLK_CPU_H */
@@ -0,0 +1,382 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 Linaro Ltd.
* Copyright (C) 2021 Dávid Virág <virag.david003@gmail.com>
* Author: Sam Protsenko <semen.protsenko@linaro.org>
* Author: Dávid Virág <virag.david003@gmail.com>
*
* This file contains shared functions used by some arm64 Exynos SoCs,
* such as Exynos7885 or Exynos850 to register and init CMUs.
*/
#include <linux/clk.h>
#include <linux/of_address.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include "clk-exynos-arm64.h"
/* PLL register bits */
#define PLL_CON1_MANUAL BIT(1)
/* Gate register bits */
#define GATE_MANUAL BIT(20)
#define GATE_ENABLE_HWACG BIT(28)
/* Option register bits */
#define OPT_EN_MEM_PWR_GATING BIT(24)
#define OPT_EN_AUTO_GATING BIT(28)
#define OPT_EN_PWR_MANAGEMENT BIT(29)
#define OPT_EN_LAYER2_CTRL BIT(30)
#define OPT_EN_DBG BIT(31)
#define CMU_OPT_GLOBAL_EN_AUTO_GATING (OPT_EN_DBG | OPT_EN_LAYER2_CTRL | \
OPT_EN_PWR_MANAGEMENT | OPT_EN_AUTO_GATING | OPT_EN_MEM_PWR_GATING)
/* PLL_CONx_PLL register offsets range */
#define PLL_CON_OFF_START 0x100
#define PLL_CON_OFF_END 0x600
/* Gate register offsets range */
#define GATE_OFF_START 0x2000
#define GATE_OFF_END 0x2fff
struct exynos_arm64_cmu_data {
struct samsung_clk_reg_dump *clk_save;
unsigned int nr_clk_save;
const struct samsung_clk_reg_dump *clk_suspend;
unsigned int nr_clk_suspend;
struct samsung_clk_reg_dump *clk_sysreg_save;
unsigned int nr_clk_sysreg;
struct clk *clk;
struct clk **pclks;
int nr_pclks;
struct samsung_clk_provider *ctx;
};
/* Check if the register offset is a GATE register */
static bool is_gate_reg(unsigned long off)
{
return off >= GATE_OFF_START && off <= GATE_OFF_END;
}
/* Check if the register offset is a PLL_CONx register */
static bool is_pll_conx_reg(unsigned long off)
{
return off >= PLL_CON_OFF_START && off <= PLL_CON_OFF_END;
}
/* Check if the register offset is a PLL_CON1 register */
static bool is_pll_con1_reg(unsigned long off)
{
return is_pll_conx_reg(off) && (off & 0xf) == 0x4 && !(off & 0x10);
}
/**
* exynos_arm64_init_clocks - Set clocks initial configuration
* @np: CMU device tree node with "reg" property (CMU addr)
* @cmu: CMU data
*
* Set manual control mode for all gate and PLL clocks.
*/
static void __init exynos_arm64_init_clocks(struct device_node *np,
const struct samsung_cmu_info *cmu)
{
const unsigned long *reg_offs = cmu->clk_regs;
size_t reg_offs_len = cmu->nr_clk_regs;
void __iomem *reg_base;
bool init_auto;
size_t i;
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
/* ensure compatibility with older DTs */
if (cmu->auto_clock_gate && samsung_is_auto_capable(np))
init_auto = true;
else
init_auto = false;
if (cmu->option_offset && init_auto) {
/*
* Enable the global automatic mode for the entire CMU.
* This overrides the individual HWACG bits in each of the
* individual gate, mux and qch registers.
*/
writel(CMU_OPT_GLOBAL_EN_AUTO_GATING,
reg_base + cmu->option_offset);
}
for (i = 0; i < reg_offs_len; ++i) {
void __iomem *reg = reg_base + reg_offs[i];
u32 val;
if (cmu->manual_plls && is_pll_con1_reg(reg_offs[i])) {
writel(PLL_CON1_MANUAL, reg);
} else if (is_gate_reg(reg_offs[i]) && !init_auto) {
/*
* Setting GATE_MANUAL bit (which is described in TRM as
* reserved!) overrides the global CMU automatic mode
* option.
*/
val = readl(reg);
val |= GATE_MANUAL;
val &= ~GATE_ENABLE_HWACG;
writel(val, reg);
}
}
iounmap(reg_base);
}
/**
* exynos_arm64_enable_bus_clk - Enable parent clock of specified CMU
*
* @dev: Device object; may be NULL if this function is not being
* called from platform driver probe function
* @np: CMU device tree node
* @cmu: CMU data
*
* Keep CMU parent clock running (needed for CMU registers access).
*
* Return: 0 on success or a negative error code on failure.
*/
static int __init exynos_arm64_enable_bus_clk(struct device *dev,
struct device_node *np, const struct samsung_cmu_info *cmu)
{
struct clk *parent_clk;
if (!cmu->clk_name)
return 0;
if (dev) {
struct exynos_arm64_cmu_data *data;
parent_clk = clk_get(dev, cmu->clk_name);
data = dev_get_drvdata(dev);
if (data)
data->clk = parent_clk;
} else {
parent_clk = of_clk_get_by_name(np, cmu->clk_name);
}
if (IS_ERR(parent_clk))
return PTR_ERR(parent_clk);
return clk_prepare_enable(parent_clk);
}
static int __init exynos_arm64_cmu_prepare_pm(struct device *dev,
const struct samsung_cmu_info *cmu)
{
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
int i, ret;
data->clk_save = samsung_clk_alloc_reg_dump(cmu->clk_regs,
cmu->nr_clk_regs);
if (!data->clk_save)
return -ENOMEM;
data->nr_clk_save = cmu->nr_clk_regs;
if (cmu->nr_sysreg_clk_regs) {
data->clk_sysreg_save =
samsung_clk_alloc_reg_dump(cmu->sysreg_clk_regs,
cmu->nr_sysreg_clk_regs);
if (!data->clk_sysreg_save) {
ret = -ENOMEM;
goto free_clk_save;
}
data->nr_clk_sysreg = cmu->nr_sysreg_clk_regs;
}
data->clk_suspend = cmu->suspend_regs;
data->nr_clk_suspend = cmu->nr_suspend_regs;
data->nr_pclks = of_clk_get_parent_count(dev->of_node);
if (!data->nr_pclks)
return 0;
data->pclks = devm_kcalloc(dev, sizeof(struct clk *), data->nr_pclks,
GFP_KERNEL);
if (!data->pclks) {
ret = -ENOMEM;
goto free_sysreg_save;
}
for (i = 0; i < data->nr_pclks; i++) {
struct clk *clk = of_clk_get(dev->of_node, i);
if (IS_ERR(clk)) {
while (--i >= 0)
clk_put(data->pclks[i]);
ret = PTR_ERR(clk);
goto free_sysreg_save;
}
data->pclks[i] = clk;
}
return 0;
free_sysreg_save:
kfree(data->clk_sysreg_save);
free_clk_save:
kfree(data->clk_save);
return ret;
}
/**
* exynos_arm64_register_cmu - Register specified Exynos CMU domain
* @dev: Device object; may be NULL if this function is not being
* called from platform driver probe function
* @np: CMU device tree node
* @cmu: CMU data
*
* Register specified CMU domain, which includes next steps:
*
* 1. Enable parent clock of @cmu CMU
* 2. Set initial registers configuration for @cmu CMU clocks
* 3. Register @cmu CMU clocks using Samsung clock framework API
*/
void __init exynos_arm64_register_cmu(struct device *dev,
struct device_node *np, const struct samsung_cmu_info *cmu)
{
int err;
/*
* Try to boot even if the parent clock enablement fails, as it might be
* already enabled by bootloader.
*/
err = exynos_arm64_enable_bus_clk(dev, np, cmu);
if (err)
pr_err("%s: could not enable bus clock %s; err = %d\n",
__func__, cmu->clk_name, err);
exynos_arm64_init_clocks(np, cmu);
samsung_cmu_register_one(np, cmu);
}
/**
* exynos_arm64_register_cmu_pm - Register Exynos CMU domain with PM support
*
* @pdev: Platform device object
* @init_clk_regs: If true, initialize CMU registers
*
* It's a version of exynos_arm64_register_cmu() with PM support. Should be
* called from probe function of platform driver.
*
* Return: 0 on success, or negative error code on error.
*/
int __init exynos_arm64_register_cmu_pm(struct platform_device *pdev,
bool init_clk_regs)
{
const struct samsung_cmu_info *cmu;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
struct exynos_arm64_cmu_data *data;
void __iomem *reg_base;
int ret;
cmu = of_device_get_match_data(dev);
data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
platform_set_drvdata(pdev, data);
ret = exynos_arm64_cmu_prepare_pm(dev, cmu);
if (ret)
return ret;
/*
* Try to boot even if the parent clock enablement fails, as it might be
* already enabled by bootloader.
*/
ret = exynos_arm64_enable_bus_clk(dev, NULL, cmu);
if (ret)
dev_err(dev, "%s: could not enable bus clock %s; err = %d\n",
__func__, cmu->clk_name, ret);
if (init_clk_regs)
exynos_arm64_init_clocks(np, cmu);
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
data->ctx = samsung_clk_init(dev, reg_base, cmu->nr_clk_ids);
/*
* Enable runtime PM here to allow the clock core using runtime PM
* for the registered clocks. Additionally, we increase the runtime
* PM usage count before registering the clocks, to prevent the
* clock core from runtime suspending the device.
*/
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
samsung_cmu_register_clocks(data->ctx, cmu, np);
samsung_clk_of_add_provider(dev->of_node, data->ctx);
/* sysreg DT nodes reference a clock in this CMU */
samsung_en_dyn_root_clk_gating(np, data->ctx, cmu, true);
pm_runtime_put_sync(dev);
return 0;
}
int exynos_arm64_cmu_suspend(struct device *dev)
{
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
int i;
samsung_clk_save(data->ctx->reg_base, NULL, data->clk_save,
data->nr_clk_save);
samsung_clk_save(NULL, data->ctx->sysreg, data->clk_sysreg_save,
data->nr_clk_sysreg);
for (i = 0; i < data->nr_pclks; i++)
clk_prepare_enable(data->pclks[i]);
/* For suspend some registers have to be set to certain values */
samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_suspend,
data->nr_clk_suspend);
for (i = 0; i < data->nr_pclks; i++)
clk_disable_unprepare(data->pclks[i]);
clk_disable_unprepare(data->clk);
return 0;
}
int exynos_arm64_cmu_resume(struct device *dev)
{
struct exynos_arm64_cmu_data *data = dev_get_drvdata(dev);
int i;
clk_prepare_enable(data->clk);
for (i = 0; i < data->nr_pclks; i++)
clk_prepare_enable(data->pclks[i]);
samsung_clk_restore(data->ctx->reg_base, NULL, data->clk_save,
data->nr_clk_save);
if (data->ctx->sysreg)
samsung_clk_restore(NULL, data->ctx->sysreg,
data->clk_sysreg_save,
data->nr_clk_sysreg);
for (i = 0; i < data->nr_pclks; i++)
clk_disable_unprepare(data->pclks[i]);
return 0;
}
@@ -0,0 +1,23 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2021 Linaro Ltd.
* Copyright (C) 2021 Dávid Virág <virag.david003@gmail.com>
* Author: Sam Protsenko <semen.protsenko@linaro.org>
* Author: Dávid Virág <virag.david003@gmail.com>
*
* This file contains shared functions used by some arm64 Exynos SoCs,
* such as Exynos7885 or Exynos850 to register and init CMUs.
*/
#ifndef __CLK_EXYNOS_ARM64_H
#define __CLK_EXYNOS_ARM64_H
#include "clk.h"
void exynos_arm64_register_cmu(struct device *dev,
struct device_node *np, const struct samsung_cmu_info *cmu);
int exynos_arm64_register_cmu_pm(struct platform_device *pdev, bool set_manual);
int exynos_arm64_cmu_suspend(struct device *dev);
int exynos_arm64_cmu_resume(struct device *dev);
#endif /* __CLK_EXYNOS_ARM64_H */
@@ -0,0 +1,303 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Author: Padmavathi Venna <padma.v@samsung.com>
*
* Common Clock Framework support for Audio Subsystem Clock Controller.
*/
#include <linux/slab.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <dt-bindings/clock/exynos-audss-clk.h>
static DEFINE_SPINLOCK(lock);
static void __iomem *reg_base;
static struct clk_hw_onecell_data *clk_data;
/*
* On Exynos5420 this will be a clock which has to be enabled before any
* access to audss registers. Typically a child of EPLL.
*
* On other platforms this will be -ENODEV.
*/
static struct clk *epll;
#define ASS_CLK_SRC 0x0
#define ASS_CLK_DIV 0x4
#define ASS_CLK_GATE 0x8
static unsigned long reg_save[][2] = {
{ ASS_CLK_SRC, 0 },
{ ASS_CLK_DIV, 0 },
{ ASS_CLK_GATE, 0 },
};
static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(reg_save); i++)
reg_save[i][1] = readl(reg_base + reg_save[i][0]);
return 0;
}
static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
{
int i;
for (i = 0; i < ARRAY_SIZE(reg_save); i++)
writel(reg_save[i][1], reg_base + reg_save[i][0]);
return 0;
}
struct exynos_audss_clk_drvdata {
unsigned int has_adma_clk:1;
unsigned int has_mst_clk:1;
unsigned int enable_epll:1;
unsigned int num_clks;
};
static const struct exynos_audss_clk_drvdata exynos4210_drvdata = {
.num_clks = EXYNOS_AUDSS_MAX_CLKS - 1,
.enable_epll = 1,
};
static const struct exynos_audss_clk_drvdata exynos5410_drvdata = {
.num_clks = EXYNOS_AUDSS_MAX_CLKS - 1,
.has_mst_clk = 1,
};
static const struct exynos_audss_clk_drvdata exynos5420_drvdata = {
.num_clks = EXYNOS_AUDSS_MAX_CLKS,
.has_adma_clk = 1,
.enable_epll = 1,
};
static const struct of_device_id exynos_audss_clk_of_match[] = {
{
.compatible = "samsung,exynos4210-audss-clock",
.data = &exynos4210_drvdata,
}, {
.compatible = "samsung,exynos5250-audss-clock",
.data = &exynos4210_drvdata,
}, {
.compatible = "samsung,exynos5410-audss-clock",
.data = &exynos5410_drvdata,
}, {
.compatible = "samsung,exynos5420-audss-clock",
.data = &exynos5420_drvdata,
},
{ },
};
MODULE_DEVICE_TABLE(of, exynos_audss_clk_of_match);
static void exynos_audss_clk_teardown(void)
{
int i;
for (i = EXYNOS_MOUT_AUDSS; i < EXYNOS_DOUT_SRP; i++) {
if (!IS_ERR(clk_data->hws[i]))
clk_hw_unregister_mux(clk_data->hws[i]);
}
for (; i < EXYNOS_SRP_CLK; i++) {
if (!IS_ERR(clk_data->hws[i]))
clk_hw_unregister_divider(clk_data->hws[i]);
}
for (; i < clk_data->num; i++) {
if (!IS_ERR(clk_data->hws[i]))
clk_hw_unregister_gate(clk_data->hws[i]);
}
}
/* register exynos_audss clocks */
static int exynos_audss_clk_probe(struct platform_device *pdev)
{
const char *mout_audss_p[] = {"fin_pll", "fout_epll"};
const char *mout_i2s_p[] = {"mout_audss", "cdclk0", "sclk_audio0"};
const char *sclk_pcm_p = "sclk_pcm0";
struct clk *pll_ref, *pll_in, *cdclk, *sclk_audio, *sclk_pcm_in;
const struct exynos_audss_clk_drvdata *variant;
struct clk_hw **clk_table;
struct device *dev = &pdev->dev;
int i, ret = 0;
variant = of_device_get_match_data(&pdev->dev);
if (!variant)
return -EINVAL;
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
epll = ERR_PTR(-ENODEV);
clk_data = devm_kzalloc(dev,
struct_size(clk_data, hws,
EXYNOS_AUDSS_MAX_CLKS),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = variant->num_clks;
clk_table = clk_data->hws;
pll_ref = devm_clk_get(dev, "pll_ref");
pll_in = devm_clk_get(dev, "pll_in");
if (!IS_ERR(pll_ref))
mout_audss_p[0] = __clk_get_name(pll_ref);
if (!IS_ERR(pll_in)) {
mout_audss_p[1] = __clk_get_name(pll_in);
if (variant->enable_epll) {
epll = pll_in;
ret = clk_prepare_enable(epll);
if (ret) {
dev_err(dev,
"failed to prepare the epll clock\n");
return ret;
}
}
}
/*
* Enable runtime PM here to allow the clock core using runtime PM
* for the registered clocks. Additionally, we increase the runtime
* PM usage count before registering the clocks, to prevent the
* clock core from runtime suspending the device.
*/
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(dev, "mout_audss",
mout_audss_p, ARRAY_SIZE(mout_audss_p),
CLK_SET_RATE_NO_REPARENT | CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
cdclk = devm_clk_get(dev, "cdclk");
sclk_audio = devm_clk_get(dev, "sclk_audio");
if (!IS_ERR(cdclk))
mout_i2s_p[1] = __clk_get_name(cdclk);
if (!IS_ERR(sclk_audio))
mout_i2s_p[2] = __clk_get_name(sclk_audio);
clk_table[EXYNOS_MOUT_I2S] = clk_hw_register_mux(dev, "mout_i2s",
mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
CLK_SET_RATE_NO_REPARENT,
reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
clk_table[EXYNOS_DOUT_SRP] = clk_hw_register_divider(dev, "dout_srp",
"mout_audss", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_DIV, 0, 4, 0, &lock);
clk_table[EXYNOS_DOUT_AUD_BUS] = clk_hw_register_divider(dev,
"dout_aud_bus", "dout_srp", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
clk_table[EXYNOS_DOUT_I2S] = clk_hw_register_divider(dev, "dout_i2s",
"mout_i2s", 0, reg_base + ASS_CLK_DIV, 8, 4, 0,
&lock);
clk_table[EXYNOS_SRP_CLK] = clk_hw_register_gate(dev, "srp_clk",
"dout_srp", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 0, 0, &lock);
clk_table[EXYNOS_I2S_BUS] = clk_hw_register_gate(dev, "i2s_bus",
"dout_aud_bus", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 2, 0, &lock);
clk_table[EXYNOS_SCLK_I2S] = clk_hw_register_gate(dev, "sclk_i2s",
"dout_i2s", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 3, 0, &lock);
clk_table[EXYNOS_PCM_BUS] = clk_hw_register_gate(dev, "pcm_bus",
"sclk_pcm", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 4, 0, &lock);
sclk_pcm_in = devm_clk_get(dev, "sclk_pcm_in");
if (!IS_ERR(sclk_pcm_in))
sclk_pcm_p = __clk_get_name(sclk_pcm_in);
clk_table[EXYNOS_SCLK_PCM] = clk_hw_register_gate(dev, "sclk_pcm",
sclk_pcm_p, CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 5, 0, &lock);
if (variant->has_adma_clk) {
clk_table[EXYNOS_ADMA] = clk_hw_register_gate(dev, "adma",
"dout_srp", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 9, 0, &lock);
}
for (i = 0; i < clk_data->num; i++) {
if (IS_ERR(clk_table[i])) {
dev_err(dev, "failed to register clock %d\n", i);
ret = PTR_ERR(clk_table[i]);
goto unregister;
}
}
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
clk_data);
if (ret) {
dev_err(dev, "failed to add clock provider\n");
goto unregister;
}
pm_runtime_put_sync(dev);
return 0;
unregister:
exynos_audss_clk_teardown();
pm_runtime_put_sync(dev);
pm_runtime_disable(dev);
if (!IS_ERR(epll))
clk_disable_unprepare(epll);
return ret;
}
static void exynos_audss_clk_remove(struct platform_device *pdev)
{
of_clk_del_provider(pdev->dev.of_node);
exynos_audss_clk_teardown();
pm_runtime_disable(&pdev->dev);
if (!IS_ERR(epll))
clk_disable_unprepare(epll);
}
static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
SET_RUNTIME_PM_OPS(exynos_audss_clk_suspend, exynos_audss_clk_resume,
NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver exynos_audss_clk_driver = {
.driver = {
.name = "exynos-audss-clk",
.of_match_table = exynos_audss_clk_of_match,
.pm = &exynos_audss_clk_pm_ops,
},
.probe = exynos_audss_clk_probe,
.remove = exynos_audss_clk_remove,
};
module_platform_driver(exynos_audss_clk_driver);
MODULE_AUTHOR("Padmavathi Venna <padma.v@samsung.com>");
MODULE_DESCRIPTION("Exynos Audio Subsystem Clock Controller");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:exynos-audss-clk");
@@ -0,0 +1,252 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Tomasz Figa <t.figa@samsung.com>
*
* Clock driver for Exynos clock output
*/
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/module.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm.h>
#define DRV_NAME "exynos-clkout"
#define EXYNOS_CLKOUT_NR_CLKS 1
#define EXYNOS_CLKOUT_PARENTS 32
#define EXYNOS_PMU_DEBUG_REG 0xa00
#define EXYNOS_CLKOUT_DISABLE_SHIFT 0
#define EXYNOS_CLKOUT_MUX_SHIFT 8
#define EXYNOS4_CLKOUT_MUX_MASK 0xf
#define EXYNOS5_CLKOUT_MUX_MASK 0x1f
struct exynos_clkout {
struct clk_gate gate;
struct clk_mux mux;
spinlock_t slock;
void __iomem *reg;
struct device_node *np;
u32 pmu_debug_save;
struct clk_hw_onecell_data data;
};
struct exynos_clkout_variant {
u32 mux_mask;
};
static const struct exynos_clkout_variant exynos_clkout_exynos4 = {
.mux_mask = EXYNOS4_CLKOUT_MUX_MASK,
};
static const struct exynos_clkout_variant exynos_clkout_exynos5 = {
.mux_mask = EXYNOS5_CLKOUT_MUX_MASK,
};
static const struct of_device_id exynos_clkout_ids[] = {
{
.compatible = "samsung,exynos3250-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos4210-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos4212-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos4412-pmu",
.data = &exynos_clkout_exynos4,
}, {
.compatible = "samsung,exynos5250-pmu",
.data = &exynos_clkout_exynos5,
}, {
.compatible = "samsung,exynos5410-pmu",
.data = &exynos_clkout_exynos5,
}, {
.compatible = "samsung,exynos5420-pmu",
.data = &exynos_clkout_exynos5,
}, {
.compatible = "samsung,exynos5433-pmu",
.data = &exynos_clkout_exynos5,
}, { }
};
/*
* Device will be instantiated as child of PMU device without its own
* device node. Therefore match compatibles against parent.
*/
static int exynos_clkout_match_parent_dev(struct device *dev, u32 *mux_mask)
{
const struct exynos_clkout_variant *variant;
const struct of_device_id *match;
if (!dev->parent) {
dev_err(dev, "not instantiated from MFD\n");
return -EINVAL;
}
/*
* 'exynos_clkout_ids' arrays is not the ids array matched by
* the dev->parent driver, so of_device_get_match_data() or
* device_get_match_data() cannot be used here.
*/
match = of_match_device(exynos_clkout_ids, dev->parent);
if (!match) {
dev_err(dev, "cannot match parent device\n");
return -EINVAL;
}
variant = match->data;
*mux_mask = variant->mux_mask;
return 0;
}
static int exynos_clkout_probe(struct platform_device *pdev)
{
const char *parent_names[EXYNOS_CLKOUT_PARENTS];
struct clk *parents[EXYNOS_CLKOUT_PARENTS];
struct exynos_clkout *clkout;
int parent_count, ret, i;
u32 mux_mask;
clkout = devm_kzalloc(&pdev->dev,
struct_size(clkout, data.hws, EXYNOS_CLKOUT_NR_CLKS),
GFP_KERNEL);
if (!clkout)
return -ENOMEM;
ret = exynos_clkout_match_parent_dev(&pdev->dev, &mux_mask);
if (ret)
return ret;
clkout->np = pdev->dev.of_node;
if (!clkout->np) {
/*
* pdev->dev.parent was checked by exynos_clkout_match_parent_dev()
* so it is not NULL.
*/
clkout->np = pdev->dev.parent->of_node;
}
platform_set_drvdata(pdev, clkout);
spin_lock_init(&clkout->slock);
parent_count = 0;
for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) {
char name[] = "clkoutXX";
snprintf(name, sizeof(name), "clkout%d", i);
parents[i] = of_clk_get_by_name(clkout->np, name);
if (IS_ERR(parents[i])) {
parent_names[i] = "none";
continue;
}
parent_names[i] = __clk_get_name(parents[i]);
parent_count = i + 1;
}
if (!parent_count)
return -EINVAL;
clkout->reg = of_iomap(clkout->np, 0);
if (!clkout->reg) {
ret = -ENODEV;
goto clks_put;
}
clkout->gate.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG;
clkout->gate.bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT;
clkout->gate.flags = CLK_GATE_SET_TO_DISABLE;
clkout->gate.lock = &clkout->slock;
clkout->mux.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG;
clkout->mux.mask = mux_mask;
clkout->mux.shift = EXYNOS_CLKOUT_MUX_SHIFT;
clkout->mux.lock = &clkout->slock;
clkout->data.num = EXYNOS_CLKOUT_NR_CLKS;
clkout->data.hws[0] = clk_hw_register_composite(NULL, "clkout",
parent_names, parent_count, &clkout->mux.hw,
&clk_mux_ops, NULL, NULL, &clkout->gate.hw,
&clk_gate_ops, CLK_SET_RATE_PARENT
| CLK_SET_RATE_NO_REPARENT);
if (IS_ERR(clkout->data.hws[0])) {
ret = PTR_ERR(clkout->data.hws[0]);
goto err_unmap;
}
ret = of_clk_add_hw_provider(clkout->np, of_clk_hw_onecell_get, &clkout->data);
if (ret)
goto err_clk_unreg;
return 0;
err_clk_unreg:
clk_hw_unregister(clkout->data.hws[0]);
err_unmap:
iounmap(clkout->reg);
clks_put:
for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i)
if (!IS_ERR(parents[i]))
clk_put(parents[i]);
dev_err(&pdev->dev, "failed to register clkout clock\n");
return ret;
}
static void exynos_clkout_remove(struct platform_device *pdev)
{
struct exynos_clkout *clkout = platform_get_drvdata(pdev);
of_clk_del_provider(clkout->np);
clk_hw_unregister(clkout->data.hws[0]);
iounmap(clkout->reg);
}
static int __maybe_unused exynos_clkout_suspend(struct device *dev)
{
struct exynos_clkout *clkout = dev_get_drvdata(dev);
clkout->pmu_debug_save = readl(clkout->reg + EXYNOS_PMU_DEBUG_REG);
return 0;
}
static int __maybe_unused exynos_clkout_resume(struct device *dev)
{
struct exynos_clkout *clkout = dev_get_drvdata(dev);
writel(clkout->pmu_debug_save, clkout->reg + EXYNOS_PMU_DEBUG_REG);
return 0;
}
static SIMPLE_DEV_PM_OPS(exynos_clkout_pm_ops, exynos_clkout_suspend,
exynos_clkout_resume);
static struct platform_driver exynos_clkout_driver = {
.driver = {
.name = DRV_NAME,
.pm = &exynos_clkout_pm_ops,
},
.probe = exynos_clkout_probe,
.remove = exynos_clkout_remove,
};
module_platform_driver(exynos_clkout_driver);
MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>");
MODULE_DESCRIPTION("Samsung Exynos clock output driver");
MODULE_ALIAS("platform:" DRV_NAME);
MODULE_LICENSE("GPL");
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,171 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
* Author: Marek Szyprowski <m.szyprowski@samsung.com>
*
* Common Clock Framework support for Exynos4412 ISP module.
*/
#include <dt-bindings/clock/exynos4.h>
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include "clk.h"
/* Exynos4x12 specific registers, which belong to ISP power domain */
#define E4X12_DIV_ISP0 0x0300
#define E4X12_DIV_ISP1 0x0304
#define E4X12_GATE_ISP0 0x0800
#define E4X12_GATE_ISP1 0x0804
/* NOTE: Must be equal to the last clock ID increased by one */
#define CLKS_NR_ISP (CLK_ISP_DIV_MCUISP1 + 1)
/*
* Support for CMU save/restore across system suspends
*/
static struct samsung_clk_reg_dump *exynos4x12_save_isp;
static const unsigned long exynos4x12_clk_isp_save[] __initconst = {
E4X12_DIV_ISP0,
E4X12_DIV_ISP1,
E4X12_GATE_ISP0,
E4X12_GATE_ISP1,
};
static struct samsung_div_clock exynos4x12_isp_div_clks[] = {
DIV(CLK_ISP_DIV_ISP0, "div_isp0", "aclk200", E4X12_DIV_ISP0, 0, 3),
DIV(CLK_ISP_DIV_ISP1, "div_isp1", "aclk200", E4X12_DIV_ISP0, 4, 3),
DIV(CLK_ISP_DIV_MCUISP0, "div_mcuisp0", "aclk400_mcuisp",
E4X12_DIV_ISP1, 4, 3),
DIV(CLK_ISP_DIV_MCUISP1, "div_mcuisp1", "div_mcuisp0",
E4X12_DIV_ISP1, 8, 3),
DIV(0, "div_mpwm", "div_isp1", E4X12_DIV_ISP1, 0, 3),
};
static struct samsung_gate_clock exynos4x12_isp_gate_clks[] = {
GATE(CLK_ISP_FIMC_ISP, "isp", "aclk200", E4X12_GATE_ISP0, 0, 0, 0),
GATE(CLK_ISP_FIMC_DRC, "drc", "aclk200", E4X12_GATE_ISP0, 1, 0, 0),
GATE(CLK_ISP_FIMC_FD, "fd", "aclk200", E4X12_GATE_ISP0, 2, 0, 0),
GATE(CLK_ISP_FIMC_LITE0, "lite0", "aclk200", E4X12_GATE_ISP0, 3, 0, 0),
GATE(CLK_ISP_FIMC_LITE1, "lite1", "aclk200", E4X12_GATE_ISP0, 4, 0, 0),
GATE(CLK_ISP_MCUISP, "mcuisp", "aclk200", E4X12_GATE_ISP0, 5, 0, 0),
GATE(CLK_ISP_GICISP, "gicisp", "aclk200", E4X12_GATE_ISP0, 7, 0, 0),
GATE(CLK_ISP_SMMU_ISP, "smmu_isp", "aclk200", E4X12_GATE_ISP0, 8, 0, 0),
GATE(CLK_ISP_SMMU_DRC, "smmu_drc", "aclk200", E4X12_GATE_ISP0, 9, 0, 0),
GATE(CLK_ISP_SMMU_FD, "smmu_fd", "aclk200", E4X12_GATE_ISP0, 10, 0, 0),
GATE(CLK_ISP_SMMU_LITE0, "smmu_lite0", "aclk200", E4X12_GATE_ISP0, 11,
0, 0),
GATE(CLK_ISP_SMMU_LITE1, "smmu_lite1", "aclk200", E4X12_GATE_ISP0, 12,
0, 0),
GATE(CLK_ISP_PPMUISPMX, "ppmuispmx", "aclk200", E4X12_GATE_ISP0, 20,
0, 0),
GATE(CLK_ISP_PPMUISPX, "ppmuispx", "aclk200", E4X12_GATE_ISP0, 21,
0, 0),
GATE(CLK_ISP_MCUCTL_ISP, "mcuctl_isp", "aclk200", E4X12_GATE_ISP0, 23,
0, 0),
GATE(CLK_ISP_MPWM_ISP, "mpwm_isp", "aclk200", E4X12_GATE_ISP0, 24,
0, 0),
GATE(CLK_ISP_I2C0_ISP, "i2c0_isp", "aclk200", E4X12_GATE_ISP0, 25,
0, 0),
GATE(CLK_ISP_I2C1_ISP, "i2c1_isp", "aclk200", E4X12_GATE_ISP0, 26,
0, 0),
GATE(CLK_ISP_MTCADC_ISP, "mtcadc_isp", "aclk200", E4X12_GATE_ISP0, 27,
0, 0),
GATE(CLK_ISP_PWM_ISP, "pwm_isp", "aclk200", E4X12_GATE_ISP0, 28, 0, 0),
GATE(CLK_ISP_WDT_ISP, "wdt_isp", "aclk200", E4X12_GATE_ISP0, 30, 0, 0),
GATE(CLK_ISP_UART_ISP, "uart_isp", "aclk200", E4X12_GATE_ISP0, 31,
0, 0),
GATE(CLK_ISP_ASYNCAXIM, "asyncaxim", "aclk200", E4X12_GATE_ISP1, 0,
0, 0),
GATE(CLK_ISP_SMMU_ISPCX, "smmu_ispcx", "aclk200", E4X12_GATE_ISP1, 4,
0, 0),
GATE(CLK_ISP_SPI0_ISP, "spi0_isp", "aclk200", E4X12_GATE_ISP1, 12,
0, 0),
GATE(CLK_ISP_SPI1_ISP, "spi1_isp", "aclk200", E4X12_GATE_ISP1, 13,
0, 0),
};
static int __maybe_unused exynos4x12_isp_clk_suspend(struct device *dev)
{
struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
samsung_clk_save(ctx->reg_base, NULL, exynos4x12_save_isp,
ARRAY_SIZE(exynos4x12_clk_isp_save));
return 0;
}
static int __maybe_unused exynos4x12_isp_clk_resume(struct device *dev)
{
struct samsung_clk_provider *ctx = dev_get_drvdata(dev);
samsung_clk_restore(ctx->reg_base, NULL, exynos4x12_save_isp,
ARRAY_SIZE(exynos4x12_clk_isp_save));
return 0;
}
static int __init exynos4x12_isp_clk_probe(struct platform_device *pdev)
{
struct samsung_clk_provider *ctx;
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
void __iomem *reg_base;
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
exynos4x12_save_isp = samsung_clk_alloc_reg_dump(exynos4x12_clk_isp_save,
ARRAY_SIZE(exynos4x12_clk_isp_save));
if (!exynos4x12_save_isp)
return -ENOMEM;
ctx = samsung_clk_init(dev, reg_base, CLKS_NR_ISP);
platform_set_drvdata(pdev, ctx);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
samsung_clk_register_div(ctx, exynos4x12_isp_div_clks,
ARRAY_SIZE(exynos4x12_isp_div_clks));
samsung_clk_register_gate(ctx, exynos4x12_isp_gate_clks,
ARRAY_SIZE(exynos4x12_isp_gate_clks));
samsung_clk_of_add_provider(np, ctx);
pm_runtime_put(dev);
return 0;
}
static const struct of_device_id exynos4x12_isp_clk_of_match[] = {
{ .compatible = "samsung,exynos4412-isp-clock", },
{ },
};
static const struct dev_pm_ops exynos4x12_isp_pm_ops = {
SET_RUNTIME_PM_OPS(exynos4x12_isp_clk_suspend,
exynos4x12_isp_clk_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver exynos4x12_isp_clk_driver __refdata = {
.driver = {
.name = "exynos4x12-isp-clk",
.of_match_table = exynos4x12_isp_clk_of_match,
.suppress_bind_attrs = true,
.pm = &exynos4x12_isp_pm_ops,
},
.probe = exynos4x12_isp_clk_probe,
};
static int __init exynos4x12_isp_clk_init(void)
{
return platform_driver_register(&exynos4x12_isp_clk_driver);
}
core_initcall(exynos4x12_isp_clk_init);
@@ -0,0 +1,195 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (c) 2018 Samsung Electronics Co., Ltd.
// Author: Marek Szyprowski <m.szyprowski@samsung.com>
// Common Clock Framework support for Exynos5 power-domain dependent clocks
#include <linux/io.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include "clk.h"
#include "clk-exynos5-subcmu.h"
static struct samsung_clk_provider *ctx;
static const struct exynos5_subcmu_info **cmu;
static int nr_cmus;
static void exynos5_subcmu_clk_save(void __iomem *base,
struct exynos5_subcmu_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd) {
rd->save = readl(base + rd->offset);
writel((rd->save & ~rd->mask) | rd->value, base + rd->offset);
rd->save &= rd->mask;
}
};
static void exynos5_subcmu_clk_restore(void __iomem *base,
struct exynos5_subcmu_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd)
writel((readl(base + rd->offset) & ~rd->mask) | rd->save,
base + rd->offset);
}
static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx,
const struct samsung_gate_clock *list, int nr_clk)
{
while (nr_clk--)
samsung_clk_add_lookup(ctx, ERR_PTR(-EPROBE_DEFER), list++->id);
}
/*
* Pass the needed clock provider context and register sub-CMU clocks
*
* NOTE: This function has to be called from the main, CLK_OF_DECLARE-
* initialized clock provider driver. This happens very early during boot
* process. Then this driver, during core_initcall registers two platform
* drivers: one which binds to the same device-tree node as CLK_OF_DECLARE
* driver and second, for handling its per-domain child-devices. Those
* platform drivers are bound to their devices a bit later in arch_initcall,
* when OF-core populates all device-tree nodes.
*/
void exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus,
const struct exynos5_subcmu_info **_cmu)
{
ctx = _ctx;
cmu = _cmu;
nr_cmus = _nr_cmus;
for (; _nr_cmus--; _cmu++) {
exynos5_subcmu_defer_gate(ctx, (*_cmu)->gate_clks,
(*_cmu)->nr_gate_clks);
exynos5_subcmu_clk_save(ctx->reg_base, (*_cmu)->suspend_regs,
(*_cmu)->nr_suspend_regs);
}
}
static int __maybe_unused exynos5_subcmu_suspend(struct device *dev)
{
struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&ctx->lock, flags);
exynos5_subcmu_clk_save(ctx->reg_base, info->suspend_regs,
info->nr_suspend_regs);
spin_unlock_irqrestore(&ctx->lock, flags);
return 0;
}
static int __maybe_unused exynos5_subcmu_resume(struct device *dev)
{
struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
unsigned long flags;
spin_lock_irqsave(&ctx->lock, flags);
exynos5_subcmu_clk_restore(ctx->reg_base, info->suspend_regs,
info->nr_suspend_regs);
spin_unlock_irqrestore(&ctx->lock, flags);
return 0;
}
static int __init exynos5_subcmu_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
pm_runtime_set_suspended(dev);
pm_runtime_enable(dev);
pm_runtime_get(dev);
ctx->dev = dev;
samsung_clk_register_div(ctx, info->div_clks, info->nr_div_clks);
samsung_clk_register_gate(ctx, info->gate_clks, info->nr_gate_clks);
ctx->dev = NULL;
pm_runtime_put_sync(dev);
return 0;
}
static const struct dev_pm_ops exynos5_subcmu_pm_ops = {
SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend,
exynos5_subcmu_resume, NULL)
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
pm_runtime_force_resume)
};
static struct platform_driver exynos5_subcmu_driver __refdata = {
.driver = {
.name = "exynos5-subcmu",
.suppress_bind_attrs = true,
.pm = &exynos5_subcmu_pm_ops,
},
.probe = exynos5_subcmu_probe,
};
static int __init exynos5_clk_register_subcmu(struct device *parent,
const struct exynos5_subcmu_info *info,
struct device_node *pd_node)
{
struct of_phandle_args genpdspec = { .np = pd_node };
struct platform_device *pdev;
int ret;
pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO);
if (!pdev)
return -ENOMEM;
pdev->dev.parent = parent;
platform_set_drvdata(pdev, (void *)info);
of_genpd_add_device(&genpdspec, &pdev->dev);
ret = platform_device_add(pdev);
if (ret)
platform_device_put(pdev);
return ret;
}
static int __init exynos5_clk_probe(struct platform_device *pdev)
{
struct device_node *np;
const char *name;
int i;
for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
if (of_property_read_string(np, "label", &name) < 0)
continue;
for (i = 0; i < nr_cmus; i++)
if (strcmp(cmu[i]->pd_name, name) == 0)
exynos5_clk_register_subcmu(&pdev->dev,
cmu[i], np);
}
return 0;
}
static const struct of_device_id exynos5_clk_of_match[] = {
{ .compatible = "samsung,exynos5250-clock", },
{ .compatible = "samsung,exynos5420-clock", },
{ .compatible = "samsung,exynos5800-clock", },
{ },
};
static struct platform_driver exynos5_clk_driver __refdata = {
.driver = {
.name = "exynos5-clock",
.of_match_table = exynos5_clk_of_match,
.suppress_bind_attrs = true,
},
.probe = exynos5_clk_probe,
};
static int __init exynos5_clk_drv_init(void)
{
platform_driver_register(&exynos5_clk_driver);
platform_driver_register(&exynos5_subcmu_driver);
return 0;
}
core_initcall(exynos5_clk_drv_init);
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __CLK_EXYNOS5_SUBCMU_H
#define __CLK_EXYNOS5_SUBCMU_H
struct exynos5_subcmu_reg_dump {
u32 offset;
u32 value;
u32 mask;
u32 save;
};
struct exynos5_subcmu_info {
const struct samsung_div_clock *div_clks;
unsigned int nr_div_clks;
const struct samsung_gate_clock *gate_clks;
unsigned int nr_gate_clks;
struct exynos5_subcmu_reg_dump *suspend_regs;
unsigned int nr_suspend_regs;
const char *pd_name;
};
void exynos5_subcmus_init(struct samsung_clk_provider *ctx, int nr_cmus,
const struct exynos5_subcmu_info **cmu);
#endif
@@ -0,0 +1,866 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* Common Clock Framework support for Exynos5250 SoC.
*/
#include <dt-bindings/clock/exynos5250.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include "clk.h"
#include "clk-cpu.h"
#include "clk-exynos5-subcmu.h"
#define APLL_LOCK 0x0
#define APLL_CON0 0x100
#define SRC_CPU 0x200
#define DIV_CPU0 0x500
#define PWR_CTRL1 0x1020
#define PWR_CTRL2 0x1024
#define MPLL_LOCK 0x4000
#define MPLL_CON0 0x4100
#define SRC_CORE1 0x4204
#define GATE_IP_ACP 0x8800
#define GATE_IP_ISP0 0xc800
#define GATE_IP_ISP1 0xc804
#define CPLL_LOCK 0x10020
#define EPLL_LOCK 0x10030
#define VPLL_LOCK 0x10040
#define GPLL_LOCK 0x10050
#define CPLL_CON0 0x10120
#define EPLL_CON0 0x10130
#define VPLL_CON0 0x10140
#define GPLL_CON0 0x10150
#define SRC_TOP0 0x10210
#define SRC_TOP1 0x10214
#define SRC_TOP2 0x10218
#define SRC_TOP3 0x1021c
#define SRC_GSCL 0x10220
#define SRC_DISP1_0 0x1022c
#define SRC_MAU 0x10240
#define SRC_FSYS 0x10244
#define SRC_GEN 0x10248
#define SRC_PERIC0 0x10250
#define SRC_PERIC1 0x10254
#define SRC_MASK_GSCL 0x10320
#define SRC_MASK_DISP1_0 0x1032c
#define SRC_MASK_MAU 0x10334
#define SRC_MASK_FSYS 0x10340
#define SRC_MASK_GEN 0x10344
#define SRC_MASK_PERIC0 0x10350
#define SRC_MASK_PERIC1 0x10354
#define DIV_TOP0 0x10510
#define DIV_TOP1 0x10514
#define DIV_GSCL 0x10520
#define DIV_DISP1_0 0x1052c
#define DIV_GEN 0x1053c
#define DIV_MAU 0x10544
#define DIV_FSYS0 0x10548
#define DIV_FSYS1 0x1054c
#define DIV_FSYS2 0x10550
#define DIV_PERIC0 0x10558
#define DIV_PERIC1 0x1055c
#define DIV_PERIC2 0x10560
#define DIV_PERIC3 0x10564
#define DIV_PERIC4 0x10568
#define DIV_PERIC5 0x1056c
#define GATE_IP_GSCL 0x10920
#define GATE_IP_DISP1 0x10928
#define GATE_IP_MFC 0x1092c
#define GATE_IP_G3D 0x10930
#define GATE_IP_GEN 0x10934
#define GATE_IP_FSYS 0x10944
#define GATE_IP_PERIC 0x10950
#define GATE_IP_PERIS 0x10960
#define BPLL_LOCK 0x20010
#define BPLL_CON0 0x20110
#define SRC_CDREX 0x20200
#define PLL_DIV2_SEL 0x20a24
/*Below definitions are used for PWR_CTRL settings*/
#define PWR_CTRL1_CORE2_DOWN_RATIO (7 << 28)
#define PWR_CTRL1_CORE1_DOWN_RATIO (7 << 16)
#define PWR_CTRL1_DIV2_DOWN_EN (1 << 9)
#define PWR_CTRL1_DIV1_DOWN_EN (1 << 8)
#define PWR_CTRL1_USE_CORE1_WFE (1 << 5)
#define PWR_CTRL1_USE_CORE0_WFE (1 << 4)
#define PWR_CTRL1_USE_CORE1_WFI (1 << 1)
#define PWR_CTRL1_USE_CORE0_WFI (1 << 0)
#define PWR_CTRL2_DIV2_UP_EN (1 << 25)
#define PWR_CTRL2_DIV1_UP_EN (1 << 24)
#define PWR_CTRL2_DUR_STANDBY2_VAL (1 << 16)
#define PWR_CTRL2_DUR_STANDBY1_VAL (1 << 8)
#define PWR_CTRL2_CORE2_UP_RATIO (1 << 4)
#define PWR_CTRL2_CORE1_UP_RATIO (1 << 0)
/* NOTE: Must be equal to the last clock ID increased by one */
#define CLKS_NR (CLK_MOUT_VPLLSRC + 1)
/* list of PLLs to be registered */
enum exynos5250_plls {
apll, mpll, cpll, epll, vpll, gpll, bpll,
nr_plls /* number of PLLs */
};
static void __iomem *reg_base;
/*
* list of controller registers to be saved and restored during a
* suspend/resume cycle.
*/
static const unsigned long exynos5250_clk_regs[] __initconst = {
SRC_CPU,
DIV_CPU0,
PWR_CTRL1,
PWR_CTRL2,
SRC_CORE1,
SRC_TOP0,
SRC_TOP1,
SRC_TOP2,
SRC_TOP3,
SRC_GSCL,
SRC_DISP1_0,
SRC_MAU,
SRC_FSYS,
SRC_GEN,
SRC_PERIC0,
SRC_PERIC1,
SRC_MASK_GSCL,
SRC_MASK_DISP1_0,
SRC_MASK_MAU,
SRC_MASK_FSYS,
SRC_MASK_GEN,
SRC_MASK_PERIC0,
SRC_MASK_PERIC1,
DIV_TOP0,
DIV_TOP1,
DIV_GSCL,
DIV_DISP1_0,
DIV_GEN,
DIV_MAU,
DIV_FSYS0,
DIV_FSYS1,
DIV_FSYS2,
DIV_PERIC0,
DIV_PERIC1,
DIV_PERIC2,
DIV_PERIC3,
DIV_PERIC4,
DIV_PERIC5,
GATE_IP_GSCL,
GATE_IP_MFC,
GATE_IP_G3D,
GATE_IP_GEN,
GATE_IP_FSYS,
GATE_IP_PERIC,
GATE_IP_PERIS,
SRC_CDREX,
PLL_DIV2_SEL,
GATE_IP_DISP1,
GATE_IP_ACP,
GATE_IP_ISP0,
GATE_IP_ISP1,
};
/* list of all parent clock list */
PNAME(mout_apll_p) = { "fin_pll", "fout_apll", };
PNAME(mout_cpu_p) = { "mout_apll", "mout_mpll", };
PNAME(mout_mpll_fout_p) = { "fout_mplldiv2", "fout_mpll" };
PNAME(mout_mpll_p) = { "fin_pll", "mout_mpll_fout" };
PNAME(mout_bpll_fout_p) = { "fout_bplldiv2", "fout_bpll" };
PNAME(mout_bpll_p) = { "fin_pll", "mout_bpll_fout" };
PNAME(mout_vpllsrc_p) = { "fin_pll", "sclk_hdmi27m" };
PNAME(mout_vpll_p) = { "mout_vpllsrc", "fout_vpll" };
PNAME(mout_cpll_p) = { "fin_pll", "fout_cpll" };
PNAME(mout_epll_p) = { "fin_pll", "fout_epll" };
PNAME(mout_gpll_p) = { "fin_pll", "fout_gpll" };
PNAME(mout_mpll_user_p) = { "fin_pll", "mout_mpll" };
PNAME(mout_bpll_user_p) = { "fin_pll", "mout_bpll" };
PNAME(mout_aclk166_p) = { "mout_cpll", "mout_mpll_user" };
PNAME(mout_aclk200_p) = { "mout_mpll_user", "mout_bpll_user" };
PNAME(mout_aclk300_p) = { "mout_aclk300_disp1_mid",
"mout_aclk300_disp1_mid1" };
PNAME(mout_aclk400_p) = { "mout_aclk400_g3d_mid", "mout_gpll" };
PNAME(mout_aclk200_sub_p) = { "fin_pll", "div_aclk200" };
PNAME(mout_aclk266_sub_p) = { "fin_pll", "div_aclk266" };
PNAME(mout_aclk300_sub_p) = { "fin_pll", "div_aclk300_disp" };
PNAME(mout_aclk300_disp1_mid1_p) = { "mout_vpll", "mout_cpll" };
PNAME(mout_aclk333_sub_p) = { "fin_pll", "div_aclk333" };
PNAME(mout_aclk400_isp_sub_p) = { "fin_pll", "div_aclk400_isp" };
PNAME(mout_hdmi_p) = { "div_hdmi_pixel", "sclk_hdmiphy" };
PNAME(mout_usb3_p) = { "mout_mpll_user", "mout_cpll" };
PNAME(mout_group1_p) = { "fin_pll", "fin_pll", "sclk_hdmi27m",
"sclk_dptxphy", "sclk_uhostphy", "sclk_hdmiphy",
"mout_mpll_user", "mout_epll", "mout_vpll",
"mout_cpll", "none", "none",
"none", "none", "none",
"none" };
PNAME(mout_audio0_p) = { "cdclk0", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "fin_pll",
"mout_mpll_user", "mout_epll", "mout_vpll",
"mout_cpll", "none", "none",
"none", "none", "none",
"none" };
PNAME(mout_audio1_p) = { "cdclk1", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "fin_pll",
"mout_mpll_user", "mout_epll", "mout_vpll",
"mout_cpll", "none", "none",
"none", "none", "none",
"none" };
PNAME(mout_audio2_p) = { "cdclk2", "fin_pll", "sclk_hdmi27m", "sclk_dptxphy",
"sclk_uhostphy", "fin_pll",
"mout_mpll_user", "mout_epll", "mout_vpll",
"mout_cpll", "none", "none",
"none", "none", "none",
"none" };
PNAME(mout_spdif_p) = { "sclk_audio0", "sclk_audio1", "sclk_audio2",
"spdif_extclk" };
/* fixed rate clocks generated outside the soc */
static struct samsung_fixed_rate_clock exynos5250_fixed_rate_ext_clks[] __initdata = {
FRATE(CLK_FIN_PLL, "fin_pll", NULL, 0, 0),
};
/* fixed rate clocks generated inside the soc */
static const struct samsung_fixed_rate_clock exynos5250_fixed_rate_clks[] __initconst = {
FRATE(CLK_SCLK_HDMIPHY, "sclk_hdmiphy", NULL, 0, 24000000),
FRATE(0, "sclk_hdmi27m", NULL, 0, 27000000),
FRATE(0, "sclk_dptxphy", NULL, 0, 24000000),
FRATE(0, "sclk_uhostphy", NULL, 0, 48000000),
};
static const struct samsung_fixed_factor_clock exynos5250_fixed_factor_clks[] __initconst = {
FFACTOR(0, "fout_mplldiv2", "fout_mpll", 1, 2, 0),
FFACTOR(0, "fout_bplldiv2", "fout_bpll", 1, 2, 0),
};
static const struct samsung_mux_clock exynos5250_pll_pmux_clks[] __initconst = {
MUX(CLK_MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, SRC_TOP2, 0, 1),
};
static const struct samsung_mux_clock exynos5250_mux_clks[] __initconst = {
/*
* NOTE: Following table is sorted by (clock domain, register address,
* bitfield shift) triplet in ascending order. When adding new entries,
* please make sure that the order is kept, to avoid merge conflicts
* and make further work with defined data easier.
*/
/*
* CMU_CPU
*/
MUX_F(CLK_MOUT_APLL, "mout_apll", mout_apll_p, SRC_CPU, 0, 1,
CLK_SET_RATE_PARENT, 0),
MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1),
/*
* CMU_CORE
*/
MUX(CLK_MOUT_MPLL, "mout_mpll", mout_mpll_p, SRC_CORE1, 8, 1),
/*
* CMU_TOP
*/
MUX(0, "mout_aclk166", mout_aclk166_p, SRC_TOP0, 8, 1),
MUX(0, "mout_aclk200", mout_aclk200_p, SRC_TOP0, 12, 1),
MUX(0, "mout_aclk300_disp1_mid", mout_aclk200_p, SRC_TOP0, 14, 1),
MUX(0, "mout_aclk300", mout_aclk300_p, SRC_TOP0, 15, 1),
MUX(0, "mout_aclk333", mout_aclk166_p, SRC_TOP0, 16, 1),
MUX(0, "mout_aclk400_g3d_mid", mout_aclk200_p, SRC_TOP0, 20, 1),
MUX(0, "mout_aclk300_disp1_mid1", mout_aclk300_disp1_mid1_p, SRC_TOP1,
8, 1),
MUX(0, "mout_aclk400_isp", mout_aclk200_p, SRC_TOP1, 24, 1),
MUX(0, "mout_aclk400_g3d", mout_aclk400_p, SRC_TOP1, 28, 1),
MUX(0, "mout_cpll", mout_cpll_p, SRC_TOP2, 8, 1),
MUX(0, "mout_epll", mout_epll_p, SRC_TOP2, 12, 1),
MUX(0, "mout_vpll", mout_vpll_p, SRC_TOP2, 16, 1),
MUX(0, "mout_mpll_user", mout_mpll_user_p, SRC_TOP2, 20, 1),
MUX(0, "mout_bpll_user", mout_bpll_user_p, SRC_TOP2, 24, 1),
MUX(CLK_MOUT_GPLL, "mout_gpll", mout_gpll_p, SRC_TOP2, 28, 1),
MUX(CLK_MOUT_ACLK200_DISP1_SUB, "mout_aclk200_disp1_sub",
mout_aclk200_sub_p, SRC_TOP3, 4, 1),
MUX(CLK_MOUT_ACLK300_DISP1_SUB, "mout_aclk300_disp1_sub",
mout_aclk300_sub_p, SRC_TOP3, 6, 1),
MUX(0, "mout_aclk266_gscl_sub", mout_aclk266_sub_p, SRC_TOP3, 8, 1),
MUX(0, "mout_aclk_266_isp_sub", mout_aclk266_sub_p, SRC_TOP3, 16, 1),
MUX(0, "mout_aclk_400_isp_sub", mout_aclk400_isp_sub_p,
SRC_TOP3, 20, 1),
MUX(0, "mout_aclk333_sub", mout_aclk333_sub_p, SRC_TOP3, 24, 1),
MUX(0, "mout_cam_bayer", mout_group1_p, SRC_GSCL, 12, 4),
MUX(0, "mout_cam0", mout_group1_p, SRC_GSCL, 16, 4),
MUX(0, "mout_cam1", mout_group1_p, SRC_GSCL, 20, 4),
MUX(0, "mout_gscl_wa", mout_group1_p, SRC_GSCL, 24, 4),
MUX(0, "mout_gscl_wb", mout_group1_p, SRC_GSCL, 28, 4),
MUX(0, "mout_fimd1", mout_group1_p, SRC_DISP1_0, 0, 4),
MUX(0, "mout_mipi1", mout_group1_p, SRC_DISP1_0, 12, 4),
MUX(0, "mout_dp", mout_group1_p, SRC_DISP1_0, 16, 4),
MUX(CLK_MOUT_HDMI, "mout_hdmi", mout_hdmi_p, SRC_DISP1_0, 20, 1),
MUX(0, "mout_audio0", mout_audio0_p, SRC_MAU, 0, 4),
MUX(0, "mout_mmc0", mout_group1_p, SRC_FSYS, 0, 4),
MUX(0, "mout_mmc1", mout_group1_p, SRC_FSYS, 4, 4),
MUX(0, "mout_mmc2", mout_group1_p, SRC_FSYS, 8, 4),
MUX(0, "mout_mmc3", mout_group1_p, SRC_FSYS, 12, 4),
MUX(0, "mout_sata", mout_aclk200_p, SRC_FSYS, 24, 1),
MUX(0, "mout_usb3", mout_usb3_p, SRC_FSYS, 28, 1),
MUX(0, "mout_jpeg", mout_group1_p, SRC_GEN, 0, 4),
MUX(0, "mout_uart0", mout_group1_p, SRC_PERIC0, 0, 4),
MUX(0, "mout_uart1", mout_group1_p, SRC_PERIC0, 4, 4),
MUX(0, "mout_uart2", mout_group1_p, SRC_PERIC0, 8, 4),
MUX(0, "mout_uart3", mout_group1_p, SRC_PERIC0, 12, 4),
MUX(0, "mout_pwm", mout_group1_p, SRC_PERIC0, 24, 4),
MUX(0, "mout_audio1", mout_audio1_p, SRC_PERIC1, 0, 4),
MUX(0, "mout_audio2", mout_audio2_p, SRC_PERIC1, 4, 4),
MUX(0, "mout_spdif", mout_spdif_p, SRC_PERIC1, 8, 2),
MUX(0, "mout_spi0", mout_group1_p, SRC_PERIC1, 16, 4),
MUX(0, "mout_spi1", mout_group1_p, SRC_PERIC1, 20, 4),
MUX(0, "mout_spi2", mout_group1_p, SRC_PERIC1, 24, 4),
/*
* CMU_CDREX
*/
MUX(0, "mout_bpll", mout_bpll_p, SRC_CDREX, 0, 1),
MUX(0, "mout_mpll_fout", mout_mpll_fout_p, PLL_DIV2_SEL, 4, 1),
MUX(0, "mout_bpll_fout", mout_bpll_fout_p, PLL_DIV2_SEL, 0, 1),
};
static const struct samsung_div_clock exynos5250_div_clks[] __initconst = {
/*
* NOTE: Following table is sorted by (clock domain, register address,
* bitfield shift) triplet in ascending order. When adding new entries,
* please make sure that the order is kept, to avoid merge conflicts
* and make further work with defined data easier.
*/
/*
* CMU_CPU
*/
DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
DIV(0, "div_apll", "mout_apll", DIV_CPU0, 24, 3),
DIV(CLK_DIV_ARM2, "div_arm2", "div_arm", DIV_CPU0, 28, 3),
/*
* CMU_TOP
*/
DIV(0, "div_aclk66", "div_aclk66_pre", DIV_TOP0, 0, 3),
DIV(0, "div_aclk166", "mout_aclk166", DIV_TOP0, 8, 3),
DIV(0, "div_aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
DIV(0, "div_aclk266", "mout_mpll_user", DIV_TOP0, 16, 3),
DIV(0, "div_aclk333", "mout_aclk333", DIV_TOP0, 20, 3),
DIV(0, "div_aclk400_g3d", "mout_aclk400_g3d", DIV_TOP0,
24, 3),
DIV(0, "div_aclk300_disp", "mout_aclk300", DIV_TOP0, 28, 3),
DIV(0, "div_aclk400_isp", "mout_aclk400_isp", DIV_TOP1, 20, 3),
DIV(0, "div_aclk66_pre", "mout_mpll_user", DIV_TOP1, 24, 3),
DIV(0, "div_cam_bayer", "mout_cam_bayer", DIV_GSCL, 12, 4),
DIV(0, "div_cam0", "mout_cam0", DIV_GSCL, 16, 4),
DIV(0, "div_cam1", "mout_cam1", DIV_GSCL, 20, 4),
DIV(0, "div_gscl_wa", "mout_gscl_wa", DIV_GSCL, 24, 4),
DIV(0, "div_gscl_wb", "mout_gscl_wb", DIV_GSCL, 28, 4),
DIV(0, "div_fimd1", "mout_fimd1", DIV_DISP1_0, 0, 4),
DIV(0, "div_mipi1", "mout_mipi1", DIV_DISP1_0, 16, 4),
DIV_F(0, "div_mipi1_pre", "div_mipi1",
DIV_DISP1_0, 20, 4, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_dp", "mout_dp", DIV_DISP1_0, 24, 4),
DIV(CLK_SCLK_PIXEL, "div_hdmi_pixel", "mout_vpll", DIV_DISP1_0, 28, 4),
DIV(0, "div_jpeg", "mout_jpeg", DIV_GEN, 4, 4),
DIV(0, "div_audio0", "mout_audio0", DIV_MAU, 0, 4),
DIV(CLK_DIV_PCM0, "div_pcm0", "sclk_audio0", DIV_MAU, 4, 8),
DIV(0, "div_sata", "mout_sata", DIV_FSYS0, 20, 4),
DIV(0, "div_usb3", "mout_usb3", DIV_FSYS0, 24, 4),
DIV(0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
DIV_F(0, "div_mmc_pre0", "div_mmc0",
DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
DIV_F(0, "div_mmc_pre1", "div_mmc1",
DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
DIV_F(0, "div_mmc_pre2", "div_mmc2",
DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_mmc3", "mout_mmc3", DIV_FSYS2, 16, 4),
DIV_F(0, "div_mmc_pre3", "div_mmc3",
DIV_FSYS2, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
DIV(0, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
DIV(0, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
DIV(0, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
DIV(0, "div_spi0", "mout_spi0", DIV_PERIC1, 0, 4),
DIV_F(0, "div_spi_pre0", "div_spi0",
DIV_PERIC1, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_spi1", "mout_spi1", DIV_PERIC1, 16, 4),
DIV_F(0, "div_spi_pre1", "div_spi1",
DIV_PERIC1, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_spi2", "mout_spi2", DIV_PERIC2, 0, 4),
DIV_F(0, "div_spi_pre2", "div_spi2",
DIV_PERIC2, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
DIV(0, "div_audio1", "mout_audio1", DIV_PERIC4, 0, 4),
DIV(0, "div_pcm1", "sclk_audio1", DIV_PERIC4, 4, 8),
DIV(0, "div_audio2", "mout_audio2", DIV_PERIC4, 16, 4),
DIV(0, "div_pcm2", "sclk_audio2", DIV_PERIC4, 20, 8),
DIV(CLK_DIV_I2S1, "div_i2s1", "sclk_audio1", DIV_PERIC5, 0, 6),
DIV(CLK_DIV_I2S2, "div_i2s2", "sclk_audio2", DIV_PERIC5, 8, 6),
};
static const struct samsung_gate_clock exynos5250_gate_clks[] __initconst = {
/*
* NOTE: Following table is sorted by (clock domain, register address,
* bitfield shift) triplet in ascending order. When adding new entries,
* please make sure that the order is kept, to avoid merge conflicts
* and make further work with defined data easier.
*/
/*
* CMU_ACP
*/
GATE(CLK_MDMA0, "mdma0", "div_aclk266", GATE_IP_ACP, 1, 0, 0),
GATE(CLK_SSS, "sss", "div_aclk266", GATE_IP_ACP, 2, 0, 0),
GATE(CLK_G2D, "g2d", "div_aclk200", GATE_IP_ACP, 3, 0, 0),
GATE(CLK_SMMU_MDMA0, "smmu_mdma0", "div_aclk266", GATE_IP_ACP, 5, 0, 0),
/*
* CMU_TOP
*/
GATE(CLK_SCLK_CAM_BAYER, "sclk_cam_bayer", "div_cam_bayer",
SRC_MASK_GSCL, 12, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_CAM0, "sclk_cam0", "div_cam0",
SRC_MASK_GSCL, 16, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_CAM1, "sclk_cam1", "div_cam1",
SRC_MASK_GSCL, 20, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_GSCL_WA, "sclk_gscl_wa", "div_gscl_wa",
SRC_MASK_GSCL, 24, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_GSCL_WB, "sclk_gscl_wb", "div_gscl_wb",
SRC_MASK_GSCL, 28, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_FIMD1, "sclk_fimd1", "div_fimd1",
SRC_MASK_DISP1_0, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MIPI1, "sclk_mipi1", "div_mipi1",
SRC_MASK_DISP1_0, 12, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_DP, "sclk_dp", "div_dp",
SRC_MASK_DISP1_0, 16, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_HDMI, "sclk_hdmi", "mout_hdmi",
SRC_MASK_DISP1_0, 20, 0, 0),
GATE(CLK_SCLK_AUDIO0, "sclk_audio0", "div_audio0",
SRC_MASK_MAU, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc_pre0",
SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc_pre1",
SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC2, "sclk_mmc2", "div_mmc_pre2",
SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC3, "sclk_mmc3", "div_mmc_pre3",
SRC_MASK_FSYS, 12, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SATA, "sclk_sata", "div_sata",
SRC_MASK_FSYS, 24, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_USB3, "sclk_usb3", "div_usb3",
SRC_MASK_FSYS, 28, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_JPEG, "sclk_jpeg", "div_jpeg",
SRC_MASK_GEN, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0",
SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1",
SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART2, "sclk_uart2", "div_uart2",
SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART3, "sclk_uart3", "div_uart3",
SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_PWM, "sclk_pwm", "div_pwm",
SRC_MASK_PERIC0, 24, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_AUDIO1, "sclk_audio1", "div_audio1",
SRC_MASK_PERIC1, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_AUDIO2, "sclk_audio2", "div_audio2",
SRC_MASK_PERIC1, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPDIF, "sclk_spdif", "mout_spdif",
SRC_MASK_PERIC1, 4, 0, 0),
GATE(CLK_SCLK_SPI0, "sclk_spi0", "div_spi_pre0",
SRC_MASK_PERIC1, 16, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI1, "sclk_spi1", "div_spi_pre1",
SRC_MASK_PERIC1, 20, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_SPI2, "sclk_spi2", "div_spi_pre2",
SRC_MASK_PERIC1, 24, CLK_SET_RATE_PARENT, 0),
GATE(CLK_GSCL0, "gscl0", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 0, 0,
0),
GATE(CLK_GSCL1, "gscl1", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 1, 0,
0),
GATE(CLK_GSCL2, "gscl2", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 2, 0,
0),
GATE(CLK_GSCL3, "gscl3", "mout_aclk266_gscl_sub", GATE_IP_GSCL, 3, 0,
0),
GATE(CLK_CAMIF_TOP, "camif_top", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 4, 0, 0),
GATE(CLK_GSCL_WA, "gscl_wa", "div_gscl_wa", GATE_IP_GSCL, 5, 0, 0),
GATE(CLK_GSCL_WB, "gscl_wb", "div_gscl_wb", GATE_IP_GSCL, 6, 0, 0),
GATE(CLK_SMMU_GSCL0, "smmu_gscl0", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 7, 0, 0),
GATE(CLK_SMMU_GSCL1, "smmu_gscl1", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 8, 0, 0),
GATE(CLK_SMMU_GSCL2, "smmu_gscl2", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 9, 0, 0),
GATE(CLK_SMMU_GSCL3, "smmu_gscl3", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 10, 0, 0),
GATE(CLK_SMMU_FIMC_LITE0, "smmu_fimc_lite0", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 11, 0, 0),
GATE(CLK_SMMU_FIMC_LITE1, "smmu_fimc_lite1", "mout_aclk266_gscl_sub",
GATE_IP_GSCL, 12, 0, 0),
GATE(CLK_MFC, "mfc", "mout_aclk333_sub", GATE_IP_MFC, 0, 0, 0),
GATE(CLK_SMMU_MFCR, "smmu_mfcr", "mout_aclk333_sub", GATE_IP_MFC, 1, 0,
0),
GATE(CLK_SMMU_MFCL, "smmu_mfcl", "mout_aclk333_sub", GATE_IP_MFC, 2, 0,
0),
GATE(CLK_G3D, "g3d", "div_aclk400_g3d", GATE_IP_G3D, 0,
CLK_SET_RATE_PARENT, 0),
GATE(CLK_ROTATOR, "rotator", "div_aclk266", GATE_IP_GEN, 1, 0, 0),
GATE(CLK_JPEG, "jpeg", "div_aclk166", GATE_IP_GEN, 2, 0, 0),
GATE(CLK_MDMA1, "mdma1", "div_aclk266", GATE_IP_GEN, 4, 0, 0),
GATE(CLK_SMMU_ROTATOR, "smmu_rotator", "div_aclk266", GATE_IP_GEN, 6, 0,
0),
GATE(CLK_SMMU_JPEG, "smmu_jpeg", "div_aclk166", GATE_IP_GEN, 7, 0, 0),
GATE(CLK_SMMU_MDMA1, "smmu_mdma1", "div_aclk266", GATE_IP_GEN, 9, 0, 0),
GATE(CLK_PDMA0, "pdma0", "div_aclk200", GATE_IP_FSYS, 1, 0, 0),
GATE(CLK_PDMA1, "pdma1", "div_aclk200", GATE_IP_FSYS, 2, 0, 0),
GATE(CLK_SATA, "sata", "div_aclk200", GATE_IP_FSYS, 6, 0, 0),
GATE(CLK_USBOTG, "usbotg", "div_aclk200", GATE_IP_FSYS, 7, 0, 0),
GATE(CLK_MIPI_HSI, "mipi_hsi", "div_aclk200", GATE_IP_FSYS, 8, 0, 0),
GATE(CLK_SDMMC0, "sdmmc0", "div_aclk200", GATE_IP_FSYS, 12, 0, 0),
GATE(CLK_SDMMC1, "sdmmc1", "div_aclk200", GATE_IP_FSYS, 13, 0, 0),
GATE(CLK_SDMMC2, "sdmmc2", "div_aclk200", GATE_IP_FSYS, 14, 0, 0),
GATE(CLK_SDMMC3, "sdmmc3", "div_aclk200", GATE_IP_FSYS, 15, 0, 0),
GATE(CLK_SROMC, "sromc", "div_aclk200", GATE_IP_FSYS, 17, 0, 0),
GATE(CLK_USB2, "usb2", "div_aclk200", GATE_IP_FSYS, 18, 0, 0),
GATE(CLK_USB3, "usb3", "div_aclk200", GATE_IP_FSYS, 19, 0, 0),
GATE(CLK_SATA_PHYCTRL, "sata_phyctrl", "div_aclk200",
GATE_IP_FSYS, 24, 0, 0),
GATE(CLK_SATA_PHYI2C, "sata_phyi2c", "div_aclk200", GATE_IP_FSYS, 25, 0,
0),
GATE(CLK_UART0, "uart0", "div_aclk66", GATE_IP_PERIC, 0, 0, 0),
GATE(CLK_UART1, "uart1", "div_aclk66", GATE_IP_PERIC, 1, 0, 0),
GATE(CLK_UART2, "uart2", "div_aclk66", GATE_IP_PERIC, 2, 0, 0),
GATE(CLK_UART3, "uart3", "div_aclk66", GATE_IP_PERIC, 3, 0, 0),
GATE(CLK_UART4, "uart4", "div_aclk66", GATE_IP_PERIC, 4, 0, 0),
GATE(CLK_I2C0, "i2c0", "div_aclk66", GATE_IP_PERIC, 6, 0, 0),
GATE(CLK_I2C1, "i2c1", "div_aclk66", GATE_IP_PERIC, 7, 0, 0),
GATE(CLK_I2C2, "i2c2", "div_aclk66", GATE_IP_PERIC, 8, 0, 0),
GATE(CLK_I2C3, "i2c3", "div_aclk66", GATE_IP_PERIC, 9, 0, 0),
GATE(CLK_I2C4, "i2c4", "div_aclk66", GATE_IP_PERIC, 10, 0, 0),
GATE(CLK_I2C5, "i2c5", "div_aclk66", GATE_IP_PERIC, 11, 0, 0),
GATE(CLK_I2C6, "i2c6", "div_aclk66", GATE_IP_PERIC, 12, 0, 0),
GATE(CLK_I2C7, "i2c7", "div_aclk66", GATE_IP_PERIC, 13, 0, 0),
GATE(CLK_I2C_HDMI, "i2c_hdmi", "div_aclk66", GATE_IP_PERIC, 14, 0, 0),
GATE(CLK_ADC, "adc", "div_aclk66", GATE_IP_PERIC, 15, 0, 0),
GATE(CLK_SPI0, "spi0", "div_aclk66", GATE_IP_PERIC, 16, 0, 0),
GATE(CLK_SPI1, "spi1", "div_aclk66", GATE_IP_PERIC, 17, 0, 0),
GATE(CLK_SPI2, "spi2", "div_aclk66", GATE_IP_PERIC, 18, 0, 0),
GATE(CLK_I2S1, "i2s1", "div_aclk66", GATE_IP_PERIC, 20, 0, 0),
GATE(CLK_I2S2, "i2s2", "div_aclk66", GATE_IP_PERIC, 21, 0, 0),
GATE(CLK_PCM1, "pcm1", "div_aclk66", GATE_IP_PERIC, 22, 0, 0),
GATE(CLK_PCM2, "pcm2", "div_aclk66", GATE_IP_PERIC, 23, 0, 0),
GATE(CLK_PWM, "pwm", "div_aclk66", GATE_IP_PERIC, 24, 0, 0),
GATE(CLK_SPDIF, "spdif", "div_aclk66", GATE_IP_PERIC, 26, 0, 0),
GATE(CLK_AC97, "ac97", "div_aclk66", GATE_IP_PERIC, 27, 0, 0),
GATE(CLK_HSI2C0, "hsi2c0", "div_aclk66", GATE_IP_PERIC, 28, 0, 0),
GATE(CLK_HSI2C1, "hsi2c1", "div_aclk66", GATE_IP_PERIC, 29, 0, 0),
GATE(CLK_HSI2C2, "hsi2c2", "div_aclk66", GATE_IP_PERIC, 30, 0, 0),
GATE(CLK_HSI2C3, "hsi2c3", "div_aclk66", GATE_IP_PERIC, 31, 0, 0),
GATE(CLK_CHIPID, "chipid", "div_aclk66", GATE_IP_PERIS, 0, 0, 0),
GATE(CLK_SYSREG, "sysreg", "div_aclk66",
GATE_IP_PERIS, 1, CLK_IGNORE_UNUSED, 0),
GATE(CLK_PMU, "pmu", "div_aclk66", GATE_IP_PERIS, 2, CLK_IGNORE_UNUSED,
0),
GATE(CLK_CMU_TOP, "cmu_top", "div_aclk66",
GATE_IP_PERIS, 3, CLK_IGNORE_UNUSED, 0),
GATE(CLK_CMU_CORE, "cmu_core", "div_aclk66",
GATE_IP_PERIS, 4, CLK_IGNORE_UNUSED, 0),
GATE(CLK_CMU_MEM, "cmu_mem", "div_aclk66",
GATE_IP_PERIS, 5, CLK_IGNORE_UNUSED, 0),
GATE(CLK_TZPC0, "tzpc0", "div_aclk66", GATE_IP_PERIS, 6, 0, 0),
GATE(CLK_TZPC1, "tzpc1", "div_aclk66", GATE_IP_PERIS, 7, 0, 0),
GATE(CLK_TZPC2, "tzpc2", "div_aclk66", GATE_IP_PERIS, 8, 0, 0),
GATE(CLK_TZPC3, "tzpc3", "div_aclk66", GATE_IP_PERIS, 9, 0, 0),
GATE(CLK_TZPC4, "tzpc4", "div_aclk66", GATE_IP_PERIS, 10, 0, 0),
GATE(CLK_TZPC5, "tzpc5", "div_aclk66", GATE_IP_PERIS, 11, 0, 0),
GATE(CLK_TZPC6, "tzpc6", "div_aclk66", GATE_IP_PERIS, 12, 0, 0),
GATE(CLK_TZPC7, "tzpc7", "div_aclk66", GATE_IP_PERIS, 13, 0, 0),
GATE(CLK_TZPC8, "tzpc8", "div_aclk66", GATE_IP_PERIS, 14, 0, 0),
GATE(CLK_TZPC9, "tzpc9", "div_aclk66", GATE_IP_PERIS, 15, 0, 0),
GATE(CLK_HDMI_CEC, "hdmi_cec", "div_aclk66", GATE_IP_PERIS, 16, 0, 0),
GATE(CLK_MCT, "mct", "div_aclk66", GATE_IP_PERIS, 18, 0, 0),
GATE(CLK_WDT, "wdt", "div_aclk66", GATE_IP_PERIS, 19, 0, 0),
GATE(CLK_RTC, "rtc", "div_aclk66", GATE_IP_PERIS, 20, 0, 0),
GATE(CLK_TMU, "tmu", "div_aclk66", GATE_IP_PERIS, 21, 0, 0),
GATE(CLK_SMMU_2D, "smmu_2d", "div_aclk200", GATE_IP_ACP, 7, 0, 0),
GATE(CLK_SMMU_FIMC_ISP, "smmu_fimc_isp", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 8, 0, 0),
GATE(CLK_SMMU_FIMC_DRC, "smmu_fimc_drc", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 9, 0, 0),
GATE(CLK_SMMU_FIMC_FD, "smmu_fimc_fd", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 10, 0, 0),
GATE(CLK_SMMU_FIMC_SCC, "smmu_fimc_scc", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 11, 0, 0),
GATE(CLK_SMMU_FIMC_SCP, "smmu_fimc_scp", "mout_aclk_266_isp_sub",
GATE_IP_ISP0, 12, 0, 0),
GATE(CLK_SMMU_FIMC_MCU, "smmu_fimc_mcu", "mout_aclk_400_isp_sub",
GATE_IP_ISP0, 13, 0, 0),
GATE(CLK_SMMU_FIMC_ODC, "smmu_fimc_odc", "mout_aclk_266_isp_sub",
GATE_IP_ISP1, 4, 0, 0),
GATE(CLK_SMMU_FIMC_DIS0, "smmu_fimc_dis0", "mout_aclk_266_isp_sub",
GATE_IP_ISP1, 5, 0, 0),
GATE(CLK_SMMU_FIMC_DIS1, "smmu_fimc_dis1", "mout_aclk_266_isp_sub",
GATE_IP_ISP1, 6, 0, 0),
GATE(CLK_SMMU_FIMC_3DNR, "smmu_fimc_3dnr", "mout_aclk_266_isp_sub",
GATE_IP_ISP1, 7, 0, 0),
};
static const struct samsung_gate_clock exynos5250_disp_gate_clks[] __initconst = {
GATE(CLK_FIMD1, "fimd1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 0, 0,
0),
GATE(CLK_MIE1, "mie1", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 1, 0,
0),
GATE(CLK_DSIM0, "dsim0", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 3, 0,
0),
GATE(CLK_DP, "dp", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 4, 0, 0),
GATE(CLK_MIXER, "mixer", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 5, 0,
0),
GATE(CLK_HDMI, "hdmi", "mout_aclk200_disp1_sub", GATE_IP_DISP1, 6, 0,
0),
GATE(CLK_SMMU_TV, "smmu_tv", "mout_aclk200_disp1_sub",
GATE_IP_DISP1, 9, 0, 0),
GATE(CLK_SMMU_FIMD1, "smmu_fimd1", "mout_aclk200_disp1_sub",
GATE_IP_DISP1, 8, 0, 0),
};
static struct exynos5_subcmu_reg_dump exynos5250_disp_suspend_regs[] = {
{ GATE_IP_DISP1, 0xffffffff, 0xffffffff }, /* DISP1 gates */
{ SRC_TOP3, 0, BIT(4) }, /* MUX mout_aclk200_disp1_sub */
{ SRC_TOP3, 0, BIT(6) }, /* MUX mout_aclk300_disp1_sub */
};
static const struct exynos5_subcmu_info exynos5250_disp_subcmu = {
.gate_clks = exynos5250_disp_gate_clks,
.nr_gate_clks = ARRAY_SIZE(exynos5250_disp_gate_clks),
.suspend_regs = exynos5250_disp_suspend_regs,
.nr_suspend_regs = ARRAY_SIZE(exynos5250_disp_suspend_regs),
.pd_name = "DISP1",
};
static const struct exynos5_subcmu_info *exynos5250_subcmus[] = {
&exynos5250_disp_subcmu,
};
static const struct samsung_pll_rate_table vpll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
PLL_36XX_RATE(24 * MHZ, 266000000, 266, 3, 3, 0),
/* Not in UM, but need for eDP on snow */
PLL_36XX_RATE(24 * MHZ, 70500000, 94, 2, 4, 0),
{ },
};
static const struct samsung_pll_rate_table epll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
/* PLL_36XX_RATE(rate, m, p, s, k) */
PLL_36XX_RATE(24 * MHZ, 192000000, 64, 2, 2, 0),
PLL_36XX_RATE(24 * MHZ, 180633605, 90, 3, 2, 20762),
PLL_36XX_RATE(24 * MHZ, 180000000, 90, 3, 2, 0),
PLL_36XX_RATE(24 * MHZ, 73728000, 98, 2, 4, 19923),
PLL_36XX_RATE(24 * MHZ, 67737602, 90, 2, 4, 20762),
PLL_36XX_RATE(24 * MHZ, 49152000, 98, 3, 4, 19923),
PLL_36XX_RATE(24 * MHZ, 45158401, 90, 3, 4, 20762),
PLL_36XX_RATE(24 * MHZ, 32768001, 131, 3, 5, 4719),
{ },
};
static const struct samsung_pll_rate_table apll_24mhz_tbl[] __initconst = {
/* sorted in descending order */
/* PLL_35XX_RATE(fin, rate, m, p, s) */
PLL_35XX_RATE(24 * MHZ, 1700000000, 425, 6, 0),
PLL_35XX_RATE(24 * MHZ, 1600000000, 200, 3, 0),
PLL_35XX_RATE(24 * MHZ, 1500000000, 250, 4, 0),
PLL_35XX_RATE(24 * MHZ, 1400000000, 175, 3, 0),
PLL_35XX_RATE(24 * MHZ, 1300000000, 325, 6, 0),
PLL_35XX_RATE(24 * MHZ, 1200000000, 200, 4, 0),
PLL_35XX_RATE(24 * MHZ, 1100000000, 275, 6, 0),
PLL_35XX_RATE(24 * MHZ, 1000000000, 125, 3, 0),
PLL_35XX_RATE(24 * MHZ, 900000000, 150, 4, 0),
PLL_35XX_RATE(24 * MHZ, 800000000, 100, 3, 0),
PLL_35XX_RATE(24 * MHZ, 700000000, 175, 3, 1),
PLL_35XX_RATE(24 * MHZ, 600000000, 200, 4, 1),
PLL_35XX_RATE(24 * MHZ, 500000000, 125, 3, 1),
PLL_35XX_RATE(24 * MHZ, 400000000, 100, 3, 1),
PLL_35XX_RATE(24 * MHZ, 300000000, 200, 4, 2),
PLL_35XX_RATE(24 * MHZ, 200000000, 100, 3, 2),
};
static struct samsung_pll_clock exynos5250_plls[nr_plls] __initdata = {
[apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK,
APLL_CON0, NULL),
[mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK,
MPLL_CON0, NULL),
[bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK,
BPLL_CON0, NULL),
[gpll] = PLL(pll_35xx, CLK_FOUT_GPLL, "fout_gpll", "fin_pll", GPLL_LOCK,
GPLL_CON0, NULL),
[cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK,
CPLL_CON0, NULL),
[epll] = PLL(pll_36xx, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK,
EPLL_CON0, NULL),
[vpll] = PLL(pll_36xx, CLK_FOUT_VPLL, "fout_vpll", "mout_vpllsrc",
VPLL_LOCK, VPLL_CON0, NULL),
};
#define E5250_CPU_DIV0(apll, pclk_dbg, atb, periph, acp, cpud) \
((((apll) << 24) | ((pclk_dbg) << 20) | ((atb) << 16) | \
((periph) << 12) | ((acp) << 8) | ((cpud) << 4)))
#define E5250_CPU_DIV1(hpm, copy) \
(((hpm) << 4) | (copy))
static const struct exynos_cpuclk_cfg_data exynos5250_armclk_d[] __initconst = {
{ 1700000, E5250_CPU_DIV0(5, 3, 7, 7, 7, 3), E5250_CPU_DIV1(2, 0), },
{ 1600000, E5250_CPU_DIV0(4, 1, 7, 7, 7, 3), E5250_CPU_DIV1(2, 0), },
{ 1500000, E5250_CPU_DIV0(4, 1, 7, 7, 7, 2), E5250_CPU_DIV1(2, 0), },
{ 1400000, E5250_CPU_DIV0(4, 1, 6, 7, 7, 2), E5250_CPU_DIV1(2, 0), },
{ 1300000, E5250_CPU_DIV0(3, 1, 6, 7, 7, 2), E5250_CPU_DIV1(2, 0), },
{ 1200000, E5250_CPU_DIV0(3, 1, 5, 7, 7, 2), E5250_CPU_DIV1(2, 0), },
{ 1100000, E5250_CPU_DIV0(3, 1, 5, 7, 7, 3), E5250_CPU_DIV1(2, 0), },
{ 1000000, E5250_CPU_DIV0(2, 1, 4, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 900000, E5250_CPU_DIV0(2, 1, 4, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 800000, E5250_CPU_DIV0(2, 1, 4, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 700000, E5250_CPU_DIV0(1, 1, 3, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 600000, E5250_CPU_DIV0(1, 1, 3, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 500000, E5250_CPU_DIV0(1, 1, 2, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 400000, E5250_CPU_DIV0(1, 1, 2, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 300000, E5250_CPU_DIV0(1, 1, 1, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 200000, E5250_CPU_DIV0(1, 1, 1, 7, 7, 1), E5250_CPU_DIV1(2, 0), },
{ 0 },
};
static const struct samsung_cpu_clock exynos5250_cpu_clks[] __initconst = {
CPU_CLK(CLK_ARM_CLK, "armclk", CLK_MOUT_APLL, CLK_MOUT_MPLL,
CLK_CPU_HAS_DIV1, 0x0, CPUCLK_LAYOUT_E4210,
exynos5250_armclk_d),
};
static const struct of_device_id ext_clk_match[] __initconst = {
{ .compatible = "samsung,clock-xxti", .data = (void *)0, },
{ },
};
/* register exynox5250 clocks */
static void __init exynos5250_clk_init(struct device_node *np)
{
struct samsung_clk_provider *ctx;
unsigned int tmp;
struct clk_hw **hws;
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
} else {
panic("%s: unable to determine soc\n", __func__);
}
ctx = samsung_clk_init(NULL, reg_base, CLKS_NR);
hws = ctx->clk_data.hws;
samsung_clk_of_register_fixed_ext(ctx, exynos5250_fixed_rate_ext_clks,
ARRAY_SIZE(exynos5250_fixed_rate_ext_clks),
ext_clk_match);
samsung_clk_register_mux(ctx, exynos5250_pll_pmux_clks,
ARRAY_SIZE(exynos5250_pll_pmux_clks));
if (clk_hw_get_rate(hws[CLK_FIN_PLL]) == 24 * MHZ) {
exynos5250_plls[epll].rate_table = epll_24mhz_tbl;
exynos5250_plls[apll].rate_table = apll_24mhz_tbl;
}
if (clk_hw_get_rate(hws[CLK_MOUT_VPLLSRC]) == 24 * MHZ)
exynos5250_plls[vpll].rate_table = vpll_24mhz_tbl;
samsung_clk_register_pll(ctx, exynos5250_plls,
ARRAY_SIZE(exynos5250_plls));
samsung_clk_register_fixed_rate(ctx, exynos5250_fixed_rate_clks,
ARRAY_SIZE(exynos5250_fixed_rate_clks));
samsung_clk_register_fixed_factor(ctx, exynos5250_fixed_factor_clks,
ARRAY_SIZE(exynos5250_fixed_factor_clks));
samsung_clk_register_mux(ctx, exynos5250_mux_clks,
ARRAY_SIZE(exynos5250_mux_clks));
samsung_clk_register_div(ctx, exynos5250_div_clks,
ARRAY_SIZE(exynos5250_div_clks));
samsung_clk_register_gate(ctx, exynos5250_gate_clks,
ARRAY_SIZE(exynos5250_gate_clks));
samsung_clk_register_cpu(ctx, exynos5250_cpu_clks,
ARRAY_SIZE(exynos5250_cpu_clks));
/*
* Enable arm clock down (in idle) and set arm divider
* ratios in WFI/WFE state.
*/
tmp = (PWR_CTRL1_CORE2_DOWN_RATIO | PWR_CTRL1_CORE1_DOWN_RATIO |
PWR_CTRL1_DIV2_DOWN_EN | PWR_CTRL1_DIV1_DOWN_EN |
PWR_CTRL1_USE_CORE1_WFE | PWR_CTRL1_USE_CORE0_WFE |
PWR_CTRL1_USE_CORE1_WFI | PWR_CTRL1_USE_CORE0_WFI);
__raw_writel(tmp, reg_base + PWR_CTRL1);
/*
* Enable arm clock up (on exiting idle). Set arm divider
* ratios when not in idle along with the standby duration
* ratios.
*/
tmp = (PWR_CTRL2_DIV2_UP_EN | PWR_CTRL2_DIV1_UP_EN |
PWR_CTRL2_DUR_STANDBY2_VAL | PWR_CTRL2_DUR_STANDBY1_VAL |
PWR_CTRL2_CORE2_UP_RATIO | PWR_CTRL2_CORE1_UP_RATIO);
__raw_writel(tmp, reg_base + PWR_CTRL2);
samsung_clk_sleep_init(reg_base, NULL, exynos5250_clk_regs,
ARRAY_SIZE(exynos5250_clk_regs));
exynos5_subcmus_init(ctx, ARRAY_SIZE(exynos5250_subcmus),
exynos5250_subcmus);
samsung_clk_of_add_provider(np, ctx);
pr_info("Exynos5250: clock setup completed, armclk=%ld\n",
clk_hw_get_rate(hws[CLK_DIV_ARM2]));
}
CLK_OF_DECLARE_DRIVER(exynos5250_clk, "samsung,exynos5250-clock", exynos5250_clk_init);
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,456 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Author: Rahul Sharma <rahul.sharma@samsung.com>
*
* Common Clock Framework support for Exynos5260 SoC.
*/
#ifndef __CLK_EXYNOS5260_H
#define __CLK_EXYNOS5260_H
/*
*Registers for CMU_AUD
*/
#define MUX_SEL_AUD 0x0200
#define MUX_ENABLE_AUD 0x0300
#define MUX_STAT_AUD 0x0400
#define MUX_IGNORE_AUD 0x0500
#define DIV_AUD0 0x0600
#define DIV_AUD1 0x0604
#define DIV_STAT_AUD0 0x0700
#define DIV_STAT_AUD1 0x0704
#define EN_ACLK_AUD 0x0800
#define EN_PCLK_AUD 0x0900
#define EN_SCLK_AUD 0x0a00
#define EN_IP_AUD 0x0b00
/*
*Registers for CMU_DISP
*/
#define MUX_SEL_DISP0 0x0200
#define MUX_SEL_DISP1 0x0204
#define MUX_SEL_DISP2 0x0208
#define MUX_SEL_DISP3 0x020C
#define MUX_SEL_DISP4 0x0210
#define MUX_ENABLE_DISP0 0x0300
#define MUX_ENABLE_DISP1 0x0304
#define MUX_ENABLE_DISP2 0x0308
#define MUX_ENABLE_DISP3 0x030c
#define MUX_ENABLE_DISP4 0x0310
#define MUX_STAT_DISP0 0x0400
#define MUX_STAT_DISP1 0x0404
#define MUX_STAT_DISP2 0x0408
#define MUX_STAT_DISP3 0x040c
#define MUX_STAT_DISP4 0x0410
#define MUX_IGNORE_DISP0 0x0500
#define MUX_IGNORE_DISP1 0x0504
#define MUX_IGNORE_DISP2 0x0508
#define MUX_IGNORE_DISP3 0x050c
#define MUX_IGNORE_DISP4 0x0510
#define DIV_DISP 0x0600
#define DIV_STAT_DISP 0x0700
#define EN_ACLK_DISP 0x0800
#define EN_PCLK_DISP 0x0900
#define EN_SCLK_DISP0 0x0a00
#define EN_SCLK_DISP1 0x0a04
#define EN_IP_DISP 0x0b00
#define EN_IP_DISP_BUS 0x0b04
/*
*Registers for CMU_EGL
*/
#define EGL_PLL_LOCK 0x0000
#define EGL_DPLL_LOCK 0x0004
#define EGL_PLL_CON0 0x0100
#define EGL_PLL_CON1 0x0104
#define EGL_PLL_FREQ_DET 0x010c
#define EGL_DPLL_CON0 0x0110
#define EGL_DPLL_CON1 0x0114
#define EGL_DPLL_FREQ_DET 0x011c
#define MUX_SEL_EGL 0x0200
#define MUX_ENABLE_EGL 0x0300
#define MUX_STAT_EGL 0x0400
#define DIV_EGL 0x0600
#define DIV_EGL_PLL_FDET 0x0604
#define DIV_STAT_EGL 0x0700
#define DIV_STAT_EGL_PLL_FDET 0x0704
#define EN_ACLK_EGL 0x0800
#define EN_PCLK_EGL 0x0900
#define EN_SCLK_EGL 0x0a00
#define EN_IP_EGL 0x0b00
#define CLKOUT_CMU_EGL 0x0c00
#define CLKOUT_CMU_EGL_DIV_STAT 0x0c04
#define ARMCLK_STOPCTRL 0x1000
#define EAGLE_EMA_CTRL 0x1008
#define EAGLE_EMA_STATUS 0x100c
#define PWR_CTRL 0x1020
#define PWR_CTRL2 0x1024
#define CLKSTOP_CTRL 0x1028
#define INTR_SPREAD_EN 0x1080
#define INTR_SPREAD_USE_STANDBYWFI 0x1084
#define INTR_SPREAD_BLOCKING_DURATION 0x1088
#define CMU_EGL_SPARE0 0x2000
#define CMU_EGL_SPARE1 0x2004
#define CMU_EGL_SPARE2 0x2008
#define CMU_EGL_SPARE3 0x200c
#define CMU_EGL_SPARE4 0x2010
/*
*Registers for CMU_FSYS
*/
#define MUX_SEL_FSYS0 0x0200
#define MUX_SEL_FSYS1 0x0204
#define MUX_ENABLE_FSYS0 0x0300
#define MUX_ENABLE_FSYS1 0x0304
#define MUX_STAT_FSYS0 0x0400
#define MUX_STAT_FSYS1 0x0404
#define MUX_IGNORE_FSYS0 0x0500
#define MUX_IGNORE_FSYS1 0x0504
#define EN_ACLK_FSYS 0x0800
#define EN_ACLK_FSYS_SECURE_RTIC 0x0804
#define EN_ACLK_FSYS_SECURE_SMMU_RTIC 0x0808
#define EN_PCLK_FSYS 0x0900
#define EN_SCLK_FSYS 0x0a00
#define EN_IP_FSYS 0x0b00
#define EN_IP_FSYS_SECURE_RTIC 0x0b04
#define EN_IP_FSYS_SECURE_SMMU_RTIC 0x0b08
/*
*Registers for CMU_G2D
*/
#define MUX_SEL_G2D 0x0200
#define MUX_ENABLE_G2D 0x0300
#define MUX_STAT_G2D 0x0400
#define DIV_G2D 0x0600
#define DIV_STAT_G2D 0x0700
#define EN_ACLK_G2D 0x0800
#define EN_ACLK_G2D_SECURE_SSS 0x0804
#define EN_ACLK_G2D_SECURE_SLIM_SSS 0x0808
#define EN_ACLK_G2D_SECURE_SMMU_SLIM_SSS 0x080c
#define EN_ACLK_G2D_SECURE_SMMU_SSS 0x0810
#define EN_ACLK_G2D_SECURE_SMMU_MDMA 0x0814
#define EN_ACLK_G2D_SECURE_SMMU_G2D 0x0818
#define EN_PCLK_G2D 0x0900
#define EN_PCLK_G2D_SECURE_SMMU_SLIM_SSS 0x0904
#define EN_PCLK_G2D_SECURE_SMMU_SSS 0x0908
#define EN_PCLK_G2D_SECURE_SMMU_MDMA 0x090c
#define EN_PCLK_G2D_SECURE_SMMU_G2D 0x0910
#define EN_IP_G2D 0x0b00
#define EN_IP_G2D_SECURE_SSS 0x0b04
#define EN_IP_G2D_SECURE_SLIM_SSS 0x0b08
#define EN_IP_G2D_SECURE_SMMU_SLIM_SSS 0x0b0c
#define EN_IP_G2D_SECURE_SMMU_SSS 0x0b10
#define EN_IP_G2D_SECURE_SMMU_MDMA 0x0b14
#define EN_IP_G2D_SECURE_SMMU_G2D 0x0b18
/*
*Registers for CMU_G3D
*/
#define G3D_PLL_LOCK 0x0000
#define G3D_PLL_CON0 0x0100
#define G3D_PLL_CON1 0x0104
#define G3D_PLL_FDET 0x010c
#define MUX_SEL_G3D 0x0200
#define MUX_EN_G3D 0x0300
#define MUX_STAT_G3D 0x0400
#define MUX_IGNORE_G3D 0x0500
#define DIV_G3D 0x0600
#define DIV_G3D_PLL_FDET 0x0604
#define DIV_STAT_G3D 0x0700
#define DIV_STAT_G3D_PLL_FDET 0x0704
#define EN_ACLK_G3D 0x0800
#define EN_PCLK_G3D 0x0900
#define EN_SCLK_G3D 0x0a00
#define EN_IP_G3D 0x0b00
#define CLKOUT_CMU_G3D 0x0c00
#define CLKOUT_CMU_G3D_DIV_STAT 0x0c04
#define G3DCLK_STOPCTRL 0x1000
#define G3D_EMA_CTRL 0x1008
#define G3D_EMA_STATUS 0x100c
/*
*Registers for CMU_GSCL
*/
#define MUX_SEL_GSCL 0x0200
#define MUX_EN_GSCL 0x0300
#define MUX_STAT_GSCL 0x0400
#define MUX_IGNORE_GSCL 0x0500
#define DIV_GSCL 0x0600
#define DIV_STAT_GSCL 0x0700
#define EN_ACLK_GSCL 0x0800
#define EN_ACLK_GSCL_FIMC 0x0804
#define EN_ACLK_GSCL_SECURE_SMMU_GSCL0 0x0808
#define EN_ACLK_GSCL_SECURE_SMMU_GSCL1 0x080c
#define EN_ACLK_GSCL_SECURE_SMMU_MSCL0 0x0810
#define EN_ACLK_GSCL_SECURE_SMMU_MSCL1 0x0814
#define EN_PCLK_GSCL 0x0900
#define EN_PCLK_GSCL_FIMC 0x0904
#define EN_PCLK_GSCL_SECURE_SMMU_GSCL0 0x0908
#define EN_PCLK_GSCL_SECURE_SMMU_GSCL1 0x090c
#define EN_PCLK_GSCL_SECURE_SMMU_MSCL0 0x0910
#define EN_PCLK_GSCL_SECURE_SMMU_MSCL1 0x0914
#define EN_SCLK_GSCL 0x0a00
#define EN_SCLK_GSCL_FIMC 0x0a04
#define EN_IP_GSCL 0x0b00
#define EN_IP_GSCL_FIMC 0x0b04
#define EN_IP_GSCL_SECURE_SMMU_GSCL0 0x0b08
#define EN_IP_GSCL_SECURE_SMMU_GSCL1 0x0b0c
#define EN_IP_GSCL_SECURE_SMMU_MSCL0 0x0b10
#define EN_IP_GSCL_SECURE_SMMU_MSCL1 0x0b14
/*
*Registers for CMU_ISP
*/
#define MUX_SEL_ISP0 0x0200
#define MUX_SEL_ISP1 0x0204
#define MUX_ENABLE_ISP0 0x0300
#define MUX_ENABLE_ISP1 0x0304
#define MUX_STAT_ISP0 0x0400
#define MUX_STAT_ISP1 0x0404
#define MUX_IGNORE_ISP0 0x0500
#define MUX_IGNORE_ISP1 0x0504
#define DIV_ISP 0x0600
#define DIV_STAT_ISP 0x0700
#define EN_ACLK_ISP0 0x0800
#define EN_ACLK_ISP1 0x0804
#define EN_PCLK_ISP0 0x0900
#define EN_PCLK_ISP1 0x0904
#define EN_SCLK_ISP 0x0a00
#define EN_IP_ISP0 0x0b00
#define EN_IP_ISP1 0x0b04
/*
*Registers for CMU_KFC
*/
#define KFC_PLL_LOCK 0x0000
#define KFC_PLL_CON0 0x0100
#define KFC_PLL_CON1 0x0104
#define KFC_PLL_FDET 0x010c
#define MUX_SEL_KFC0 0x0200
#define MUX_SEL_KFC2 0x0208
#define MUX_ENABLE_KFC0 0x0300
#define MUX_ENABLE_KFC2 0x0308
#define MUX_STAT_KFC0 0x0400
#define MUX_STAT_KFC2 0x0408
#define DIV_KFC 0x0600
#define DIV_KFC_PLL_FDET 0x0604
#define DIV_STAT_KFC 0x0700
#define DIV_STAT_KFC_PLL_FDET 0x0704
#define EN_ACLK_KFC 0x0800
#define EN_PCLK_KFC 0x0900
#define EN_SCLK_KFC 0x0a00
#define EN_IP_KFC 0x0b00
#define CLKOUT_CMU_KFC 0x0c00
#define CLKOUT_CMU_KFC_DIV_STAT 0x0c04
#define ARMCLK_STOPCTRL_KFC 0x1000
#define ARM_EMA_CTRL 0x1008
#define ARM_EMA_STATUS 0x100c
#define PWR_CTRL_KFC 0x1020
#define PWR_CTRL2_KFC 0x1024
#define CLKSTOP_CTRL_KFC 0x1028
#define INTR_SPREAD_ENABLE_KFC 0x1080
#define INTR_SPREAD_USE_STANDBYWFI_KFC 0x1084
#define INTR_SPREAD_BLOCKING_DURATION_KFC 0x1088
#define CMU_KFC_SPARE0 0x2000
#define CMU_KFC_SPARE1 0x2004
#define CMU_KFC_SPARE2 0x2008
#define CMU_KFC_SPARE3 0x200c
#define CMU_KFC_SPARE4 0x2010
/*
*Registers for CMU_MFC
*/
#define MUX_SEL_MFC 0x0200
#define MUX_ENABLE_MFC 0x0300
#define MUX_STAT_MFC 0x0400
#define DIV_MFC 0x0600
#define DIV_STAT_MFC 0x0700
#define EN_ACLK_MFC 0x0800
#define EN_ACLK_SECURE_SMMU2_MFC 0x0804
#define EN_PCLK_MFC 0x0900
#define EN_PCLK_SECURE_SMMU2_MFC 0x0904
#define EN_IP_MFC 0x0b00
#define EN_IP_MFC_SECURE_SMMU2_MFC 0x0b04
/*
*Registers for CMU_MIF
*/
#define MEM_PLL_LOCK 0x0000
#define BUS_PLL_LOCK 0x0004
#define MEDIA_PLL_LOCK 0x0008
#define MEM_PLL_CON0 0x0100
#define MEM_PLL_CON1 0x0104
#define MEM_PLL_FDET 0x010c
#define BUS_PLL_CON0 0x0110
#define BUS_PLL_CON1 0x0114
#define BUS_PLL_FDET 0x011c
#define MEDIA_PLL_CON0 0x0120
#define MEDIA_PLL_CON1 0x0124
#define MEDIA_PLL_FDET 0x012c
#define MUX_SEL_MIF 0x0200
#define MUX_ENABLE_MIF 0x0300
#define MUX_STAT_MIF 0x0400
#define MUX_IGNORE_MIF 0x0500
#define DIV_MIF 0x0600
#define DIV_MIF_PLL_FDET 0x0604
#define DIV_STAT_MIF 0x0700
#define DIV_STAT_MIF_PLL_FDET 0x0704
#define EN_ACLK_MIF 0x0800
#define EN_ACLK_MIF_SECURE_DREX1_TZ 0x0804
#define EN_ACLK_MIF_SECURE_DREX0_TZ 0x0808
#define EN_ACLK_MIF_SECURE_INTMEM 0x080c
#define EN_PCLK_MIF 0x0900
#define EN_PCLK_MIF_SECURE_MONOCNT 0x0904
#define EN_PCLK_MIF_SECURE_RTC_APBIF 0x0908
#define EN_PCLK_MIF_SECURE_DREX1_TZ 0x090c
#define EN_PCLK_MIF_SECURE_DREX0_TZ 0x0910
#define EN_SCLK_MIF 0x0a00
#define EN_IP_MIF 0x0b00
#define EN_IP_MIF_SECURE_MONOCNT 0x0b04
#define EN_IP_MIF_SECURE_RTC_APBIF 0x0b08
#define EN_IP_MIF_SECURE_DREX1_TZ 0x0b0c
#define EN_IP_MIF_SECURE_DREX0_TZ 0x0b10
#define EN_IP_MIF_SECURE_INTEMEM 0x0b14
#define CLKOUT_CMU_MIF_DIV_STAT 0x0c04
#define DREX_FREQ_CTRL 0x1000
#define PAUSE 0x1004
#define DDRPHY_LOCK_CTRL 0x1008
#define CLKOUT_CMU_MIF 0xcb00
/*
*Registers for CMU_PERI
*/
#define MUX_SEL_PERI 0x0200
#define MUX_SEL_PERI1 0x0204
#define MUX_ENABLE_PERI 0x0300
#define MUX_ENABLE_PERI1 0x0304
#define MUX_STAT_PERI 0x0400
#define MUX_STAT_PERI1 0x0404
#define MUX_IGNORE_PERI 0x0500
#define MUX_IGNORE_PERI1 0x0504
#define DIV_PERI 0x0600
#define DIV_STAT_PERI 0x0700
#define EN_PCLK_PERI0 0x0800
#define EN_PCLK_PERI1 0x0804
#define EN_PCLK_PERI2 0x0808
#define EN_PCLK_PERI3 0x080c
#define EN_PCLK_PERI_SECURE_CHIPID 0x0810
#define EN_PCLK_PERI_SECURE_PROVKEY0 0x0814
#define EN_PCLK_PERI_SECURE_PROVKEY1 0x0818
#define EN_PCLK_PERI_SECURE_SECKEY 0x081c
#define EN_PCLK_PERI_SECURE_ANTIRBKCNT 0x0820
#define EN_PCLK_PERI_SECURE_TOP_RTC 0x0824
#define EN_PCLK_PERI_SECURE_TZPC 0x0828
#define EN_SCLK_PERI 0x0a00
#define EN_SCLK_PERI_SECURE_TOP_RTC 0x0a04
#define EN_IP_PERI0 0x0b00
#define EN_IP_PERI1 0x0b04
#define EN_IP_PERI2 0x0b08
#define EN_IP_PERI_SECURE_CHIPID 0x0b0c
#define EN_IP_PERI_SECURE_PROVKEY0 0x0b10
#define EN_IP_PERI_SECURE_PROVKEY1 0x0b14
#define EN_IP_PERI_SECURE_SECKEY 0x0b18
#define EN_IP_PERI_SECURE_ANTIRBKCNT 0x0b1c
#define EN_IP_PERI_SECURE_TOP_RTC 0x0b20
#define EN_IP_PERI_SECURE_TZPC 0x0b24
/*
*Registers for CMU_TOP
*/
#define DISP_PLL_LOCK 0x0000
#define AUD_PLL_LOCK 0x0004
#define DISP_PLL_CON0 0x0100
#define DISP_PLL_CON1 0x0104
#define DISP_PLL_FDET 0x0108
#define AUD_PLL_CON0 0x0110
#define AUD_PLL_CON1 0x0114
#define AUD_PLL_CON2 0x0118
#define AUD_PLL_FDET 0x011c
#define MUX_SEL_TOP_PLL0 0x0200
#define MUX_SEL_TOP_MFC 0x0204
#define MUX_SEL_TOP_G2D 0x0208
#define MUX_SEL_TOP_GSCL 0x020c
#define MUX_SEL_TOP_ISP10 0x0214
#define MUX_SEL_TOP_ISP11 0x0218
#define MUX_SEL_TOP_DISP0 0x021c
#define MUX_SEL_TOP_DISP1 0x0220
#define MUX_SEL_TOP_BUS 0x0224
#define MUX_SEL_TOP_PERI0 0x0228
#define MUX_SEL_TOP_PERI1 0x022c
#define MUX_SEL_TOP_FSYS 0x0230
#define MUX_ENABLE_TOP_PLL0 0x0300
#define MUX_ENABLE_TOP_MFC 0x0304
#define MUX_ENABLE_TOP_G2D 0x0308
#define MUX_ENABLE_TOP_GSCL 0x030c
#define MUX_ENABLE_TOP_ISP10 0x0314
#define MUX_ENABLE_TOP_ISP11 0x0318
#define MUX_ENABLE_TOP_DISP0 0x031c
#define MUX_ENABLE_TOP_DISP1 0x0320
#define MUX_ENABLE_TOP_BUS 0x0324
#define MUX_ENABLE_TOP_PERI0 0x0328
#define MUX_ENABLE_TOP_PERI1 0x032c
#define MUX_ENABLE_TOP_FSYS 0x0330
#define MUX_STAT_TOP_PLL0 0x0400
#define MUX_STAT_TOP_MFC 0x0404
#define MUX_STAT_TOP_G2D 0x0408
#define MUX_STAT_TOP_GSCL 0x040c
#define MUX_STAT_TOP_ISP10 0x0414
#define MUX_STAT_TOP_ISP11 0x0418
#define MUX_STAT_TOP_DISP0 0x041c
#define MUX_STAT_TOP_DISP1 0x0420
#define MUX_STAT_TOP_BUS 0x0424
#define MUX_STAT_TOP_PERI0 0x0428
#define MUX_STAT_TOP_PERI1 0x042c
#define MUX_STAT_TOP_FSYS 0x0430
#define MUX_IGNORE_TOP_PLL0 0x0500
#define MUX_IGNORE_TOP_MFC 0x0504
#define MUX_IGNORE_TOP_G2D 0x0508
#define MUX_IGNORE_TOP_GSCL 0x050c
#define MUX_IGNORE_TOP_ISP10 0x0514
#define MUX_IGNORE_TOP_ISP11 0x0518
#define MUX_IGNORE_TOP_DISP0 0x051c
#define MUX_IGNORE_TOP_DISP1 0x0520
#define MUX_IGNORE_TOP_BUS 0x0524
#define MUX_IGNORE_TOP_PERI0 0x0528
#define MUX_IGNORE_TOP_PERI1 0x052c
#define MUX_IGNORE_TOP_FSYS 0x0530
#define DIV_TOP_G2D_MFC 0x0600
#define DIV_TOP_GSCL_ISP0 0x0604
#define DIV_TOP_ISP10 0x0608
#define DIV_TOP_ISP11 0x060c
#define DIV_TOP_DISP 0x0610
#define DIV_TOP_BUS 0x0614
#define DIV_TOP_PERI0 0x0618
#define DIV_TOP_PERI1 0x061c
#define DIV_TOP_PERI2 0x0620
#define DIV_TOP_FSYS0 0x0624
#define DIV_TOP_FSYS1 0x0628
#define DIV_TOP_HPM 0x062c
#define DIV_TOP_PLL_FDET 0x0630
#define DIV_STAT_TOP_G2D_MFC 0x0700
#define DIV_STAT_TOP_GSCL_ISP0 0x0704
#define DIV_STAT_TOP_ISP10 0x0708
#define DIV_STAT_TOP_ISP11 0x070c
#define DIV_STAT_TOP_DISP 0x0710
#define DIV_STAT_TOP_BUS 0x0714
#define DIV_STAT_TOP_PERI0 0x0718
#define DIV_STAT_TOP_PERI1 0x071c
#define DIV_STAT_TOP_PERI2 0x0720
#define DIV_STAT_TOP_FSYS0 0x0724
#define DIV_STAT_TOP_FSYS1 0x0728
#define DIV_STAT_TOP_HPM 0x072c
#define DIV_STAT_TOP_PLL_FDET 0x0730
#define EN_ACLK_TOP 0x0800
#define EN_SCLK_TOP 0x0a00
#define EN_IP_TOP 0x0b00
#define CLKOUT_CMU_TOP 0x0c00
#define CLKOUT_CMU_TOP_DIV_STAT 0x0c04
#endif /*__CLK_EXYNOS5260_H */
@@ -0,0 +1,279 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Author: Tarek Dakhran <t.dakhran@samsung.com>
*
* Common Clock Framework support for Exynos5410 SoC.
*/
#include <dt-bindings/clock/exynos5410.h>
#include <linux/clk-provider.h>
#include <linux/clk.h>
#include "clk.h"
#define APLL_LOCK 0x0
#define APLL_CON0 0x100
#define CPLL_LOCK 0x10020
#define CPLL_CON0 0x10120
#define EPLL_LOCK 0x10040
#define EPLL_CON0 0x10130
#define MPLL_LOCK 0x4000
#define MPLL_CON0 0x4100
#define BPLL_LOCK 0x20010
#define BPLL_CON0 0x20110
#define KPLL_LOCK 0x28000
#define KPLL_CON0 0x28100
#define SRC_CPU 0x200
#define DIV_CPU0 0x500
#define SRC_CPERI1 0x4204
#define GATE_IP_G2D 0x8800
#define DIV_TOP0 0x10510
#define DIV_TOP1 0x10514
#define DIV_FSYS0 0x10548
#define DIV_FSYS1 0x1054c
#define DIV_FSYS2 0x10550
#define DIV_PERIC0 0x10558
#define DIV_PERIC3 0x10564
#define SRC_TOP0 0x10210
#define SRC_TOP1 0x10214
#define SRC_TOP2 0x10218
#define SRC_FSYS 0x10244
#define SRC_PERIC0 0x10250
#define SRC_MASK_FSYS 0x10340
#define SRC_MASK_PERIC0 0x10350
#define GATE_BUS_FSYS0 0x10740
#define GATE_TOP_SCLK_FSYS 0x10840
#define GATE_TOP_SCLK_PERIC 0x10850
#define GATE_IP_FSYS 0x10944
#define GATE_IP_PERIC 0x10950
#define GATE_IP_PERIS 0x10960
#define SRC_CDREX 0x20200
#define SRC_KFC 0x28200
#define DIV_KFC0 0x28500
/* NOTE: Must be equal to the last clock ID increased by one */
#define CLKS_NR 512
/* list of PLLs */
enum exynos5410_plls {
apll, cpll, epll, mpll,
bpll, kpll,
nr_plls /* number of PLLs */
};
/* list of all parent clocks */
PNAME(apll_p) = { "fin_pll", "fout_apll", };
PNAME(bpll_p) = { "fin_pll", "fout_bpll", };
PNAME(cpll_p) = { "fin_pll", "fout_cpll" };
PNAME(epll_p) = { "fin_pll", "fout_epll" };
PNAME(mpll_p) = { "fin_pll", "fout_mpll", };
PNAME(kpll_p) = { "fin_pll", "fout_kpll", };
PNAME(mout_cpu_p) = { "mout_apll", "sclk_mpll", };
PNAME(mout_kfc_p) = { "mout_kpll", "sclk_mpll", };
PNAME(mpll_user_p) = { "fin_pll", "sclk_mpll", };
PNAME(bpll_user_p) = { "fin_pll", "sclk_bpll", };
PNAME(mpll_bpll_p) = { "sclk_mpll_muxed", "sclk_bpll_muxed", };
PNAME(sclk_mpll_bpll_p) = { "sclk_mpll_bpll", "fin_pll", };
PNAME(group2_p) = { "fin_pll", "fin_pll", "none", "none",
"none", "none", "sclk_mpll_bpll",
"none", "none", "sclk_cpll" };
static const struct samsung_mux_clock exynos5410_mux_clks[] __initconst = {
MUX(0, "mout_apll", apll_p, SRC_CPU, 0, 1),
MUX(0, "mout_cpu", mout_cpu_p, SRC_CPU, 16, 1),
MUX(0, "mout_kpll", kpll_p, SRC_KFC, 0, 1),
MUX(0, "mout_kfc", mout_kfc_p, SRC_KFC, 16, 1),
MUX(0, "sclk_mpll", mpll_p, SRC_CPERI1, 8, 1),
MUX(0, "sclk_mpll_muxed", mpll_user_p, SRC_TOP2, 20, 1),
MUX(0, "sclk_bpll", bpll_p, SRC_CDREX, 0, 1),
MUX(0, "sclk_bpll_muxed", bpll_user_p, SRC_TOP2, 24, 1),
MUX(0, "sclk_epll", epll_p, SRC_TOP2, 12, 1),
MUX(0, "sclk_cpll", cpll_p, SRC_TOP2, 8, 1),
MUX(0, "sclk_mpll_bpll", mpll_bpll_p, SRC_TOP1, 20, 1),
MUX(0, "mout_mmc0", group2_p, SRC_FSYS, 0, 4),
MUX(0, "mout_mmc1", group2_p, SRC_FSYS, 4, 4),
MUX(0, "mout_mmc2", group2_p, SRC_FSYS, 8, 4),
MUX(0, "mout_usbd300", sclk_mpll_bpll_p, SRC_FSYS, 28, 1),
MUX(0, "mout_usbd301", sclk_mpll_bpll_p, SRC_FSYS, 29, 1),
MUX(0, "mout_uart0", group2_p, SRC_PERIC0, 0, 4),
MUX(0, "mout_uart1", group2_p, SRC_PERIC0, 4, 4),
MUX(0, "mout_uart2", group2_p, SRC_PERIC0, 8, 4),
MUX(0, "mout_uart3", group2_p, SRC_PERIC0, 12, 4),
MUX(0, "mout_pwm", group2_p, SRC_PERIC0, 24, 4),
MUX(0, "mout_aclk200", mpll_bpll_p, SRC_TOP0, 12, 1),
MUX(0, "mout_aclk400", mpll_bpll_p, SRC_TOP0, 20, 1),
};
static const struct samsung_div_clock exynos5410_div_clks[] __initconst = {
DIV(0, "div_arm", "mout_cpu", DIV_CPU0, 0, 3),
DIV(0, "div_arm2", "div_arm", DIV_CPU0, 28, 3),
DIV(0, "div_acp", "div_arm2", DIV_CPU0, 8, 3),
DIV(0, "div_cpud", "div_arm2", DIV_CPU0, 4, 3),
DIV(0, "div_atb", "div_arm2", DIV_CPU0, 16, 3),
DIV(0, "pclk_dbg", "div_arm2", DIV_CPU0, 20, 3),
DIV(0, "div_kfc", "mout_kfc", DIV_KFC0, 0, 3),
DIV(0, "div_aclk", "div_kfc", DIV_KFC0, 4, 3),
DIV(0, "div_pclk", "div_kfc", DIV_KFC0, 20, 3),
DIV(0, "aclk66_pre", "sclk_mpll_muxed", DIV_TOP1, 24, 3),
DIV(0, "aclk66", "aclk66_pre", DIV_TOP0, 0, 3),
DIV(0, "dout_usbphy300", "mout_usbd300", DIV_FSYS0, 16, 4),
DIV(0, "dout_usbphy301", "mout_usbd301", DIV_FSYS0, 20, 4),
DIV(0, "dout_usbd300", "mout_usbd300", DIV_FSYS0, 24, 4),
DIV(0, "dout_usbd301", "mout_usbd301", DIV_FSYS0, 28, 4),
DIV(0, "div_mmc0", "mout_mmc0", DIV_FSYS1, 0, 4),
DIV(0, "div_mmc1", "mout_mmc1", DIV_FSYS1, 16, 4),
DIV(0, "div_mmc2", "mout_mmc2", DIV_FSYS2, 0, 4),
DIV_F(0, "div_mmc_pre0", "div_mmc0",
DIV_FSYS1, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(0, "div_mmc_pre1", "div_mmc1",
DIV_FSYS1, 24, 8, CLK_SET_RATE_PARENT, 0),
DIV_F(0, "div_mmc_pre2", "div_mmc2",
DIV_FSYS2, 8, 8, CLK_SET_RATE_PARENT, 0),
DIV(0, "div_uart0", "mout_uart0", DIV_PERIC0, 0, 4),
DIV(0, "div_uart1", "mout_uart1", DIV_PERIC0, 4, 4),
DIV(0, "div_uart2", "mout_uart2", DIV_PERIC0, 8, 4),
DIV(0, "div_uart3", "mout_uart3", DIV_PERIC0, 12, 4),
DIV(0, "dout_pwm", "mout_pwm", DIV_PERIC3, 0, 4),
DIV(0, "aclk200", "mout_aclk200", DIV_TOP0, 12, 3),
DIV(0, "aclk266", "mpll_user_p", DIV_TOP0, 16, 3),
DIV(0, "aclk400", "mout_aclk400", DIV_TOP0, 24, 3),
};
static const struct samsung_gate_clock exynos5410_gate_clks[] __initconst = {
GATE(CLK_SSS, "sss", "aclk266", GATE_IP_G2D, 2, 0, 0),
GATE(CLK_MCT, "mct", "aclk66", GATE_IP_PERIS, 18, 0, 0),
GATE(CLK_WDT, "wdt", "aclk66", GATE_IP_PERIS, 19, 0, 0),
GATE(CLK_RTC, "rtc", "aclk66", GATE_IP_PERIS, 20, 0, 0),
GATE(CLK_TMU, "tmu", "aclk66", GATE_IP_PERIS, 21, 0, 0),
GATE(CLK_SCLK_MMC0, "sclk_mmc0", "div_mmc_pre0",
SRC_MASK_FSYS, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC1, "sclk_mmc1", "div_mmc_pre1",
SRC_MASK_FSYS, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_MMC2, "sclk_mmc2", "div_mmc_pre2",
SRC_MASK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_MMC0, "sdmmc0", "aclk200", GATE_BUS_FSYS0, 12, 0, 0),
GATE(CLK_MMC1, "sdmmc1", "aclk200", GATE_BUS_FSYS0, 13, 0, 0),
GATE(CLK_MMC2, "sdmmc2", "aclk200", GATE_BUS_FSYS0, 14, 0, 0),
GATE(CLK_PDMA1, "pdma1", "aclk200", GATE_BUS_FSYS0, 2, 0, 0),
GATE(CLK_PDMA0, "pdma0", "aclk200", GATE_BUS_FSYS0, 1, 0, 0),
GATE(CLK_SCLK_USBPHY301, "sclk_usbphy301", "dout_usbphy301",
GATE_TOP_SCLK_FSYS, 7, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_USBPHY300, "sclk_usbphy300", "dout_usbphy300",
GATE_TOP_SCLK_FSYS, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_USBD300, "sclk_usbd300", "dout_usbd300",
GATE_TOP_SCLK_FSYS, 9, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_USBD301, "sclk_usbd301", "dout_usbd301",
GATE_TOP_SCLK_FSYS, 10, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_PWM, "sclk_pwm", "dout_pwm",
GATE_TOP_SCLK_PERIC, 11, CLK_SET_RATE_PARENT, 0),
GATE(CLK_UART0, "uart0", "aclk66", GATE_IP_PERIC, 0, 0, 0),
GATE(CLK_UART1, "uart1", "aclk66", GATE_IP_PERIC, 1, 0, 0),
GATE(CLK_UART2, "uart2", "aclk66", GATE_IP_PERIC, 2, 0, 0),
GATE(CLK_UART3, "uart3", "aclk66", GATE_IP_PERIC, 3, 0, 0),
GATE(CLK_I2C0, "i2c0", "aclk66", GATE_IP_PERIC, 6, 0, 0),
GATE(CLK_I2C1, "i2c1", "aclk66", GATE_IP_PERIC, 7, 0, 0),
GATE(CLK_I2C2, "i2c2", "aclk66", GATE_IP_PERIC, 8, 0, 0),
GATE(CLK_I2C3, "i2c3", "aclk66", GATE_IP_PERIC, 9, 0, 0),
GATE(CLK_USI0, "usi0", "aclk66", GATE_IP_PERIC, 10, 0, 0),
GATE(CLK_USI1, "usi1", "aclk66", GATE_IP_PERIC, 11, 0, 0),
GATE(CLK_USI2, "usi2", "aclk66", GATE_IP_PERIC, 12, 0, 0),
GATE(CLK_USI3, "usi3", "aclk66", GATE_IP_PERIC, 13, 0, 0),
GATE(CLK_TSADC, "tsadc", "aclk66", GATE_IP_PERIC, 15, 0, 0),
GATE(CLK_PWM, "pwm", "aclk66", GATE_IP_PERIC, 24, 0, 0),
GATE(CLK_SCLK_UART0, "sclk_uart0", "div_uart0",
SRC_MASK_PERIC0, 0, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART1, "sclk_uart1", "div_uart1",
SRC_MASK_PERIC0, 4, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART2, "sclk_uart2", "div_uart2",
SRC_MASK_PERIC0, 8, CLK_SET_RATE_PARENT, 0),
GATE(CLK_SCLK_UART3, "sclk_uart3", "div_uart3",
SRC_MASK_PERIC0, 12, CLK_SET_RATE_PARENT, 0),
GATE(CLK_USBH20, "usbh20", "aclk200_fsys", GATE_IP_FSYS, 18, 0, 0),
GATE(CLK_USBD300, "usbd300", "aclk200_fsys", GATE_IP_FSYS, 19, 0, 0),
GATE(CLK_USBD301, "usbd301", "aclk200_fsys", GATE_IP_FSYS, 20, 0, 0),
};
static const struct samsung_pll_rate_table exynos5410_pll2550x_24mhz_tbl[] __initconst = {
PLL_36XX_RATE(24 * MHZ, 400000000U, 200, 3, 2, 0),
PLL_36XX_RATE(24 * MHZ, 333000000U, 111, 2, 2, 0),
PLL_36XX_RATE(24 * MHZ, 300000000U, 100, 2, 2, 0),
PLL_36XX_RATE(24 * MHZ, 266000000U, 266, 3, 3, 0),
PLL_36XX_RATE(24 * MHZ, 200000000U, 200, 3, 3, 0),
PLL_36XX_RATE(24 * MHZ, 192000000U, 192, 3, 3, 0),
PLL_36XX_RATE(24 * MHZ, 166000000U, 166, 3, 3, 0),
PLL_36XX_RATE(24 * MHZ, 133000000U, 266, 3, 4, 0),
PLL_36XX_RATE(24 * MHZ, 100000000U, 200, 3, 4, 0),
PLL_36XX_RATE(24 * MHZ, 66000000U, 176, 2, 5, 0),
};
static struct samsung_pll_clock exynos5410_plls[nr_plls] __initdata = {
[apll] = PLL(pll_35xx, CLK_FOUT_APLL, "fout_apll", "fin_pll", APLL_LOCK,
APLL_CON0, NULL),
[cpll] = PLL(pll_35xx, CLK_FOUT_CPLL, "fout_cpll", "fin_pll", CPLL_LOCK,
CPLL_CON0, NULL),
[epll] = PLL(pll_2650x, CLK_FOUT_EPLL, "fout_epll", "fin_pll", EPLL_LOCK,
EPLL_CON0, NULL),
[mpll] = PLL(pll_35xx, CLK_FOUT_MPLL, "fout_mpll", "fin_pll", MPLL_LOCK,
MPLL_CON0, NULL),
[bpll] = PLL(pll_35xx, CLK_FOUT_BPLL, "fout_bpll", "fin_pll", BPLL_LOCK,
BPLL_CON0, NULL),
[kpll] = PLL(pll_35xx, CLK_FOUT_KPLL, "fout_kpll", "fin_pll", KPLL_LOCK,
KPLL_CON0, NULL),
};
static const struct samsung_cmu_info cmu __initconst = {
.pll_clks = exynos5410_plls,
.nr_pll_clks = ARRAY_SIZE(exynos5410_plls),
.mux_clks = exynos5410_mux_clks,
.nr_mux_clks = ARRAY_SIZE(exynos5410_mux_clks),
.div_clks = exynos5410_div_clks,
.nr_div_clks = ARRAY_SIZE(exynos5410_div_clks),
.gate_clks = exynos5410_gate_clks,
.nr_gate_clks = ARRAY_SIZE(exynos5410_gate_clks),
.nr_clk_ids = CLKS_NR,
};
/* register exynos5410 clocks */
static void __init exynos5410_clk_init(struct device_node *np)
{
struct clk *xxti = of_clk_get(np, 0);
if (!IS_ERR(xxti) && clk_get_rate(xxti) == 24 * MHZ)
exynos5410_plls[epll].rate_table = exynos5410_pll2550x_24mhz_tbl;
samsung_cmu_register_one(np, &cmu);
pr_debug("Exynos5410: clock setup completed.\n");
}
CLK_OF_DECLARE(exynos5410_clk, "samsung,exynos5410-clock", exynos5410_clk_init);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,847 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2021 Dávid Virág <virag.david003@gmail.com>
* Author: Dávid Virág <virag.david003@gmail.com>
*
* Common Clock Framework support for Exynos7885 SoC.
*/
#include <linux/clk-provider.h>
#include <linux/of.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/exynos7885.h>
#include "clk.h"
#include "clk-exynos-arm64.h"
/* NOTE: Must be equal to the last clock ID increased by one */
#define CLKS_NR_TOP (CLK_MOUT_SHARED1_PLL + 1)
#define CLKS_NR_CORE (CLK_GOUT_TREX_P_CORE_PCLK_P_CORE + 1)
#define CLKS_NR_PERI (CLK_GOUT_WDT1_PCLK + 1)
#define CLKS_NR_FSYS (CLK_FSYS_USB30DRD_REF_CLK + 1)
/* ---- CMU_TOP ------------------------------------------------------------- */
/* Register Offset definitions for CMU_TOP (0x12060000) */
#define PLL_LOCKTIME_PLL_SHARED0 0x0000
#define PLL_LOCKTIME_PLL_SHARED1 0x0004
#define PLL_CON0_PLL_SHARED0 0x0100
#define PLL_CON0_PLL_SHARED1 0x0120
#define CLK_CON_MUX_MUX_CLKCMU_CORE_BUS 0x1014
#define CLK_CON_MUX_MUX_CLKCMU_CORE_CCI 0x1018
#define CLK_CON_MUX_MUX_CLKCMU_CORE_G3D 0x101c
#define CLK_CON_MUX_MUX_CLKCMU_FSYS_BUS 0x1028
#define CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_CARD 0x102c
#define CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_EMBD 0x1030
#define CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_SDIO 0x1034
#define CLK_CON_MUX_MUX_CLKCMU_FSYS_USB30DRD 0x1038
#define CLK_CON_MUX_MUX_CLKCMU_PERI_BUS 0x1058
#define CLK_CON_MUX_MUX_CLKCMU_PERI_SPI0 0x105c
#define CLK_CON_MUX_MUX_CLKCMU_PERI_SPI1 0x1060
#define CLK_CON_MUX_MUX_CLKCMU_PERI_UART0 0x1064
#define CLK_CON_MUX_MUX_CLKCMU_PERI_UART1 0x1068
#define CLK_CON_MUX_MUX_CLKCMU_PERI_UART2 0x106c
#define CLK_CON_MUX_MUX_CLKCMU_PERI_USI0 0x1070
#define CLK_CON_MUX_MUX_CLKCMU_PERI_USI1 0x1074
#define CLK_CON_MUX_MUX_CLKCMU_PERI_USI2 0x1078
#define CLK_CON_DIV_CLKCMU_CORE_BUS 0x181c
#define CLK_CON_DIV_CLKCMU_CORE_CCI 0x1820
#define CLK_CON_DIV_CLKCMU_CORE_G3D 0x1824
#define CLK_CON_DIV_CLKCMU_FSYS_BUS 0x1844
#define CLK_CON_DIV_CLKCMU_FSYS_MMC_CARD 0x1848
#define CLK_CON_DIV_CLKCMU_FSYS_MMC_EMBD 0x184c
#define CLK_CON_DIV_CLKCMU_FSYS_MMC_SDIO 0x1850
#define CLK_CON_DIV_CLKCMU_FSYS_USB30DRD 0x1854
#define CLK_CON_DIV_CLKCMU_PERI_BUS 0x1874
#define CLK_CON_DIV_CLKCMU_PERI_SPI0 0x1878
#define CLK_CON_DIV_CLKCMU_PERI_SPI1 0x187c
#define CLK_CON_DIV_CLKCMU_PERI_UART0 0x1880
#define CLK_CON_DIV_CLKCMU_PERI_UART1 0x1884
#define CLK_CON_DIV_CLKCMU_PERI_UART2 0x1888
#define CLK_CON_DIV_CLKCMU_PERI_USI0 0x188c
#define CLK_CON_DIV_CLKCMU_PERI_USI1 0x1890
#define CLK_CON_DIV_CLKCMU_PERI_USI2 0x1894
#define CLK_CON_DIV_PLL_SHARED0_DIV2 0x189c
#define CLK_CON_DIV_PLL_SHARED0_DIV3 0x18a0
#define CLK_CON_DIV_PLL_SHARED0_DIV4 0x18a4
#define CLK_CON_DIV_PLL_SHARED0_DIV5 0x18a8
#define CLK_CON_DIV_PLL_SHARED1_DIV2 0x18ac
#define CLK_CON_DIV_PLL_SHARED1_DIV3 0x18b0
#define CLK_CON_DIV_PLL_SHARED1_DIV4 0x18b4
#define CLK_CON_GAT_GATE_CLKCMUC_PERI_UART1 0x2004
#define CLK_CON_GAT_GATE_CLKCMU_CORE_BUS 0x201c
#define CLK_CON_GAT_GATE_CLKCMU_CORE_CCI 0x2020
#define CLK_CON_GAT_GATE_CLKCMU_CORE_G3D 0x2024
#define CLK_CON_GAT_GATE_CLKCMU_FSYS_BUS 0x2044
#define CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_CARD 0x2048
#define CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_EMBD 0x204c
#define CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_SDIO 0x2050
#define CLK_CON_GAT_GATE_CLKCMU_FSYS_USB30DRD 0x2054
#define CLK_CON_GAT_GATE_CLKCMU_PERI_BUS 0x207c
#define CLK_CON_GAT_GATE_CLKCMU_PERI_SPI0 0x2080
#define CLK_CON_GAT_GATE_CLKCMU_PERI_SPI1 0x2084
#define CLK_CON_GAT_GATE_CLKCMU_PERI_UART0 0x2088
#define CLK_CON_GAT_GATE_CLKCMU_PERI_UART2 0x208c
#define CLK_CON_GAT_GATE_CLKCMU_PERI_USI0 0x2090
#define CLK_CON_GAT_GATE_CLKCMU_PERI_USI1 0x2094
#define CLK_CON_GAT_GATE_CLKCMU_PERI_USI2 0x2098
static const unsigned long top_clk_regs[] __initconst = {
PLL_LOCKTIME_PLL_SHARED0,
PLL_LOCKTIME_PLL_SHARED1,
PLL_CON0_PLL_SHARED0,
PLL_CON0_PLL_SHARED1,
CLK_CON_MUX_MUX_CLKCMU_CORE_BUS,
CLK_CON_MUX_MUX_CLKCMU_CORE_CCI,
CLK_CON_MUX_MUX_CLKCMU_CORE_G3D,
CLK_CON_MUX_MUX_CLKCMU_FSYS_BUS,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_CARD,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_EMBD,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_SDIO,
CLK_CON_MUX_MUX_CLKCMU_FSYS_USB30DRD,
CLK_CON_MUX_MUX_CLKCMU_PERI_BUS,
CLK_CON_MUX_MUX_CLKCMU_PERI_SPI0,
CLK_CON_MUX_MUX_CLKCMU_PERI_SPI1,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART0,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART1,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART2,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI0,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI1,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI2,
CLK_CON_DIV_CLKCMU_CORE_BUS,
CLK_CON_DIV_CLKCMU_CORE_CCI,
CLK_CON_DIV_CLKCMU_CORE_G3D,
CLK_CON_DIV_CLKCMU_FSYS_BUS,
CLK_CON_DIV_CLKCMU_FSYS_MMC_CARD,
CLK_CON_DIV_CLKCMU_FSYS_MMC_EMBD,
CLK_CON_DIV_CLKCMU_FSYS_MMC_SDIO,
CLK_CON_DIV_CLKCMU_FSYS_USB30DRD,
CLK_CON_DIV_CLKCMU_PERI_BUS,
CLK_CON_DIV_CLKCMU_PERI_SPI0,
CLK_CON_DIV_CLKCMU_PERI_SPI1,
CLK_CON_DIV_CLKCMU_PERI_UART0,
CLK_CON_DIV_CLKCMU_PERI_UART1,
CLK_CON_DIV_CLKCMU_PERI_UART2,
CLK_CON_DIV_CLKCMU_PERI_USI0,
CLK_CON_DIV_CLKCMU_PERI_USI1,
CLK_CON_DIV_CLKCMU_PERI_USI2,
CLK_CON_DIV_PLL_SHARED0_DIV2,
CLK_CON_DIV_PLL_SHARED0_DIV3,
CLK_CON_DIV_PLL_SHARED0_DIV4,
CLK_CON_DIV_PLL_SHARED0_DIV5,
CLK_CON_DIV_PLL_SHARED1_DIV2,
CLK_CON_DIV_PLL_SHARED1_DIV3,
CLK_CON_DIV_PLL_SHARED1_DIV4,
CLK_CON_GAT_GATE_CLKCMUC_PERI_UART1,
CLK_CON_GAT_GATE_CLKCMU_CORE_BUS,
CLK_CON_GAT_GATE_CLKCMU_CORE_CCI,
CLK_CON_GAT_GATE_CLKCMU_CORE_G3D,
CLK_CON_GAT_GATE_CLKCMU_FSYS_BUS,
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_CARD,
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_EMBD,
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_SDIO,
CLK_CON_GAT_GATE_CLKCMU_FSYS_USB30DRD,
CLK_CON_GAT_GATE_CLKCMU_PERI_BUS,
CLK_CON_GAT_GATE_CLKCMU_PERI_SPI0,
CLK_CON_GAT_GATE_CLKCMU_PERI_SPI1,
CLK_CON_GAT_GATE_CLKCMU_PERI_UART0,
CLK_CON_GAT_GATE_CLKCMU_PERI_UART2,
CLK_CON_GAT_GATE_CLKCMU_PERI_USI0,
CLK_CON_GAT_GATE_CLKCMU_PERI_USI1,
CLK_CON_GAT_GATE_CLKCMU_PERI_USI2,
};
static const struct samsung_pll_clock top_pll_clks[] __initconst = {
PLL(pll_1417x, CLK_FOUT_SHARED0_PLL, "fout_shared0_pll", "oscclk",
PLL_LOCKTIME_PLL_SHARED0, PLL_CON0_PLL_SHARED0,
NULL),
PLL(pll_1417x, CLK_FOUT_SHARED1_PLL, "fout_shared1_pll", "oscclk",
PLL_LOCKTIME_PLL_SHARED1, PLL_CON0_PLL_SHARED1,
NULL),
};
/* List of parent clocks for Muxes in CMU_TOP */
PNAME(mout_shared0_pll_p) = { "oscclk", "fout_shared0_pll" };
PNAME(mout_shared1_pll_p) = { "oscclk", "fout_shared1_pll" };
/* List of parent clocks for Muxes in CMU_TOP: for CMU_CORE */
PNAME(mout_core_bus_p) = { "dout_shared0_div2", "dout_shared1_div2",
"dout_shared0_div3", "dout_shared0_div3" };
PNAME(mout_core_cci_p) = { "dout_shared0_div2", "dout_shared1_div2",
"dout_shared0_div3", "dout_shared0_div3" };
PNAME(mout_core_g3d_p) = { "dout_shared0_div2", "dout_shared1_div2",
"dout_shared0_div3", "dout_shared0_div3" };
/* List of parent clocks for Muxes in CMU_TOP: for CMU_PERI */
PNAME(mout_peri_bus_p) = { "dout_shared0_div4", "dout_shared1_div4" };
PNAME(mout_peri_spi0_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_spi1_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_uart0_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_uart1_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_uart2_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_usi0_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_usi1_p) = { "oscclk", "dout_shared0_div4" };
PNAME(mout_peri_usi2_p) = { "oscclk", "dout_shared0_div4" };
/* List of parent clocks for Muxes in CMU_TOP: for CMU_FSYS */
PNAME(mout_fsys_bus_p) = { "dout_shared0_div2", "dout_shared1_div2" };
PNAME(mout_fsys_mmc_card_p) = { "dout_shared0_div2", "dout_shared1_div2" };
PNAME(mout_fsys_mmc_embd_p) = { "dout_shared0_div2", "dout_shared1_div2" };
PNAME(mout_fsys_mmc_sdio_p) = { "dout_shared0_div2", "dout_shared1_div2" };
PNAME(mout_fsys_usb30drd_p) = { "dout_shared0_div4", "dout_shared1_div4" };
static const struct samsung_mux_clock top_mux_clks[] __initconst = {
/* TOP */
MUX(CLK_MOUT_SHARED0_PLL, "mout_shared0_pll", mout_shared0_pll_p,
PLL_CON0_PLL_SHARED0, 4, 1),
MUX(CLK_MOUT_SHARED1_PLL, "mout_shared1_pll", mout_shared1_pll_p,
PLL_CON0_PLL_SHARED1, 4, 1),
/* CORE */
MUX(CLK_MOUT_CORE_BUS, "mout_core_bus", mout_core_bus_p,
CLK_CON_MUX_MUX_CLKCMU_CORE_BUS, 0, 2),
MUX(CLK_MOUT_CORE_CCI, "mout_core_cci", mout_core_cci_p,
CLK_CON_MUX_MUX_CLKCMU_CORE_CCI, 0, 2),
MUX(CLK_MOUT_CORE_G3D, "mout_core_g3d", mout_core_g3d_p,
CLK_CON_MUX_MUX_CLKCMU_CORE_G3D, 0, 2),
/* PERI */
MUX(CLK_MOUT_PERI_BUS, "mout_peri_bus", mout_peri_bus_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_BUS, 0, 1),
MUX(CLK_MOUT_PERI_SPI0, "mout_peri_spi0", mout_peri_spi0_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_SPI0, 0, 1),
MUX(CLK_MOUT_PERI_SPI1, "mout_peri_spi1", mout_peri_spi1_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_SPI1, 0, 1),
MUX(CLK_MOUT_PERI_UART0, "mout_peri_uart0", mout_peri_uart0_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART0, 0, 1),
MUX(CLK_MOUT_PERI_UART1, "mout_peri_uart1", mout_peri_uart1_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART1, 0, 1),
MUX(CLK_MOUT_PERI_UART2, "mout_peri_uart2", mout_peri_uart2_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_UART2, 0, 1),
MUX(CLK_MOUT_PERI_USI0, "mout_peri_usi0", mout_peri_usi0_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI0, 0, 1),
MUX(CLK_MOUT_PERI_USI1, "mout_peri_usi1", mout_peri_usi1_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI1, 0, 1),
MUX(CLK_MOUT_PERI_USI2, "mout_peri_usi2", mout_peri_usi2_p,
CLK_CON_MUX_MUX_CLKCMU_PERI_USI2, 0, 1),
/* FSYS */
MUX(CLK_MOUT_FSYS_BUS, "mout_fsys_bus", mout_fsys_bus_p,
CLK_CON_MUX_MUX_CLKCMU_FSYS_BUS, 0, 1),
MUX(CLK_MOUT_FSYS_MMC_CARD, "mout_fsys_mmc_card", mout_fsys_mmc_card_p,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_CARD, 0, 1),
MUX(CLK_MOUT_FSYS_MMC_EMBD, "mout_fsys_mmc_embd", mout_fsys_mmc_embd_p,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_EMBD, 0, 1),
MUX(CLK_MOUT_FSYS_MMC_SDIO, "mout_fsys_mmc_sdio", mout_fsys_mmc_sdio_p,
CLK_CON_MUX_MUX_CLKCMU_FSYS_MMC_SDIO, 0, 1),
MUX(CLK_MOUT_FSYS_USB30DRD, "mout_fsys_usb30drd", mout_fsys_usb30drd_p,
CLK_CON_MUX_MUX_CLKCMU_FSYS_USB30DRD, 0, 1),
};
static const struct samsung_div_clock top_div_clks[] __initconst = {
/* TOP */
DIV(CLK_DOUT_SHARED0_DIV2, "dout_shared0_div2", "mout_shared0_pll",
CLK_CON_DIV_PLL_SHARED0_DIV2, 0, 1),
DIV(CLK_DOUT_SHARED0_DIV3, "dout_shared0_div3", "mout_shared0_pll",
CLK_CON_DIV_PLL_SHARED0_DIV3, 0, 2),
DIV(CLK_DOUT_SHARED0_DIV4, "dout_shared0_div4", "dout_shared0_div2",
CLK_CON_DIV_PLL_SHARED0_DIV4, 0, 1),
DIV(CLK_DOUT_SHARED0_DIV5, "dout_shared0_div5", "mout_shared0_pll",
CLK_CON_DIV_PLL_SHARED0_DIV5, 0, 3),
DIV(CLK_DOUT_SHARED1_DIV2, "dout_shared1_div2", "mout_shared1_pll",
CLK_CON_DIV_PLL_SHARED1_DIV2, 0, 1),
DIV(CLK_DOUT_SHARED1_DIV3, "dout_shared1_div3", "mout_shared1_pll",
CLK_CON_DIV_PLL_SHARED1_DIV3, 0, 2),
DIV(CLK_DOUT_SHARED1_DIV4, "dout_shared1_div4", "dout_shared1_div2",
CLK_CON_DIV_PLL_SHARED1_DIV4, 0, 1),
/* CORE */
DIV(CLK_DOUT_CORE_BUS, "dout_core_bus", "gout_core_bus",
CLK_CON_DIV_CLKCMU_CORE_BUS, 0, 3),
DIV(CLK_DOUT_CORE_CCI, "dout_core_cci", "gout_core_cci",
CLK_CON_DIV_CLKCMU_CORE_CCI, 0, 3),
DIV(CLK_DOUT_CORE_G3D, "dout_core_g3d", "gout_core_g3d",
CLK_CON_DIV_CLKCMU_CORE_G3D, 0, 3),
/* PERI */
DIV(CLK_DOUT_PERI_BUS, "dout_peri_bus", "gout_peri_bus",
CLK_CON_DIV_CLKCMU_PERI_BUS, 0, 4),
DIV(CLK_DOUT_PERI_SPI0, "dout_peri_spi0", "gout_peri_spi0",
CLK_CON_DIV_CLKCMU_PERI_SPI0, 0, 6),
DIV(CLK_DOUT_PERI_SPI1, "dout_peri_spi1", "gout_peri_spi1",
CLK_CON_DIV_CLKCMU_PERI_SPI1, 0, 6),
DIV(CLK_DOUT_PERI_UART0, "dout_peri_uart0", "gout_peri_uart0",
CLK_CON_DIV_CLKCMU_PERI_UART0, 0, 4),
DIV(CLK_DOUT_PERI_UART1, "dout_peri_uart1", "gout_peri_uart1",
CLK_CON_DIV_CLKCMU_PERI_UART1, 0, 4),
DIV(CLK_DOUT_PERI_UART2, "dout_peri_uart2", "gout_peri_uart2",
CLK_CON_DIV_CLKCMU_PERI_UART2, 0, 4),
DIV(CLK_DOUT_PERI_USI0, "dout_peri_usi0", "gout_peri_usi0",
CLK_CON_DIV_CLKCMU_PERI_USI0, 0, 4),
DIV(CLK_DOUT_PERI_USI1, "dout_peri_usi1", "gout_peri_usi1",
CLK_CON_DIV_CLKCMU_PERI_USI1, 0, 4),
DIV(CLK_DOUT_PERI_USI2, "dout_peri_usi2", "gout_peri_usi2",
CLK_CON_DIV_CLKCMU_PERI_USI2, 0, 4),
/* FSYS */
DIV(CLK_DOUT_FSYS_BUS, "dout_fsys_bus", "gout_fsys_bus",
CLK_CON_DIV_CLKCMU_FSYS_BUS, 0, 4),
DIV(CLK_DOUT_FSYS_MMC_CARD, "dout_fsys_mmc_card", "gout_fsys_mmc_card",
CLK_CON_DIV_CLKCMU_FSYS_MMC_CARD, 0, 9),
DIV(CLK_DOUT_FSYS_MMC_EMBD, "dout_fsys_mmc_embd", "gout_fsys_mmc_embd",
CLK_CON_DIV_CLKCMU_FSYS_MMC_EMBD, 0, 9),
DIV(CLK_DOUT_FSYS_MMC_SDIO, "dout_fsys_mmc_sdio", "gout_fsys_mmc_sdio",
CLK_CON_DIV_CLKCMU_FSYS_MMC_SDIO, 0, 9),
DIV(CLK_DOUT_FSYS_USB30DRD, "dout_fsys_usb30drd", "gout_fsys_usb30drd",
CLK_CON_DIV_CLKCMU_FSYS_USB30DRD, 0, 4),
};
static const struct samsung_gate_clock top_gate_clks[] __initconst = {
/* CORE */
GATE(CLK_GOUT_CORE_BUS, "gout_core_bus", "mout_core_bus",
CLK_CON_GAT_GATE_CLKCMU_CORE_BUS, 21, 0, 0),
GATE(CLK_GOUT_CORE_CCI, "gout_core_cci", "mout_core_cci",
CLK_CON_GAT_GATE_CLKCMU_CORE_CCI, 21, 0, 0),
GATE(CLK_GOUT_CORE_G3D, "gout_core_g3d", "mout_core_g3d",
CLK_CON_GAT_GATE_CLKCMU_CORE_G3D, 21, 0, 0),
/* PERI */
GATE(CLK_GOUT_PERI_BUS, "gout_peri_bus", "mout_peri_bus",
CLK_CON_GAT_GATE_CLKCMU_PERI_BUS, 21, 0, 0),
GATE(CLK_GOUT_PERI_SPI0, "gout_peri_spi0", "mout_peri_spi0",
CLK_CON_GAT_GATE_CLKCMU_PERI_SPI0, 21, 0, 0),
GATE(CLK_GOUT_PERI_SPI1, "gout_peri_spi1", "mout_peri_spi1",
CLK_CON_GAT_GATE_CLKCMU_PERI_SPI1, 21, 0, 0),
GATE(CLK_GOUT_PERI_UART0, "gout_peri_uart0", "mout_peri_uart0",
CLK_CON_GAT_GATE_CLKCMU_PERI_UART0, 21, 0, 0),
GATE(CLK_GOUT_PERI_UART1, "gout_peri_uart1", "mout_peri_uart1",
CLK_CON_GAT_GATE_CLKCMUC_PERI_UART1, 21, 0, 0),
GATE(CLK_GOUT_PERI_UART2, "gout_peri_uart2", "mout_peri_uart2",
CLK_CON_GAT_GATE_CLKCMU_PERI_UART2, 21, 0, 0),
GATE(CLK_GOUT_PERI_USI0, "gout_peri_usi0", "mout_peri_usi0",
CLK_CON_GAT_GATE_CLKCMU_PERI_USI0, 21, 0, 0),
GATE(CLK_GOUT_PERI_USI1, "gout_peri_usi1", "mout_peri_usi1",
CLK_CON_GAT_GATE_CLKCMU_PERI_USI1, 21, 0, 0),
GATE(CLK_GOUT_PERI_USI2, "gout_peri_usi2", "mout_peri_usi2",
CLK_CON_GAT_GATE_CLKCMU_PERI_USI2, 21, 0, 0),
/* FSYS */
GATE(CLK_GOUT_FSYS_BUS, "gout_fsys_bus", "mout_fsys_bus",
CLK_CON_GAT_GATE_CLKCMU_FSYS_BUS, 21, 0, 0),
GATE(CLK_GOUT_FSYS_MMC_CARD, "gout_fsys_mmc_card", "mout_fsys_mmc_card",
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_CARD, 21, 0, 0),
GATE(CLK_GOUT_FSYS_MMC_EMBD, "gout_fsys_mmc_embd", "mout_fsys_mmc_embd",
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_EMBD, 21, 0, 0),
GATE(CLK_GOUT_FSYS_MMC_SDIO, "gout_fsys_mmc_sdio", "mout_fsys_mmc_sdio",
CLK_CON_GAT_GATE_CLKCMU_FSYS_MMC_SDIO, 21, 0, 0),
GATE(CLK_GOUT_FSYS_USB30DRD, "gout_fsys_usb30drd", "mout_fsys_usb30drd",
CLK_CON_GAT_GATE_CLKCMU_FSYS_USB30DRD, 21, 0, 0),
};
static const struct samsung_cmu_info top_cmu_info __initconst = {
.pll_clks = top_pll_clks,
.nr_pll_clks = ARRAY_SIZE(top_pll_clks),
.mux_clks = top_mux_clks,
.nr_mux_clks = ARRAY_SIZE(top_mux_clks),
.div_clks = top_div_clks,
.nr_div_clks = ARRAY_SIZE(top_div_clks),
.gate_clks = top_gate_clks,
.nr_gate_clks = ARRAY_SIZE(top_gate_clks),
.nr_clk_ids = CLKS_NR_TOP,
.clk_regs = top_clk_regs,
.nr_clk_regs = ARRAY_SIZE(top_clk_regs),
};
static void __init exynos7885_cmu_top_init(struct device_node *np)
{
exynos_arm64_register_cmu(NULL, np, &top_cmu_info);
}
/* Register CMU_TOP early, as it's a dependency for other early domains */
CLK_OF_DECLARE(exynos7885_cmu_top, "samsung,exynos7885-cmu-top",
exynos7885_cmu_top_init);
/* ---- CMU_PERI ------------------------------------------------------------ */
/* Register Offset definitions for CMU_PERI (0x10010000) */
#define PLL_CON0_MUX_CLKCMU_PERI_BUS_USER 0x0100
#define PLL_CON0_MUX_CLKCMU_PERI_SPI0_USER 0x0120
#define PLL_CON0_MUX_CLKCMU_PERI_SPI1_USER 0x0140
#define PLL_CON0_MUX_CLKCMU_PERI_UART0_USER 0x0160
#define PLL_CON0_MUX_CLKCMU_PERI_UART1_USER 0x0180
#define PLL_CON0_MUX_CLKCMU_PERI_UART2_USER 0x01a0
#define PLL_CON0_MUX_CLKCMU_PERI_USI0_USER 0x01c0
#define PLL_CON0_MUX_CLKCMU_PERI_USI1_USER 0x01e0
#define PLL_CON0_MUX_CLKCMU_PERI_USI2_USER 0x0200
#define CLK_CON_GAT_GOUT_PERI_GPIO_TOP_PCLK 0x2024
#define CLK_CON_GAT_GOUT_PERI_HSI2C_0_PCLK 0x2028
#define CLK_CON_GAT_GOUT_PERI_HSI2C_1_PCLK 0x202c
#define CLK_CON_GAT_GOUT_PERI_HSI2C_2_PCLK 0x2030
#define CLK_CON_GAT_GOUT_PERI_HSI2C_3_PCLK 0x2034
#define CLK_CON_GAT_GOUT_PERI_I2C_0_PCLK 0x2038
#define CLK_CON_GAT_GOUT_PERI_I2C_1_PCLK 0x203c
#define CLK_CON_GAT_GOUT_PERI_I2C_2_PCLK 0x2040
#define CLK_CON_GAT_GOUT_PERI_I2C_3_PCLK 0x2044
#define CLK_CON_GAT_GOUT_PERI_I2C_4_PCLK 0x2048
#define CLK_CON_GAT_GOUT_PERI_I2C_5_PCLK 0x204c
#define CLK_CON_GAT_GOUT_PERI_I2C_6_PCLK 0x2050
#define CLK_CON_GAT_GOUT_PERI_I2C_7_PCLK 0x2054
#define CLK_CON_GAT_GOUT_PERI_PWM_MOTOR_PCLK 0x2058
#define CLK_CON_GAT_GOUT_PERI_SPI_0_PCLK 0x205c
#define CLK_CON_GAT_GOUT_PERI_SPI_0_EXT_CLK 0x2060
#define CLK_CON_GAT_GOUT_PERI_SPI_1_PCLK 0x2064
#define CLK_CON_GAT_GOUT_PERI_SPI_1_EXT_CLK 0x2068
#define CLK_CON_GAT_GOUT_PERI_UART_0_EXT_UCLK 0x206c
#define CLK_CON_GAT_GOUT_PERI_UART_0_PCLK 0x2070
#define CLK_CON_GAT_GOUT_PERI_UART_1_EXT_UCLK 0x2074
#define CLK_CON_GAT_GOUT_PERI_UART_1_PCLK 0x2078
#define CLK_CON_GAT_GOUT_PERI_UART_2_EXT_UCLK 0x207c
#define CLK_CON_GAT_GOUT_PERI_UART_2_PCLK 0x2080
#define CLK_CON_GAT_GOUT_PERI_USI0_PCLK 0x2084
#define CLK_CON_GAT_GOUT_PERI_USI0_SCLK 0x2088
#define CLK_CON_GAT_GOUT_PERI_USI1_PCLK 0x208c
#define CLK_CON_GAT_GOUT_PERI_USI1_SCLK 0x2090
#define CLK_CON_GAT_GOUT_PERI_USI2_PCLK 0x2094
#define CLK_CON_GAT_GOUT_PERI_USI2_SCLK 0x2098
#define CLK_CON_GAT_GOUT_PERI_MCT_PCLK 0x20a0
#define CLK_CON_GAT_GOUT_PERI_SYSREG_PERI_PCLK 0x20b0
#define CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER0_PCLK 0x20b4
#define CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER1_PCLK 0x20b8
static const unsigned long peri_clk_regs[] __initconst = {
PLL_CON0_MUX_CLKCMU_PERI_BUS_USER,
PLL_CON0_MUX_CLKCMU_PERI_SPI0_USER,
PLL_CON0_MUX_CLKCMU_PERI_SPI1_USER,
PLL_CON0_MUX_CLKCMU_PERI_UART0_USER,
PLL_CON0_MUX_CLKCMU_PERI_UART1_USER,
PLL_CON0_MUX_CLKCMU_PERI_UART2_USER,
PLL_CON0_MUX_CLKCMU_PERI_USI0_USER,
PLL_CON0_MUX_CLKCMU_PERI_USI1_USER,
PLL_CON0_MUX_CLKCMU_PERI_USI2_USER,
CLK_CON_GAT_GOUT_PERI_GPIO_TOP_PCLK,
CLK_CON_GAT_GOUT_PERI_HSI2C_0_PCLK,
CLK_CON_GAT_GOUT_PERI_HSI2C_1_PCLK,
CLK_CON_GAT_GOUT_PERI_HSI2C_2_PCLK,
CLK_CON_GAT_GOUT_PERI_HSI2C_3_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_0_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_1_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_2_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_3_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_4_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_5_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_6_PCLK,
CLK_CON_GAT_GOUT_PERI_I2C_7_PCLK,
CLK_CON_GAT_GOUT_PERI_PWM_MOTOR_PCLK,
CLK_CON_GAT_GOUT_PERI_SPI_0_PCLK,
CLK_CON_GAT_GOUT_PERI_SPI_0_EXT_CLK,
CLK_CON_GAT_GOUT_PERI_SPI_1_PCLK,
CLK_CON_GAT_GOUT_PERI_SPI_1_EXT_CLK,
CLK_CON_GAT_GOUT_PERI_UART_0_EXT_UCLK,
CLK_CON_GAT_GOUT_PERI_UART_0_PCLK,
CLK_CON_GAT_GOUT_PERI_UART_1_EXT_UCLK,
CLK_CON_GAT_GOUT_PERI_UART_1_PCLK,
CLK_CON_GAT_GOUT_PERI_UART_2_EXT_UCLK,
CLK_CON_GAT_GOUT_PERI_UART_2_PCLK,
CLK_CON_GAT_GOUT_PERI_USI0_PCLK,
CLK_CON_GAT_GOUT_PERI_USI0_SCLK,
CLK_CON_GAT_GOUT_PERI_USI1_PCLK,
CLK_CON_GAT_GOUT_PERI_USI1_SCLK,
CLK_CON_GAT_GOUT_PERI_USI2_PCLK,
CLK_CON_GAT_GOUT_PERI_USI2_SCLK,
CLK_CON_GAT_GOUT_PERI_MCT_PCLK,
CLK_CON_GAT_GOUT_PERI_SYSREG_PERI_PCLK,
CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER0_PCLK,
CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER1_PCLK,
};
/* List of parent clocks for Muxes in CMU_PERI */
PNAME(mout_peri_bus_user_p) = { "oscclk", "dout_peri_bus" };
PNAME(mout_peri_spi0_user_p) = { "oscclk", "dout_peri_spi0" };
PNAME(mout_peri_spi1_user_p) = { "oscclk", "dout_peri_spi1" };
PNAME(mout_peri_uart0_user_p) = { "oscclk", "dout_peri_uart0" };
PNAME(mout_peri_uart1_user_p) = { "oscclk", "dout_peri_uart1" };
PNAME(mout_peri_uart2_user_p) = { "oscclk", "dout_peri_uart2" };
PNAME(mout_peri_usi0_user_p) = { "oscclk", "dout_peri_usi0" };
PNAME(mout_peri_usi1_user_p) = { "oscclk", "dout_peri_usi1" };
PNAME(mout_peri_usi2_user_p) = { "oscclk", "dout_peri_usi2" };
static const struct samsung_mux_clock peri_mux_clks[] __initconst = {
MUX(CLK_MOUT_PERI_BUS_USER, "mout_peri_bus_user", mout_peri_bus_user_p,
PLL_CON0_MUX_CLKCMU_PERI_BUS_USER, 4, 1),
MUX(CLK_MOUT_PERI_SPI0_USER, "mout_peri_spi0_user", mout_peri_spi0_user_p,
PLL_CON0_MUX_CLKCMU_PERI_SPI0_USER, 4, 1),
MUX(CLK_MOUT_PERI_SPI1_USER, "mout_peri_spi1_user", mout_peri_spi1_user_p,
PLL_CON0_MUX_CLKCMU_PERI_SPI1_USER, 4, 1),
MUX(CLK_MOUT_PERI_UART0_USER, "mout_peri_uart0_user",
mout_peri_uart0_user_p, PLL_CON0_MUX_CLKCMU_PERI_UART0_USER, 4, 1),
MUX(CLK_MOUT_PERI_UART1_USER, "mout_peri_uart1_user",
mout_peri_uart1_user_p, PLL_CON0_MUX_CLKCMU_PERI_UART1_USER, 4, 1),
MUX(CLK_MOUT_PERI_UART2_USER, "mout_peri_uart2_user",
mout_peri_uart2_user_p, PLL_CON0_MUX_CLKCMU_PERI_UART2_USER, 4, 1),
MUX(CLK_MOUT_PERI_USI0_USER, "mout_peri_usi0_user",
mout_peri_usi0_user_p, PLL_CON0_MUX_CLKCMU_PERI_USI0_USER, 4, 1),
MUX(CLK_MOUT_PERI_USI1_USER, "mout_peri_usi1_user",
mout_peri_usi1_user_p, PLL_CON0_MUX_CLKCMU_PERI_USI1_USER, 4, 1),
MUX(CLK_MOUT_PERI_USI2_USER, "mout_peri_usi2_user",
mout_peri_usi2_user_p, PLL_CON0_MUX_CLKCMU_PERI_USI2_USER, 4, 1),
};
static const struct samsung_gate_clock peri_gate_clks[] __initconst = {
/* TODO: Should be enabled in GPIO driver (or made CLK_IS_CRITICAL) */
GATE(CLK_GOUT_GPIO_TOP_PCLK, "gout_gpio_top_pclk",
"mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_GPIO_TOP_PCLK, 21, CLK_IGNORE_UNUSED, 0),
GATE(CLK_GOUT_HSI2C0_PCLK, "gout_hsi2c0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_HSI2C_0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_HSI2C1_PCLK, "gout_hsi2c1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_HSI2C_1_PCLK, 21, 0, 0),
GATE(CLK_GOUT_HSI2C2_PCLK, "gout_hsi2c2_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_HSI2C_2_PCLK, 21, 0, 0),
GATE(CLK_GOUT_HSI2C3_PCLK, "gout_hsi2c3_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_HSI2C_3_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C0_PCLK, "gout_i2c0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C1_PCLK, "gout_i2c1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_1_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C2_PCLK, "gout_i2c2_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_2_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C3_PCLK, "gout_i2c3_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_3_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C4_PCLK, "gout_i2c4_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_4_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C5_PCLK, "gout_i2c5_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_5_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C6_PCLK, "gout_i2c6_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_6_PCLK, 21, 0, 0),
GATE(CLK_GOUT_I2C7_PCLK, "gout_i2c7_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_I2C_7_PCLK, 21, 0, 0),
GATE(CLK_GOUT_PWM_MOTOR_PCLK, "gout_pwm_motor_pclk",
"mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_PWM_MOTOR_PCLK, 21, 0, 0),
GATE(CLK_GOUT_SPI0_PCLK, "gout_spi0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_SPI_0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_SPI0_EXT_CLK, "gout_spi0_ipclk", "mout_peri_spi0_user",
CLK_CON_GAT_GOUT_PERI_SPI_0_EXT_CLK, 21, 0, 0),
GATE(CLK_GOUT_SPI1_PCLK, "gout_spi1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_SPI_1_PCLK, 21, 0, 0),
GATE(CLK_GOUT_SPI1_EXT_CLK, "gout_spi1_ipclk", "mout_peri_spi1_user",
CLK_CON_GAT_GOUT_PERI_SPI_1_EXT_CLK, 21, 0, 0),
GATE(CLK_GOUT_UART0_EXT_UCLK, "gout_uart0_ext_uclk", "mout_peri_uart0_user",
CLK_CON_GAT_GOUT_PERI_UART_0_EXT_UCLK, 21, 0, 0),
GATE(CLK_GOUT_UART0_PCLK, "gout_uart0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_UART_0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_UART1_EXT_UCLK, "gout_uart1_ext_uclk", "mout_peri_uart1_user",
CLK_CON_GAT_GOUT_PERI_UART_1_EXT_UCLK, 21, 0, 0),
GATE(CLK_GOUT_UART1_PCLK, "gout_uart1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_UART_1_PCLK, 21, 0, 0),
GATE(CLK_GOUT_UART2_EXT_UCLK, "gout_uart2_ext_uclk", "mout_peri_uart2_user",
CLK_CON_GAT_GOUT_PERI_UART_2_EXT_UCLK, 21, 0, 0),
GATE(CLK_GOUT_UART2_PCLK, "gout_uart2_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_UART_2_PCLK, 21, 0, 0),
GATE(CLK_GOUT_USI0_PCLK, "gout_usi0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_USI0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_USI0_SCLK, "gout_usi0_sclk", "mout_peri_usi0_user",
CLK_CON_GAT_GOUT_PERI_USI0_SCLK, 21, 0, 0),
GATE(CLK_GOUT_USI1_PCLK, "gout_usi1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_USI1_PCLK, 21, 0, 0),
GATE(CLK_GOUT_USI1_SCLK, "gout_usi1_sclk", "mout_peri_usi1_user",
CLK_CON_GAT_GOUT_PERI_USI1_SCLK, 21, 0, 0),
GATE(CLK_GOUT_USI2_PCLK, "gout_usi2_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_USI2_PCLK, 21, 0, 0),
GATE(CLK_GOUT_USI2_SCLK, "gout_usi2_sclk", "mout_peri_usi2_user",
CLK_CON_GAT_GOUT_PERI_USI2_SCLK, 21, 0, 0),
GATE(CLK_GOUT_MCT_PCLK, "gout_mct_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_MCT_PCLK, 21, 0, 0),
GATE(CLK_GOUT_SYSREG_PERI_PCLK, "gout_sysreg_peri_pclk",
"mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_SYSREG_PERI_PCLK, 21, 0, 0),
GATE(CLK_GOUT_WDT0_PCLK, "gout_wdt0_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER0_PCLK, 21, 0, 0),
GATE(CLK_GOUT_WDT1_PCLK, "gout_wdt1_pclk", "mout_peri_bus_user",
CLK_CON_GAT_GOUT_PERI_WDT_CLUSTER1_PCLK, 21, 0, 0),
};
static const struct samsung_cmu_info peri_cmu_info __initconst = {
.mux_clks = peri_mux_clks,
.nr_mux_clks = ARRAY_SIZE(peri_mux_clks),
.gate_clks = peri_gate_clks,
.nr_gate_clks = ARRAY_SIZE(peri_gate_clks),
.nr_clk_ids = CLKS_NR_PERI,
.clk_regs = peri_clk_regs,
.nr_clk_regs = ARRAY_SIZE(peri_clk_regs),
.clk_name = "dout_peri_bus",
};
static void __init exynos7885_cmu_peri_init(struct device_node *np)
{
exynos_arm64_register_cmu(NULL, np, &peri_cmu_info);
}
/* Register CMU_PERI early, as it's needed for MCT timer */
CLK_OF_DECLARE(exynos7885_cmu_peri, "samsung,exynos7885-cmu-peri",
exynos7885_cmu_peri_init);
/* ---- CMU_CORE ------------------------------------------------------------ */
/* Register Offset definitions for CMU_CORE (0x12000000) */
#define PLL_CON0_MUX_CLKCMU_CORE_BUS_USER 0x0100
#define PLL_CON0_MUX_CLKCMU_CORE_CCI_USER 0x0120
#define PLL_CON0_MUX_CLKCMU_CORE_G3D_USER 0x0140
#define CLK_CON_MUX_MUX_CLK_CORE_GIC 0x1000
#define CLK_CON_DIV_DIV_CLK_CORE_BUSP 0x1800
#define CLK_CON_GAT_GOUT_CORE_CCI_550_ACLK 0x2054
#define CLK_CON_GAT_GOUT_CORE_GIC400_CLK 0x2058
#define CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_ACLK 0x215c
#define CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_GCLK 0x2160
#define CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_PCLK 0x2164
#define CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_ACLK_P_CORE 0x2168
#define CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_CCLK_P_CORE 0x216c
#define CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK 0x2170
#define CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK_P_CORE 0x2174
static const unsigned long core_clk_regs[] __initconst = {
PLL_CON0_MUX_CLKCMU_CORE_BUS_USER,
PLL_CON0_MUX_CLKCMU_CORE_CCI_USER,
PLL_CON0_MUX_CLKCMU_CORE_G3D_USER,
CLK_CON_MUX_MUX_CLK_CORE_GIC,
CLK_CON_DIV_DIV_CLK_CORE_BUSP,
CLK_CON_GAT_GOUT_CORE_CCI_550_ACLK,
CLK_CON_GAT_GOUT_CORE_GIC400_CLK,
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_ACLK,
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_GCLK,
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_PCLK,
CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_ACLK_P_CORE,
CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_CCLK_P_CORE,
CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK,
CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK_P_CORE,
};
/* List of parent clocks for Muxes in CMU_CORE */
PNAME(mout_core_bus_user_p) = { "oscclk", "dout_core_bus" };
PNAME(mout_core_cci_user_p) = { "oscclk", "dout_core_cci" };
PNAME(mout_core_g3d_user_p) = { "oscclk", "dout_core_g3d" };
PNAME(mout_core_gic_p) = { "dout_core_busp", "oscclk" };
static const struct samsung_mux_clock core_mux_clks[] __initconst = {
MUX(CLK_MOUT_CORE_BUS_USER, "mout_core_bus_user", mout_core_bus_user_p,
PLL_CON0_MUX_CLKCMU_CORE_BUS_USER, 4, 1),
MUX(CLK_MOUT_CORE_CCI_USER, "mout_core_cci_user", mout_core_cci_user_p,
PLL_CON0_MUX_CLKCMU_CORE_CCI_USER, 4, 1),
MUX(CLK_MOUT_CORE_G3D_USER, "mout_core_g3d_user", mout_core_g3d_user_p,
PLL_CON0_MUX_CLKCMU_CORE_G3D_USER, 4, 1),
MUX(CLK_MOUT_CORE_GIC, "mout_core_gic", mout_core_gic_p,
CLK_CON_MUX_MUX_CLK_CORE_GIC, 0, 1),
};
static const struct samsung_div_clock core_div_clks[] __initconst = {
DIV(CLK_DOUT_CORE_BUSP, "dout_core_busp", "mout_core_bus_user",
CLK_CON_DIV_DIV_CLK_CORE_BUSP, 0, 2),
};
static const struct samsung_gate_clock core_gate_clks[] __initconst = {
/* CCI (interconnect) clock must be always running */
GATE(CLK_GOUT_CCI_ACLK, "gout_cci_aclk", "mout_core_cci_user",
CLK_CON_GAT_GOUT_CORE_CCI_550_ACLK, 21, CLK_IS_CRITICAL, 0),
/* GIC (interrupt controller) clock must be always running */
GATE(CLK_GOUT_GIC400_CLK, "gout_gic400_clk", "mout_core_gic",
CLK_CON_GAT_GOUT_CORE_GIC400_CLK, 21, CLK_IS_CRITICAL, 0),
/*
* TREX D and P Core (seems to be related to "bus traffic shaper")
* clocks must always be running
*/
GATE(CLK_GOUT_TREX_D_CORE_ACLK, "gout_trex_d_core_aclk", "mout_core_bus_user",
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_ACLK, 21, CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_D_CORE_GCLK, "gout_trex_d_core_gclk", "mout_core_g3d_user",
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_GCLK, 21, CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_D_CORE_PCLK, "gout_trex_d_core_pclk", "dout_core_busp",
CLK_CON_GAT_GOUT_CORE_TREX_D_CORE_PCLK, 21, CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_P_CORE_ACLK_P_CORE, "gout_trex_p_core_aclk_p_core",
"mout_core_bus_user", CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_ACLK_P_CORE, 21,
CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_P_CORE_CCLK_P_CORE, "gout_trex_p_core_cclk_p_core",
"mout_core_cci_user", CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_CCLK_P_CORE, 21,
CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_P_CORE_PCLK, "gout_trex_p_core_pclk", "dout_core_busp",
CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK, 21, CLK_IS_CRITICAL, 0),
GATE(CLK_GOUT_TREX_P_CORE_PCLK_P_CORE, "gout_trex_p_core_pclk_p_core",
"dout_core_busp", CLK_CON_GAT_GOUT_CORE_TREX_P_CORE_PCLK_P_CORE, 21,
CLK_IS_CRITICAL, 0),
};
static const struct samsung_cmu_info core_cmu_info __initconst = {
.mux_clks = core_mux_clks,
.nr_mux_clks = ARRAY_SIZE(core_mux_clks),
.div_clks = core_div_clks,
.nr_div_clks = ARRAY_SIZE(core_div_clks),
.gate_clks = core_gate_clks,
.nr_gate_clks = ARRAY_SIZE(core_gate_clks),
.nr_clk_ids = CLKS_NR_CORE,
.clk_regs = core_clk_regs,
.nr_clk_regs = ARRAY_SIZE(core_clk_regs),
.clk_name = "dout_core_bus",
};
/* ---- CMU_FSYS ------------------------------------------------------------ */
/* Register Offset definitions for CMU_FSYS (0x13400000) */
#define PLL_LOCKTIME_PLL_USB 0x0000
#define PLL_CON0_MUX_CLKCMU_FSYS_BUS_USER 0x0100
#define PLL_CON0_MUX_CLKCMU_FSYS_MMC_CARD_USER 0x0120
#define PLL_CON0_MUX_CLKCMU_FSYS_MMC_EMBD_USER 0x0140
#define PLL_CON0_MUX_CLKCMU_FSYS_MMC_SDIO_USER 0x0160
#define PLL_CON0_MUX_CLKCMU_FSYS_USB30DRD_USER 0x0180
#define PLL_CON0_PLL_USB 0x01a0
#define CLK_CON_GAT_CLK_FSYS_USB20PHY_CLKCORE 0x200c
#define CLK_CON_GAT_GOUT_FSYS_MMC_CARD_I_ACLK 0x2030
#define CLK_CON_GAT_GOUT_FSYS_MMC_CARD_SDCLKIN 0x2034
#define CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_I_ACLK 0x2038
#define CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_SDCLKIN 0x203c
#define CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_I_ACLK 0x2040
#define CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_SDCLKIN 0x2044
#define CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_20PHYCTRL 0x2068
#define CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_0 0x206c
#define CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_1 0x2070
#define CLK_CON_GAT_GOUT_FSYS_USB30DRD_BUS_CLK_EARLY 0x2074
#define CLK_CON_GAT_GOUT_FSYS_USB30DRD_REF_CLK 0x2078
static const unsigned long fsys_clk_regs[] __initconst = {
PLL_LOCKTIME_PLL_USB,
PLL_CON0_MUX_CLKCMU_FSYS_BUS_USER,
PLL_CON0_MUX_CLKCMU_FSYS_MMC_CARD_USER,
PLL_CON0_MUX_CLKCMU_FSYS_MMC_EMBD_USER,
PLL_CON0_MUX_CLKCMU_FSYS_MMC_SDIO_USER,
PLL_CON0_MUX_CLKCMU_FSYS_USB30DRD_USER,
PLL_CON0_PLL_USB,
CLK_CON_GAT_CLK_FSYS_USB20PHY_CLKCORE,
CLK_CON_GAT_GOUT_FSYS_MMC_CARD_I_ACLK,
CLK_CON_GAT_GOUT_FSYS_MMC_CARD_SDCLKIN,
CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_I_ACLK,
CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_SDCLKIN,
CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_I_ACLK,
CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_SDCLKIN,
CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_20PHYCTRL,
CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_0,
CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_1,
CLK_CON_GAT_GOUT_FSYS_USB30DRD_BUS_CLK_EARLY,
CLK_CON_GAT_GOUT_FSYS_USB30DRD_REF_CLK,
};
static const struct samsung_pll_rate_table pll_usb_rate_table[] __initconst = {
PLL_35XX_RATE(26 * MHZ, 50000000U, 400, 13, 4),
};
static const struct samsung_pll_clock fsys_pll_clks[] __initconst = {
PLL(pll_1418x, CLK_FOUT_USB_PLL, "fout_usb_pll", "oscclk",
PLL_LOCKTIME_PLL_USB, PLL_CON0_PLL_USB,
pll_usb_rate_table),
};
/* List of parent clocks for Muxes in CMU_FSYS */
PNAME(mout_fsys_bus_user_p) = { "oscclk", "dout_fsys_bus" };
PNAME(mout_fsys_mmc_card_user_p) = { "oscclk", "dout_fsys_mmc_card" };
PNAME(mout_fsys_mmc_embd_user_p) = { "oscclk", "dout_fsys_mmc_embd" };
PNAME(mout_fsys_mmc_sdio_user_p) = { "oscclk", "dout_fsys_mmc_sdio" };
PNAME(mout_fsys_usb30drd_user_p) = { "oscclk", "dout_fsys_usb30drd" };
PNAME(mout_usb_pll_p) = { "oscclk", "fout_usb_pll" };
static const struct samsung_mux_clock fsys_mux_clks[] __initconst = {
MUX(CLK_MOUT_FSYS_BUS_USER, "mout_fsys_bus_user", mout_fsys_bus_user_p,
PLL_CON0_MUX_CLKCMU_FSYS_BUS_USER, 4, 1),
MUX_F(CLK_MOUT_FSYS_MMC_CARD_USER, "mout_fsys_mmc_card_user",
mout_fsys_mmc_card_user_p, PLL_CON0_MUX_CLKCMU_FSYS_MMC_CARD_USER,
4, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(CLK_MOUT_FSYS_MMC_EMBD_USER, "mout_fsys_mmc_embd_user",
mout_fsys_mmc_embd_user_p, PLL_CON0_MUX_CLKCMU_FSYS_MMC_EMBD_USER,
4, 1, CLK_SET_RATE_PARENT, 0),
MUX_F(CLK_MOUT_FSYS_MMC_SDIO_USER, "mout_fsys_mmc_sdio_user",
mout_fsys_mmc_sdio_user_p, PLL_CON0_MUX_CLKCMU_FSYS_MMC_SDIO_USER,
4, 1, CLK_SET_RATE_PARENT, 0),
MUX(CLK_MOUT_FSYS_USB30DRD_USER, "mout_fsys_usb30drd_user",
mout_fsys_usb30drd_user_p, PLL_CON0_MUX_CLKCMU_FSYS_USB30DRD_USER,
4, 1),
nMUX_F(CLK_MOUT_USB_PLL, "mout_usb_pll", mout_usb_pll_p,
PLL_CON0_PLL_USB, 4, 1, CLK_SET_RATE_PARENT, 0),
};
static const struct samsung_gate_clock fsys_gate_clks[] __initconst = {
GATE(CLK_FSYS_USB20PHY_CLKCORE, "clk_fsys_usb20phy_clkcore", "mout_usb_pll",
CLK_CON_GAT_CLK_FSYS_USB20PHY_CLKCORE, 21, CLK_SET_RATE_PARENT, 0),
GATE(CLK_GOUT_MMC_CARD_ACLK, "gout_mmc_card_aclk", "mout_fsys_bus_user",
CLK_CON_GAT_GOUT_FSYS_MMC_CARD_I_ACLK, 21, 0, 0),
GATE(CLK_GOUT_MMC_CARD_SDCLKIN, "gout_mmc_card_sdclkin",
"mout_fsys_mmc_card_user", CLK_CON_GAT_GOUT_FSYS_MMC_CARD_SDCLKIN,
21, CLK_SET_RATE_PARENT, 0),
GATE(CLK_GOUT_MMC_EMBD_ACLK, "gout_mmc_embd_aclk", "mout_fsys_bus_user",
CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_I_ACLK, 21, 0, 0),
GATE(CLK_GOUT_MMC_EMBD_SDCLKIN, "gout_mmc_embd_sdclkin",
"mout_fsys_mmc_embd_user", CLK_CON_GAT_GOUT_FSYS_MMC_EMBD_SDCLKIN,
21, CLK_SET_RATE_PARENT, 0),
GATE(CLK_GOUT_MMC_SDIO_ACLK, "gout_mmc_sdio_aclk", "mout_fsys_bus_user",
CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_I_ACLK, 21, 0, 0),
GATE(CLK_GOUT_MMC_SDIO_SDCLKIN, "gout_mmc_sdio_sdclkin",
"mout_fsys_mmc_sdio_user", CLK_CON_GAT_GOUT_FSYS_MMC_SDIO_SDCLKIN,
21, CLK_SET_RATE_PARENT, 0),
GATE(CLK_FSYS_USB30DRD_ACLK_20PHYCTRL, "clk_fsys_usb30drd_aclk_20phyctrl",
"mout_fsys_bus_user", CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_20PHYCTRL, 21, 0, 0),
GATE(CLK_FSYS_USB30DRD_ACLK_30PHYCTRL_0, "clk_fsys_usb30drd_aclk_30phyctrl_0",
"mout_fsys_bus_user", CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_0, 21, 0, 0),
GATE(CLK_FSYS_USB30DRD_ACLK_30PHYCTRL_1, "clk_fsys_usb30drd_aclk_30phyctrl_1",
"mout_fsys_bus_user", CLK_CON_GAT_GOUT_FSYS_USB30DRD_ACLK_30PHYCTRL_1, 21, 0, 0),
GATE(CLK_FSYS_USB30DRD_BUS_CLK_EARLY, "clk_fsys_usb30drd_bus_clk_early",
"mout_fsys_bus_user", CLK_CON_GAT_GOUT_FSYS_USB30DRD_BUS_CLK_EARLY, 21, 0, 0),
GATE(CLK_FSYS_USB30DRD_REF_CLK, "clk_fsys_usb30drd_ref_clk", "mout_fsys_usb30drd_user",
CLK_CON_GAT_GOUT_FSYS_USB30DRD_REF_CLK, 21, 0, 0),
};
static const struct samsung_cmu_info fsys_cmu_info __initconst = {
.pll_clks = fsys_pll_clks,
.nr_pll_clks = ARRAY_SIZE(fsys_pll_clks),
.mux_clks = fsys_mux_clks,
.nr_mux_clks = ARRAY_SIZE(fsys_mux_clks),
.gate_clks = fsys_gate_clks,
.nr_gate_clks = ARRAY_SIZE(fsys_gate_clks),
.nr_clk_ids = CLKS_NR_FSYS,
.clk_regs = fsys_clk_regs,
.nr_clk_regs = ARRAY_SIZE(fsys_clk_regs),
.clk_name = "dout_fsys_bus",
};
/* ---- platform_driver ----------------------------------------------------- */
static int __init exynos7885_cmu_probe(struct platform_device *pdev)
{
const struct samsung_cmu_info *info;
struct device *dev = &pdev->dev;
info = of_device_get_match_data(dev);
exynos_arm64_register_cmu(dev, dev->of_node, info);
return 0;
}
static const struct of_device_id exynos7885_cmu_of_match[] = {
{
.compatible = "samsung,exynos7885-cmu-core",
.data = &core_cmu_info,
}, {
.compatible = "samsung,exynos7885-cmu-fsys",
.data = &fsys_cmu_info,
}, {
},
};
static struct platform_driver exynos7885_cmu_driver __refdata = {
.driver = {
.name = "exynos7885-cmu",
.of_match_table = exynos7885_cmu_of_match,
.suppress_bind_attrs = true,
},
.probe = exynos7885_cmu_probe,
};
static int __init exynos7885_cmu_init(void)
{
return platform_driver_register(&exynos7885_cmu_driver);
}
core_initcall(exynos7885_cmu_init);
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+145
View File
@@ -0,0 +1,145 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
*
* Common Clock Framework support for all PLL's in Samsung platforms
*/
#ifndef __SAMSUNG_CLK_PLL_H
#define __SAMSUNG_CLK_PLL_H
enum samsung_pll_type {
pll_2126,
pll_3000,
pll_35xx,
pll_36xx,
pll_2550,
pll_2650,
pll_4500,
pll_4502,
pll_4508,
pll_4600,
pll_4650,
pll_4650c,
pll_6552,
pll_6552_s3c2416,
pll_6553,
pll_2550x,
pll_2550xx,
pll_2650x,
pll_2650xx,
pll_1417x,
pll_1418x,
pll_1450x,
pll_1451x,
pll_1452x,
pll_1460x,
pll_0818x,
pll_0822x,
pll_0831x,
pll_142xx,
pll_0516x,
pll_0517x,
pll_0518x,
pll_531x,
pll_1051x,
pll_1052x,
pll_0717x,
pll_0718x,
pll_0732x,
pll_4311,
pll_1017x,
pll_1031x,
pll_a9fracm,
pll_a9fraco,
};
#define PLL_RATE(_fin, _m, _p, _s, _k, _ks) \
((u64)(_fin) * (BIT(_ks) * (_m) + (_k)) / BIT(_ks) / ((_p) << (_s)))
#define PLL_VALID_RATE(_fin, _fout, _m, _p, _s, _k, _ks) ((_fout) + \
BUILD_BUG_ON_ZERO(PLL_RATE(_fin, _m, _p, _s, _k, _ks) != (_fout)))
#define PLL_FRACO_RATE(_fin, _m, _p, _s, _k, _ks) \
((u64)(_fin) * (BIT(_ks) * (_m) + (_k)) / BIT(_ks) / ((_p) * ((_s) + 1)))
#define PLL_FRACO_VALID_RATE(_fin, _fout, _m, _p, _s, _k, _ks) ((_fout) + \
BUILD_BUG_ON_ZERO(PLL_FRACO_RATE(_fin, _m, _p, _s, _k, _ks) != (_fout)))
#define PLL_35XX_RATE(_fin, _rate, _m, _p, _s) \
{ \
.rate = PLL_VALID_RATE(_fin, _rate, \
_m, _p, _s, 0, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
}
#define PLL_36XX_RATE(_fin, _rate, _m, _p, _s, _k) \
{ \
.rate = PLL_VALID_RATE(_fin, _rate, \
_m, _p, _s, _k, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.kdiv = (_k), \
}
#define PLL_4508_RATE(_fin, _rate, _m, _p, _s, _afc) \
{ \
.rate = PLL_VALID_RATE(_fin, _rate, \
_m, _p, _s - 1, 0, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.afc = (_afc), \
}
#define PLL_4600_RATE(_fin, _rate, _m, _p, _s, _k, _vsel) \
{ \
.rate = PLL_VALID_RATE(_fin, _rate, \
_m, _p, _s, _k, 16), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.kdiv = (_k), \
.vsel = (_vsel), \
}
#define PLL_4650_RATE(_fin, _rate, _m, _p, _s, _k, _mfr, _mrr, _vsel) \
{ \
.rate = PLL_VALID_RATE(_fin, _rate, \
_m, _p, _s, _k, 10), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.kdiv = (_k), \
.mfr = (_mfr), \
.mrr = (_mrr), \
.vsel = (_vsel), \
}
#define PLL_A9FRACO_RATE(_fin, _rate, _m, _p, _s, _k) \
{ \
.rate = PLL_FRACO_VALID_RATE(_fin, _rate, \
_m, _p, _s, _k, 24), \
.mdiv = (_m), \
.pdiv = (_p), \
.sdiv = (_s), \
.kdiv = (_k), \
}
/* NOTE: Rate table should be kept sorted in descending order. */
struct samsung_pll_rate_table {
unsigned int rate;
unsigned int pdiv;
unsigned int mdiv;
unsigned int sdiv;
unsigned int kdiv;
unsigned int afc;
unsigned int mfr;
unsigned int mrr;
unsigned int vsel;
};
#endif /* __SAMSUNG_CLK_PLL_H */
@@ -0,0 +1,479 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Tomasz Figa <tomasz.figa at gmail.com>
*
* Common Clock Framework support for all S3C64xx SoCs.
*/
#include <linux/slab.h>
#include <linux/clk-provider.h>
#include <linux/clk/samsung.h>
#include <linux/of_address.h>
#include <dt-bindings/clock/samsung,s3c64xx-clock.h>
#include "clk.h"
#include "clk-pll.h"
/* S3C64xx clock controller register offsets. */
#define APLL_LOCK 0x000
#define MPLL_LOCK 0x004
#define EPLL_LOCK 0x008
#define APLL_CON 0x00c
#define MPLL_CON 0x010
#define EPLL_CON0 0x014
#define EPLL_CON1 0x018
#define CLK_SRC 0x01c
#define CLK_DIV0 0x020
#define CLK_DIV1 0x024
#define CLK_DIV2 0x028
#define HCLK_GATE 0x030
#define PCLK_GATE 0x034
#define SCLK_GATE 0x038
#define MEM0_GATE 0x03c
#define CLK_SRC2 0x10c
#define OTHERS 0x900
/* Helper macros to define clock arrays. */
#define FIXED_RATE_CLOCKS(name) \
static struct samsung_fixed_rate_clock name[]
#define MUX_CLOCKS(name) \
static struct samsung_mux_clock name[]
#define DIV_CLOCKS(name) \
static struct samsung_div_clock name[]
#define GATE_CLOCKS(name) \
static struct samsung_gate_clock name[]
/* Helper macros for gate types present on S3C64xx. */
#define GATE_BUS(_id, cname, pname, o, b) \
GATE(_id, cname, pname, o, b, 0, 0)
#define GATE_SCLK(_id, cname, pname, o, b) \
GATE(_id, cname, pname, o, b, CLK_SET_RATE_PARENT, 0)
#define GATE_ON(_id, cname, pname, o, b) \
GATE(_id, cname, pname, o, b, CLK_IGNORE_UNUSED, 0)
static void __iomem *reg_base;
static bool is_s3c6400;
/*
* List of controller registers to be saved and restored during
* a suspend/resume cycle.
*/
static unsigned long s3c64xx_clk_regs[] __initdata = {
APLL_LOCK,
MPLL_LOCK,
EPLL_LOCK,
APLL_CON,
MPLL_CON,
EPLL_CON0,
EPLL_CON1,
CLK_SRC,
CLK_DIV0,
CLK_DIV1,
CLK_DIV2,
HCLK_GATE,
PCLK_GATE,
SCLK_GATE,
};
static unsigned long s3c6410_clk_regs[] __initdata = {
CLK_SRC2,
MEM0_GATE,
};
/* List of parent clocks common for all S3C64xx SoCs. */
PNAME(spi_mmc_p) = { "mout_epll", "dout_mpll", "fin_pll", "clk27m" };
PNAME(uart_p) = { "mout_epll", "dout_mpll" };
PNAME(audio0_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk0",
"pcmcdclk0", "none", "none", "none" };
PNAME(audio1_p) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk1",
"pcmcdclk0", "none", "none", "none" };
PNAME(mfc_p) = { "hclkx2", "mout_epll" };
PNAME(apll_p) = { "fin_pll", "fout_apll" };
PNAME(mpll_p) = { "fin_pll", "fout_mpll" };
PNAME(epll_p) = { "fin_pll", "fout_epll" };
PNAME(hclkx2_p) = { "mout_mpll", "mout_apll" };
/* S3C6400-specific parent clocks. */
PNAME(scaler_lcd_p6400) = { "mout_epll", "dout_mpll", "none", "none" };
PNAME(irda_p6400) = { "mout_epll", "dout_mpll", "none", "clk48m" };
PNAME(uhost_p6400) = { "clk48m", "mout_epll", "dout_mpll", "none" };
/* S3C6410-specific parent clocks. */
PNAME(clk27_p6410) = { "clk27m", "fin_pll" };
PNAME(scaler_lcd_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "none" };
PNAME(irda_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "clk48m" };
PNAME(uhost_p6410) = { "clk48m", "mout_epll", "dout_mpll", "fin_pll" };
PNAME(audio2_p6410) = { "mout_epll", "dout_mpll", "fin_pll", "iiscdclk2",
"pcmcdclk1", "none", "none", "none" };
/* Fixed rate clocks generated outside the SoC. */
FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_ext_clks) __initdata = {
FRATE(0, "fin_pll", NULL, 0, 0),
FRATE(0, "xusbxti", NULL, 0, 0),
};
/* Fixed rate clocks generated inside the SoC. */
FIXED_RATE_CLOCKS(s3c64xx_fixed_rate_clks) __initdata = {
FRATE(CLK27M, "clk27m", NULL, 0, 27000000),
FRATE(CLK48M, "clk48m", NULL, 0, 48000000),
};
/* List of clock muxes present on all S3C64xx SoCs. */
MUX_CLOCKS(s3c64xx_mux_clks) __initdata = {
MUX_F(0, "mout_syncmux", hclkx2_p, OTHERS, 6, 1, 0, CLK_MUX_READ_ONLY),
MUX(MOUT_APLL, "mout_apll", apll_p, CLK_SRC, 0, 1),
MUX(MOUT_MPLL, "mout_mpll", mpll_p, CLK_SRC, 1, 1),
MUX(MOUT_EPLL, "mout_epll", epll_p, CLK_SRC, 2, 1),
MUX(MOUT_MFC, "mout_mfc", mfc_p, CLK_SRC, 4, 1),
MUX(MOUT_AUDIO0, "mout_audio0", audio0_p, CLK_SRC, 7, 3),
MUX(MOUT_AUDIO1, "mout_audio1", audio1_p, CLK_SRC, 10, 3),
MUX(MOUT_UART, "mout_uart", uart_p, CLK_SRC, 13, 1),
MUX(MOUT_SPI0, "mout_spi0", spi_mmc_p, CLK_SRC, 14, 2),
MUX(MOUT_SPI1, "mout_spi1", spi_mmc_p, CLK_SRC, 16, 2),
MUX(MOUT_MMC0, "mout_mmc0", spi_mmc_p, CLK_SRC, 18, 2),
MUX(MOUT_MMC1, "mout_mmc1", spi_mmc_p, CLK_SRC, 20, 2),
MUX(MOUT_MMC2, "mout_mmc2", spi_mmc_p, CLK_SRC, 22, 2),
};
/* List of clock muxes present on S3C6400. */
MUX_CLOCKS(s3c6400_mux_clks) __initdata = {
MUX(MOUT_UHOST, "mout_uhost", uhost_p6400, CLK_SRC, 5, 2),
MUX(MOUT_IRDA, "mout_irda", irda_p6400, CLK_SRC, 24, 2),
MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6400, CLK_SRC, 26, 2),
MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6400, CLK_SRC, 28, 2),
};
/* List of clock muxes present on S3C6410. */
MUX_CLOCKS(s3c6410_mux_clks) __initdata = {
MUX(MOUT_UHOST, "mout_uhost", uhost_p6410, CLK_SRC, 5, 2),
MUX(MOUT_IRDA, "mout_irda", irda_p6410, CLK_SRC, 24, 2),
MUX(MOUT_LCD, "mout_lcd", scaler_lcd_p6410, CLK_SRC, 26, 2),
MUX(MOUT_SCALER, "mout_scaler", scaler_lcd_p6410, CLK_SRC, 28, 2),
MUX(MOUT_DAC27, "mout_dac27", clk27_p6410, CLK_SRC, 30, 1),
MUX(MOUT_TV27, "mout_tv27", clk27_p6410, CLK_SRC, 31, 1),
MUX(MOUT_AUDIO2, "mout_audio2", audio2_p6410, CLK_SRC2, 0, 3),
};
/* List of clock dividers present on all S3C64xx SoCs. */
DIV_CLOCKS(s3c64xx_div_clks) __initdata = {
DIV(DOUT_MPLL, "dout_mpll", "mout_mpll", CLK_DIV0, 4, 1),
DIV(HCLKX2, "hclkx2", "mout_syncmux", CLK_DIV0, 9, 3),
DIV(HCLK, "hclk", "hclkx2", CLK_DIV0, 8, 1),
DIV(PCLK, "pclk", "hclkx2", CLK_DIV0, 12, 4),
DIV(DOUT_SECUR, "dout_secur", "hclkx2", CLK_DIV0, 18, 2),
DIV(DOUT_CAM, "dout_cam", "hclkx2", CLK_DIV0, 20, 4),
DIV(DOUT_JPEG, "dout_jpeg", "hclkx2", CLK_DIV0, 24, 4),
DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV0, 28, 4),
DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV1, 0, 4),
DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV1, 4, 4),
DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV1, 8, 4),
DIV(DOUT_LCD, "dout_lcd", "mout_lcd", CLK_DIV1, 12, 4),
DIV(DOUT_SCALER, "dout_scaler", "mout_scaler", CLK_DIV1, 16, 4),
DIV(DOUT_UHOST, "dout_uhost", "mout_uhost", CLK_DIV1, 20, 4),
DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV2, 0, 4),
DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV2, 4, 4),
DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV2, 8, 4),
DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV2, 12, 4),
DIV(DOUT_UART, "dout_uart", "mout_uart", CLK_DIV2, 16, 4),
DIV(DOUT_IRDA, "dout_irda", "mout_irda", CLK_DIV2, 20, 4),
};
/* List of clock dividers present on S3C6400. */
DIV_CLOCKS(s3c6400_div_clks) __initdata = {
DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 3),
};
/* List of clock dividers present on S3C6410. */
DIV_CLOCKS(s3c6410_div_clks) __initdata = {
DIV(ARMCLK, "armclk", "mout_apll", CLK_DIV0, 0, 4),
DIV(DOUT_FIMC, "dout_fimc", "hclk", CLK_DIV1, 24, 4),
DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV2, 24, 4),
};
/* List of clock gates present on all S3C64xx SoCs. */
GATE_CLOCKS(s3c64xx_gate_clks) __initdata = {
GATE_BUS(HCLK_UHOST, "hclk_uhost", "hclk", HCLK_GATE, 29),
GATE_BUS(HCLK_SECUR, "hclk_secur", "hclk", HCLK_GATE, 28),
GATE_BUS(HCLK_SDMA1, "hclk_sdma1", "hclk", HCLK_GATE, 27),
GATE_BUS(HCLK_SDMA0, "hclk_sdma0", "hclk", HCLK_GATE, 26),
GATE_ON(HCLK_DDR1, "hclk_ddr1", "hclk", HCLK_GATE, 24),
GATE_BUS(HCLK_USB, "hclk_usb", "hclk", HCLK_GATE, 20),
GATE_BUS(HCLK_HSMMC2, "hclk_hsmmc2", "hclk", HCLK_GATE, 19),
GATE_BUS(HCLK_HSMMC1, "hclk_hsmmc1", "hclk", HCLK_GATE, 18),
GATE_BUS(HCLK_HSMMC0, "hclk_hsmmc0", "hclk", HCLK_GATE, 17),
GATE_BUS(HCLK_MDP, "hclk_mdp", "hclk", HCLK_GATE, 16),
GATE_BUS(HCLK_DHOST, "hclk_dhost", "hclk", HCLK_GATE, 15),
GATE_BUS(HCLK_IHOST, "hclk_ihost", "hclk", HCLK_GATE, 14),
GATE_BUS(HCLK_DMA1, "hclk_dma1", "hclk", HCLK_GATE, 13),
GATE_BUS(HCLK_DMA0, "hclk_dma0", "hclk", HCLK_GATE, 12),
GATE_BUS(HCLK_JPEG, "hclk_jpeg", "hclk", HCLK_GATE, 11),
GATE_BUS(HCLK_CAMIF, "hclk_camif", "hclk", HCLK_GATE, 10),
GATE_BUS(HCLK_SCALER, "hclk_scaler", "hclk", HCLK_GATE, 9),
GATE_BUS(HCLK_2D, "hclk_2d", "hclk", HCLK_GATE, 8),
GATE_BUS(HCLK_TV, "hclk_tv", "hclk", HCLK_GATE, 7),
GATE_BUS(HCLK_POST0, "hclk_post0", "hclk", HCLK_GATE, 5),
GATE_BUS(HCLK_ROT, "hclk_rot", "hclk", HCLK_GATE, 4),
GATE_BUS(HCLK_LCD, "hclk_lcd", "hclk", HCLK_GATE, 3),
GATE_BUS(HCLK_TZIC, "hclk_tzic", "hclk", HCLK_GATE, 2),
GATE_ON(HCLK_INTC, "hclk_intc", "hclk", HCLK_GATE, 1),
GATE_ON(PCLK_SKEY, "pclk_skey", "pclk", PCLK_GATE, 24),
GATE_ON(PCLK_CHIPID, "pclk_chipid", "pclk", PCLK_GATE, 23),
GATE_BUS(PCLK_SPI1, "pclk_spi1", "pclk", PCLK_GATE, 22),
GATE_BUS(PCLK_SPI0, "pclk_spi0", "pclk", PCLK_GATE, 21),
GATE_BUS(PCLK_HSIRX, "pclk_hsirx", "pclk", PCLK_GATE, 20),
GATE_BUS(PCLK_HSITX, "pclk_hsitx", "pclk", PCLK_GATE, 19),
GATE_ON(PCLK_GPIO, "pclk_gpio", "pclk", PCLK_GATE, 18),
GATE_BUS(PCLK_IIC0, "pclk_iic0", "pclk", PCLK_GATE, 17),
GATE_BUS(PCLK_IIS1, "pclk_iis1", "pclk", PCLK_GATE, 16),
GATE_BUS(PCLK_IIS0, "pclk_iis0", "pclk", PCLK_GATE, 15),
GATE_BUS(PCLK_AC97, "pclk_ac97", "pclk", PCLK_GATE, 14),
GATE_BUS(PCLK_TZPC, "pclk_tzpc", "pclk", PCLK_GATE, 13),
GATE_BUS(PCLK_TSADC, "pclk_tsadc", "pclk", PCLK_GATE, 12),
GATE_BUS(PCLK_KEYPAD, "pclk_keypad", "pclk", PCLK_GATE, 11),
GATE_BUS(PCLK_IRDA, "pclk_irda", "pclk", PCLK_GATE, 10),
GATE_BUS(PCLK_PCM1, "pclk_pcm1", "pclk", PCLK_GATE, 9),
GATE_BUS(PCLK_PCM0, "pclk_pcm0", "pclk", PCLK_GATE, 8),
GATE_BUS(PCLK_PWM, "pclk_pwm", "pclk", PCLK_GATE, 7),
GATE_BUS(PCLK_RTC, "pclk_rtc", "pclk", PCLK_GATE, 6),
GATE_BUS(PCLK_WDT, "pclk_wdt", "pclk", PCLK_GATE, 5),
GATE_BUS(PCLK_UART3, "pclk_uart3", "pclk", PCLK_GATE, 4),
GATE_BUS(PCLK_UART2, "pclk_uart2", "pclk", PCLK_GATE, 3),
GATE_BUS(PCLK_UART1, "pclk_uart1", "pclk", PCLK_GATE, 2),
GATE_BUS(PCLK_UART0, "pclk_uart0", "pclk", PCLK_GATE, 1),
GATE_BUS(PCLK_MFC, "pclk_mfc", "pclk", PCLK_GATE, 0),
GATE_SCLK(SCLK_UHOST, "sclk_uhost", "dout_uhost", SCLK_GATE, 30),
GATE_SCLK(SCLK_MMC2_48, "sclk_mmc2_48", "clk48m", SCLK_GATE, 29),
GATE_SCLK(SCLK_MMC1_48, "sclk_mmc1_48", "clk48m", SCLK_GATE, 28),
GATE_SCLK(SCLK_MMC0_48, "sclk_mmc0_48", "clk48m", SCLK_GATE, 27),
GATE_SCLK(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", SCLK_GATE, 26),
GATE_SCLK(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", SCLK_GATE, 25),
GATE_SCLK(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", SCLK_GATE, 24),
GATE_SCLK(SCLK_SPI1_48, "sclk_spi1_48", "clk48m", SCLK_GATE, 23),
GATE_SCLK(SCLK_SPI0_48, "sclk_spi0_48", "clk48m", SCLK_GATE, 22),
GATE_SCLK(SCLK_SPI1, "sclk_spi1", "dout_spi1", SCLK_GATE, 21),
GATE_SCLK(SCLK_SPI0, "sclk_spi0", "dout_spi0", SCLK_GATE, 20),
GATE_SCLK(SCLK_DAC27, "sclk_dac27", "mout_dac27", SCLK_GATE, 19),
GATE_SCLK(SCLK_TV27, "sclk_tv27", "mout_tv27", SCLK_GATE, 18),
GATE_SCLK(SCLK_SCALER27, "sclk_scaler27", "clk27m", SCLK_GATE, 17),
GATE_SCLK(SCLK_SCALER, "sclk_scaler", "dout_scaler", SCLK_GATE, 16),
GATE_SCLK(SCLK_LCD27, "sclk_lcd27", "clk27m", SCLK_GATE, 15),
GATE_SCLK(SCLK_LCD, "sclk_lcd", "dout_lcd", SCLK_GATE, 14),
GATE_SCLK(SCLK_POST0_27, "sclk_post0_27", "clk27m", SCLK_GATE, 12),
GATE_SCLK(SCLK_POST0, "sclk_post0", "dout_lcd", SCLK_GATE, 10),
GATE_SCLK(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", SCLK_GATE, 9),
GATE_SCLK(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", SCLK_GATE, 8),
GATE_SCLK(SCLK_SECUR, "sclk_secur", "dout_secur", SCLK_GATE, 7),
GATE_SCLK(SCLK_IRDA, "sclk_irda", "dout_irda", SCLK_GATE, 6),
GATE_SCLK(SCLK_UART, "sclk_uart", "dout_uart", SCLK_GATE, 5),
GATE_SCLK(SCLK_MFC, "sclk_mfc", "dout_mfc", SCLK_GATE, 3),
GATE_SCLK(SCLK_CAM, "sclk_cam", "dout_cam", SCLK_GATE, 2),
GATE_SCLK(SCLK_JPEG, "sclk_jpeg", "dout_jpeg", SCLK_GATE, 1),
};
/* List of clock gates present on S3C6400. */
GATE_CLOCKS(s3c6400_gate_clks) __initdata = {
GATE_ON(HCLK_DDR0, "hclk_ddr0", "hclk", HCLK_GATE, 23),
GATE_SCLK(SCLK_ONENAND, "sclk_onenand", "parent", SCLK_GATE, 4),
};
/* List of clock gates present on S3C6410. */
GATE_CLOCKS(s3c6410_gate_clks) __initdata = {
GATE_BUS(HCLK_3DSE, "hclk_3dse", "hclk", HCLK_GATE, 31),
GATE_ON(HCLK_IROM, "hclk_irom", "hclk", HCLK_GATE, 25),
GATE_ON(HCLK_MEM1, "hclk_mem1", "hclk", HCLK_GATE, 22),
GATE_ON(HCLK_MEM0, "hclk_mem0", "hclk", HCLK_GATE, 21),
GATE_BUS(HCLK_MFC, "hclk_mfc", "hclk", HCLK_GATE, 0),
GATE_BUS(PCLK_IIC1, "pclk_iic1", "pclk", PCLK_GATE, 27),
GATE_BUS(PCLK_IIS2, "pclk_iis2", "pclk", PCLK_GATE, 26),
GATE_SCLK(SCLK_FIMC, "sclk_fimc", "dout_fimc", SCLK_GATE, 13),
GATE_SCLK(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", SCLK_GATE, 11),
GATE_BUS(MEM0_CFCON, "mem0_cfcon", "hclk_mem0", MEM0_GATE, 5),
GATE_BUS(MEM0_ONENAND1, "mem0_onenand1", "hclk_mem0", MEM0_GATE, 4),
GATE_BUS(MEM0_ONENAND0, "mem0_onenand0", "hclk_mem0", MEM0_GATE, 3),
GATE_BUS(MEM0_NFCON, "mem0_nfcon", "hclk_mem0", MEM0_GATE, 2),
GATE_ON(MEM0_SROM, "mem0_srom", "hclk_mem0", MEM0_GATE, 1),
};
/* List of PLL clocks. */
static struct samsung_pll_clock s3c64xx_pll_clks[] __initdata = {
PLL(pll_6552, FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON, NULL),
PLL(pll_6552, FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON, NULL),
PLL(pll_6553, FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, NULL),
};
/* Aliases for common s3c64xx clocks. */
static struct samsung_clock_alias s3c64xx_clock_aliases[] = {
ALIAS(FOUT_APLL, NULL, "fout_apll"),
ALIAS(FOUT_MPLL, NULL, "fout_mpll"),
ALIAS(FOUT_EPLL, NULL, "fout_epll"),
ALIAS(MOUT_EPLL, NULL, "mout_epll"),
ALIAS(DOUT_MPLL, NULL, "dout_mpll"),
ALIAS(HCLKX2, NULL, "hclk2"),
ALIAS(HCLK, NULL, "hclk"),
ALIAS(PCLK, NULL, "pclk"),
ALIAS(PCLK, NULL, "clk_uart_baud2"),
ALIAS(ARMCLK, NULL, "armclk"),
ALIAS(HCLK_UHOST, "s3c2410-ohci", "usb-host"),
ALIAS(HCLK_USB, "s3c-hsotg", "otg"),
ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "hsmmc"),
ALIAS(HCLK_HSMMC2, "s3c-sdhci.2", "mmc_busclk.0"),
ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "hsmmc"),
ALIAS(HCLK_HSMMC1, "s3c-sdhci.1", "mmc_busclk.0"),
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "hsmmc"),
ALIAS(HCLK_HSMMC0, "s3c-sdhci.0", "mmc_busclk.0"),
ALIAS(HCLK_DMA1, "dma-pl080s.1", "apb_pclk"),
ALIAS(HCLK_DMA0, "dma-pl080s.0", "apb_pclk"),
ALIAS(HCLK_CAMIF, "s3c-camif", "camif"),
ALIAS(HCLK_LCD, "s3c-fb", "lcd"),
ALIAS(PCLK_SPI1, "s3c6410-spi.1", "spi"),
ALIAS(PCLK_SPI0, "s3c6410-spi.0", "spi"),
ALIAS(PCLK_IIC0, "s3c2440-i2c.0", "i2c"),
ALIAS(PCLK_IIS1, "samsung-i2s.1", "iis"),
ALIAS(PCLK_IIS0, "samsung-i2s.0", "iis"),
ALIAS(PCLK_AC97, "samsung-ac97", "ac97"),
ALIAS(PCLK_TSADC, "s3c64xx-adc", "adc"),
ALIAS(PCLK_KEYPAD, "samsung-keypad", "keypad"),
ALIAS(PCLK_PCM1, "samsung-pcm.1", "pcm"),
ALIAS(PCLK_PCM0, "samsung-pcm.0", "pcm"),
ALIAS(PCLK_PWM, NULL, "timers"),
ALIAS(PCLK_RTC, "s3c64xx-rtc", "rtc"),
ALIAS(PCLK_WDT, NULL, "watchdog"),
ALIAS(PCLK_UART3, "s3c6400-uart.3", "uart"),
ALIAS(PCLK_UART2, "s3c6400-uart.2", "uart"),
ALIAS(PCLK_UART1, "s3c6400-uart.1", "uart"),
ALIAS(PCLK_UART0, "s3c6400-uart.0", "uart"),
ALIAS(SCLK_UHOST, "s3c2410-ohci", "usb-bus-host"),
ALIAS(SCLK_MMC2, "s3c-sdhci.2", "mmc_busclk.2"),
ALIAS(SCLK_MMC1, "s3c-sdhci.1", "mmc_busclk.2"),
ALIAS(SCLK_MMC0, "s3c-sdhci.0", "mmc_busclk.2"),
ALIAS(PCLK_SPI1, "s3c6410-spi.1", "spi_busclk0"),
ALIAS(SCLK_SPI1, "s3c6410-spi.1", "spi_busclk2"),
ALIAS(PCLK_SPI0, "s3c6410-spi.0", "spi_busclk0"),
ALIAS(SCLK_SPI0, "s3c6410-spi.0", "spi_busclk2"),
ALIAS(SCLK_AUDIO1, "samsung-pcm.1", "audio-bus"),
ALIAS(SCLK_AUDIO1, "samsung-i2s.1", "audio-bus"),
ALIAS(SCLK_AUDIO0, "samsung-pcm.0", "audio-bus"),
ALIAS(SCLK_AUDIO0, "samsung-i2s.0", "audio-bus"),
ALIAS(SCLK_UART, NULL, "clk_uart_baud3"),
ALIAS(SCLK_CAM, "s3c-camif", "camera"),
};
/* Aliases for s3c6400-specific clocks. */
static struct samsung_clock_alias s3c6400_clock_aliases[] = {
/* Nothing to place here yet. */
};
/* Aliases for s3c6410-specific clocks. */
static struct samsung_clock_alias s3c6410_clock_aliases[] = {
ALIAS(PCLK_IIC1, "s3c2440-i2c.1", "i2c"),
ALIAS(PCLK_IIS2, "samsung-i2s.2", "iis"),
ALIAS(SCLK_FIMC, "s3c-camif", "fimc"),
ALIAS(SCLK_AUDIO2, "samsung-i2s.2", "audio-bus"),
ALIAS(MEM0_SROM, NULL, "srom"),
};
static void __init s3c64xx_clk_register_fixed_ext(
struct samsung_clk_provider *ctx,
unsigned long fin_pll_f,
unsigned long xusbxti_f)
{
s3c64xx_fixed_rate_ext_clks[0].fixed_rate = fin_pll_f;
s3c64xx_fixed_rate_ext_clks[1].fixed_rate = xusbxti_f;
samsung_clk_register_fixed_rate(ctx, s3c64xx_fixed_rate_ext_clks,
ARRAY_SIZE(s3c64xx_fixed_rate_ext_clks));
}
/* Register s3c64xx clocks. */
void __init s3c64xx_clk_init(struct device_node *np, unsigned long xtal_f,
unsigned long xusbxti_f, bool s3c6400,
void __iomem *base)
{
struct samsung_clk_provider *ctx;
struct clk_hw **hws;
reg_base = base;
is_s3c6400 = s3c6400;
if (np) {
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
}
ctx = samsung_clk_init(NULL, reg_base, NR_CLKS);
hws = ctx->clk_data.hws;
/* Register external clocks. */
if (!np)
s3c64xx_clk_register_fixed_ext(ctx, xtal_f, xusbxti_f);
/* Register PLLs. */
samsung_clk_register_pll(ctx, s3c64xx_pll_clks,
ARRAY_SIZE(s3c64xx_pll_clks));
/* Register common internal clocks. */
samsung_clk_register_fixed_rate(ctx, s3c64xx_fixed_rate_clks,
ARRAY_SIZE(s3c64xx_fixed_rate_clks));
samsung_clk_register_mux(ctx, s3c64xx_mux_clks,
ARRAY_SIZE(s3c64xx_mux_clks));
samsung_clk_register_div(ctx, s3c64xx_div_clks,
ARRAY_SIZE(s3c64xx_div_clks));
samsung_clk_register_gate(ctx, s3c64xx_gate_clks,
ARRAY_SIZE(s3c64xx_gate_clks));
/* Register SoC-specific clocks. */
if (is_s3c6400) {
samsung_clk_register_mux(ctx, s3c6400_mux_clks,
ARRAY_SIZE(s3c6400_mux_clks));
samsung_clk_register_div(ctx, s3c6400_div_clks,
ARRAY_SIZE(s3c6400_div_clks));
samsung_clk_register_gate(ctx, s3c6400_gate_clks,
ARRAY_SIZE(s3c6400_gate_clks));
samsung_clk_register_alias(ctx, s3c6400_clock_aliases,
ARRAY_SIZE(s3c6400_clock_aliases));
} else {
samsung_clk_register_mux(ctx, s3c6410_mux_clks,
ARRAY_SIZE(s3c6410_mux_clks));
samsung_clk_register_div(ctx, s3c6410_div_clks,
ARRAY_SIZE(s3c6410_div_clks));
samsung_clk_register_gate(ctx, s3c6410_gate_clks,
ARRAY_SIZE(s3c6410_gate_clks));
samsung_clk_register_alias(ctx, s3c6410_clock_aliases,
ARRAY_SIZE(s3c6410_clock_aliases));
}
samsung_clk_register_alias(ctx, s3c64xx_clock_aliases,
ARRAY_SIZE(s3c64xx_clock_aliases));
samsung_clk_sleep_init(reg_base, NULL, s3c64xx_clk_regs,
ARRAY_SIZE(s3c64xx_clk_regs));
if (!is_s3c6400)
samsung_clk_sleep_init(reg_base, NULL, s3c6410_clk_regs,
ARRAY_SIZE(s3c6410_clk_regs));
samsung_clk_of_add_provider(np, ctx);
pr_info("%s clocks: apll = %lu, mpll = %lu\n"
"\tepll = %lu, arm_clk = %lu\n",
is_s3c6400 ? "S3C6400" : "S3C6410",
clk_hw_get_rate(hws[MOUT_APLL]),
clk_hw_get_rate(hws[MOUT_MPLL]),
clk_hw_get_rate(hws[MOUT_EPLL]),
clk_hw_get_rate(hws[ARMCLK]));
}
static void __init s3c6400_clk_init(struct device_node *np)
{
s3c64xx_clk_init(np, 0, 0, true, NULL);
}
CLK_OF_DECLARE(s3c6400_clk, "samsung,s3c6400-clock", s3c6400_clk_init);
static void __init s3c6410_clk_init(struct device_node *np)
{
s3c64xx_clk_init(np, 0, 0, false, NULL);
}
CLK_OF_DECLARE(s3c6410_clk, "samsung,s3c6410-clock", s3c6410_clk_init);
@@ -0,0 +1,213 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 Tomasz Figa <t.figa@samsung.com>
*
* Based on Exynos Audio Subsystem Clock Controller driver:
*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Author: Padmavathi Venna <padma.v@samsung.com>
*
* Driver for Audio Subsystem Clock Controller of S5PV210-compatible SoCs.
*/
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include <linux/syscore_ops.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <dt-bindings/clock/s5pv210-audss.h>
static DEFINE_SPINLOCK(lock);
static void __iomem *reg_base;
static struct clk_hw_onecell_data *clk_data;
#define ASS_CLK_SRC 0x0
#define ASS_CLK_DIV 0x4
#define ASS_CLK_GATE 0x8
#ifdef CONFIG_PM_SLEEP
static unsigned long reg_save[][2] = {
{ASS_CLK_SRC, 0},
{ASS_CLK_DIV, 0},
{ASS_CLK_GATE, 0},
};
static int s5pv210_audss_clk_suspend(void *data)
{
int i;
for (i = 0; i < ARRAY_SIZE(reg_save); i++)
reg_save[i][1] = readl(reg_base + reg_save[i][0]);
return 0;
}
static void s5pv210_audss_clk_resume(void *data)
{
int i;
for (i = 0; i < ARRAY_SIZE(reg_save); i++)
writel(reg_save[i][1], reg_base + reg_save[i][0]);
}
static const struct syscore_ops s5pv210_audss_clk_syscore_ops = {
.suspend = s5pv210_audss_clk_suspend,
.resume = s5pv210_audss_clk_resume,
};
static struct syscore s5pv210_audss_clk_syscore = {
.ops = &s5pv210_audss_clk_syscore_ops,
};
#endif /* CONFIG_PM_SLEEP */
/* register s5pv210_audss clocks */
static int s5pv210_audss_clk_probe(struct platform_device *pdev)
{
int i, ret = 0;
const char *mout_audss_p[2];
const char *mout_i2s_p[3];
const char *hclk_p;
struct clk_hw **clk_table;
struct clk *hclk, *pll_ref, *pll_in, *cdclk, *sclk_audio;
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
return PTR_ERR(reg_base);
clk_data = devm_kzalloc(&pdev->dev,
struct_size(clk_data, hws, AUDSS_MAX_CLKS),
GFP_KERNEL);
if (!clk_data)
return -ENOMEM;
clk_data->num = AUDSS_MAX_CLKS;
clk_table = clk_data->hws;
hclk = devm_clk_get(&pdev->dev, "hclk");
if (IS_ERR(hclk)) {
dev_err(&pdev->dev, "failed to get hclk clock\n");
return PTR_ERR(hclk);
}
pll_in = devm_clk_get(&pdev->dev, "fout_epll");
if (IS_ERR(pll_in)) {
dev_err(&pdev->dev, "failed to get fout_epll clock\n");
return PTR_ERR(pll_in);
}
sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio0");
if (IS_ERR(sclk_audio)) {
dev_err(&pdev->dev, "failed to get sclk_audio0 clock\n");
return PTR_ERR(sclk_audio);
}
/* iiscdclk0 is an optional external I2S codec clock */
cdclk = devm_clk_get(&pdev->dev, "iiscdclk0");
pll_ref = devm_clk_get(&pdev->dev, "xxti");
if (!IS_ERR(pll_ref))
mout_audss_p[0] = __clk_get_name(pll_ref);
else
mout_audss_p[0] = "xxti";
mout_audss_p[1] = __clk_get_name(pll_in);
clk_table[CLK_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
mout_audss_p, ARRAY_SIZE(mout_audss_p),
CLK_SET_RATE_NO_REPARENT,
reg_base + ASS_CLK_SRC, 0, 1, 0, &lock);
mout_i2s_p[0] = "mout_audss";
if (!IS_ERR(cdclk))
mout_i2s_p[1] = __clk_get_name(cdclk);
else
mout_i2s_p[1] = "iiscdclk0";
mout_i2s_p[2] = __clk_get_name(sclk_audio);
clk_table[CLK_MOUT_I2S_A] = clk_hw_register_mux(NULL, "mout_i2s_audss",
mout_i2s_p, ARRAY_SIZE(mout_i2s_p),
CLK_SET_RATE_NO_REPARENT,
reg_base + ASS_CLK_SRC, 2, 2, 0, &lock);
clk_table[CLK_DOUT_AUD_BUS] = clk_hw_register_divider(NULL,
"dout_aud_bus", "mout_audss", 0,
reg_base + ASS_CLK_DIV, 0, 4, 0, &lock);
clk_table[CLK_DOUT_I2S_A] = clk_hw_register_divider(NULL,
"dout_i2s_audss", "mout_i2s_audss", 0,
reg_base + ASS_CLK_DIV, 4, 4, 0, &lock);
clk_table[CLK_I2S] = clk_hw_register_gate(NULL, "i2s_audss",
"dout_i2s_audss", CLK_SET_RATE_PARENT,
reg_base + ASS_CLK_GATE, 6, 0, &lock);
hclk_p = __clk_get_name(hclk);
clk_table[CLK_HCLK_I2S] = clk_hw_register_gate(NULL, "hclk_i2s_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 5, 0, &lock);
clk_table[CLK_HCLK_UART] = clk_hw_register_gate(NULL, "hclk_uart_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 4, 0, &lock);
clk_table[CLK_HCLK_HWA] = clk_hw_register_gate(NULL, "hclk_hwa_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 3, 0, &lock);
clk_table[CLK_HCLK_DMA] = clk_hw_register_gate(NULL, "hclk_dma_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 2, 0, &lock);
clk_table[CLK_HCLK_BUF] = clk_hw_register_gate(NULL, "hclk_buf_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 1, 0, &lock);
clk_table[CLK_HCLK_RP] = clk_hw_register_gate(NULL, "hclk_rp_audss",
hclk_p, CLK_IGNORE_UNUSED,
reg_base + ASS_CLK_GATE, 0, 0, &lock);
for (i = 0; i < clk_data->num; i++) {
if (IS_ERR(clk_table[i])) {
dev_err(&pdev->dev, "failed to register clock %d\n", i);
ret = PTR_ERR(clk_table[i]);
goto unregister;
}
}
ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
clk_data);
if (ret) {
dev_err(&pdev->dev, "failed to add clock provider\n");
goto unregister;
}
#ifdef CONFIG_PM_SLEEP
register_syscore(&s5pv210_audss_clk_syscore);
#endif
return 0;
unregister:
for (i = 0; i < clk_data->num; i++) {
if (!IS_ERR(clk_table[i]))
clk_hw_unregister(clk_table[i]);
}
return ret;
}
static const struct of_device_id s5pv210_audss_clk_of_match[] = {
{ .compatible = "samsung,s5pv210-audss-clock", },
{},
};
static struct platform_driver s5pv210_audss_clk_driver = {
.driver = {
.name = "s5pv210-audss-clk",
.suppress_bind_attrs = true,
.of_match_table = s5pv210_audss_clk_of_match,
},
.probe = s5pv210_audss_clk_probe,
};
static int __init s5pv210_audss_clk_init(void)
{
return platform_driver_register(&s5pv210_audss_clk_driver);
}
core_initcall(s5pv210_audss_clk_init);
@@ -0,0 +1,817 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Author: Mateusz Krawczuk <m.krawczuk@partner.samsung.com>
*
* Based on clock drivers for S3C64xx and Exynos4 SoCs.
*
* Common Clock Framework support for all S5PC110/S5PV210 SoCs.
*/
#include <linux/clk-provider.h>
#include <linux/of_address.h>
#include "clk.h"
#include "clk-pll.h"
#include <dt-bindings/clock/s5pv210.h>
/* S5PC110/S5PV210 clock controller register offsets */
#define APLL_LOCK 0x0000
#define MPLL_LOCK 0x0008
#define EPLL_LOCK 0x0010
#define VPLL_LOCK 0x0020
#define APLL_CON0 0x0100
#define APLL_CON1 0x0104
#define MPLL_CON 0x0108
#define EPLL_CON0 0x0110
#define EPLL_CON1 0x0114
#define VPLL_CON 0x0120
#define CLK_SRC0 0x0200
#define CLK_SRC1 0x0204
#define CLK_SRC2 0x0208
#define CLK_SRC3 0x020c
#define CLK_SRC4 0x0210
#define CLK_SRC5 0x0214
#define CLK_SRC6 0x0218
#define CLK_SRC_MASK0 0x0280
#define CLK_SRC_MASK1 0x0284
#define CLK_DIV0 0x0300
#define CLK_DIV1 0x0304
#define CLK_DIV2 0x0308
#define CLK_DIV3 0x030c
#define CLK_DIV4 0x0310
#define CLK_DIV5 0x0314
#define CLK_DIV6 0x0318
#define CLK_DIV7 0x031c
#define CLK_GATE_MAIN0 0x0400
#define CLK_GATE_MAIN1 0x0404
#define CLK_GATE_MAIN2 0x0408
#define CLK_GATE_PERI0 0x0420
#define CLK_GATE_PERI1 0x0424
#define CLK_GATE_SCLK0 0x0440
#define CLK_GATE_SCLK1 0x0444
#define CLK_GATE_IP0 0x0460
#define CLK_GATE_IP1 0x0464
#define CLK_GATE_IP2 0x0468
#define CLK_GATE_IP3 0x046c
#define CLK_GATE_IP4 0x0470
#define CLK_GATE_BLOCK 0x0480
#define CLK_GATE_IP5 0x0484
#define CLK_OUT 0x0500
#define MISC 0xe000
#define OM_STAT 0xe100
/* IDs of PLLs available on S5PV210/S5P6442 SoCs */
enum {
apll,
mpll,
epll,
vpll,
};
/* IDs of external clocks (used for legacy boards) */
enum {
xxti,
xusbxti,
};
static void __iomem *reg_base;
/* List of registers that need to be preserved across suspend/resume. */
static unsigned long s5pv210_clk_regs[] __initdata = {
CLK_SRC0,
CLK_SRC1,
CLK_SRC2,
CLK_SRC3,
CLK_SRC4,
CLK_SRC5,
CLK_SRC6,
CLK_SRC_MASK0,
CLK_SRC_MASK1,
CLK_DIV0,
CLK_DIV1,
CLK_DIV2,
CLK_DIV3,
CLK_DIV4,
CLK_DIV5,
CLK_DIV6,
CLK_DIV7,
CLK_GATE_MAIN0,
CLK_GATE_MAIN1,
CLK_GATE_MAIN2,
CLK_GATE_PERI0,
CLK_GATE_PERI1,
CLK_GATE_SCLK0,
CLK_GATE_SCLK1,
CLK_GATE_IP0,
CLK_GATE_IP1,
CLK_GATE_IP2,
CLK_GATE_IP3,
CLK_GATE_IP4,
CLK_GATE_IP5,
CLK_GATE_BLOCK,
APLL_LOCK,
MPLL_LOCK,
EPLL_LOCK,
VPLL_LOCK,
APLL_CON0,
APLL_CON1,
MPLL_CON,
EPLL_CON0,
EPLL_CON1,
VPLL_CON,
CLK_OUT,
};
/* Mux parent lists. */
static const char *const fin_pll_p[] __initconst = {
"xxti",
"xusbxti"
};
static const char *const mout_apll_p[] __initconst = {
"fin_pll",
"fout_apll"
};
static const char *const mout_mpll_p[] __initconst = {
"fin_pll",
"fout_mpll"
};
static const char *const mout_epll_p[] __initconst = {
"fin_pll",
"fout_epll"
};
static const char *const mout_vpllsrc_p[] __initconst = {
"fin_pll",
"sclk_hdmi27m"
};
static const char *const mout_vpll_p[] __initconst = {
"mout_vpllsrc",
"fout_vpll"
};
static const char *const mout_group1_p[] __initconst = {
"dout_a2m",
"mout_mpll",
"mout_epll",
"mout_vpll"
};
static const char *const mout_group2_p[] __initconst = {
"xxti",
"xusbxti",
"sclk_hdmi27m",
"sclk_usbphy0",
"sclk_usbphy1",
"sclk_hdmiphy",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_audio0_p[] __initconst = {
"xxti",
"pcmcdclk0",
"sclk_hdmi27m",
"sclk_usbphy0",
"sclk_usbphy1",
"sclk_hdmiphy",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_audio1_p[] __initconst = {
"i2scdclk1",
"pcmcdclk1",
"sclk_hdmi27m",
"sclk_usbphy0",
"sclk_usbphy1",
"sclk_hdmiphy",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_audio2_p[] __initconst = {
"i2scdclk2",
"pcmcdclk2",
"sclk_hdmi27m",
"sclk_usbphy0",
"sclk_usbphy1",
"sclk_hdmiphy",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_spdif_p[] __initconst = {
"dout_audio0",
"dout_audio1",
"dout_audio3",
};
static const char *const mout_group3_p[] __initconst = {
"mout_apll",
"mout_mpll"
};
static const char *const mout_group4_p[] __initconst = {
"mout_mpll",
"dout_a2m"
};
static const char *const mout_flash_p[] __initconst = {
"dout_hclkd",
"dout_hclkp"
};
static const char *const mout_dac_p[] __initconst = {
"mout_vpll",
"sclk_hdmiphy"
};
static const char *const mout_hdmi_p[] __initconst = {
"sclk_hdmiphy",
"dout_tblk"
};
static const char *const mout_mixer_p[] __initconst = {
"mout_dac",
"mout_hdmi"
};
static const char *const mout_vpll_6442_p[] __initconst = {
"fin_pll",
"fout_vpll"
};
static const char *const mout_mixer_6442_p[] __initconst = {
"mout_vpll",
"dout_mixer"
};
static const char *const mout_d0sync_6442_p[] __initconst = {
"mout_dsys",
"div_apll"
};
static const char *const mout_d1sync_6442_p[] __initconst = {
"mout_psys",
"div_apll"
};
static const char *const mout_group2_6442_p[] __initconst = {
"fin_pll",
"none",
"none",
"sclk_usbphy0",
"none",
"none",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_audio0_6442_p[] __initconst = {
"fin_pll",
"pcmcdclk0",
"none",
"sclk_usbphy0",
"none",
"none",
"mout_mpll",
"mout_epll",
"mout_vpll",
};
static const char *const mout_audio1_6442_p[] __initconst = {
"i2scdclk1",
"pcmcdclk1",
"none",
"sclk_usbphy0",
"none",
"none",
"mout_mpll",
"mout_epll",
"mout_vpll",
"fin_pll",
};
static const char *const mout_clksel_p[] __initconst = {
"fout_apll_clkout",
"fout_mpll_clkout",
"fout_epll",
"fout_vpll",
"sclk_usbphy0",
"sclk_usbphy1",
"sclk_hdmiphy",
"rtc",
"rtc_tick",
"dout_hclkm",
"dout_pclkm",
"dout_hclkd",
"dout_pclkd",
"dout_hclkp",
"dout_pclkp",
"dout_apll_clkout",
"dout_hpm",
"xxti",
"xusbxti",
"div_dclk"
};
static const char *const mout_clksel_6442_p[] __initconst = {
"fout_apll_clkout",
"fout_mpll_clkout",
"fout_epll",
"fout_vpll",
"sclk_usbphy0",
"none",
"none",
"rtc",
"rtc_tick",
"none",
"none",
"dout_hclkd",
"dout_pclkd",
"dout_hclkp",
"dout_pclkp",
"dout_apll_clkout",
"none",
"fin_pll",
"none",
"div_dclk"
};
static const char *const mout_clkout_p[] __initconst = {
"dout_clkout",
"none",
"xxti",
"xusbxti"
};
/* Common fixed factor clocks. */
static const struct samsung_fixed_factor_clock ffactor_clks[] __initconst = {
FFACTOR(FOUT_APLL_CLKOUT, "fout_apll_clkout", "fout_apll", 1, 4, 0),
FFACTOR(FOUT_MPLL_CLKOUT, "fout_mpll_clkout", "fout_mpll", 1, 2, 0),
FFACTOR(DOUT_APLL_CLKOUT, "dout_apll_clkout", "dout_apll", 1, 4, 0),
};
/* PLL input mux (fin_pll), which needs to be registered before PLLs. */
static const struct samsung_mux_clock early_mux_clks[] __initconst = {
MUX_F(FIN_PLL, "fin_pll", fin_pll_p, OM_STAT, 0, 1,
CLK_MUX_READ_ONLY, 0),
};
/* Common clock muxes. */
static const struct samsung_mux_clock mux_clks[] __initconst = {
MUX(MOUT_FLASH, "mout_flash", mout_flash_p, CLK_SRC0, 28, 1),
MUX(MOUT_PSYS, "mout_psys", mout_group4_p, CLK_SRC0, 24, 1),
MUX(MOUT_DSYS, "mout_dsys", mout_group4_p, CLK_SRC0, 20, 1),
MUX(MOUT_MSYS, "mout_msys", mout_group3_p, CLK_SRC0, 16, 1),
MUX(MOUT_EPLL, "mout_epll", mout_epll_p, CLK_SRC0, 8, 1),
MUX(MOUT_MPLL, "mout_mpll", mout_mpll_p, CLK_SRC0, 4, 1),
MUX(MOUT_APLL, "mout_apll", mout_apll_p, CLK_SRC0, 0, 1),
MUX(MOUT_CLKOUT, "mout_clkout", mout_clkout_p, MISC, 8, 2),
};
/* S5PV210-specific clock muxes. */
static const struct samsung_mux_clock s5pv210_mux_clks[] __initconst = {
MUX(MOUT_VPLL, "mout_vpll", mout_vpll_p, CLK_SRC0, 12, 1),
MUX(MOUT_VPLLSRC, "mout_vpllsrc", mout_vpllsrc_p, CLK_SRC1, 28, 1),
MUX(MOUT_CSIS, "mout_csis", mout_group2_p, CLK_SRC1, 24, 4),
MUX(MOUT_FIMD, "mout_fimd", mout_group2_p, CLK_SRC1, 20, 4),
MUX(MOUT_CAM1, "mout_cam1", mout_group2_p, CLK_SRC1, 16, 4),
MUX(MOUT_CAM0, "mout_cam0", mout_group2_p, CLK_SRC1, 12, 4),
MUX(MOUT_DAC, "mout_dac", mout_dac_p, CLK_SRC1, 8, 1),
MUX(MOUT_MIXER, "mout_mixer", mout_mixer_p, CLK_SRC1, 4, 1),
MUX(MOUT_HDMI, "mout_hdmi", mout_hdmi_p, CLK_SRC1, 0, 1),
MUX(MOUT_G2D, "mout_g2d", mout_group1_p, CLK_SRC2, 8, 2),
MUX(MOUT_MFC, "mout_mfc", mout_group1_p, CLK_SRC2, 4, 2),
MUX(MOUT_G3D, "mout_g3d", mout_group1_p, CLK_SRC2, 0, 2),
MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_p, CLK_SRC3, 20, 4),
MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_p, CLK_SRC3, 16, 4),
MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_p, CLK_SRC3, 12, 4),
MUX(MOUT_UART3, "mout_uart3", mout_group2_p, CLK_SRC4, 28, 4),
MUX(MOUT_UART2, "mout_uart2", mout_group2_p, CLK_SRC4, 24, 4),
MUX(MOUT_UART1, "mout_uart1", mout_group2_p, CLK_SRC4, 20, 4),
MUX(MOUT_UART0, "mout_uart0", mout_group2_p, CLK_SRC4, 16, 4),
MUX(MOUT_MMC3, "mout_mmc3", mout_group2_p, CLK_SRC4, 12, 4),
MUX(MOUT_MMC2, "mout_mmc2", mout_group2_p, CLK_SRC4, 8, 4),
MUX(MOUT_MMC1, "mout_mmc1", mout_group2_p, CLK_SRC4, 4, 4),
MUX(MOUT_MMC0, "mout_mmc0", mout_group2_p, CLK_SRC4, 0, 4),
MUX(MOUT_PWM, "mout_pwm", mout_group2_p, CLK_SRC5, 12, 4),
MUX(MOUT_SPI1, "mout_spi1", mout_group2_p, CLK_SRC5, 4, 4),
MUX(MOUT_SPI0, "mout_spi0", mout_group2_p, CLK_SRC5, 0, 4),
MUX(MOUT_DMC0, "mout_dmc0", mout_group1_p, CLK_SRC6, 24, 2),
MUX(MOUT_PWI, "mout_pwi", mout_group2_p, CLK_SRC6, 20, 4),
MUX(MOUT_HPM, "mout_hpm", mout_group3_p, CLK_SRC6, 16, 1),
MUX(MOUT_SPDIF, "mout_spdif", mout_spdif_p, CLK_SRC6, 12, 2),
MUX(MOUT_AUDIO2, "mout_audio2", mout_audio2_p, CLK_SRC6, 8, 4),
MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_p, CLK_SRC6, 4, 4),
MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_p, CLK_SRC6, 0, 4),
MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_p, CLK_OUT, 12, 5),
};
/* S5P6442-specific clock muxes. */
static const struct samsung_mux_clock s5p6442_mux_clks[] __initconst = {
MUX(MOUT_VPLL, "mout_vpll", mout_vpll_6442_p, CLK_SRC0, 12, 1),
MUX(MOUT_FIMD, "mout_fimd", mout_group2_6442_p, CLK_SRC1, 20, 4),
MUX(MOUT_CAM1, "mout_cam1", mout_group2_6442_p, CLK_SRC1, 16, 4),
MUX(MOUT_CAM0, "mout_cam0", mout_group2_6442_p, CLK_SRC1, 12, 4),
MUX(MOUT_MIXER, "mout_mixer", mout_mixer_6442_p, CLK_SRC1, 4, 1),
MUX(MOUT_D0SYNC, "mout_d0sync", mout_d0sync_6442_p, CLK_SRC2, 28, 1),
MUX(MOUT_D1SYNC, "mout_d1sync", mout_d1sync_6442_p, CLK_SRC2, 24, 1),
MUX(MOUT_FIMC2, "mout_fimc2", mout_group2_6442_p, CLK_SRC3, 20, 4),
MUX(MOUT_FIMC1, "mout_fimc1", mout_group2_6442_p, CLK_SRC3, 16, 4),
MUX(MOUT_FIMC0, "mout_fimc0", mout_group2_6442_p, CLK_SRC3, 12, 4),
MUX(MOUT_UART2, "mout_uart2", mout_group2_6442_p, CLK_SRC4, 24, 4),
MUX(MOUT_UART1, "mout_uart1", mout_group2_6442_p, CLK_SRC4, 20, 4),
MUX(MOUT_UART0, "mout_uart0", mout_group2_6442_p, CLK_SRC4, 16, 4),
MUX(MOUT_MMC2, "mout_mmc2", mout_group2_6442_p, CLK_SRC4, 8, 4),
MUX(MOUT_MMC1, "mout_mmc1", mout_group2_6442_p, CLK_SRC4, 4, 4),
MUX(MOUT_MMC0, "mout_mmc0", mout_group2_6442_p, CLK_SRC4, 0, 4),
MUX(MOUT_PWM, "mout_pwm", mout_group2_6442_p, CLK_SRC5, 12, 4),
MUX(MOUT_SPI0, "mout_spi0", mout_group2_6442_p, CLK_SRC5, 0, 4),
MUX(MOUT_AUDIO1, "mout_audio1", mout_audio1_6442_p, CLK_SRC6, 4, 4),
MUX(MOUT_AUDIO0, "mout_audio0", mout_audio0_6442_p, CLK_SRC6, 0, 4),
MUX(MOUT_CLKSEL, "mout_clksel", mout_clksel_6442_p, CLK_OUT, 12, 5),
};
/* S5PV210-specific fixed rate clocks generated inside the SoC. */
static const struct samsung_fixed_rate_clock s5pv210_frate_clks[] __initconst = {
FRATE(SCLK_HDMI27M, "sclk_hdmi27m", NULL, 0, 27000000),
FRATE(SCLK_HDMIPHY, "sclk_hdmiphy", NULL, 0, 27000000),
FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, 0, 48000000),
FRATE(SCLK_USBPHY1, "sclk_usbphy1", NULL, 0, 48000000),
};
/* S5P6442-specific fixed rate clocks generated inside the SoC. */
static const struct samsung_fixed_rate_clock s5p6442_frate_clks[] __initconst = {
FRATE(SCLK_USBPHY0, "sclk_usbphy0", NULL, 0, 30000000),
};
/* Common clock dividers. */
static const struct samsung_div_clock div_clks[] __initconst = {
DIV(DOUT_PCLKP, "dout_pclkp", "dout_hclkp", CLK_DIV0, 28, 3),
DIV(DOUT_PCLKD, "dout_pclkd", "dout_hclkd", CLK_DIV0, 20, 3),
DIV(DOUT_A2M, "dout_a2m", "mout_apll", CLK_DIV0, 4, 3),
DIV(DOUT_APLL, "dout_apll", "mout_msys", CLK_DIV0, 0, 3),
DIV(DOUT_FIMD, "dout_fimd", "mout_fimd", CLK_DIV1, 20, 4),
DIV(DOUT_CAM1, "dout_cam1", "mout_cam1", CLK_DIV1, 16, 4),
DIV(DOUT_CAM0, "dout_cam0", "mout_cam0", CLK_DIV1, 12, 4),
DIV(DOUT_FIMC2, "dout_fimc2", "mout_fimc2", CLK_DIV3, 20, 4),
DIV(DOUT_FIMC1, "dout_fimc1", "mout_fimc1", CLK_DIV3, 16, 4),
DIV(DOUT_FIMC0, "dout_fimc0", "mout_fimc0", CLK_DIV3, 12, 4),
DIV(DOUT_UART2, "dout_uart2", "mout_uart2", CLK_DIV4, 24, 4),
DIV(DOUT_UART1, "dout_uart1", "mout_uart1", CLK_DIV4, 20, 4),
DIV(DOUT_UART0, "dout_uart0", "mout_uart0", CLK_DIV4, 16, 4),
DIV(DOUT_MMC2, "dout_mmc2", "mout_mmc2", CLK_DIV4, 8, 4),
DIV(DOUT_MMC1, "dout_mmc1", "mout_mmc1", CLK_DIV4, 4, 4),
DIV(DOUT_MMC0, "dout_mmc0", "mout_mmc0", CLK_DIV4, 0, 4),
DIV(DOUT_PWM, "dout_pwm", "mout_pwm", CLK_DIV5, 12, 4),
DIV(DOUT_SPI0, "dout_spi0", "mout_spi0", CLK_DIV5, 0, 4),
DIV(DOUT_FLASH, "dout_flash", "mout_flash", CLK_DIV6, 12, 3),
DIV(DOUT_AUDIO1, "dout_audio1", "mout_audio1", CLK_DIV6, 4, 4),
DIV(DOUT_AUDIO0, "dout_audio0", "mout_audio0", CLK_DIV6, 0, 4),
DIV(DOUT_CLKOUT, "dout_clkout", "mout_clksel", CLK_OUT, 20, 4),
};
/* S5PV210-specific clock dividers. */
static const struct samsung_div_clock s5pv210_div_clks[] __initconst = {
DIV(DOUT_HCLKP, "dout_hclkp", "mout_psys", CLK_DIV0, 24, 4),
DIV(DOUT_HCLKD, "dout_hclkd", "mout_dsys", CLK_DIV0, 16, 4),
DIV(DOUT_PCLKM, "dout_pclkm", "dout_hclkm", CLK_DIV0, 12, 3),
DIV(DOUT_HCLKM, "dout_hclkm", "dout_apll", CLK_DIV0, 8, 3),
DIV(DOUT_CSIS, "dout_csis", "mout_csis", CLK_DIV1, 28, 4),
DIV(DOUT_TBLK, "dout_tblk", "mout_vpll", CLK_DIV1, 0, 4),
DIV(DOUT_G2D, "dout_g2d", "mout_g2d", CLK_DIV2, 8, 4),
DIV(DOUT_MFC, "dout_mfc", "mout_mfc", CLK_DIV2, 4, 4),
DIV(DOUT_G3D, "dout_g3d", "mout_g3d", CLK_DIV2, 0, 4),
DIV(DOUT_UART3, "dout_uart3", "mout_uart3", CLK_DIV4, 28, 4),
DIV(DOUT_MMC3, "dout_mmc3", "mout_mmc3", CLK_DIV4, 12, 4),
DIV(DOUT_SPI1, "dout_spi1", "mout_spi1", CLK_DIV5, 4, 4),
DIV(DOUT_DMC0, "dout_dmc0", "mout_dmc0", CLK_DIV6, 28, 4),
DIV(DOUT_PWI, "dout_pwi", "mout_pwi", CLK_DIV6, 24, 4),
DIV(DOUT_HPM, "dout_hpm", "dout_copy", CLK_DIV6, 20, 3),
DIV(DOUT_COPY, "dout_copy", "mout_hpm", CLK_DIV6, 16, 3),
DIV(DOUT_AUDIO2, "dout_audio2", "mout_audio2", CLK_DIV6, 8, 4),
DIV(DOUT_DPM, "dout_dpm", "dout_pclkp", CLK_DIV7, 8, 7),
DIV(DOUT_DVSEM, "dout_dvsem", "dout_pclkp", CLK_DIV7, 0, 7),
};
/* S5P6442-specific clock dividers. */
static const struct samsung_div_clock s5p6442_div_clks[] __initconst = {
DIV(DOUT_HCLKP, "dout_hclkp", "mout_d1sync", CLK_DIV0, 24, 4),
DIV(DOUT_HCLKD, "dout_hclkd", "mout_d0sync", CLK_DIV0, 16, 4),
DIV(DOUT_MIXER, "dout_mixer", "mout_vpll", CLK_DIV1, 0, 4),
};
/* Common clock gates. */
static const struct samsung_gate_clock gate_clks[] __initconst = {
GATE(CLK_ROTATOR, "rotator", "dout_hclkd", CLK_GATE_IP0, 29, 0, 0),
GATE(CLK_FIMC2, "fimc2", "dout_hclkd", CLK_GATE_IP0, 26, 0, 0),
GATE(CLK_FIMC1, "fimc1", "dout_hclkd", CLK_GATE_IP0, 25, 0, 0),
GATE(CLK_FIMC0, "fimc0", "dout_hclkd", CLK_GATE_IP0, 24, 0, 0),
GATE(CLK_PDMA0, "pdma0", "dout_hclkp", CLK_GATE_IP0, 3, 0, 0),
GATE(CLK_MDMA, "mdma", "dout_hclkd", CLK_GATE_IP0, 2, 0, 0),
GATE(CLK_SROMC, "sromc", "dout_hclkp", CLK_GATE_IP1, 26, 0, 0),
GATE(CLK_NANDXL, "nandxl", "dout_hclkp", CLK_GATE_IP1, 24, 0, 0),
GATE(CLK_USB_OTG, "usb_otg", "dout_hclkp", CLK_GATE_IP1, 16, 0, 0),
GATE(CLK_TVENC, "tvenc", "dout_hclkd", CLK_GATE_IP1, 10, 0, 0),
GATE(CLK_MIXER, "mixer", "dout_hclkd", CLK_GATE_IP1, 9, 0, 0),
GATE(CLK_VP, "vp", "dout_hclkd", CLK_GATE_IP1, 8, 0, 0),
GATE(CLK_FIMD, "fimd", "dout_hclkd", CLK_GATE_IP1, 0, 0, 0),
GATE(CLK_HSMMC2, "hsmmc2", "dout_hclkp", CLK_GATE_IP2, 18, 0, 0),
GATE(CLK_HSMMC1, "hsmmc1", "dout_hclkp", CLK_GATE_IP2, 17, 0, 0),
GATE(CLK_HSMMC0, "hsmmc0", "dout_hclkp", CLK_GATE_IP2, 16, 0, 0),
GATE(CLK_MODEMIF, "modemif", "dout_hclkp", CLK_GATE_IP2, 9, 0, 0),
GATE(CLK_SECSS, "secss", "dout_hclkp", CLK_GATE_IP2, 0, 0, 0),
GATE(CLK_PCM1, "pcm1", "dout_pclkp", CLK_GATE_IP3, 29, 0, 0),
GATE(CLK_PCM0, "pcm0", "dout_pclkp", CLK_GATE_IP3, 28, 0, 0),
GATE(CLK_TSADC, "tsadc", "dout_pclkp", CLK_GATE_IP3, 24, 0, 0),
GATE(CLK_PWM, "pwm", "dout_pclkp", CLK_GATE_IP3, 23, 0, 0),
GATE(CLK_WDT, "watchdog", "dout_pclkp", CLK_GATE_IP3, 22, 0, 0),
GATE(CLK_KEYIF, "keyif", "dout_pclkp", CLK_GATE_IP3, 21, 0, 0),
GATE(CLK_UART2, "uart2", "dout_pclkp", CLK_GATE_IP3, 19, 0, 0),
GATE(CLK_UART1, "uart1", "dout_pclkp", CLK_GATE_IP3, 18, 0, 0),
GATE(CLK_UART0, "uart0", "dout_pclkp", CLK_GATE_IP3, 17, 0, 0),
GATE(CLK_SYSTIMER, "systimer", "dout_pclkp", CLK_GATE_IP3, 16, 0, 0),
GATE(CLK_RTC, "rtc", "dout_pclkp", CLK_GATE_IP3, 15, 0, 0),
GATE(CLK_SPI0, "spi0", "dout_pclkp", CLK_GATE_IP3, 12, 0, 0),
GATE(CLK_I2C2, "i2c2", "dout_pclkp", CLK_GATE_IP3, 9, 0, 0),
GATE(CLK_I2C0, "i2c0", "dout_pclkp", CLK_GATE_IP3, 7, 0, 0),
GATE(CLK_I2S1, "i2s1", "dout_pclkp", CLK_GATE_IP3, 5, 0, 0),
GATE(CLK_I2S0, "i2s0", "dout_pclkp", CLK_GATE_IP3, 4, 0, 0),
GATE(CLK_SECKEY, "seckey", "dout_pclkp", CLK_GATE_IP4, 3, 0, 0),
GATE(CLK_CHIPID, "chipid", "dout_pclkp", CLK_GATE_IP4, 0, 0, 0),
GATE(SCLK_AUDIO1, "sclk_audio1", "dout_audio1", CLK_SRC_MASK0, 25,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_AUDIO0, "sclk_audio0", "dout_audio0", CLK_SRC_MASK0, 24,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_PWM, "sclk_pwm", "dout_pwm", CLK_SRC_MASK0, 19,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI0, "sclk_spi0", "dout_spi0", CLK_SRC_MASK0, 16,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_UART2, "sclk_uart2", "dout_uart2", CLK_SRC_MASK0, 14,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_UART1, "sclk_uart1", "dout_uart1", CLK_SRC_MASK0, 13,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_UART0, "sclk_uart0", "dout_uart0", CLK_SRC_MASK0, 12,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_MMC2, "sclk_mmc2", "dout_mmc2", CLK_SRC_MASK0, 10,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_MMC1, "sclk_mmc1", "dout_mmc1", CLK_SRC_MASK0, 9,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_MMC0, "sclk_mmc0", "dout_mmc0", CLK_SRC_MASK0, 8,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_FIMD, "sclk_fimd", "dout_fimd", CLK_SRC_MASK0, 5,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_CAM1, "sclk_cam1", "dout_cam1", CLK_SRC_MASK0, 4,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_CAM0, "sclk_cam0", "dout_cam0", CLK_SRC_MASK0, 3,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_MIXER, "sclk_mixer", "mout_mixer", CLK_SRC_MASK0, 1,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_FIMC2, "sclk_fimc2", "dout_fimc2", CLK_SRC_MASK1, 4,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_FIMC1, "sclk_fimc1", "dout_fimc1", CLK_SRC_MASK1, 3,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_FIMC0, "sclk_fimc0", "dout_fimc0", CLK_SRC_MASK1, 2,
CLK_SET_RATE_PARENT, 0),
};
/* S5PV210-specific clock gates. */
static const struct samsung_gate_clock s5pv210_gate_clks[] __initconst = {
GATE(CLK_CSIS, "clk_csis", "dout_hclkd", CLK_GATE_IP0, 31, 0, 0),
GATE(CLK_MFC, "mfc", "dout_hclkm", CLK_GATE_IP0, 16, 0, 0),
GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0),
GATE(CLK_G3D, "g3d", "dout_hclkm", CLK_GATE_IP0, 8, 0, 0),
GATE(CLK_IMEM, "imem", "dout_hclkm", CLK_GATE_IP0, 5, 0, 0),
GATE(CLK_PDMA1, "pdma1", "dout_hclkp", CLK_GATE_IP0, 4, 0, 0),
GATE(CLK_NFCON, "nfcon", "dout_hclkp", CLK_GATE_IP1, 28, 0, 0),
GATE(CLK_CFCON, "cfcon", "dout_hclkp", CLK_GATE_IP1, 25, 0, 0),
GATE(CLK_USB_HOST, "usb_host", "dout_hclkp", CLK_GATE_IP1, 17, 0, 0),
GATE(CLK_HDMI, "hdmi", "dout_hclkd", CLK_GATE_IP1, 11, 0, 0),
GATE(CLK_DSIM, "dsim", "dout_pclkd", CLK_GATE_IP1, 2, 0, 0),
GATE(CLK_TZIC3, "tzic3", "dout_hclkm", CLK_GATE_IP2, 31, 0, 0),
GATE(CLK_TZIC2, "tzic2", "dout_hclkm", CLK_GATE_IP2, 30, 0, 0),
GATE(CLK_TZIC1, "tzic1", "dout_hclkm", CLK_GATE_IP2, 29, 0, 0),
GATE(CLK_TZIC0, "tzic0", "dout_hclkm", CLK_GATE_IP2, 28, 0, 0),
GATE(CLK_TSI, "tsi", "dout_hclkd", CLK_GATE_IP2, 20, 0, 0),
GATE(CLK_HSMMC3, "hsmmc3", "dout_hclkp", CLK_GATE_IP2, 19, 0, 0),
GATE(CLK_JTAG, "jtag", "dout_hclkp", CLK_GATE_IP2, 11, 0, 0),
GATE(CLK_CORESIGHT, "coresight", "dout_pclkp", CLK_GATE_IP2, 8, 0, 0),
GATE(CLK_SDM, "sdm", "dout_pclkm", CLK_GATE_IP2, 1, 0, 0),
GATE(CLK_PCM2, "pcm2", "dout_pclkp", CLK_GATE_IP3, 30, 0, 0),
GATE(CLK_UART3, "uart3", "dout_pclkp", CLK_GATE_IP3, 20, 0, 0),
GATE(CLK_SPI1, "spi1", "dout_pclkp", CLK_GATE_IP3, 13, 0, 0),
GATE(CLK_I2C_HDMI_PHY, "i2c_hdmi_phy", "dout_pclkd",
CLK_GATE_IP3, 11, 0, 0),
GATE(CLK_I2C1, "i2c1", "dout_pclkd", CLK_GATE_IP3, 10, 0, 0),
GATE(CLK_I2S2, "i2s2", "dout_pclkp", CLK_GATE_IP3, 6, 0, 0),
GATE(CLK_AC97, "ac97", "dout_pclkp", CLK_GATE_IP3, 1, 0, 0),
GATE(CLK_SPDIF, "spdif", "dout_pclkp", CLK_GATE_IP3, 0, 0, 0),
GATE(CLK_TZPC3, "tzpc.3", "dout_pclkd", CLK_GATE_IP4, 8, 0, 0),
GATE(CLK_TZPC2, "tzpc.2", "dout_pclkd", CLK_GATE_IP4, 7, 0, 0),
GATE(CLK_TZPC1, "tzpc.1", "dout_pclkp", CLK_GATE_IP4, 6, 0, 0),
GATE(CLK_TZPC0, "tzpc.0", "dout_pclkm", CLK_GATE_IP4, 5, 0, 0),
GATE(CLK_IEM_APC, "iem_apc", "dout_pclkp", CLK_GATE_IP4, 2, 0, 0),
GATE(CLK_IEM_IEC, "iem_iec", "dout_pclkp", CLK_GATE_IP4, 1, 0, 0),
GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP5, 29, 0, 0),
GATE(SCLK_SPDIF, "sclk_spdif", "mout_spdif", CLK_SRC_MASK0, 27,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_AUDIO2, "sclk_audio2", "dout_audio2", CLK_SRC_MASK0, 26,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_SPI1, "sclk_spi1", "dout_spi1", CLK_SRC_MASK0, 17,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_UART3, "sclk_uart3", "dout_uart3", CLK_SRC_MASK0, 15,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_MMC3, "sclk_mmc3", "dout_mmc3", CLK_SRC_MASK0, 11,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_CSIS, "sclk_csis", "dout_csis", CLK_SRC_MASK0, 6,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_DAC, "sclk_dac", "mout_dac", CLK_SRC_MASK0, 2,
CLK_SET_RATE_PARENT, 0),
GATE(SCLK_HDMI, "sclk_hdmi", "mout_hdmi", CLK_SRC_MASK0, 0,
CLK_SET_RATE_PARENT, 0),
};
/* S5P6442-specific clock gates. */
static const struct samsung_gate_clock s5p6442_gate_clks[] __initconst = {
GATE(CLK_JPEG, "jpeg", "dout_hclkd", CLK_GATE_IP0, 28, 0, 0),
GATE(CLK_MFC, "mfc", "dout_hclkd", CLK_GATE_IP0, 16, 0, 0),
GATE(CLK_G2D, "g2d", "dout_hclkd", CLK_GATE_IP0, 12, 0, 0),
GATE(CLK_G3D, "g3d", "dout_hclkd", CLK_GATE_IP0, 8, 0, 0),
GATE(CLK_IMEM, "imem", "dout_hclkd", CLK_GATE_IP0, 5, 0, 0),
GATE(CLK_ETB, "etb", "dout_hclkd", CLK_GATE_IP1, 31, 0, 0),
GATE(CLK_ETM, "etm", "dout_hclkd", CLK_GATE_IP1, 30, 0, 0),
GATE(CLK_I2C1, "i2c1", "dout_pclkp", CLK_GATE_IP3, 8, 0, 0),
GATE(SCLK_DAC, "sclk_dac", "mout_vpll", CLK_SRC_MASK0, 2,
CLK_SET_RATE_PARENT, 0),
};
/*
* Clock aliases for legacy clkdev look-up.
* NOTE: Needed only to support legacy board files.
*/
static const struct samsung_clock_alias s5pv210_aliases[] __initconst = {
ALIAS(DOUT_APLL, NULL, "armclk"),
ALIAS(DOUT_HCLKM, NULL, "hclk_msys"),
ALIAS(MOUT_DMC0, NULL, "sclk_dmc0"),
};
/* S5PV210-specific PLLs. */
static const struct samsung_pll_clock s5pv210_pll_clks[] __initconst = {
[apll] = PLL(pll_4508, FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, NULL),
[mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON, NULL),
[epll] = PLL(pll_4600, FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, NULL),
[vpll] = PLL(pll_4502, FOUT_VPLL, "fout_vpll", "mout_vpllsrc",
VPLL_LOCK, VPLL_CON, NULL),
};
/* S5P6442-specific PLLs. */
static const struct samsung_pll_clock s5p6442_pll_clks[] __initconst = {
[apll] = PLL(pll_4502, FOUT_APLL, "fout_apll", "fin_pll",
APLL_LOCK, APLL_CON0, NULL),
[mpll] = PLL(pll_4502, FOUT_MPLL, "fout_mpll", "fin_pll",
MPLL_LOCK, MPLL_CON, NULL),
[epll] = PLL(pll_4500, FOUT_EPLL, "fout_epll", "fin_pll",
EPLL_LOCK, EPLL_CON0, NULL),
[vpll] = PLL(pll_4500, FOUT_VPLL, "fout_vpll", "fin_pll",
VPLL_LOCK, VPLL_CON, NULL),
};
static void __init __s5pv210_clk_init(struct device_node *np,
unsigned long xxti_f,
unsigned long xusbxti_f,
bool is_s5p6442)
{
struct samsung_clk_provider *ctx;
struct clk_hw **hws;
ctx = samsung_clk_init(NULL, reg_base, NR_CLKS);
hws = ctx->clk_data.hws;
samsung_clk_register_mux(ctx, early_mux_clks,
ARRAY_SIZE(early_mux_clks));
if (is_s5p6442) {
samsung_clk_register_fixed_rate(ctx, s5p6442_frate_clks,
ARRAY_SIZE(s5p6442_frate_clks));
samsung_clk_register_pll(ctx, s5p6442_pll_clks,
ARRAY_SIZE(s5p6442_pll_clks));
samsung_clk_register_mux(ctx, s5p6442_mux_clks,
ARRAY_SIZE(s5p6442_mux_clks));
samsung_clk_register_div(ctx, s5p6442_div_clks,
ARRAY_SIZE(s5p6442_div_clks));
samsung_clk_register_gate(ctx, s5p6442_gate_clks,
ARRAY_SIZE(s5p6442_gate_clks));
} else {
samsung_clk_register_fixed_rate(ctx, s5pv210_frate_clks,
ARRAY_SIZE(s5pv210_frate_clks));
samsung_clk_register_pll(ctx, s5pv210_pll_clks,
ARRAY_SIZE(s5pv210_pll_clks));
samsung_clk_register_mux(ctx, s5pv210_mux_clks,
ARRAY_SIZE(s5pv210_mux_clks));
samsung_clk_register_div(ctx, s5pv210_div_clks,
ARRAY_SIZE(s5pv210_div_clks));
samsung_clk_register_gate(ctx, s5pv210_gate_clks,
ARRAY_SIZE(s5pv210_gate_clks));
}
samsung_clk_register_mux(ctx, mux_clks, ARRAY_SIZE(mux_clks));
samsung_clk_register_div(ctx, div_clks, ARRAY_SIZE(div_clks));
samsung_clk_register_gate(ctx, gate_clks, ARRAY_SIZE(gate_clks));
samsung_clk_register_fixed_factor(ctx, ffactor_clks,
ARRAY_SIZE(ffactor_clks));
samsung_clk_register_alias(ctx, s5pv210_aliases,
ARRAY_SIZE(s5pv210_aliases));
samsung_clk_sleep_init(reg_base, NULL, s5pv210_clk_regs,
ARRAY_SIZE(s5pv210_clk_regs));
samsung_clk_of_add_provider(np, ctx);
pr_info("%s clocks: mout_apll = %ld, mout_mpll = %ld\n"
"\tmout_epll = %ld, mout_vpll = %ld\n",
is_s5p6442 ? "S5P6442" : "S5PV210",
clk_hw_get_rate(hws[MOUT_APLL]),
clk_hw_get_rate(hws[MOUT_MPLL]),
clk_hw_get_rate(hws[MOUT_EPLL]),
clk_hw_get_rate(hws[MOUT_VPLL]));
}
static void __init s5pv210_clk_dt_init(struct device_node *np)
{
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
__s5pv210_clk_init(np, 0, 0, false);
}
CLK_OF_DECLARE(s5pv210_clk, "samsung,s5pv210-clock", s5pv210_clk_dt_init);
static void __init s5p6442_clk_dt_init(struct device_node *np)
{
reg_base = of_iomap(np, 0);
if (!reg_base)
panic("%s: failed to map registers\n", __func__);
__s5pv210_clk_init(np, 0, 0, true);
}
CLK_OF_DECLARE(s5p6442_clk, "samsung,s5p6442-clock", s5p6442_clk_dt_init);
+559
View File
@@ -0,0 +1,559 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* This file includes utility functions to register clocks to common
* clock framework for Samsung platforms.
*/
#include <linux/slab.h>
#include <linux/clkdev.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of_address.h>
#include <linux/regmap.h>
#include <linux/syscore_ops.h>
#include "clk.h"
static LIST_HEAD(clock_reg_cache_list);
void samsung_clk_save(void __iomem *base,
struct regmap *regmap,
struct samsung_clk_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd) {
if (base)
rd->value = readl(base + rd->offset);
else if (regmap)
regmap_read(regmap, rd->offset, &rd->value);
}
}
void samsung_clk_restore(void __iomem *base,
struct regmap *regmap,
const struct samsung_clk_reg_dump *rd,
unsigned int num_regs)
{
for (; num_regs > 0; --num_regs, ++rd) {
if (base)
writel(rd->value, base + rd->offset);
else if (regmap)
regmap_write(regmap, rd->offset, rd->value);
}
}
struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
const unsigned long *rdump,
unsigned long nr_rdump)
{
struct samsung_clk_reg_dump *rd;
unsigned int i;
rd = kzalloc_objs(*rd, nr_rdump);
if (!rd)
return NULL;
for (i = 0; i < nr_rdump; ++i)
rd[i].offset = rdump[i];
return rd;
}
/**
* samsung_clk_init() - Create and initialize a clock provider object
* @dev: CMU device to enable runtime PM, or NULL if RPM is not needed
* @base: Start address (mapped) of CMU registers
* @nr_clks: Total clock count to allocate in clock provider object
*
* Setup the essentials required to support clock lookup using Common Clock
* Framework.
*
* Return: Allocated and initialized clock provider object.
*/
struct samsung_clk_provider * __init samsung_clk_init(struct device *dev,
void __iomem *base, unsigned long nr_clks)
{
struct samsung_clk_provider *ctx;
int i;
ctx = kzalloc_flex(*ctx, clk_data.hws, nr_clks);
if (!ctx)
panic("could not allocate clock provider context.\n");
ctx->clk_data.num = nr_clks;
for (i = 0; i < nr_clks; ++i)
ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
ctx->dev = dev;
ctx->reg_base = base;
spin_lock_init(&ctx->lock);
return ctx;
}
void __init samsung_clk_of_add_provider(struct device_node *np,
struct samsung_clk_provider *ctx)
{
if (np) {
if (of_clk_add_hw_provider(np, of_clk_hw_onecell_get,
&ctx->clk_data))
panic("could not register clk provider\n");
}
}
/* add a clock instance to the clock lookup table used for dt based lookup */
void samsung_clk_add_lookup(struct samsung_clk_provider *ctx,
struct clk_hw *clk_hw, unsigned int id)
{
if (id)
ctx->clk_data.hws[id] = clk_hw;
}
/* register a list of aliases */
void __init samsung_clk_register_alias(struct samsung_clk_provider *ctx,
const struct samsung_clock_alias *list,
unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx, ret;
for (idx = 0; idx < nr_clk; idx++, list++) {
if (!list->id) {
pr_err("%s: clock id missing for index %d\n", __func__,
idx);
continue;
}
clk_hw = ctx->clk_data.hws[list->id];
if (!clk_hw) {
pr_err("%s: failed to find clock %d\n", __func__,
list->id);
continue;
}
ret = clk_hw_register_clkdev(clk_hw, list->alias,
list->dev_name);
if (ret)
pr_err("%s: failed to register lookup %s\n",
__func__, list->alias);
}
}
/* register a list of fixed clocks */
void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
const struct samsung_fixed_rate_clock *list,
unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk_hw = clk_hw_register_fixed_rate(ctx->dev, list->name,
list->parent_name, list->flags, list->fixed_rate);
if (IS_ERR(clk_hw)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(ctx, clk_hw, list->id);
}
}
/* register a list of fixed factor clocks */
void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
const struct samsung_fixed_factor_clock *list, unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk_hw = clk_hw_register_fixed_factor(ctx->dev, list->name,
list->parent_name, list->flags, list->mult, list->div);
if (IS_ERR(clk_hw)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(ctx, clk_hw, list->id);
}
}
/* register a list of mux clocks */
void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
const struct samsung_mux_clock *list,
unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
clk_hw = clk_hw_register_mux(ctx->dev, list->name,
list->parent_names, list->num_parents, list->flags,
ctx->reg_base + list->offset,
list->shift, list->width, list->mux_flags, &ctx->lock);
if (IS_ERR(clk_hw)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(ctx, clk_hw, list->id);
}
}
/* register a list of div clocks */
void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
const struct samsung_div_clock *list,
unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx;
for (idx = 0; idx < nr_clk; idx++, list++) {
if (list->table)
clk_hw = clk_hw_register_divider_table(ctx->dev,
list->name, list->parent_name, list->flags,
ctx->reg_base + list->offset,
list->shift, list->width, list->div_flags,
list->table, &ctx->lock);
else
clk_hw = clk_hw_register_divider(ctx->dev, list->name,
list->parent_name, list->flags,
ctx->reg_base + list->offset, list->shift,
list->width, list->div_flags, &ctx->lock);
if (IS_ERR(clk_hw)) {
pr_err("%s: failed to register clock %s\n", __func__,
list->name);
continue;
}
samsung_clk_add_lookup(ctx, clk_hw, list->id);
}
}
/*
* Some older DT's have an incorrect CMU resource size which is incompatible
* with the auto clock mode feature. In such cases we switch back to manual
* clock gating mode.
*/
bool samsung_is_auto_capable(struct device_node *np)
{
struct resource res;
resource_size_t size;
if (of_address_to_resource(np, 0, &res))
return false;
size = resource_size(&res);
if (size != 0x10000) {
pr_warn("%pOF: incorrect res size for automatic clocks\n", np);
return false;
}
return true;
}
#define ACG_MSK GENMASK(6, 4)
#define CLK_IDLE GENMASK(5, 4)
static int samsung_auto_clk_gate_is_en(struct clk_hw *hw)
{
u32 reg;
struct clk_gate *gate = to_clk_gate(hw);
reg = readl(gate->reg);
return ((reg & ACG_MSK) == CLK_IDLE) ? 0 : 1;
}
/* enable and disable are nops in automatic clock mode */
static int samsung_auto_clk_gate_en(struct clk_hw *hw)
{
return 0;
}
static void samsung_auto_clk_gate_dis(struct clk_hw *hw)
{
}
static const struct clk_ops samsung_auto_clk_gate_ops = {
.enable = samsung_auto_clk_gate_en,
.disable = samsung_auto_clk_gate_dis,
.is_enabled = samsung_auto_clk_gate_is_en,
};
struct clk_hw *samsung_register_auto_gate(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock)
{
struct clk_gate *gate;
struct clk_hw *hw;
struct clk_init_data init = {};
int ret = -EINVAL;
/* allocate the gate */
gate = kzalloc_obj(*gate);
if (!gate)
return ERR_PTR(-ENOMEM);
init.name = name;
init.ops = &samsung_auto_clk_gate_ops;
init.flags = flags;
init.parent_names = parent_name ? &parent_name : NULL;
init.parent_hws = parent_hw ? &parent_hw : NULL;
init.parent_data = parent_data;
if (parent_name || parent_hw || parent_data)
init.num_parents = 1;
else
init.num_parents = 0;
/* struct clk_gate assignments */
gate->reg = reg;
gate->bit_idx = bit_idx;
gate->flags = clk_gate_flags;
gate->lock = lock;
gate->hw.init = &init;
hw = &gate->hw;
if (dev || !np)
ret = clk_hw_register(dev, hw);
else if (np)
ret = of_clk_hw_register(np, hw);
if (ret) {
kfree(gate);
hw = ERR_PTR(ret);
}
return hw;
}
/* register a list of gate clocks */
void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
const struct samsung_gate_clock *list,
unsigned int nr_clk)
{
struct clk_hw *clk_hw;
unsigned int idx;
void __iomem *reg_offs;
for (idx = 0; idx < nr_clk; idx++, list++) {
reg_offs = ctx->reg_base + list->offset;
if (ctx->auto_clock_gate && ctx->gate_dbg_offset)
clk_hw = samsung_register_auto_gate(ctx->dev, NULL,
list->name, list->parent_name, NULL, NULL,
list->flags, reg_offs + ctx->gate_dbg_offset,
list->bit_idx, list->gate_flags, &ctx->lock);
else
clk_hw = clk_hw_register_gate(ctx->dev, list->name,
list->parent_name, list->flags,
ctx->reg_base + list->offset, list->bit_idx,
list->gate_flags, &ctx->lock);
if (IS_ERR(clk_hw)) {
pr_err("%s: failed to register clock %s: %pe\n", __func__,
list->name, clk_hw);
continue;
}
samsung_clk_add_lookup(ctx, clk_hw, list->id);
}
}
/*
* obtain the clock speed of all external fixed clock sources from device
* tree and register it
*/
void __init samsung_clk_of_register_fixed_ext(struct samsung_clk_provider *ctx,
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
const struct of_device_id *clk_matches)
{
const struct of_device_id *match;
struct device_node *clk_np;
u32 freq;
for_each_matching_node_and_match(clk_np, clk_matches, &match) {
if (of_property_read_u32(clk_np, "clock-frequency", &freq))
continue;
fixed_rate_clk[(unsigned long)match->data].fixed_rate = freq;
}
samsung_clk_register_fixed_rate(ctx, fixed_rate_clk, nr_fixed_rate_clk);
}
#ifdef CONFIG_PM_SLEEP
static int samsung_clk_suspend(void *data)
{
struct samsung_clock_reg_cache *reg_cache;
list_for_each_entry(reg_cache, &clock_reg_cache_list, node) {
samsung_clk_save(reg_cache->reg_base, reg_cache->sysreg,
reg_cache->rdump, reg_cache->rd_num);
samsung_clk_restore(reg_cache->reg_base, reg_cache->sysreg,
reg_cache->rsuspend,
reg_cache->rsuspend_num);
}
return 0;
}
static void samsung_clk_resume(void *data)
{
struct samsung_clock_reg_cache *reg_cache;
list_for_each_entry(reg_cache, &clock_reg_cache_list, node)
samsung_clk_restore(reg_cache->reg_base, reg_cache->sysreg,
reg_cache->rdump, reg_cache->rd_num);
}
static const struct syscore_ops samsung_clk_syscore_ops = {
.suspend = samsung_clk_suspend,
.resume = samsung_clk_resume,
};
static struct syscore samsung_clk_syscore = {
.ops = &samsung_clk_syscore_ops,
};
void samsung_clk_extended_sleep_init(void __iomem *reg_base,
struct regmap *sysreg,
const unsigned long *rdump,
unsigned long nr_rdump,
const struct samsung_clk_reg_dump *rsuspend,
unsigned long nr_rsuspend)
{
struct samsung_clock_reg_cache *reg_cache;
reg_cache = kzalloc_obj(struct samsung_clock_reg_cache);
if (!reg_cache)
panic("could not allocate register reg_cache.\n");
reg_cache->rdump = samsung_clk_alloc_reg_dump(rdump, nr_rdump);
if (!reg_cache->rdump)
panic("could not allocate register dump storage.\n");
if (list_empty(&clock_reg_cache_list))
register_syscore(&samsung_clk_syscore);
reg_cache->reg_base = reg_base;
reg_cache->sysreg = sysreg;
reg_cache->rd_num = nr_rdump;
reg_cache->rsuspend = rsuspend;
reg_cache->rsuspend_num = nr_rsuspend;
list_add_tail(&reg_cache->node, &clock_reg_cache_list);
}
#endif
/**
* samsung_cmu_register_clocks() - Register all clocks provided in CMU object
* @ctx: Clock provider object
* @cmu: CMU object with clocks to register
* @np: CMU device tree node
*/
void __init samsung_cmu_register_clocks(struct samsung_clk_provider *ctx,
const struct samsung_cmu_info *cmu,
struct device_node *np)
{
if (cmu->auto_clock_gate && samsung_is_auto_capable(np))
ctx->auto_clock_gate = cmu->auto_clock_gate;
ctx->gate_dbg_offset = cmu->gate_dbg_offset;
ctx->option_offset = cmu->option_offset;
ctx->drcg_offset = cmu->drcg_offset;
ctx->memclk_offset = cmu->memclk_offset;
if (cmu->pll_clks)
samsung_clk_register_pll(ctx, cmu->pll_clks, cmu->nr_pll_clks);
if (cmu->mux_clks)
samsung_clk_register_mux(ctx, cmu->mux_clks, cmu->nr_mux_clks);
if (cmu->div_clks)
samsung_clk_register_div(ctx, cmu->div_clks, cmu->nr_div_clks);
if (cmu->gate_clks)
samsung_clk_register_gate(ctx, cmu->gate_clks,
cmu->nr_gate_clks);
if (cmu->fixed_clks)
samsung_clk_register_fixed_rate(ctx, cmu->fixed_clks,
cmu->nr_fixed_clks);
if (cmu->fixed_factor_clks)
samsung_clk_register_fixed_factor(ctx, cmu->fixed_factor_clks,
cmu->nr_fixed_factor_clks);
if (cmu->cpu_clks)
samsung_clk_register_cpu(ctx, cmu->cpu_clks, cmu->nr_cpu_clks);
}
/* Each bit enable/disables DRCG of a bus component */
#define DRCG_EN_MSK GENMASK(31, 0)
#define MEMCLK_EN BIT(0)
/* Enable Dynamic Root Clock Gating (DRCG) of bus components */
void samsung_en_dyn_root_clk_gating(struct device_node *np,
struct samsung_clk_provider *ctx,
const struct samsung_cmu_info *cmu,
bool cmu_has_pm)
{
if (!ctx->auto_clock_gate)
return;
ctx->sysreg = syscon_regmap_lookup_by_phandle(np, "samsung,sysreg");
if (IS_ERR(ctx->sysreg)) {
pr_warn("%pOF: Unable to get CMU sysreg\n", np);
ctx->sysreg = NULL;
} else {
/* Enable DRCG for all bus components */
regmap_write(ctx->sysreg, ctx->drcg_offset, DRCG_EN_MSK);
/* Enable memclk gate (not present on all sysreg) */
if (ctx->memclk_offset)
regmap_write_bits(ctx->sysreg, ctx->memclk_offset,
MEMCLK_EN, 0x0);
if (!cmu_has_pm)
/*
* When a CMU has PM support, clocks are saved/restored
* via its PM handlers, so only register them with the
* syscore suspend / resume paths if PM is not in use.
*/
samsung_clk_extended_sleep_init(NULL, ctx->sysreg,
cmu->sysreg_clk_regs,
cmu->nr_sysreg_clk_regs,
NULL, 0);
}
}
/*
* Common function which registers plls, muxes, dividers and gates
* for each CMU. It also add CMU register list to register cache.
*/
struct samsung_clk_provider * __init samsung_cmu_register_one(
struct device_node *np,
const struct samsung_cmu_info *cmu)
{
void __iomem *reg_base;
struct samsung_clk_provider *ctx;
reg_base = of_iomap(np, 0);
if (!reg_base) {
panic("%s: failed to map registers\n", __func__);
return NULL;
}
ctx = samsung_clk_init(NULL, reg_base, cmu->nr_clk_ids);
samsung_cmu_register_clocks(ctx, cmu, np);
if (cmu->clk_regs)
samsung_clk_extended_sleep_init(reg_base, NULL,
cmu->clk_regs, cmu->nr_clk_regs,
cmu->suspend_regs, cmu->nr_suspend_regs);
samsung_clk_of_add_provider(np, ctx);
/* sysreg DT nodes reference a clock in this CMU */
samsung_en_dyn_root_clk_gating(np, ctx, cmu, false);
return ctx;
}
+492
View File
@@ -0,0 +1,492 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2013 Samsung Electronics Co., Ltd.
* Copyright (c) 2013 Linaro Ltd.
* Author: Thomas Abraham <thomas.ab@samsung.com>
*
* Common Clock Framework support for all Samsung platforms
*/
#ifndef __SAMSUNG_CLK_H
#define __SAMSUNG_CLK_H
#include <linux/clk-provider.h>
#include <linux/mod_devicetable.h>
#include <linux/regmap.h>
#include "clk-pll.h"
#include "clk-cpu.h"
/**
* struct samsung_clk_provider - information about clock provider
* @reg_base: virtual address for the register base
* @dev: clock provider device needed for runtime PM
* @sysreg: syscon regmap for clock-provider sysreg controller
* @lock: maintains exclusion between callbacks for a given clock-provider
* @auto_clock_gate: enable auto clk mode for all clocks in clock-provider
* @gate_dbg_offset: gate debug reg offset. Used for all gates in auto clk mode
* @option_offset: option reg offset. Enables auto mode for clock-provider
* @drcg_offset: dynamic root clk gate enable register offset in sysreg
* @memclk_offset: memclk enable register offset in sysreg
* @clk_data: holds clock related data like clk_hw* and number of clocks
*/
struct samsung_clk_provider {
void __iomem *reg_base;
struct device *dev;
struct regmap *sysreg;
spinlock_t lock;
bool auto_clock_gate;
u32 gate_dbg_offset;
u32 option_offset;
u32 drcg_offset;
u32 memclk_offset;
/* clk_data must be the last entry due to variable length 'hws' array */
struct clk_hw_onecell_data clk_data;
};
/**
* struct samsung_clock_alias - information about mux clock
* @id: platform specific id of the clock
* @dev_name: name of the device to which this clock belongs
* @alias: optional clock alias name to be assigned to this clock
*/
struct samsung_clock_alias {
unsigned int id;
const char *dev_name;
const char *alias;
};
#define ALIAS(_id, dname, a) \
{ \
.id = _id, \
.dev_name = dname, \
.alias = a, \
}
#define MHZ (1000 * 1000)
/**
* struct samsung_fixed_rate_clock - information about fixed-rate clock
* @id: platform specific id of the clock
* @name: name of this fixed-rate clock
* @parent_name: optional parent clock name
* @flags: optional fixed-rate clock flags
* @fixed_rate: fixed clock rate of this clock
*/
struct samsung_fixed_rate_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long flags;
unsigned long fixed_rate;
};
#define FRATE(_id, cname, pname, f, frate) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.fixed_rate = frate, \
}
/**
* struct samsung_fixed_factor_clock - information about fixed-factor clock
* @id: platform specific id of the clock
* @name: name of this fixed-factor clock
* @parent_name: parent clock name
* @mult: fixed multiplication factor
* @div: fixed division factor
* @flags: optional fixed-factor clock flags
*/
struct samsung_fixed_factor_clock {
unsigned int id;
char *name;
const char *parent_name;
unsigned long mult;
unsigned long div;
unsigned long flags;
};
#define FFACTOR(_id, cname, pname, m, d, f) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.mult = m, \
.div = d, \
.flags = f, \
}
/**
* struct samsung_mux_clock - information about mux clock
* @id: platform specific id of the clock
* @name: name of this mux clock
* @parent_names: array of pointer to parent clock names
* @num_parents: number of parents listed in @parent_names
* @flags: optional flags for basic clock
* @offset: offset of the register for configuring the mux
* @shift: starting bit location of the mux control bit-field in @reg
* @width: width of the mux control bit-field in @reg
* @mux_flags: flags for mux-type clock
*/
struct samsung_mux_clock {
unsigned int id;
const char *name;
const char *const *parent_names;
u8 num_parents;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 mux_flags;
};
#define __MUX(_id, cname, pnames, o, s, w, f, mf) \
{ \
.id = _id, \
.name = cname, \
.parent_names = pnames, \
.num_parents = ARRAY_SIZE(pnames), \
.flags = f, \
.offset = o, \
.shift = s, \
.width = w, \
.mux_flags = mf, \
}
#define MUX(_id, cname, pnames, o, s, w) \
__MUX(_id, cname, pnames, o, s, w, CLK_SET_RATE_NO_REPARENT, 0)
#define MUX_F(_id, cname, pnames, o, s, w, f, mf) \
__MUX(_id, cname, pnames, o, s, w, (f) | CLK_SET_RATE_NO_REPARENT, mf)
/* Used by MUX clocks where reparenting on clock rate change is allowed. */
#define nMUX(_id, cname, pnames, o, s, w) \
__MUX(_id, cname, pnames, o, s, w, 0, 0)
#define nMUX_F(_id, cname, pnames, o, s, w, f, mf) \
__MUX(_id, cname, pnames, o, s, w, f, mf)
/**
* struct samsung_div_clock - information about div clock
* @id: platform specific id of the clock
* @name: name of this div clock
* @parent_name: name of the parent clock
* @flags: optional flags for basic clock
* @offset: offset of the register for configuring the div
* @shift: starting bit location of the div control bit-field in @reg
* @width: width of the bitfield
* @div_flags: flags for div-type clock
* @table: array of divider/value pairs ending with a div set to 0
*/
struct samsung_div_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 shift;
u8 width;
u8 div_flags;
struct clk_div_table *table;
};
#define __DIV(_id, cname, pname, o, s, w, f, df, t) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.offset = o, \
.shift = s, \
.width = w, \
.div_flags = df, \
.table = t, \
}
#define DIV(_id, cname, pname, o, s, w) \
__DIV(_id, cname, pname, o, s, w, 0, 0, NULL)
#define DIV_F(_id, cname, pname, o, s, w, f, df) \
__DIV(_id, cname, pname, o, s, w, f, df, NULL)
#define DIV_T(_id, cname, pname, o, s, w, t) \
__DIV(_id, cname, pname, o, s, w, 0, 0, t)
/**
* struct samsung_gate_clock - information about gate clock
* @id: platform specific id of the clock
* @name: name of this gate clock
* @parent_name: name of the parent clock
* @flags: optional flags for basic clock
* @offset: offset of the register for configuring the gate
* @bit_idx: bit index of the gate control bit-field in @reg
* @gate_flags: flags for gate-type clock
*/
struct samsung_gate_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
unsigned long offset;
u8 bit_idx;
u8 gate_flags;
};
#define __GATE(_id, cname, pname, o, b, f, gf) \
{ \
.id = _id, \
.name = cname, \
.parent_name = pname, \
.flags = f, \
.offset = o, \
.bit_idx = b, \
.gate_flags = gf, \
}
#define GATE(_id, cname, pname, o, b, f, gf) \
__GATE(_id, cname, pname, o, b, f, gf)
#define PNAME(x) static const char * const x[] __initconst
/**
* struct samsung_clk_reg_dump - register dump of clock controller registers
* @offset: clock register offset from the controller base address
* @value: the value to be register at offset
*/
struct samsung_clk_reg_dump {
u32 offset;
u32 value;
};
/**
* struct samsung_pll_clock - information about pll clock
* @id: platform specific id of the clock
* @name: name of this pll clock
* @parent_name: name of the parent clock
* @flags: optional flags for basic clock
* @con_offset: offset of the register for configuring the PLL
* @lock_offset: offset of the register for locking the PLL
* @type: type of PLL to be registered
* @rate_table: array of PLL settings for possible PLL rates
*/
struct samsung_pll_clock {
unsigned int id;
const char *name;
const char *parent_name;
unsigned long flags;
int con_offset;
int lock_offset;
enum samsung_pll_type type;
const struct samsung_pll_rate_table *rate_table;
};
#define __PLL(_typ, _id, _name, _pname, _flags, _lock, _con, _rtable) \
{ \
.id = _id, \
.type = _typ, \
.name = _name, \
.parent_name = _pname, \
.flags = _flags, \
.con_offset = _con, \
.lock_offset = _lock, \
.rate_table = _rtable, \
}
#define PLL(_typ, _id, _name, _pname, _lock, _con, _rtable) \
__PLL(_typ, _id, _name, _pname, CLK_GET_RATE_NOCACHE, _lock, \
_con, _rtable)
struct samsung_cpu_clock {
unsigned int id;
const char *name;
unsigned int parent_id;
unsigned int alt_parent_id;
unsigned long flags;
int offset;
enum exynos_cpuclk_layout reg_layout;
const struct exynos_cpuclk_cfg_data *cfg;
};
#define CPU_CLK(_id, _name, _pid, _apid, _flags, _offset, _layout, _cfg) \
{ \
.id = _id, \
.name = _name, \
.parent_id = _pid, \
.alt_parent_id = _apid, \
.flags = _flags, \
.offset = _offset, \
.reg_layout = _layout, \
.cfg = _cfg, \
}
struct samsung_clock_reg_cache {
struct list_head node;
void __iomem *reg_base;
struct regmap *sysreg;
struct samsung_clk_reg_dump *rdump;
unsigned int rd_num;
const struct samsung_clk_reg_dump *rsuspend;
unsigned int rsuspend_num;
};
/**
* struct samsung_cmu_info - all clocks information needed for CMU registration
* @pll_clks: list of PLL clocks
* @nr_pll_clks: count of clocks in @pll_clks
* @mux_clks: list of mux clocks
* @nr_mux_clks: count of clocks in @mux_clks
* @div_clks: list of div clocks
* @nr_div_clks: count of clocks in @div_clks
* @gate_clks: list of gate clocks
* @nr_gate_clks: count of clocks in @gate_clks
* @fixed_clks: list of fixed clocks
* @nr_fixed_clks: count clocks in @fixed_clks
* @fixed_factor_clks: list of fixed factor clocks
* @nr_fixed_factor_clks: count of clocks in @fixed_factor_clks
* @nr_clk_ids: total number of clocks with IDs assigned
* @cpu_clks: list of CPU clocks
* @nr_cpu_clks: count of clocks in @cpu_clks
* @clk_regs: list of clock registers
* @nr_clk_regs: count of clock registers in @clk_regs
* @suspend_regs: list of clock registers to set before suspend
* @nr_suspend_regs: count of clock registers in @suspend_regs
* @clk_name: name of the parent clock needed for CMU register access
* @sysreg_clk_regs: list of sysreg clock registers
* @nr_sysreg_clk_regs: count of clock registers in @sysreg_clk_regs
* @manual_plls: Enable manual control for PLL clocks
* @auto_clock_gate: enable auto clock mode for all components in CMU
* @gate_dbg_offset: gate debug reg offset. Used by all gates in auto clk mode
* @option_offset: option reg offset. Enables auto clk mode for entire CMU
* @drcg_offset: dynamic root clk gate enable register offset in sysreg
* @memclk_offset: memclk enable register offset in sysreg
*/
struct samsung_cmu_info {
const struct samsung_pll_clock *pll_clks;
unsigned int nr_pll_clks;
const struct samsung_mux_clock *mux_clks;
unsigned int nr_mux_clks;
const struct samsung_div_clock *div_clks;
unsigned int nr_div_clks;
const struct samsung_gate_clock *gate_clks;
unsigned int nr_gate_clks;
const struct samsung_fixed_rate_clock *fixed_clks;
unsigned int nr_fixed_clks;
const struct samsung_fixed_factor_clock *fixed_factor_clks;
unsigned int nr_fixed_factor_clks;
unsigned int nr_clk_ids;
const struct samsung_cpu_clock *cpu_clks;
unsigned int nr_cpu_clks;
const unsigned long *clk_regs;
unsigned int nr_clk_regs;
const struct samsung_clk_reg_dump *suspend_regs;
unsigned int nr_suspend_regs;
const char *clk_name;
const unsigned long *sysreg_clk_regs;
unsigned int nr_sysreg_clk_regs;
/* ARM64 Exynos CMUs */
bool manual_plls;
bool auto_clock_gate;
u32 gate_dbg_offset;
u32 option_offset;
u32 drcg_offset;
u32 memclk_offset;
};
struct samsung_clk_provider *samsung_clk_init(struct device *dev,
void __iomem *base, unsigned long nr_clks);
void samsung_clk_of_add_provider(struct device_node *np,
struct samsung_clk_provider *ctx);
void samsung_clk_of_register_fixed_ext(
struct samsung_clk_provider *ctx,
struct samsung_fixed_rate_clock *fixed_rate_clk,
unsigned int nr_fixed_rate_clk,
const struct of_device_id *clk_matches);
void samsung_clk_add_lookup(struct samsung_clk_provider *ctx,
struct clk_hw *clk_hw, unsigned int id);
void samsung_clk_register_alias(struct samsung_clk_provider *ctx,
const struct samsung_clock_alias *list,
unsigned int nr_clk);
void samsung_clk_register_fixed_rate(
struct samsung_clk_provider *ctx,
const struct samsung_fixed_rate_clock *clk_list,
unsigned int nr_clk);
void samsung_clk_register_fixed_factor(
struct samsung_clk_provider *ctx,
const struct samsung_fixed_factor_clock *list,
unsigned int nr_clk);
void samsung_clk_register_mux(struct samsung_clk_provider *ctx,
const struct samsung_mux_clock *clk_list,
unsigned int nr_clk);
void samsung_clk_register_div(struct samsung_clk_provider *ctx,
const struct samsung_div_clock *clk_list,
unsigned int nr_clk);
void samsung_clk_register_gate(struct samsung_clk_provider *ctx,
const struct samsung_gate_clock *clk_list,
unsigned int nr_clk);
void samsung_clk_register_pll(struct samsung_clk_provider *ctx,
const struct samsung_pll_clock *pll_list,
unsigned int nr_clk);
void samsung_clk_register_cpu(struct samsung_clk_provider *ctx,
const struct samsung_cpu_clock *list, unsigned int nr_clk);
void samsung_cmu_register_clocks(struct samsung_clk_provider *ctx,
const struct samsung_cmu_info *cmu,
struct device_node *np);
struct samsung_clk_provider *samsung_cmu_register_one(
struct device_node *,
const struct samsung_cmu_info *);
#ifdef CONFIG_PM_SLEEP
void samsung_clk_extended_sleep_init(void __iomem *reg_base,
struct regmap *sysreg,
const unsigned long *rdump,
unsigned long nr_rdump,
const struct samsung_clk_reg_dump *rsuspend,
unsigned long nr_rsuspend);
#else
static inline void samsung_clk_extended_sleep_init(void __iomem *reg_base,
struct regmap *sysreg,
const unsigned long *rdump,
unsigned long nr_rdump,
const struct samsung_clk_reg_dump *rsuspend,
unsigned long nr_rsuspend) {}
#endif
#define samsung_clk_sleep_init(reg_base, sysreg, rdump, nr_rdump) \
samsung_clk_extended_sleep_init(reg_base, sysreg, rdump, nr_rdump, \
NULL, 0)
void samsung_clk_save(void __iomem *base,
struct regmap *regmap,
struct samsung_clk_reg_dump *rd,
unsigned int num_regs);
void samsung_clk_restore(void __iomem *base,
struct regmap *regmap,
const struct samsung_clk_reg_dump *rd,
unsigned int num_regs);
struct samsung_clk_reg_dump *samsung_clk_alloc_reg_dump(
const unsigned long *rdump,
unsigned long nr_rdump);
void samsung_en_dyn_root_clk_gating(struct device_node *np,
struct samsung_clk_provider *ctx,
const struct samsung_cmu_info *cmu,
bool cmu_has_pm);
struct clk_hw *samsung_register_auto_gate(struct device *dev,
struct device_node *np, const char *name,
const char *parent_name, const struct clk_hw *parent_hw,
const struct clk_parent_data *parent_data,
unsigned long flags,
void __iomem *reg, u8 bit_idx,
u8 clk_gate_flags, spinlock_t *lock);
bool samsung_is_auto_capable(struct device_node *np);
#endif /* __SAMSUNG_CLK_H */