forked from luck/tmp_suning_uos_patched
Merge branch 'clk-pm-runtime' into clk-next
* clk-pm-runtime: clk: samsung: exynos-audss: Add support for runtime PM clk: samsung: exynos-audss: Use local variable for controller's device clk: samsung: exynos5433: Add support for runtime PM clk: samsung: Add support for runtime PM clk: Add support for runtime PM
This commit is contained in:
commit
e7cc33358a
|
@ -33,6 +33,12 @@ Required Properties:
|
|||
- clock-names: Aliases for the above clocks. They should be "pll_ref",
|
||||
"pll_in", "cdclk", "sclk_audio", and "sclk_pcm_in" respectively.
|
||||
|
||||
Optional Properties:
|
||||
|
||||
- power-domains: a phandle to respective power domain node as described by
|
||||
generic PM domain bindings (see power/power_domain.txt for more
|
||||
information).
|
||||
|
||||
The following is the list of clocks generated by the controller. Each clock is
|
||||
assigned an identifier and client nodes use this identifier to specify the
|
||||
clock which they consume. Some of the clocks are available only on a particular
|
||||
|
|
|
@ -168,6 +168,11 @@ Required Properties:
|
|||
- aclk_cam1_400
|
||||
- aclk_cam1_552
|
||||
|
||||
Optional properties:
|
||||
- power-domains: a phandle to respective power domain node as described by
|
||||
generic PM domain bindings (see power/power_domain.txt for more
|
||||
information).
|
||||
|
||||
Each clock is assigned an identifier and client nodes can use this identifier
|
||||
to specify the clock which they consume.
|
||||
|
||||
|
@ -270,6 +275,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
clocks = <&xxti>,
|
||||
<&cmu_top CLK_ACLK_G2D_266>,
|
||||
<&cmu_top CLK_ACLK_G2D_400>;
|
||||
power-domains = <&pd_g2d>;
|
||||
};
|
||||
|
||||
cmu_disp: clock-controller@13b90000 {
|
||||
|
@ -295,6 +301,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
<&cmu_mif CLK_SCLK_DECON_ECLK_DISP>,
|
||||
<&cmu_mif CLK_SCLK_DECON_TV_VCLK_DISP>,
|
||||
<&cmu_mif CLK_ACLK_DISP_333>;
|
||||
power-domains = <&pd_disp>;
|
||||
};
|
||||
|
||||
cmu_aud: clock-controller@114c0000 {
|
||||
|
@ -304,6 +311,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
|
||||
clock-names = "oscclk", "fout_aud_pll";
|
||||
clocks = <&xxti>, <&cmu_top CLK_FOUT_AUD_PLL>;
|
||||
power-domains = <&pd_aud>;
|
||||
};
|
||||
|
||||
cmu_bus0: clock-controller@13600000 {
|
||||
|
@ -340,6 +348,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
|
||||
clock-names = "oscclk", "aclk_g3d_400";
|
||||
clocks = <&xxti>, <&cmu_top CLK_ACLK_G3D_400>;
|
||||
power-domains = <&pd_g3d>;
|
||||
};
|
||||
|
||||
cmu_gscl: clock-controller@13cf0000 {
|
||||
|
@ -353,6 +362,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
clocks = <&xxti>,
|
||||
<&cmu_top CLK_ACLK_GSCL_111>,
|
||||
<&cmu_top CLK_ACLK_GSCL_333>;
|
||||
power-domains = <&pd_gscl>;
|
||||
};
|
||||
|
||||
cmu_apollo: clock-controller@11900000 {
|
||||
|
@ -384,6 +394,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
clocks = <&xxti>,
|
||||
<&cmu_top CLK_SCLK_JPEG_MSCL>,
|
||||
<&cmu_top CLK_ACLK_MSCL_400>;
|
||||
power-domains = <&pd_mscl>;
|
||||
};
|
||||
|
||||
cmu_mfc: clock-controller@15280000 {
|
||||
|
@ -393,6 +404,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
|
||||
clock-names = "oscclk", "aclk_mfc_400";
|
||||
clocks = <&xxti>, <&cmu_top CLK_ACLK_MFC_400>;
|
||||
power-domains = <&pd_mfc>;
|
||||
};
|
||||
|
||||
cmu_hevc: clock-controller@14f80000 {
|
||||
|
@ -402,6 +414,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
|
||||
clock-names = "oscclk", "aclk_hevc_400";
|
||||
clocks = <&xxti>, <&cmu_top CLK_ACLK_HEVC_400>;
|
||||
power-domains = <&pd_hevc>;
|
||||
};
|
||||
|
||||
cmu_isp: clock-controller@146d0000 {
|
||||
|
@ -415,6 +428,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
clocks = <&xxti>,
|
||||
<&cmu_top CLK_ACLK_ISP_DIS_400>,
|
||||
<&cmu_top CLK_ACLK_ISP_400>;
|
||||
power-domains = <&pd_isp>;
|
||||
};
|
||||
|
||||
cmu_cam0: clock-controller@120d0000 {
|
||||
|
@ -430,6 +444,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
<&cmu_top CLK_ACLK_CAM0_333>,
|
||||
<&cmu_top CLK_ACLK_CAM0_400>,
|
||||
<&cmu_top CLK_ACLK_CAM0_552>;
|
||||
power-domains = <&pd_cam0>;
|
||||
};
|
||||
|
||||
cmu_cam1: clock-controller@145d0000 {
|
||||
|
@ -451,6 +466,7 @@ Example 2: Examples of clock controller nodes are listed below.
|
|||
<&cmu_top CLK_ACLK_CAM1_333>,
|
||||
<&cmu_top CLK_ACLK_CAM1_400>,
|
||||
<&cmu_top CLK_ACLK_CAM1_552>;
|
||||
power-domains = <&pd_cam1>;
|
||||
};
|
||||
|
||||
Example 3: UART controller node that consumes the clock generated by the clock
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include <linux/of.h>
|
||||
#include <linux/device.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/clkdev.h>
|
||||
|
||||
|
@ -46,6 +47,7 @@ struct clk_core {
|
|||
const struct clk_ops *ops;
|
||||
struct clk_hw *hw;
|
||||
struct module *owner;
|
||||
struct device *dev;
|
||||
struct clk_core *parent;
|
||||
const char **parent_names;
|
||||
struct clk_core **parents;
|
||||
|
@ -87,6 +89,26 @@ struct clk {
|
|||
struct hlist_node clks_node;
|
||||
};
|
||||
|
||||
/*** runtime pm ***/
|
||||
static int clk_pm_runtime_get(struct clk_core *core)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (!core->dev)
|
||||
return 0;
|
||||
|
||||
ret = pm_runtime_get_sync(core->dev);
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
static void clk_pm_runtime_put(struct clk_core *core)
|
||||
{
|
||||
if (!core->dev)
|
||||
return;
|
||||
|
||||
pm_runtime_put_sync(core->dev);
|
||||
}
|
||||
|
||||
/*** locking ***/
|
||||
static void clk_prepare_lock(void)
|
||||
{
|
||||
|
@ -150,6 +172,8 @@ static void clk_enable_unlock(unsigned long flags)
|
|||
|
||||
static bool clk_core_is_prepared(struct clk_core *core)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
/*
|
||||
* .is_prepared is optional for clocks that can prepare
|
||||
* fall back to software usage counter if it is missing
|
||||
|
@ -157,11 +181,18 @@ static bool clk_core_is_prepared(struct clk_core *core)
|
|||
if (!core->ops->is_prepared)
|
||||
return core->prepare_count;
|
||||
|
||||
return core->ops->is_prepared(core->hw);
|
||||
if (!clk_pm_runtime_get(core)) {
|
||||
ret = core->ops->is_prepared(core->hw);
|
||||
clk_pm_runtime_put(core);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool clk_core_is_enabled(struct clk_core *core)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
/*
|
||||
* .is_enabled is only mandatory for clocks that gate
|
||||
* fall back to software usage counter if .is_enabled is missing
|
||||
|
@ -169,7 +200,29 @@ static bool clk_core_is_enabled(struct clk_core *core)
|
|||
if (!core->ops->is_enabled)
|
||||
return core->enable_count;
|
||||
|
||||
return core->ops->is_enabled(core->hw);
|
||||
/*
|
||||
* Check if clock controller's device is runtime active before
|
||||
* calling .is_enabled callback. If not, assume that clock is
|
||||
* disabled, because we might be called from atomic context, from
|
||||
* which pm_runtime_get() is not allowed.
|
||||
* This function is called mainly from clk_disable_unused_subtree,
|
||||
* which ensures proper runtime pm activation of controller before
|
||||
* taking enable spinlock, but the below check is needed if one tries
|
||||
* to call it from other places.
|
||||
*/
|
||||
if (core->dev) {
|
||||
pm_runtime_get_noresume(core->dev);
|
||||
if (!pm_runtime_active(core->dev)) {
|
||||
ret = false;
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
ret = core->ops->is_enabled(core->hw);
|
||||
done:
|
||||
clk_pm_runtime_put(core);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*** helper functions ***/
|
||||
|
@ -489,6 +542,8 @@ static void clk_core_unprepare(struct clk_core *core)
|
|||
if (core->ops->unprepare)
|
||||
core->ops->unprepare(core->hw);
|
||||
|
||||
clk_pm_runtime_put(core);
|
||||
|
||||
trace_clk_unprepare_complete(core);
|
||||
clk_core_unprepare(core->parent);
|
||||
}
|
||||
|
@ -530,10 +585,14 @@ static int clk_core_prepare(struct clk_core *core)
|
|||
return 0;
|
||||
|
||||
if (core->prepare_count == 0) {
|
||||
ret = clk_core_prepare(core->parent);
|
||||
ret = clk_pm_runtime_get(core);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = clk_core_prepare(core->parent);
|
||||
if (ret)
|
||||
goto runtime_put;
|
||||
|
||||
trace_clk_prepare(core);
|
||||
|
||||
if (core->ops->prepare)
|
||||
|
@ -541,15 +600,18 @@ static int clk_core_prepare(struct clk_core *core)
|
|||
|
||||
trace_clk_prepare_complete(core);
|
||||
|
||||
if (ret) {
|
||||
clk_core_unprepare(core->parent);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto unprepare;
|
||||
}
|
||||
|
||||
core->prepare_count++;
|
||||
|
||||
return 0;
|
||||
unprepare:
|
||||
clk_core_unprepare(core->parent);
|
||||
runtime_put:
|
||||
clk_pm_runtime_put(core);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int clk_core_prepare_lock(struct clk_core *core)
|
||||
|
@ -745,6 +807,9 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
|
|||
if (core->flags & CLK_IGNORE_UNUSED)
|
||||
return;
|
||||
|
||||
if (clk_pm_runtime_get(core))
|
||||
return;
|
||||
|
||||
if (clk_core_is_prepared(core)) {
|
||||
trace_clk_unprepare(core);
|
||||
if (core->ops->unprepare_unused)
|
||||
|
@ -753,6 +818,8 @@ static void clk_unprepare_unused_subtree(struct clk_core *core)
|
|||
core->ops->unprepare(core->hw);
|
||||
trace_clk_unprepare_complete(core);
|
||||
}
|
||||
|
||||
clk_pm_runtime_put(core);
|
||||
}
|
||||
|
||||
static void clk_disable_unused_subtree(struct clk_core *core)
|
||||
|
@ -768,6 +835,9 @@ static void clk_disable_unused_subtree(struct clk_core *core)
|
|||
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
||||
clk_core_prepare_enable(core->parent);
|
||||
|
||||
if (clk_pm_runtime_get(core))
|
||||
goto unprepare_out;
|
||||
|
||||
flags = clk_enable_lock();
|
||||
|
||||
if (core->enable_count)
|
||||
|
@ -792,6 +862,8 @@ static void clk_disable_unused_subtree(struct clk_core *core)
|
|||
|
||||
unlock_out:
|
||||
clk_enable_unlock(flags);
|
||||
clk_pm_runtime_put(core);
|
||||
unprepare_out:
|
||||
if (core->flags & CLK_OPS_PARENT_ENABLE)
|
||||
clk_core_disable_unprepare(core->parent);
|
||||
}
|
||||
|
@ -1038,9 +1110,13 @@ EXPORT_SYMBOL_GPL(clk_get_accuracy);
|
|||
static unsigned long clk_recalc(struct clk_core *core,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
if (core->ops->recalc_rate)
|
||||
return core->ops->recalc_rate(core->hw, parent_rate);
|
||||
return parent_rate;
|
||||
unsigned long rate = parent_rate;
|
||||
|
||||
if (core->ops->recalc_rate && !clk_pm_runtime_get(core)) {
|
||||
rate = core->ops->recalc_rate(core->hw, parent_rate);
|
||||
clk_pm_runtime_put(core);
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1565,6 +1641,7 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
|||
{
|
||||
struct clk_core *top, *fail_clk;
|
||||
unsigned long rate = req_rate;
|
||||
int ret = 0;
|
||||
|
||||
if (!core)
|
||||
return 0;
|
||||
|
@ -1581,21 +1658,28 @@ static int clk_core_set_rate_nolock(struct clk_core *core,
|
|||
if (!top)
|
||||
return -EINVAL;
|
||||
|
||||
ret = clk_pm_runtime_get(core);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* notify that we are about to change rates */
|
||||
fail_clk = clk_propagate_rate_change(top, PRE_RATE_CHANGE);
|
||||
if (fail_clk) {
|
||||
pr_debug("%s: failed to set %s rate\n", __func__,
|
||||
fail_clk->name);
|
||||
clk_propagate_rate_change(top, ABORT_RATE_CHANGE);
|
||||
return -EBUSY;
|
||||
ret = -EBUSY;
|
||||
goto err;
|
||||
}
|
||||
|
||||
/* change the rates */
|
||||
clk_change_rate(top);
|
||||
|
||||
core->req_rate = req_rate;
|
||||
err:
|
||||
clk_pm_runtime_put(core);
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1826,12 +1910,16 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
|
|||
p_rate = parent->rate;
|
||||
}
|
||||
|
||||
ret = clk_pm_runtime_get(core);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
/* propagate PRE_RATE_CHANGE notifications */
|
||||
ret = __clk_speculate_rates(core, p_rate);
|
||||
|
||||
/* abort if a driver objects */
|
||||
if (ret & NOTIFY_STOP_MASK)
|
||||
goto out;
|
||||
goto runtime_put;
|
||||
|
||||
/* do the re-parent */
|
||||
ret = __clk_set_parent(core, parent, p_index);
|
||||
|
@ -1844,6 +1932,8 @@ static int clk_core_set_parent(struct clk_core *core, struct clk_core *parent)
|
|||
__clk_recalc_accuracies(core);
|
||||
}
|
||||
|
||||
runtime_put:
|
||||
clk_pm_runtime_put(core);
|
||||
out:
|
||||
clk_prepare_unlock();
|
||||
|
||||
|
@ -2350,7 +2440,7 @@ static inline void clk_debug_unregister(struct clk_core *core)
|
|||
*/
|
||||
static int __clk_core_init(struct clk_core *core)
|
||||
{
|
||||
int i, ret = 0;
|
||||
int i, ret;
|
||||
struct clk_core *orphan;
|
||||
struct hlist_node *tmp2;
|
||||
unsigned long rate;
|
||||
|
@ -2360,6 +2450,10 @@ static int __clk_core_init(struct clk_core *core)
|
|||
|
||||
clk_prepare_lock();
|
||||
|
||||
ret = clk_pm_runtime_get(core);
|
||||
if (ret)
|
||||
goto unlock;
|
||||
|
||||
/* check to see if a clock with this name is already registered */
|
||||
if (clk_core_lookup(core->name)) {
|
||||
pr_debug("%s: clk %s already initialized\n",
|
||||
|
@ -2512,6 +2606,8 @@ static int __clk_core_init(struct clk_core *core)
|
|||
|
||||
kref_init(&core->ref);
|
||||
out:
|
||||
clk_pm_runtime_put(core);
|
||||
unlock:
|
||||
clk_prepare_unlock();
|
||||
|
||||
if (!ret)
|
||||
|
@ -2583,6 +2679,8 @@ struct clk *clk_register(struct device *dev, struct clk_hw *hw)
|
|||
goto fail_name;
|
||||
}
|
||||
core->ops = hw->init->ops;
|
||||
if (dev && pm_runtime_enabled(dev))
|
||||
core->dev = dev;
|
||||
if (dev && dev->driver)
|
||||
core->owner = dev->driver->owner;
|
||||
core->hw = hw;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/syscore_ops.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <dt-bindings/clock/exynos-audss-clk.h>
|
||||
|
||||
|
@ -36,14 +37,13 @@ static struct clk *epll;
|
|||
#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 exynos_audss_clk_suspend(struct device *dev)
|
||||
static int __maybe_unused exynos_audss_clk_suspend(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -53,7 +53,7 @@ static int exynos_audss_clk_suspend(struct device *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int exynos_audss_clk_resume(struct device *dev)
|
||||
static int __maybe_unused exynos_audss_clk_resume(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -62,7 +62,6 @@ static int exynos_audss_clk_resume(struct device *dev)
|
|||
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
struct exynos_audss_clk_drvdata {
|
||||
unsigned int has_adma_clk:1;
|
||||
|
@ -135,6 +134,7 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
|
|||
const struct exynos_audss_clk_drvdata *variant;
|
||||
struct clk_hw **clk_table;
|
||||
struct resource *res;
|
||||
struct device *dev = &pdev->dev;
|
||||
int i, ret = 0;
|
||||
|
||||
variant = of_device_get_match_data(&pdev->dev);
|
||||
|
@ -142,15 +142,15 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
|
|||
return -EINVAL;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg_base = devm_ioremap_resource(&pdev->dev, res);
|
||||
reg_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(reg_base)) {
|
||||
dev_err(&pdev->dev, "failed to map audss registers\n");
|
||||
dev_err(dev, "failed to map audss registers\n");
|
||||
return PTR_ERR(reg_base);
|
||||
}
|
||||
|
||||
epll = ERR_PTR(-ENODEV);
|
||||
|
||||
clk_data = devm_kzalloc(&pdev->dev,
|
||||
clk_data = devm_kzalloc(dev,
|
||||
sizeof(*clk_data) +
|
||||
sizeof(*clk_data->hws) * EXYNOS_AUDSS_MAX_CLKS,
|
||||
GFP_KERNEL);
|
||||
|
@ -160,8 +160,8 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
|
|||
clk_data->num = variant->num_clks;
|
||||
clk_table = clk_data->hws;
|
||||
|
||||
pll_ref = devm_clk_get(&pdev->dev, "pll_ref");
|
||||
pll_in = devm_clk_get(&pdev->dev, "pll_in");
|
||||
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)) {
|
||||
|
@ -172,88 +172,103 @@ static int exynos_audss_clk_probe(struct platform_device *pdev)
|
|||
|
||||
ret = clk_prepare_enable(epll);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev,
|
||||
dev_err(dev,
|
||||
"failed to prepare the epll clock\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
clk_table[EXYNOS_MOUT_AUDSS] = clk_hw_register_mux(NULL, "mout_audss",
|
||||
|
||||
/*
|
||||
* 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(&pdev->dev, "cdclk");
|
||||
sclk_audio = devm_clk_get(&pdev->dev, "sclk_audio");
|
||||
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(NULL, "mout_i2s",
|
||||
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(NULL, "dout_srp",
|
||||
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(NULL,
|
||||
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(NULL, "dout_i2s",
|
||||
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(NULL, "srp_clk",
|
||||
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(NULL, "i2s_bus",
|
||||
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(NULL, "sclk_i2s",
|
||||
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(NULL, "pcm_bus",
|
||||
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(&pdev->dev, "sclk_pcm_in");
|
||||
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(NULL, "sclk_pcm",
|
||||
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(NULL, "adma",
|
||||
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(&pdev->dev, "failed to register clock %d\n", 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(pdev->dev.of_node, of_clk_hw_onecell_get,
|
||||
ret = of_clk_add_hw_provider(dev->of_node, of_clk_hw_onecell_get,
|
||||
clk_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to add clock provider\n");
|
||||
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);
|
||||
|
@ -266,6 +281,7 @@ static int 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);
|
||||
|
@ -274,8 +290,10 @@ static int exynos_audss_clk_remove(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
static const struct dev_pm_ops exynos_audss_clk_pm_ops = {
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(exynos_audss_clk_suspend,
|
||||
exynos_audss_clk_resume)
|
||||
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 = {
|
||||
|
|
|
@ -9,9 +9,13 @@
|
|||
* Common Clock Framework support for Exynos5433 SoC.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include <dt-bindings/clock/exynos5433.h>
|
||||
|
||||
|
@ -1991,6 +1995,14 @@ static const unsigned long fsys_clk_regs[] __initconst = {
|
|||
ENABLE_IP_FSYS1,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump fsys_suspend_regs[] = {
|
||||
{ MUX_SEL_FSYS0, 0 },
|
||||
{ MUX_SEL_FSYS1, 0 },
|
||||
{ MUX_SEL_FSYS2, 0 },
|
||||
{ MUX_SEL_FSYS3, 0 },
|
||||
{ MUX_SEL_FSYS4, 0 },
|
||||
};
|
||||
|
||||
static const struct samsung_fixed_rate_clock fsys_fixed_clks[] __initconst = {
|
||||
/* PHY clocks from USBDRD30_PHY */
|
||||
FRATE(CLK_PHYCLK_USBDRD30_UDRD30_PHYCLOCK_PHY,
|
||||
|
@ -2296,16 +2308,11 @@ static const struct samsung_cmu_info fsys_cmu_info __initconst = {
|
|||
.nr_clk_ids = FSYS_NR_CLK,
|
||||
.clk_regs = fsys_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(fsys_clk_regs),
|
||||
.suspend_regs = fsys_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(fsys_suspend_regs),
|
||||
.clk_name = "aclk_fsys_200",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_fsys_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &fsys_cmu_info);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(exynos5433_cmu_fsys, "samsung,exynos5433-cmu-fsys",
|
||||
exynos5433_cmu_fsys_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_G2D
|
||||
*/
|
||||
|
@ -2335,6 +2342,10 @@ static const unsigned long g2d_clk_regs[] __initconst = {
|
|||
DIV_ENABLE_IP_G2D_SECURE_SMMU_G2D,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump g2d_suspend_regs[] = {
|
||||
{ MUX_SEL_G2D0, 0 },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_aclk_g2d_266_user_p) = { "oscclk", "aclk_g2d_266", };
|
||||
PNAME(mout_aclk_g2d_400_user_p) = { "oscclk", "aclk_g2d_400", };
|
||||
|
@ -2420,16 +2431,11 @@ static const struct samsung_cmu_info g2d_cmu_info __initconst = {
|
|||
.nr_clk_ids = G2D_NR_CLK,
|
||||
.clk_regs = g2d_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(g2d_clk_regs),
|
||||
.suspend_regs = g2d_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(g2d_suspend_regs),
|
||||
.clk_name = "aclk_g2d_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_g2d_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &g2d_cmu_info);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(exynos5433_cmu_g2d, "samsung,exynos5433-cmu-g2d",
|
||||
exynos5433_cmu_g2d_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_DISP
|
||||
*/
|
||||
|
@ -2494,6 +2500,18 @@ static const unsigned long disp_clk_regs[] __initconst = {
|
|||
CLKOUT_CMU_DISP_DIV_STAT,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump disp_suspend_regs[] = {
|
||||
/* PLL has to be enabled for suspend */
|
||||
{ DISP_PLL_CON0, 0x85f40502 },
|
||||
/* ignore status of external PHY muxes during suspend to avoid hangs */
|
||||
{ MUX_IGNORE_DISP2, 0x00111111 },
|
||||
{ MUX_SEL_DISP0, 0 },
|
||||
{ MUX_SEL_DISP1, 0 },
|
||||
{ MUX_SEL_DISP2, 0 },
|
||||
{ MUX_SEL_DISP3, 0 },
|
||||
{ MUX_SEL_DISP4, 0 },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_disp_pll_p) = { "oscclk", "fout_disp_pll", };
|
||||
PNAME(mout_sclk_dsim1_user_p) = { "oscclk", "sclk_dsim1_disp", };
|
||||
|
@ -2841,16 +2859,11 @@ static const struct samsung_cmu_info disp_cmu_info __initconst = {
|
|||
.nr_clk_ids = DISP_NR_CLK,
|
||||
.clk_regs = disp_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(disp_clk_regs),
|
||||
.suspend_regs = disp_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(disp_suspend_regs),
|
||||
.clk_name = "aclk_disp_333",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_disp_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &disp_cmu_info);
|
||||
}
|
||||
|
||||
CLK_OF_DECLARE(exynos5433_cmu_disp, "samsung,exynos5433-cmu-disp",
|
||||
exynos5433_cmu_disp_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_AUD
|
||||
*/
|
||||
|
@ -2885,6 +2898,11 @@ static const unsigned long aud_clk_regs[] __initconst = {
|
|||
ENABLE_IP_AUD1,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump aud_suspend_regs[] = {
|
||||
{ MUX_SEL_AUD0, 0 },
|
||||
{ MUX_SEL_AUD1, 0 },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_aud_pll_user_aud_p) = { "oscclk", "fout_aud_pll", };
|
||||
PNAME(mout_sclk_aud_pcm_p) = { "mout_aud_pll_user", "ioclk_audiocdclk0",};
|
||||
|
@ -3011,16 +3029,11 @@ static const struct samsung_cmu_info aud_cmu_info __initconst = {
|
|||
.nr_clk_ids = AUD_NR_CLK,
|
||||
.clk_regs = aud_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(aud_clk_regs),
|
||||
.suspend_regs = aud_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(aud_suspend_regs),
|
||||
.clk_name = "fout_aud_pll",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_aud_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &aud_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_aud, "samsung,exynos5433-cmu-aud",
|
||||
exynos5433_cmu_aud_init);
|
||||
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_BUS{0|1|2}
|
||||
*/
|
||||
|
@ -3222,6 +3235,10 @@ static const unsigned long g3d_clk_regs[] __initconst = {
|
|||
CLK_STOPCTRL,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump g3d_suspend_regs[] = {
|
||||
{ MUX_SEL_G3D, 0 },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_aclk_g3d_400_p) = { "mout_g3d_pll", "aclk_g3d_400", };
|
||||
PNAME(mout_g3d_pll_p) = { "oscclk", "fout_g3d_pll", };
|
||||
|
@ -3295,15 +3312,11 @@ static const struct samsung_cmu_info g3d_cmu_info __initconst = {
|
|||
.nr_clk_ids = G3D_NR_CLK,
|
||||
.clk_regs = g3d_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(g3d_clk_regs),
|
||||
.suspend_regs = g3d_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(g3d_suspend_regs),
|
||||
.clk_name = "aclk_g3d_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_g3d_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &g3d_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_g3d, "samsung,exynos5433-cmu-g3d",
|
||||
exynos5433_cmu_g3d_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_GSCL
|
||||
*/
|
||||
|
@ -3342,6 +3355,12 @@ static const unsigned long gscl_clk_regs[] __initconst = {
|
|||
ENABLE_IP_GSCL_SECURE_SMMU_GSCL2,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump gscl_suspend_regs[] = {
|
||||
{ MUX_SEL_GSCL, 0 },
|
||||
{ ENABLE_ACLK_GSCL, 0xfff },
|
||||
{ ENABLE_PCLK_GSCL, 0xff },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(aclk_gscl_111_user_p) = { "oscclk", "aclk_gscl_111", };
|
||||
PNAME(aclk_gscl_333_user_p) = { "oscclk", "aclk_gscl_333", };
|
||||
|
@ -3436,15 +3455,11 @@ static const struct samsung_cmu_info gscl_cmu_info __initconst = {
|
|||
.nr_clk_ids = GSCL_NR_CLK,
|
||||
.clk_regs = gscl_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(gscl_clk_regs),
|
||||
.suspend_regs = gscl_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(gscl_suspend_regs),
|
||||
.clk_name = "aclk_gscl_111",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_gscl_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &gscl_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_gscl, "samsung,exynos5433-cmu-gscl",
|
||||
exynos5433_cmu_gscl_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_APOLLO
|
||||
*/
|
||||
|
@ -3970,6 +3985,11 @@ static const unsigned long mscl_clk_regs[] __initconst = {
|
|||
ENABLE_IP_MSCL_SECURE_SMMU_JPEG,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump mscl_suspend_regs[] = {
|
||||
{ MUX_SEL_MSCL0, 0 },
|
||||
{ MUX_SEL_MSCL1, 0 },
|
||||
};
|
||||
|
||||
/* list of all parent clock list */
|
||||
PNAME(mout_sclk_jpeg_user_p) = { "oscclk", "sclk_jpeg_mscl", };
|
||||
PNAME(mout_aclk_mscl_400_user_p) = { "oscclk", "aclk_mscl_400", };
|
||||
|
@ -4082,15 +4102,11 @@ static const struct samsung_cmu_info mscl_cmu_info __initconst = {
|
|||
.nr_clk_ids = MSCL_NR_CLK,
|
||||
.clk_regs = mscl_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(mscl_clk_regs),
|
||||
.suspend_regs = mscl_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(mscl_suspend_regs),
|
||||
.clk_name = "aclk_mscl_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_mscl_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &mscl_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_mscl, "samsung,exynos5433-cmu-mscl",
|
||||
exynos5433_cmu_mscl_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_MFC
|
||||
*/
|
||||
|
@ -4120,6 +4136,10 @@ static const unsigned long mfc_clk_regs[] __initconst = {
|
|||
ENABLE_IP_MFC_SECURE_SMMU_MFC,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump mfc_suspend_regs[] = {
|
||||
{ MUX_SEL_MFC, 0 },
|
||||
};
|
||||
|
||||
PNAME(mout_aclk_mfc_400_user_p) = { "oscclk", "aclk_mfc_400", };
|
||||
|
||||
static const struct samsung_mux_clock mfc_mux_clks[] __initconst = {
|
||||
|
@ -4190,15 +4210,11 @@ static const struct samsung_cmu_info mfc_cmu_info __initconst = {
|
|||
.nr_clk_ids = MFC_NR_CLK,
|
||||
.clk_regs = mfc_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(mfc_clk_regs),
|
||||
.suspend_regs = mfc_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(mfc_suspend_regs),
|
||||
.clk_name = "aclk_mfc_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_mfc_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &mfc_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_mfc, "samsung,exynos5433-cmu-mfc",
|
||||
exynos5433_cmu_mfc_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_HEVC
|
||||
*/
|
||||
|
@ -4228,6 +4244,10 @@ static const unsigned long hevc_clk_regs[] __initconst = {
|
|||
ENABLE_IP_HEVC_SECURE_SMMU_HEVC,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump hevc_suspend_regs[] = {
|
||||
{ MUX_SEL_HEVC, 0 },
|
||||
};
|
||||
|
||||
PNAME(mout_aclk_hevc_400_user_p) = { "oscclk", "aclk_hevc_400", };
|
||||
|
||||
static const struct samsung_mux_clock hevc_mux_clks[] __initconst = {
|
||||
|
@ -4300,15 +4320,11 @@ static const struct samsung_cmu_info hevc_cmu_info __initconst = {
|
|||
.nr_clk_ids = HEVC_NR_CLK,
|
||||
.clk_regs = hevc_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(hevc_clk_regs),
|
||||
.suspend_regs = hevc_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(hevc_suspend_regs),
|
||||
.clk_name = "aclk_hevc_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_hevc_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &hevc_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_hevc, "samsung,exynos5433-cmu-hevc",
|
||||
exynos5433_cmu_hevc_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_ISP
|
||||
*/
|
||||
|
@ -4342,6 +4358,10 @@ static const unsigned long isp_clk_regs[] __initconst = {
|
|||
ENABLE_IP_ISP3,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump isp_suspend_regs[] = {
|
||||
{ MUX_SEL_ISP, 0 },
|
||||
};
|
||||
|
||||
PNAME(mout_aclk_isp_dis_400_user_p) = { "oscclk", "aclk_isp_dis_400", };
|
||||
PNAME(mout_aclk_isp_400_user_p) = { "oscclk", "aclk_isp_400", };
|
||||
|
||||
|
@ -4553,15 +4573,11 @@ static const struct samsung_cmu_info isp_cmu_info __initconst = {
|
|||
.nr_clk_ids = ISP_NR_CLK,
|
||||
.clk_regs = isp_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(isp_clk_regs),
|
||||
.suspend_regs = isp_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(isp_suspend_regs),
|
||||
.clk_name = "aclk_isp_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_isp_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &isp_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_isp, "samsung,exynos5433-cmu-isp",
|
||||
exynos5433_cmu_isp_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_CAM0
|
||||
*/
|
||||
|
@ -4625,6 +4641,15 @@ static const unsigned long cam0_clk_regs[] __initconst = {
|
|||
ENABLE_IP_CAM02,
|
||||
ENABLE_IP_CAM03,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump cam0_suspend_regs[] = {
|
||||
{ MUX_SEL_CAM00, 0 },
|
||||
{ MUX_SEL_CAM01, 0 },
|
||||
{ MUX_SEL_CAM02, 0 },
|
||||
{ MUX_SEL_CAM03, 0 },
|
||||
{ MUX_SEL_CAM04, 0 },
|
||||
};
|
||||
|
||||
PNAME(mout_aclk_cam0_333_user_p) = { "oscclk", "aclk_cam0_333", };
|
||||
PNAME(mout_aclk_cam0_400_user_p) = { "oscclk", "aclk_cam0_400", };
|
||||
PNAME(mout_aclk_cam0_552_user_p) = { "oscclk", "aclk_cam0_552", };
|
||||
|
@ -5030,15 +5055,11 @@ static const struct samsung_cmu_info cam0_cmu_info __initconst = {
|
|||
.nr_clk_ids = CAM0_NR_CLK,
|
||||
.clk_regs = cam0_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(cam0_clk_regs),
|
||||
.suspend_regs = cam0_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(cam0_suspend_regs),
|
||||
.clk_name = "aclk_cam0_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_cam0_init(struct device_node *np)
|
||||
{
|
||||
samsung_cmu_register_one(np, &cam0_cmu_info);
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_cam0, "samsung,exynos5433-cmu-cam0",
|
||||
exynos5433_cmu_cam0_init);
|
||||
|
||||
/*
|
||||
* Register offset definitions for CMU_CAM1
|
||||
*/
|
||||
|
@ -5085,6 +5106,12 @@ static const unsigned long cam1_clk_regs[] __initconst = {
|
|||
ENABLE_IP_CAM12,
|
||||
};
|
||||
|
||||
static const struct samsung_clk_reg_dump cam1_suspend_regs[] = {
|
||||
{ MUX_SEL_CAM10, 0 },
|
||||
{ MUX_SEL_CAM11, 0 },
|
||||
{ MUX_SEL_CAM12, 0 },
|
||||
};
|
||||
|
||||
PNAME(mout_sclk_isp_uart_user_p) = { "oscclk", "sclk_isp_uart_cam1", };
|
||||
PNAME(mout_sclk_isp_spi1_user_p) = { "oscclk", "sclk_isp_spi1_cam1", };
|
||||
PNAME(mout_sclk_isp_spi0_user_p) = { "oscclk", "sclk_isp_spi0_cam1", };
|
||||
|
@ -5403,11 +5430,223 @@ static const struct samsung_cmu_info cam1_cmu_info __initconst = {
|
|||
.nr_clk_ids = CAM1_NR_CLK,
|
||||
.clk_regs = cam1_clk_regs,
|
||||
.nr_clk_regs = ARRAY_SIZE(cam1_clk_regs),
|
||||
.suspend_regs = cam1_suspend_regs,
|
||||
.nr_suspend_regs = ARRAY_SIZE(cam1_suspend_regs),
|
||||
.clk_name = "aclk_cam1_400",
|
||||
};
|
||||
|
||||
static void __init exynos5433_cmu_cam1_init(struct device_node *np)
|
||||
|
||||
struct exynos5433_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 clk *clk;
|
||||
struct clk **pclks;
|
||||
int nr_pclks;
|
||||
|
||||
/* must be the last entry */
|
||||
struct samsung_clk_provider ctx;
|
||||
};
|
||||
|
||||
static int exynos5433_cmu_suspend(struct device *dev)
|
||||
{
|
||||
samsung_cmu_register_one(np, &cam1_cmu_info);
|
||||
struct exynos5433_cmu_data *data = dev_get_drvdata(dev);
|
||||
int i;
|
||||
|
||||
samsung_clk_save(data->ctx.reg_base, data->clk_save,
|
||||
data->nr_clk_save);
|
||||
|
||||
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, 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;
|
||||
}
|
||||
CLK_OF_DECLARE(exynos5433_cmu_cam1, "samsung,exynos5433-cmu-cam1",
|
||||
exynos5433_cmu_cam1_init);
|
||||
|
||||
static int exynos5433_cmu_resume(struct device *dev)
|
||||
{
|
||||
struct exynos5433_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, data->clk_save,
|
||||
data->nr_clk_save);
|
||||
|
||||
for (i = 0; i < data->nr_pclks; i++)
|
||||
clk_disable_unprepare(data->pclks[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init exynos5433_cmu_probe(struct platform_device *pdev)
|
||||
{
|
||||
const struct samsung_cmu_info *info;
|
||||
struct exynos5433_cmu_data *data;
|
||||
struct samsung_clk_provider *ctx;
|
||||
struct device *dev = &pdev->dev;
|
||||
struct resource *res;
|
||||
void __iomem *reg_base;
|
||||
int i;
|
||||
|
||||
info = of_device_get_match_data(dev);
|
||||
|
||||
data = devm_kzalloc(dev, sizeof(*data) +
|
||||
sizeof(*data->ctx.clk_data.hws) * info->nr_clk_ids,
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
ctx = &data->ctx;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
reg_base = devm_ioremap_resource(dev, res);
|
||||
if (IS_ERR(reg_base)) {
|
||||
dev_err(dev, "failed to map registers\n");
|
||||
return PTR_ERR(reg_base);
|
||||
}
|
||||
|
||||
for (i = 0; i < info->nr_clk_ids; ++i)
|
||||
ctx->clk_data.hws[i] = ERR_PTR(-ENOENT);
|
||||
|
||||
ctx->clk_data.num = info->nr_clk_ids;
|
||||
ctx->reg_base = reg_base;
|
||||
ctx->dev = dev;
|
||||
spin_lock_init(&ctx->lock);
|
||||
|
||||
data->clk_save = samsung_clk_alloc_reg_dump(info->clk_regs,
|
||||
info->nr_clk_regs);
|
||||
data->nr_clk_save = info->nr_clk_regs;
|
||||
data->clk_suspend = info->suspend_regs;
|
||||
data->nr_clk_suspend = info->nr_suspend_regs;
|
||||
data->nr_pclks = of_count_phandle_with_args(dev->of_node, "clocks",
|
||||
"#clock-cells");
|
||||
if (data->nr_pclks > 0) {
|
||||
data->pclks = devm_kcalloc(dev, sizeof(struct clk *),
|
||||
data->nr_pclks, GFP_KERNEL);
|
||||
|
||||
for (i = 0; i < data->nr_pclks; i++) {
|
||||
struct clk *clk = of_clk_get(dev->of_node, i);
|
||||
|
||||
if (IS_ERR(clk))
|
||||
return PTR_ERR(clk);
|
||||
data->pclks[i] = clk;
|
||||
}
|
||||
}
|
||||
|
||||
if (info->clk_name)
|
||||
data->clk = clk_get(dev, info->clk_name);
|
||||
clk_prepare_enable(data->clk);
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
|
||||
if (info->pll_clks)
|
||||
samsung_clk_register_pll(ctx, info->pll_clks, info->nr_pll_clks,
|
||||
reg_base);
|
||||
if (info->mux_clks)
|
||||
samsung_clk_register_mux(ctx, info->mux_clks,
|
||||
info->nr_mux_clks);
|
||||
if (info->div_clks)
|
||||
samsung_clk_register_div(ctx, info->div_clks,
|
||||
info->nr_div_clks);
|
||||
if (info->gate_clks)
|
||||
samsung_clk_register_gate(ctx, info->gate_clks,
|
||||
info->nr_gate_clks);
|
||||
if (info->fixed_clks)
|
||||
samsung_clk_register_fixed_rate(ctx, info->fixed_clks,
|
||||
info->nr_fixed_clks);
|
||||
if (info->fixed_factor_clks)
|
||||
samsung_clk_register_fixed_factor(ctx, info->fixed_factor_clks,
|
||||
info->nr_fixed_factor_clks);
|
||||
|
||||
samsung_clk_of_add_provider(dev->of_node, ctx);
|
||||
pm_runtime_put_sync(dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id exynos5433_cmu_of_match[] = {
|
||||
{
|
||||
.compatible = "samsung,exynos5433-cmu-aud",
|
||||
.data = &aud_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-cam0",
|
||||
.data = &cam0_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-cam1",
|
||||
.data = &cam1_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-disp",
|
||||
.data = &disp_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-g2d",
|
||||
.data = &g2d_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-g3d",
|
||||
.data = &g3d_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-fsys",
|
||||
.data = &fsys_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-gscl",
|
||||
.data = &gscl_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-mfc",
|
||||
.data = &mfc_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-hevc",
|
||||
.data = &hevc_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-isp",
|
||||
.data = &isp_cmu_info,
|
||||
}, {
|
||||
.compatible = "samsung,exynos5433-cmu-mscl",
|
||||
.data = &mscl_cmu_info,
|
||||
}, {
|
||||
},
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops exynos5433_cmu_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(exynos5433_cmu_suspend, exynos5433_cmu_resume,
|
||||
NULL)
|
||||
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||
pm_runtime_force_resume)
|
||||
};
|
||||
|
||||
static struct platform_driver exynos5433_cmu_driver __refdata = {
|
||||
.driver = {
|
||||
.name = "exynos5433-cmu",
|
||||
.of_match_table = exynos5433_cmu_of_match,
|
||||
.suppress_bind_attrs = true,
|
||||
.pm = &exynos5433_cmu_pm_ops,
|
||||
},
|
||||
.probe = exynos5433_cmu_probe,
|
||||
};
|
||||
|
||||
static int __init exynos5433_cmu_init(void)
|
||||
{
|
||||
return platform_driver_register(&exynos5433_cmu_driver);
|
||||
}
|
||||
core_initcall(exynos5433_cmu_init);
|
||||
|
|
|
@ -1388,7 +1388,7 @@ static void __init _samsung_clk_register_pll(struct samsung_clk_provider *ctx,
|
|||
pll->lock_reg = base + pll_clk->lock_offset;
|
||||
pll->con_reg = base + pll_clk->con_offset;
|
||||
|
||||
ret = clk_hw_register(NULL, &pll->hw);
|
||||
ret = clk_hw_register(ctx->dev, &pll->hw);
|
||||
if (ret) {
|
||||
pr_err("%s: failed to register pll clock %s : %d\n",
|
||||
__func__, pll_clk->name, ret);
|
||||
|
|
|
@ -134,7 +134,7 @@ void __init samsung_clk_register_fixed_rate(struct samsung_clk_provider *ctx,
|
|||
unsigned int idx, ret;
|
||||
|
||||
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||||
clk_hw = clk_hw_register_fixed_rate(NULL, list->name,
|
||||
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__,
|
||||
|
@ -163,7 +163,7 @@ void __init samsung_clk_register_fixed_factor(struct samsung_clk_provider *ctx,
|
|||
unsigned int idx;
|
||||
|
||||
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||||
clk_hw = clk_hw_register_fixed_factor(NULL, list->name,
|
||||
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__,
|
||||
|
@ -184,7 +184,7 @@ void __init samsung_clk_register_mux(struct samsung_clk_provider *ctx,
|
|||
unsigned int idx, ret;
|
||||
|
||||
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||||
clk_hw = clk_hw_register_mux(NULL, list->name,
|
||||
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);
|
||||
|
@ -217,13 +217,13 @@ void __init samsung_clk_register_div(struct samsung_clk_provider *ctx,
|
|||
|
||||
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||||
if (list->table)
|
||||
clk_hw = clk_hw_register_divider_table(NULL,
|
||||
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(NULL, list->name,
|
||||
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);
|
||||
|
@ -255,7 +255,7 @@ void __init samsung_clk_register_gate(struct samsung_clk_provider *ctx,
|
|||
unsigned int idx, ret;
|
||||
|
||||
for (idx = 0; idx < nr_clk; idx++, list++) {
|
||||
clk_hw = clk_hw_register_gate(NULL, list->name, list->parent_name,
|
||||
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)) {
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
*/
|
||||
struct samsung_clk_provider {
|
||||
void __iomem *reg_base;
|
||||
struct device *dev;
|
||||
spinlock_t lock;
|
||||
/* clk_data must be the last entry due to variable lenght 'hws' array */
|
||||
struct clk_hw_onecell_data clk_data;
|
||||
|
@ -352,6 +353,12 @@ struct samsung_cmu_info {
|
|||
/* list and number of clocks registers */
|
||||
const unsigned long *clk_regs;
|
||||
unsigned int nr_clk_regs;
|
||||
|
||||
/* list and number of clocks registers to set before suspend */
|
||||
const struct samsung_clk_reg_dump *suspend_regs;
|
||||
unsigned int nr_suspend_regs;
|
||||
/* name of the parent clock needed for CMU register access */
|
||||
const char *clk_name;
|
||||
};
|
||||
|
||||
extern struct samsung_clk_provider *__init samsung_clk_init(
|
||||
|
|
Loading…
Reference in New Issue
Block a user