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