0001
0002
0003
0004
0005
0006
0007 #include <linux/io.h>
0008 #include <linux/of_platform.h>
0009 #include <linux/platform_device.h>
0010 #include <linux/pm_domain.h>
0011 #include <linux/pm_runtime.h>
0012
0013 #include "clk.h"
0014 #include "clk-exynos5-subcmu.h"
0015
0016 static struct samsung_clk_provider *ctx;
0017 static const struct exynos5_subcmu_info **cmu;
0018 static int nr_cmus;
0019
0020 static void exynos5_subcmu_clk_save(void __iomem *base,
0021 struct exynos5_subcmu_reg_dump *rd,
0022 unsigned int num_regs)
0023 {
0024 for (; num_regs > 0; --num_regs, ++rd) {
0025 rd->save = readl(base + rd->offset);
0026 writel((rd->save & ~rd->mask) | rd->value, base + rd->offset);
0027 rd->save &= rd->mask;
0028 }
0029 };
0030
0031 static void exynos5_subcmu_clk_restore(void __iomem *base,
0032 struct exynos5_subcmu_reg_dump *rd,
0033 unsigned int num_regs)
0034 {
0035 for (; num_regs > 0; --num_regs, ++rd)
0036 writel((readl(base + rd->offset) & ~rd->mask) | rd->save,
0037 base + rd->offset);
0038 }
0039
0040 static void exynos5_subcmu_defer_gate(struct samsung_clk_provider *ctx,
0041 const struct samsung_gate_clock *list, int nr_clk)
0042 {
0043 while (nr_clk--)
0044 samsung_clk_add_lookup(ctx, ERR_PTR(-EPROBE_DEFER), list++->id);
0045 }
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055
0056
0057
0058 void exynos5_subcmus_init(struct samsung_clk_provider *_ctx, int _nr_cmus,
0059 const struct exynos5_subcmu_info **_cmu)
0060 {
0061 ctx = _ctx;
0062 cmu = _cmu;
0063 nr_cmus = _nr_cmus;
0064
0065 for (; _nr_cmus--; _cmu++) {
0066 exynos5_subcmu_defer_gate(ctx, (*_cmu)->gate_clks,
0067 (*_cmu)->nr_gate_clks);
0068 exynos5_subcmu_clk_save(ctx->reg_base, (*_cmu)->suspend_regs,
0069 (*_cmu)->nr_suspend_regs);
0070 }
0071 }
0072
0073 static int __maybe_unused exynos5_subcmu_suspend(struct device *dev)
0074 {
0075 struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
0076 unsigned long flags;
0077
0078 spin_lock_irqsave(&ctx->lock, flags);
0079 exynos5_subcmu_clk_save(ctx->reg_base, info->suspend_regs,
0080 info->nr_suspend_regs);
0081 spin_unlock_irqrestore(&ctx->lock, flags);
0082
0083 return 0;
0084 }
0085
0086 static int __maybe_unused exynos5_subcmu_resume(struct device *dev)
0087 {
0088 struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
0089 unsigned long flags;
0090
0091 spin_lock_irqsave(&ctx->lock, flags);
0092 exynos5_subcmu_clk_restore(ctx->reg_base, info->suspend_regs,
0093 info->nr_suspend_regs);
0094 spin_unlock_irqrestore(&ctx->lock, flags);
0095
0096 return 0;
0097 }
0098
0099 static int __init exynos5_subcmu_probe(struct platform_device *pdev)
0100 {
0101 struct device *dev = &pdev->dev;
0102 struct exynos5_subcmu_info *info = dev_get_drvdata(dev);
0103
0104 pm_runtime_set_suspended(dev);
0105 pm_runtime_enable(dev);
0106 pm_runtime_get(dev);
0107
0108 ctx->dev = dev;
0109 samsung_clk_register_div(ctx, info->div_clks, info->nr_div_clks);
0110 samsung_clk_register_gate(ctx, info->gate_clks, info->nr_gate_clks);
0111 ctx->dev = NULL;
0112
0113 pm_runtime_put_sync(dev);
0114
0115 return 0;
0116 }
0117
0118 static const struct dev_pm_ops exynos5_subcmu_pm_ops = {
0119 SET_RUNTIME_PM_OPS(exynos5_subcmu_suspend,
0120 exynos5_subcmu_resume, NULL)
0121 SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
0122 pm_runtime_force_resume)
0123 };
0124
0125 static struct platform_driver exynos5_subcmu_driver __refdata = {
0126 .driver = {
0127 .name = "exynos5-subcmu",
0128 .suppress_bind_attrs = true,
0129 .pm = &exynos5_subcmu_pm_ops,
0130 },
0131 .probe = exynos5_subcmu_probe,
0132 };
0133
0134 static int __init exynos5_clk_register_subcmu(struct device *parent,
0135 const struct exynos5_subcmu_info *info,
0136 struct device_node *pd_node)
0137 {
0138 struct of_phandle_args genpdspec = { .np = pd_node };
0139 struct platform_device *pdev;
0140 int ret;
0141
0142 pdev = platform_device_alloc("exynos5-subcmu", PLATFORM_DEVID_AUTO);
0143 if (!pdev)
0144 return -ENOMEM;
0145
0146 pdev->dev.parent = parent;
0147 platform_set_drvdata(pdev, (void *)info);
0148 of_genpd_add_device(&genpdspec, &pdev->dev);
0149 ret = platform_device_add(pdev);
0150 if (ret)
0151 platform_device_put(pdev);
0152
0153 return ret;
0154 }
0155
0156 static int __init exynos5_clk_probe(struct platform_device *pdev)
0157 {
0158 struct device_node *np;
0159 const char *name;
0160 int i;
0161
0162 for_each_compatible_node(np, NULL, "samsung,exynos4210-pd") {
0163 if (of_property_read_string(np, "label", &name) < 0)
0164 continue;
0165 for (i = 0; i < nr_cmus; i++)
0166 if (strcmp(cmu[i]->pd_name, name) == 0)
0167 exynos5_clk_register_subcmu(&pdev->dev,
0168 cmu[i], np);
0169 }
0170 return 0;
0171 }
0172
0173 static const struct of_device_id exynos5_clk_of_match[] = {
0174 { .compatible = "samsung,exynos5250-clock", },
0175 { .compatible = "samsung,exynos5420-clock", },
0176 { .compatible = "samsung,exynos5800-clock", },
0177 { },
0178 };
0179
0180 static struct platform_driver exynos5_clk_driver __refdata = {
0181 .driver = {
0182 .name = "exynos5-clock",
0183 .of_match_table = exynos5_clk_of_match,
0184 .suppress_bind_attrs = true,
0185 },
0186 .probe = exynos5_clk_probe,
0187 };
0188
0189 static int __init exynos5_clk_drv_init(void)
0190 {
0191 platform_driver_register(&exynos5_clk_driver);
0192 platform_driver_register(&exynos5_subcmu_driver);
0193 return 0;
0194 }
0195 core_initcall(exynos5_clk_drv_init);