Merge tag 'locks-v4.9-1' of git://git.samba.org/jlayton/linux
[cascardo/linux.git] / drivers / clk / mvebu / armada-37xx-xtal.c
1 /*
2  * Marvell Armada 37xx SoC xtal clocks
3  *
4  * Copyright (C) 2016 Marvell
5  *
6  * Gregory CLEMENT <gregory.clement@free-electrons.com>
7  *
8  * This file is licensed under the terms of the GNU General Public
9  * License version 2.  This program is licensed "as is" without any
10  * warranty of any kind, whether express or implied.
11  */
12
13 #include <linux/clk-provider.h>
14 #include <linux/mfd/syscon.h>
15 #include <linux/platform_device.h>
16 #include <linux/regmap.h>
17
18 #define NB_GPIO1_LATCH  0xC
19 #define XTAL_MODE           BIT(31)
20
21 static int armada_3700_xtal_clock_probe(struct platform_device *pdev)
22 {
23         struct device_node *np = pdev->dev.of_node;
24         const char *xtal_name = "xtal";
25         struct device_node *parent;
26         struct regmap *regmap;
27         struct clk_hw *xtal_hw;
28         unsigned int rate;
29         u32 reg;
30         int ret;
31
32         xtal_hw = devm_kzalloc(&pdev->dev, sizeof(*xtal_hw), GFP_KERNEL);
33         if (!xtal_hw)
34                 return -ENOMEM;
35
36         platform_set_drvdata(pdev, xtal_hw);
37
38         parent = np->parent;
39         if (!parent) {
40                 dev_err(&pdev->dev, "no parent\n");
41                 return -ENODEV;
42         }
43
44         regmap = syscon_node_to_regmap(parent);
45         if (IS_ERR(regmap)) {
46                 dev_err(&pdev->dev, "cannot get regmap\n");
47                 return PTR_ERR(regmap);
48         }
49
50         ret = regmap_read(regmap, NB_GPIO1_LATCH, &reg);
51         if (ret) {
52                 dev_err(&pdev->dev, "cannot read from regmap\n");
53                 return ret;
54         }
55
56         if (reg & XTAL_MODE)
57                 rate = 40000000;
58         else
59                 rate = 25000000;
60
61         of_property_read_string_index(np, "clock-output-names", 0, &xtal_name);
62         xtal_hw = clk_hw_register_fixed_rate(NULL, xtal_name, NULL, 0, rate);
63         if (IS_ERR(xtal_hw))
64                 return PTR_ERR(xtal_hw);
65         ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, xtal_hw);
66
67         return ret;
68 }
69
70 static int armada_3700_xtal_clock_remove(struct platform_device *pdev)
71 {
72         of_clk_del_provider(pdev->dev.of_node);
73
74         return 0;
75 }
76
77 static const struct of_device_id armada_3700_xtal_clock_of_match[] = {
78         { .compatible = "marvell,armada-3700-xtal-clock", },
79         { }
80 };
81
82 static struct platform_driver armada_3700_xtal_clock_driver = {
83         .probe = armada_3700_xtal_clock_probe,
84         .remove = armada_3700_xtal_clock_remove,
85         .driver         = {
86                 .name   = "marvell-armada-3700-xtal-clock",
87                 .of_match_table = armada_3700_xtal_clock_of_match,
88         },
89 };
90
91 builtin_platform_driver(armada_3700_xtal_clock_driver);