1 /*
2  * Renesas R-Car USB2.0 clock selector
3  *
4  * Copyright (C) 2017 Renesas Electronics Corp.
5  *
6  * Based on renesas-cpg-mssr.c
7  *
8  * Copyright (C) 2015 Glider bvba
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; version 2 of the License.
13  */
14 
15 #include <linux/clk.h>
16 #include <linux/clk-provider.h>
17 #include <linux/device.h>
18 #include <linux/init.h>
19 #include <linux/module.h>
20 #include <linux/of_device.h>
21 #include <linux/platform_device.h>
22 #include <linux/pm.h>
23 #include <linux/pm_runtime.h>
24 #include <linux/slab.h>
25 
26 #define USB20_CLKSET0		0x00
27 #define CLKSET0_INTCLK_EN	BIT(11)
28 #define CLKSET0_PRIVATE		BIT(0)
29 #define CLKSET0_EXTAL_ONLY	(CLKSET0_INTCLK_EN | CLKSET0_PRIVATE)
30 
31 struct usb2_clock_sel_priv {
32 	void __iomem *base;
33 	struct clk_hw hw;
34 	bool extal;
35 	bool xtal;
36 };
37 #define to_priv(_hw)	container_of(_hw, struct usb2_clock_sel_priv, hw)
38 
39 static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv *priv)
40 {
41 	u16 val = readw(priv->base + USB20_CLKSET0);
42 
43 	pr_debug("%s: enter %d %d %x\n", __func__,
44 		 priv->extal, priv->xtal, val);
45 
46 	if (priv->extal && !priv->xtal && val != CLKSET0_EXTAL_ONLY)
47 		writew(CLKSET0_EXTAL_ONLY, priv->base + USB20_CLKSET0);
48 }
49 
50 static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv *priv)
51 {
52 	if (priv->extal && !priv->xtal)
53 		writew(CLKSET0_PRIVATE, priv->base + USB20_CLKSET0);
54 }
55 
56 static int usb2_clock_sel_enable(struct clk_hw *hw)
57 {
58 	usb2_clock_sel_enable_extal_only(to_priv(hw));
59 
60 	return 0;
61 }
62 
63 static void usb2_clock_sel_disable(struct clk_hw *hw)
64 {
65 	usb2_clock_sel_disable_extal_only(to_priv(hw));
66 }
67 
68 /*
69  * This module seems a mux, but this driver assumes a gate because
70  * ehci/ohci platform drivers don't support clk_set_parent() for now.
71  * If this driver acts as a gate, ehci/ohci-platform drivers don't need
72  * any modification.
73  */
74 static const struct clk_ops usb2_clock_sel_clock_ops = {
75 	.enable = usb2_clock_sel_enable,
76 	.disable = usb2_clock_sel_disable,
77 };
78 
79 static const struct of_device_id rcar_usb2_clock_sel_match[] = {
80 	{ .compatible = "renesas,rcar-gen3-usb2-clock-sel" },
81 	{ }
82 };
83 
84 static int rcar_usb2_clock_sel_suspend(struct device *dev)
85 {
86 	struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev);
87 
88 	usb2_clock_sel_disable_extal_only(priv);
89 	pm_runtime_put(dev);
90 
91 	return 0;
92 }
93 
94 static int rcar_usb2_clock_sel_resume(struct device *dev)
95 {
96 	struct usb2_clock_sel_priv *priv = dev_get_drvdata(dev);
97 
98 	pm_runtime_get_sync(dev);
99 	usb2_clock_sel_enable_extal_only(priv);
100 
101 	return 0;
102 }
103 
104 static int rcar_usb2_clock_sel_remove(struct platform_device *pdev)
105 {
106 	struct device *dev = &pdev->dev;
107 	struct usb2_clock_sel_priv *priv = platform_get_drvdata(pdev);
108 
109 	of_clk_del_provider(dev->of_node);
110 	clk_hw_unregister(&priv->hw);
111 	pm_runtime_put(dev);
112 	pm_runtime_disable(dev);
113 
114 	return 0;
115 }
116 
117 static int rcar_usb2_clock_sel_probe(struct platform_device *pdev)
118 {
119 	struct device *dev = &pdev->dev;
120 	struct device_node *np = dev->of_node;
121 	struct usb2_clock_sel_priv *priv;
122 	struct resource *res;
123 	struct clk *clk;
124 	struct clk_init_data init;
125 
126 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
127 	if (!priv)
128 		return -ENOMEM;
129 
130 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
131 	priv->base = devm_ioremap_resource(dev, res);
132 	if (IS_ERR(priv->base))
133 		return PTR_ERR(priv->base);
134 
135 	pm_runtime_enable(dev);
136 	pm_runtime_get_sync(dev);
137 
138 	clk = devm_clk_get(dev, "usb_extal");
139 	if (!IS_ERR(clk) && !clk_prepare_enable(clk)) {
140 		priv->extal = !!clk_get_rate(clk);
141 		clk_disable_unprepare(clk);
142 	}
143 	clk = devm_clk_get(dev, "usb_xtal");
144 	if (!IS_ERR(clk) && !clk_prepare_enable(clk)) {
145 		priv->xtal = !!clk_get_rate(clk);
146 		clk_disable_unprepare(clk);
147 	}
148 
149 	if (!priv->extal && !priv->xtal) {
150 		dev_err(dev, "This driver needs usb_extal or usb_xtal\n");
151 		return -ENOENT;
152 	}
153 
154 	platform_set_drvdata(pdev, priv);
155 	dev_set_drvdata(dev, priv);
156 
157 	init.name = "rcar_usb2_clock_sel";
158 	init.ops = &usb2_clock_sel_clock_ops;
159 	init.flags = 0;
160 	init.parent_names = NULL;
161 	init.num_parents = 0;
162 	priv->hw.init = &init;
163 
164 	clk = clk_register(NULL, &priv->hw);
165 	if (IS_ERR(clk))
166 		return PTR_ERR(clk);
167 
168 	return of_clk_add_hw_provider(np, of_clk_hw_simple_get, &priv->hw);
169 }
170 
171 static const struct dev_pm_ops rcar_usb2_clock_sel_pm_ops = {
172 	.suspend	= rcar_usb2_clock_sel_suspend,
173 	.resume		= rcar_usb2_clock_sel_resume,
174 };
175 
176 static struct platform_driver rcar_usb2_clock_sel_driver = {
177 	.driver		= {
178 		.name	= "rcar-usb2-clock-sel",
179 		.of_match_table = rcar_usb2_clock_sel_match,
180 		.pm	= &rcar_usb2_clock_sel_pm_ops,
181 	},
182 	.probe		= rcar_usb2_clock_sel_probe,
183 	.remove		= rcar_usb2_clock_sel_remove,
184 };
185 builtin_platform_driver(rcar_usb2_clock_sel_driver);
186 
187 MODULE_DESCRIPTION("Renesas R-Car USB2 clock selector Driver");
188 MODULE_LICENSE("GPL v2");
189