139 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Clock driver for the ARM Integrator/IM-PD1 board4 * Copyright (C) 2012-2013 Linus Walleij5 */6#include <linux/clk-provider.h>7#include <linux/clkdev.h>8#include <linux/err.h>9#include <linux/io.h>10#include <linux/platform_device.h>11#include <linux/module.h>12#include <linux/mfd/syscon.h>13#include <linux/regmap.h>14 15#include "icst.h"16#include "clk-icst.h"17 18#define IMPD1_OSC1 0x0019#define IMPD1_OSC2 0x0420#define IMPD1_LOCK 0x0821 22/*23 * There are two VCO's on the IM-PD124 */25 26static const struct icst_params impd1_vco1_params = {27 .ref = 24000000, /* 24 MHz */28 .vco_max = ICST525_VCO_MAX_3V,29 .vco_min = ICST525_VCO_MIN,30 .vd_min = 12,31 .vd_max = 519,32 .rd_min = 3,33 .rd_max = 120,34 .s2div = icst525_s2div,35 .idx2s = icst525_idx2s,36};37 38static const struct clk_icst_desc impd1_icst1_desc = {39 .params = &impd1_vco1_params,40 .vco_offset = IMPD1_OSC1,41 .lock_offset = IMPD1_LOCK,42};43 44static const struct icst_params impd1_vco2_params = {45 .ref = 24000000, /* 24 MHz */46 .vco_max = ICST525_VCO_MAX_3V,47 .vco_min = ICST525_VCO_MIN,48 .vd_min = 12,49 .vd_max = 519,50 .rd_min = 3,51 .rd_max = 120,52 .s2div = icst525_s2div,53 .idx2s = icst525_idx2s,54};55 56static const struct clk_icst_desc impd1_icst2_desc = {57 .params = &impd1_vco2_params,58 .vco_offset = IMPD1_OSC2,59 .lock_offset = IMPD1_LOCK,60};61 62static int integrator_impd1_clk_spawn(struct device *dev,63 struct device_node *parent,64 struct device_node *np)65{66 struct regmap *map;67 struct clk *clk = ERR_PTR(-EINVAL);68 const char *name = np->name;69 const char *parent_name;70 const struct clk_icst_desc *desc;71 int ret;72 73 map = syscon_node_to_regmap(parent);74 if (IS_ERR(map)) {75 pr_err("no regmap for syscon IM-PD1 ICST clock parent\n");76 return PTR_ERR(map);77 }78 79 if (of_device_is_compatible(np, "arm,impd1-vco1")) {80 desc = &impd1_icst1_desc;81 } else if (of_device_is_compatible(np, "arm,impd1-vco2")) {82 desc = &impd1_icst2_desc;83 } else {84 dev_err(dev, "not a clock node %s\n", name);85 return -ENODEV;86 }87 88 of_property_read_string(np, "clock-output-names", &name);89 parent_name = of_clk_get_parent_name(np, 0);90 clk = icst_clk_setup(NULL, desc, name, parent_name, map,91 ICST_INTEGRATOR_IM_PD1);92 if (!IS_ERR(clk)) {93 of_clk_add_provider(np, of_clk_src_simple_get, clk);94 ret = 0;95 } else {96 dev_err(dev, "error setting up IM-PD1 ICST clock\n");97 ret = PTR_ERR(clk);98 }99 100 return ret;101}102 103static int integrator_impd1_clk_probe(struct platform_device *pdev)104{105 struct device *dev = &pdev->dev;106 struct device_node *np = dev->of_node;107 struct device_node *child;108 int ret = 0;109 110 for_each_available_child_of_node(np, child) {111 ret = integrator_impd1_clk_spawn(dev, np, child);112 if (ret) {113 of_node_put(child);114 break;115 }116 }117 118 return ret;119}120 121static const struct of_device_id impd1_syscon_match[] = {122 { .compatible = "arm,im-pd1-syscon", },123 {}124};125MODULE_DEVICE_TABLE(of, impd1_syscon_match);126 127static struct platform_driver impd1_clk_driver = {128 .driver = {129 .name = "impd1-clk",130 .of_match_table = impd1_syscon_match,131 },132 .probe = integrator_impd1_clk_probe,133};134builtin_platform_driver(impd1_clk_driver);135 136MODULE_AUTHOR("Linus Walleij <linusw@kernel.org>");137MODULE_DESCRIPTION("Arm IM-PD1 module clock driver");138MODULE_LICENSE("GPL v2");139