0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/slab.h>
0010 #include <linux/clk.h>
0011 #include <linux/clk-provider.h>
0012 #include <linux/module.h>
0013 #include <linux/io.h>
0014 #include <linux/of.h>
0015 #include <linux/of_address.h>
0016 #include <linux/of_device.h>
0017 #include <linux/platform_device.h>
0018 #include <linux/pm.h>
0019
0020 #define EXYNOS_CLKOUT_NR_CLKS 1
0021 #define EXYNOS_CLKOUT_PARENTS 32
0022
0023 #define EXYNOS_PMU_DEBUG_REG 0xa00
0024 #define EXYNOS_CLKOUT_DISABLE_SHIFT 0
0025 #define EXYNOS_CLKOUT_MUX_SHIFT 8
0026 #define EXYNOS4_CLKOUT_MUX_MASK 0xf
0027 #define EXYNOS5_CLKOUT_MUX_MASK 0x1f
0028
0029 struct exynos_clkout {
0030 struct clk_gate gate;
0031 struct clk_mux mux;
0032 spinlock_t slock;
0033 void __iomem *reg;
0034 struct device_node *np;
0035 u32 pmu_debug_save;
0036 struct clk_hw_onecell_data data;
0037 };
0038
0039 struct exynos_clkout_variant {
0040 u32 mux_mask;
0041 };
0042
0043 static const struct exynos_clkout_variant exynos_clkout_exynos4 = {
0044 .mux_mask = EXYNOS4_CLKOUT_MUX_MASK,
0045 };
0046
0047 static const struct exynos_clkout_variant exynos_clkout_exynos5 = {
0048 .mux_mask = EXYNOS5_CLKOUT_MUX_MASK,
0049 };
0050
0051 static const struct of_device_id exynos_clkout_ids[] = {
0052 {
0053 .compatible = "samsung,exynos3250-pmu",
0054 .data = &exynos_clkout_exynos4,
0055 }, {
0056 .compatible = "samsung,exynos4210-pmu",
0057 .data = &exynos_clkout_exynos4,
0058 }, {
0059 .compatible = "samsung,exynos4412-pmu",
0060 .data = &exynos_clkout_exynos4,
0061 }, {
0062 .compatible = "samsung,exynos5250-pmu",
0063 .data = &exynos_clkout_exynos5,
0064 }, {
0065 .compatible = "samsung,exynos5410-pmu",
0066 .data = &exynos_clkout_exynos5,
0067 }, {
0068 .compatible = "samsung,exynos5420-pmu",
0069 .data = &exynos_clkout_exynos5,
0070 }, {
0071 .compatible = "samsung,exynos5433-pmu",
0072 .data = &exynos_clkout_exynos5,
0073 }, { }
0074 };
0075 MODULE_DEVICE_TABLE(of, exynos_clkout_ids);
0076
0077
0078
0079
0080
0081 static int exynos_clkout_match_parent_dev(struct device *dev, u32 *mux_mask)
0082 {
0083 const struct exynos_clkout_variant *variant;
0084 const struct of_device_id *match;
0085
0086 if (!dev->parent) {
0087 dev_err(dev, "not instantiated from MFD\n");
0088 return -EINVAL;
0089 }
0090
0091 match = of_match_device(exynos_clkout_ids, dev->parent);
0092 if (!match) {
0093 dev_err(dev, "cannot match parent device\n");
0094 return -EINVAL;
0095 }
0096 variant = match->data;
0097
0098 *mux_mask = variant->mux_mask;
0099
0100 return 0;
0101 }
0102
0103 static int exynos_clkout_probe(struct platform_device *pdev)
0104 {
0105 const char *parent_names[EXYNOS_CLKOUT_PARENTS];
0106 struct clk *parents[EXYNOS_CLKOUT_PARENTS];
0107 struct exynos_clkout *clkout;
0108 int parent_count, ret, i;
0109 u32 mux_mask;
0110
0111 clkout = devm_kzalloc(&pdev->dev,
0112 struct_size(clkout, data.hws, EXYNOS_CLKOUT_NR_CLKS),
0113 GFP_KERNEL);
0114 if (!clkout)
0115 return -ENOMEM;
0116
0117 ret = exynos_clkout_match_parent_dev(&pdev->dev, &mux_mask);
0118 if (ret)
0119 return ret;
0120
0121 clkout->np = pdev->dev.of_node;
0122 if (!clkout->np) {
0123
0124
0125
0126
0127 clkout->np = pdev->dev.parent->of_node;
0128 }
0129
0130 platform_set_drvdata(pdev, clkout);
0131
0132 spin_lock_init(&clkout->slock);
0133
0134 parent_count = 0;
0135 for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) {
0136 char name[] = "clkoutXX";
0137
0138 snprintf(name, sizeof(name), "clkout%d", i);
0139 parents[i] = of_clk_get_by_name(clkout->np, name);
0140 if (IS_ERR(parents[i])) {
0141 parent_names[i] = "none";
0142 continue;
0143 }
0144
0145 parent_names[i] = __clk_get_name(parents[i]);
0146 parent_count = i + 1;
0147 }
0148
0149 if (!parent_count)
0150 return -EINVAL;
0151
0152 clkout->reg = of_iomap(clkout->np, 0);
0153 if (!clkout->reg) {
0154 ret = -ENODEV;
0155 goto clks_put;
0156 }
0157
0158 clkout->gate.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG;
0159 clkout->gate.bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT;
0160 clkout->gate.flags = CLK_GATE_SET_TO_DISABLE;
0161 clkout->gate.lock = &clkout->slock;
0162
0163 clkout->mux.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG;
0164 clkout->mux.mask = mux_mask;
0165 clkout->mux.shift = EXYNOS_CLKOUT_MUX_SHIFT;
0166 clkout->mux.lock = &clkout->slock;
0167
0168 clkout->data.hws[0] = clk_hw_register_composite(NULL, "clkout",
0169 parent_names, parent_count, &clkout->mux.hw,
0170 &clk_mux_ops, NULL, NULL, &clkout->gate.hw,
0171 &clk_gate_ops, CLK_SET_RATE_PARENT
0172 | CLK_SET_RATE_NO_REPARENT);
0173 if (IS_ERR(clkout->data.hws[0])) {
0174 ret = PTR_ERR(clkout->data.hws[0]);
0175 goto err_unmap;
0176 }
0177
0178 clkout->data.num = EXYNOS_CLKOUT_NR_CLKS;
0179 ret = of_clk_add_hw_provider(clkout->np, of_clk_hw_onecell_get, &clkout->data);
0180 if (ret)
0181 goto err_clk_unreg;
0182
0183 return 0;
0184
0185 err_clk_unreg:
0186 clk_hw_unregister(clkout->data.hws[0]);
0187 err_unmap:
0188 iounmap(clkout->reg);
0189 clks_put:
0190 for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i)
0191 if (!IS_ERR(parents[i]))
0192 clk_put(parents[i]);
0193
0194 dev_err(&pdev->dev, "failed to register clkout clock\n");
0195
0196 return ret;
0197 }
0198
0199 static int exynos_clkout_remove(struct platform_device *pdev)
0200 {
0201 struct exynos_clkout *clkout = platform_get_drvdata(pdev);
0202
0203 of_clk_del_provider(clkout->np);
0204 clk_hw_unregister(clkout->data.hws[0]);
0205 iounmap(clkout->reg);
0206
0207 return 0;
0208 }
0209
0210 static int __maybe_unused exynos_clkout_suspend(struct device *dev)
0211 {
0212 struct exynos_clkout *clkout = dev_get_drvdata(dev);
0213
0214 clkout->pmu_debug_save = readl(clkout->reg + EXYNOS_PMU_DEBUG_REG);
0215
0216 return 0;
0217 }
0218
0219 static int __maybe_unused exynos_clkout_resume(struct device *dev)
0220 {
0221 struct exynos_clkout *clkout = dev_get_drvdata(dev);
0222
0223 writel(clkout->pmu_debug_save, clkout->reg + EXYNOS_PMU_DEBUG_REG);
0224
0225 return 0;
0226 }
0227
0228 static SIMPLE_DEV_PM_OPS(exynos_clkout_pm_ops, exynos_clkout_suspend,
0229 exynos_clkout_resume);
0230
0231 static struct platform_driver exynos_clkout_driver = {
0232 .driver = {
0233 .name = "exynos-clkout",
0234 .of_match_table = exynos_clkout_ids,
0235 .pm = &exynos_clkout_pm_ops,
0236 },
0237 .probe = exynos_clkout_probe,
0238 .remove = exynos_clkout_remove,
0239 };
0240 module_platform_driver(exynos_clkout_driver);
0241
0242 MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>");
0243 MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>");
0244 MODULE_DESCRIPTION("Samsung Exynos clock output driver");
0245 MODULE_LICENSE("GPL");