2 * Renesas R-Car USB2.0 clock selector
4 * Copyright (C) 2017 Renesas Electronics Corp.
6 * Based on renesas-cpg-mssr.c
8 * Copyright (C) 2015 Glider bvba
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.
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>
23 #include <linux/pm_runtime.h>
24 #include <linux/slab.h>
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)
31 struct usb2_clock_sel_priv
{
37 #define to_priv(_hw) container_of(_hw, struct usb2_clock_sel_priv, hw)
39 static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv
*priv
)
41 u16 val
= readw(priv
->base
+ USB20_CLKSET0
);
43 pr_debug("%s: enter %d %d %x\n", __func__
,
44 priv
->extal
, priv
->xtal
, val
);
46 if (priv
->extal
&& !priv
->xtal
&& val
!= CLKSET0_EXTAL_ONLY
)
47 writew(CLKSET0_EXTAL_ONLY
, priv
->base
+ USB20_CLKSET0
);
50 static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv
*priv
)
52 if (priv
->extal
&& !priv
->xtal
)
53 writew(CLKSET0_PRIVATE
, priv
->base
+ USB20_CLKSET0
);
56 static int usb2_clock_sel_enable(struct clk_hw
*hw
)
58 usb2_clock_sel_enable_extal_only(to_priv(hw
));
63 static void usb2_clock_sel_disable(struct clk_hw
*hw
)
65 usb2_clock_sel_disable_extal_only(to_priv(hw
));
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
74 static const struct clk_ops usb2_clock_sel_clock_ops
= {
75 .enable
= usb2_clock_sel_enable
,
76 .disable
= usb2_clock_sel_disable
,
79 static const struct of_device_id rcar_usb2_clock_sel_match
[] = {
80 { .compatible
= "renesas,rcar-gen3-usb2-clock-sel" },
84 static int rcar_usb2_clock_sel_suspend(struct device
*dev
)
86 struct usb2_clock_sel_priv
*priv
= dev_get_drvdata(dev
);
88 usb2_clock_sel_disable_extal_only(priv
);
94 static int rcar_usb2_clock_sel_resume(struct device
*dev
)
96 struct usb2_clock_sel_priv
*priv
= dev_get_drvdata(dev
);
98 pm_runtime_get_sync(dev
);
99 usb2_clock_sel_enable_extal_only(priv
);
104 static int rcar_usb2_clock_sel_remove(struct platform_device
*pdev
)
106 struct device
*dev
= &pdev
->dev
;
107 struct usb2_clock_sel_priv
*priv
= platform_get_drvdata(pdev
);
109 of_clk_del_provider(dev
->of_node
);
110 clk_hw_unregister(&priv
->hw
);
112 pm_runtime_disable(dev
);
117 static int rcar_usb2_clock_sel_probe(struct platform_device
*pdev
)
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
;
124 struct clk_init_data init
;
126 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
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
);
135 pm_runtime_enable(dev
);
136 pm_runtime_get_sync(dev
);
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
);
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
);
149 if (!priv
->extal
&& !priv
->xtal
) {
150 dev_err(dev
, "This driver needs usb_extal or usb_xtal\n");
154 platform_set_drvdata(pdev
, priv
);
155 dev_set_drvdata(dev
, priv
);
157 init
.name
= "rcar_usb2_clock_sel";
158 init
.ops
= &usb2_clock_sel_clock_ops
;
160 init
.parent_names
= NULL
;
161 init
.num_parents
= 0;
162 priv
->hw
.init
= &init
;
164 clk
= clk_register(NULL
, &priv
->hw
);
168 return of_clk_add_hw_provider(np
, of_clk_hw_simple_get
, &priv
->hw
);
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
,
176 static struct platform_driver rcar_usb2_clock_sel_driver
= {
178 .name
= "rcar-usb2-clock-sel",
179 .of_match_table
= rcar_usb2_clock_sel_match
,
180 .pm
= &rcar_usb2_clock_sel_pm_ops
,
182 .probe
= rcar_usb2_clock_sel_probe
,
183 .remove
= rcar_usb2_clock_sel_remove
,
185 builtin_platform_driver(rcar_usb2_clock_sel_driver
);
187 MODULE_DESCRIPTION("Renesas R-Car USB2 clock selector Driver");
188 MODULE_LICENSE("GPL v2");