1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * Copyright (c) 2014 Samsung Electronics Co., Ltd. 4 * Author: Tomasz Figa <t.figa@samsung.com> 5 * 6 * Clock driver for Exynos clock output 7 */ 8 9 #include <linux/slab.h> 10 #include <linux/clk.h> 11 #include <linux/clk-provider.h> 12 #include <linux/module.h> 13 #include <linux/mod_devicetable.h> 14 #include <linux/io.h> 15 #include <linux/of.h> 16 #include <linux/of_address.h> 17 #include <linux/of_device.h> 18 #include <linux/platform_device.h> 19 #include <linux/pm.h> 20 21 #define DRV_NAME "exynos-clkout" 22 23 #define EXYNOS_CLKOUT_NR_CLKS 1 24 #define EXYNOS_CLKOUT_PARENTS 32 25 26 #define EXYNOS_PMU_DEBUG_REG 0xa00 27 #define EXYNOS_CLKOUT_DISABLE_SHIFT 0 28 #define EXYNOS_CLKOUT_MUX_SHIFT 8 29 #define EXYNOS4_CLKOUT_MUX_MASK 0xf 30 #define EXYNOS5_CLKOUT_MUX_MASK 0x1f 31 32 struct exynos_clkout { 33 struct clk_gate gate; 34 struct clk_mux mux; 35 spinlock_t slock; 36 void __iomem *reg; 37 struct device_node *np; 38 u32 pmu_debug_save; 39 struct clk_hw_onecell_data data; 40 }; 41 42 struct exynos_clkout_variant { 43 u32 mux_mask; 44 }; 45 46 static const struct exynos_clkout_variant exynos_clkout_exynos4 = { 47 .mux_mask = EXYNOS4_CLKOUT_MUX_MASK, 48 }; 49 50 static const struct exynos_clkout_variant exynos_clkout_exynos5 = { 51 .mux_mask = EXYNOS5_CLKOUT_MUX_MASK, 52 }; 53 54 static const struct of_device_id exynos_clkout_ids[] = { 55 { 56 .compatible = "samsung,exynos3250-pmu", 57 .data = &exynos_clkout_exynos4, 58 }, { 59 .compatible = "samsung,exynos4210-pmu", 60 .data = &exynos_clkout_exynos4, 61 }, { 62 .compatible = "samsung,exynos4212-pmu", 63 .data = &exynos_clkout_exynos4, 64 }, { 65 .compatible = "samsung,exynos4412-pmu", 66 .data = &exynos_clkout_exynos4, 67 }, { 68 .compatible = "samsung,exynos5250-pmu", 69 .data = &exynos_clkout_exynos5, 70 }, { 71 .compatible = "samsung,exynos5410-pmu", 72 .data = &exynos_clkout_exynos5, 73 }, { 74 .compatible = "samsung,exynos5420-pmu", 75 .data = &exynos_clkout_exynos5, 76 }, { 77 .compatible = "samsung,exynos5433-pmu", 78 .data = &exynos_clkout_exynos5, 79 }, { } 80 }; 81 82 /* 83 * Device will be instantiated as child of PMU device without its own 84 * device node. Therefore match compatibles against parent. 85 */ 86 static int exynos_clkout_match_parent_dev(struct device *dev, u32 *mux_mask) 87 { 88 const struct exynos_clkout_variant *variant; 89 const struct of_device_id *match; 90 91 if (!dev->parent) { 92 dev_err(dev, "not instantiated from MFD\n"); 93 return -EINVAL; 94 } 95 96 /* 97 * 'exynos_clkout_ids' arrays is not the ids array matched by 98 * the dev->parent driver, so of_device_get_match_data() or 99 * device_get_match_data() cannot be used here. 100 */ 101 match = of_match_device(exynos_clkout_ids, dev->parent); 102 if (!match) { 103 dev_err(dev, "cannot match parent device\n"); 104 return -EINVAL; 105 } 106 variant = match->data; 107 108 *mux_mask = variant->mux_mask; 109 110 return 0; 111 } 112 113 static int exynos_clkout_probe(struct platform_device *pdev) 114 { 115 const char *parent_names[EXYNOS_CLKOUT_PARENTS]; 116 struct clk *parents[EXYNOS_CLKOUT_PARENTS]; 117 struct exynos_clkout *clkout; 118 int parent_count, ret, i; 119 u32 mux_mask; 120 121 clkout = devm_kzalloc(&pdev->dev, 122 struct_size(clkout, data.hws, EXYNOS_CLKOUT_NR_CLKS), 123 GFP_KERNEL); 124 if (!clkout) 125 return -ENOMEM; 126 127 ret = exynos_clkout_match_parent_dev(&pdev->dev, &mux_mask); 128 if (ret) 129 return ret; 130 131 clkout->np = pdev->dev.of_node; 132 if (!clkout->np) { 133 /* 134 * pdev->dev.parent was checked by exynos_clkout_match_parent_dev() 135 * so it is not NULL. 136 */ 137 clkout->np = pdev->dev.parent->of_node; 138 } 139 140 platform_set_drvdata(pdev, clkout); 141 142 spin_lock_init(&clkout->slock); 143 144 parent_count = 0; 145 for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) { 146 char name[] = "clkoutXX"; 147 148 snprintf(name, sizeof(name), "clkout%d", i); 149 parents[i] = of_clk_get_by_name(clkout->np, name); 150 if (IS_ERR(parents[i])) { 151 parent_names[i] = "none"; 152 continue; 153 } 154 155 parent_names[i] = __clk_get_name(parents[i]); 156 parent_count = i + 1; 157 } 158 159 if (!parent_count) 160 return -EINVAL; 161 162 clkout->reg = of_iomap(clkout->np, 0); 163 if (!clkout->reg) { 164 ret = -ENODEV; 165 goto clks_put; 166 } 167 168 clkout->gate.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG; 169 clkout->gate.bit_idx = EXYNOS_CLKOUT_DISABLE_SHIFT; 170 clkout->gate.flags = CLK_GATE_SET_TO_DISABLE; 171 clkout->gate.lock = &clkout->slock; 172 173 clkout->mux.reg = clkout->reg + EXYNOS_PMU_DEBUG_REG; 174 clkout->mux.mask = mux_mask; 175 clkout->mux.shift = EXYNOS_CLKOUT_MUX_SHIFT; 176 clkout->mux.lock = &clkout->slock; 177 178 clkout->data.hws[0] = clk_hw_register_composite(NULL, "clkout", 179 parent_names, parent_count, &clkout->mux.hw, 180 &clk_mux_ops, NULL, NULL, &clkout->gate.hw, 181 &clk_gate_ops, CLK_SET_RATE_PARENT 182 | CLK_SET_RATE_NO_REPARENT); 183 if (IS_ERR(clkout->data.hws[0])) { 184 ret = PTR_ERR(clkout->data.hws[0]); 185 goto err_unmap; 186 } 187 188 clkout->data.num = EXYNOS_CLKOUT_NR_CLKS; 189 ret = of_clk_add_hw_provider(clkout->np, of_clk_hw_onecell_get, &clkout->data); 190 if (ret) 191 goto err_clk_unreg; 192 193 return 0; 194 195 err_clk_unreg: 196 clk_hw_unregister(clkout->data.hws[0]); 197 err_unmap: 198 iounmap(clkout->reg); 199 clks_put: 200 for (i = 0; i < EXYNOS_CLKOUT_PARENTS; ++i) 201 if (!IS_ERR(parents[i])) 202 clk_put(parents[i]); 203 204 dev_err(&pdev->dev, "failed to register clkout clock\n"); 205 206 return ret; 207 } 208 209 static void exynos_clkout_remove(struct platform_device *pdev) 210 { 211 struct exynos_clkout *clkout = platform_get_drvdata(pdev); 212 213 of_clk_del_provider(clkout->np); 214 clk_hw_unregister(clkout->data.hws[0]); 215 iounmap(clkout->reg); 216 } 217 218 static int __maybe_unused exynos_clkout_suspend(struct device *dev) 219 { 220 struct exynos_clkout *clkout = dev_get_drvdata(dev); 221 222 clkout->pmu_debug_save = readl(clkout->reg + EXYNOS_PMU_DEBUG_REG); 223 224 return 0; 225 } 226 227 static int __maybe_unused exynos_clkout_resume(struct device *dev) 228 { 229 struct exynos_clkout *clkout = dev_get_drvdata(dev); 230 231 writel(clkout->pmu_debug_save, clkout->reg + EXYNOS_PMU_DEBUG_REG); 232 233 return 0; 234 } 235 236 static SIMPLE_DEV_PM_OPS(exynos_clkout_pm_ops, exynos_clkout_suspend, 237 exynos_clkout_resume); 238 239 static struct platform_driver exynos_clkout_driver = { 240 .driver = { 241 .name = DRV_NAME, 242 .pm = &exynos_clkout_pm_ops, 243 }, 244 .probe = exynos_clkout_probe, 245 .remove = exynos_clkout_remove, 246 }; 247 module_platform_driver(exynos_clkout_driver); 248 249 MODULE_AUTHOR("Krzysztof Kozlowski <krzk@kernel.org>"); 250 MODULE_AUTHOR("Tomasz Figa <tomasz.figa@gmail.com>"); 251 MODULE_DESCRIPTION("Samsung Exynos clock output driver"); 252 MODULE_ALIAS("platform:" DRV_NAME); 253 MODULE_LICENSE("GPL"); 254