Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
0004  * Author: Tomasz Figa <t.figa@samsung.com>
0005  *
0006  * Clock driver for Exynos clock output
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  * Device will be instantiated as child of PMU device without its own
0079  * device node.  Therefore match compatibles against parent.
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          * pdev->dev.parent was checked by exynos_clkout_match_parent_dev()
0125          * so it is not NULL.
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");