1 // SPDX-License-Identifier: GPL-2.0
3 * Renesas R-Car USB2.0 clock selector
5 * Copyright (C) 2017 Renesas Electronics Corp.
7 * Based on renesas-cpg-mssr.c
9 * Copyright (C) 2015 Glider bvba
12 #include <linux/clk.h>
13 #include <linux/clk-provider.h>
14 #include <linux/device.h>
15 #include <linux/init.h>
17 #include <linux/module.h>
18 #include <linux/of_device.h>
19 #include <linux/platform_device.h>
21 #include <linux/pm_runtime.h>
22 #include <linux/slab.h>
24 #define USB20_CLKSET0 0x00
25 #define CLKSET0_INTCLK_EN BIT(11)
26 #define CLKSET0_PRIVATE BIT(0)
27 #define CLKSET0_EXTAL_ONLY (CLKSET0_INTCLK_EN | CLKSET0_PRIVATE)
29 struct usb2_clock_sel_priv
{
35 #define to_priv(_hw) container_of(_hw, struct usb2_clock_sel_priv, hw)
37 static void usb2_clock_sel_enable_extal_only(struct usb2_clock_sel_priv
*priv
)
39 u16 val
= readw(priv
->base
+ USB20_CLKSET0
);
41 pr_debug("%s: enter %d %d %x\n", __func__
,
42 priv
->extal
, priv
->xtal
, val
);
44 if (priv
->extal
&& !priv
->xtal
&& val
!= CLKSET0_EXTAL_ONLY
)
45 writew(CLKSET0_EXTAL_ONLY
, priv
->base
+ USB20_CLKSET0
);
48 static void usb2_clock_sel_disable_extal_only(struct usb2_clock_sel_priv
*priv
)
50 if (priv
->extal
&& !priv
->xtal
)
51 writew(CLKSET0_PRIVATE
, priv
->base
+ USB20_CLKSET0
);
54 static int usb2_clock_sel_enable(struct clk_hw
*hw
)
56 usb2_clock_sel_enable_extal_only(to_priv(hw
));
61 static void usb2_clock_sel_disable(struct clk_hw
*hw
)
63 usb2_clock_sel_disable_extal_only(to_priv(hw
));
67 * This module seems a mux, but this driver assumes a gate because
68 * ehci/ohci platform drivers don't support clk_set_parent() for now.
69 * If this driver acts as a gate, ehci/ohci-platform drivers don't need
72 static const struct clk_ops usb2_clock_sel_clock_ops
= {
73 .enable
= usb2_clock_sel_enable
,
74 .disable
= usb2_clock_sel_disable
,
77 static const struct of_device_id rcar_usb2_clock_sel_match
[] = {
78 { .compatible
= "renesas,rcar-gen3-usb2-clock-sel" },
82 static int rcar_usb2_clock_sel_suspend(struct device
*dev
)
84 struct usb2_clock_sel_priv
*priv
= dev_get_drvdata(dev
);
86 usb2_clock_sel_disable_extal_only(priv
);
92 static int rcar_usb2_clock_sel_resume(struct device
*dev
)
94 struct usb2_clock_sel_priv
*priv
= dev_get_drvdata(dev
);
96 pm_runtime_get_sync(dev
);
97 usb2_clock_sel_enable_extal_only(priv
);
102 static int rcar_usb2_clock_sel_remove(struct platform_device
*pdev
)
104 struct device
*dev
= &pdev
->dev
;
105 struct usb2_clock_sel_priv
*priv
= platform_get_drvdata(pdev
);
107 of_clk_del_provider(dev
->of_node
);
108 clk_hw_unregister(&priv
->hw
);
110 pm_runtime_disable(dev
);
115 static int rcar_usb2_clock_sel_probe(struct platform_device
*pdev
)
117 struct device
*dev
= &pdev
->dev
;
118 struct device_node
*np
= dev
->of_node
;
119 struct usb2_clock_sel_priv
*priv
;
121 struct clk_init_data init
;
123 priv
= devm_kzalloc(dev
, sizeof(*priv
), GFP_KERNEL
);
127 priv
->base
= devm_platform_ioremap_resource(pdev
, 0);
128 if (IS_ERR(priv
->base
))
129 return PTR_ERR(priv
->base
);
131 pm_runtime_enable(dev
);
132 pm_runtime_get_sync(dev
);
134 clk
= devm_clk_get(dev
, "usb_extal");
135 if (!IS_ERR(clk
) && !clk_prepare_enable(clk
)) {
136 priv
->extal
= !!clk_get_rate(clk
);
137 clk_disable_unprepare(clk
);
139 clk
= devm_clk_get(dev
, "usb_xtal");
140 if (!IS_ERR(clk
) && !clk_prepare_enable(clk
)) {
141 priv
->xtal
= !!clk_get_rate(clk
);
142 clk_disable_unprepare(clk
);
145 if (!priv
->extal
&& !priv
->xtal
) {
146 dev_err(dev
, "This driver needs usb_extal or usb_xtal\n");
150 platform_set_drvdata(pdev
, priv
);
151 dev_set_drvdata(dev
, priv
);
153 init
.name
= "rcar_usb2_clock_sel";
154 init
.ops
= &usb2_clock_sel_clock_ops
;
156 init
.parent_names
= NULL
;
157 init
.num_parents
= 0;
158 priv
->hw
.init
= &init
;
160 clk
= clk_register(NULL
, &priv
->hw
);
164 return of_clk_add_hw_provider(np
, of_clk_hw_simple_get
, &priv
->hw
);
167 static const struct dev_pm_ops rcar_usb2_clock_sel_pm_ops
= {
168 .suspend
= rcar_usb2_clock_sel_suspend
,
169 .resume
= rcar_usb2_clock_sel_resume
,
172 static struct platform_driver rcar_usb2_clock_sel_driver
= {
174 .name
= "rcar-usb2-clock-sel",
175 .of_match_table
= rcar_usb2_clock_sel_match
,
176 .pm
= &rcar_usb2_clock_sel_pm_ops
,
178 .probe
= rcar_usb2_clock_sel_probe
,
179 .remove
= rcar_usb2_clock_sel_remove
,
181 builtin_platform_driver(rcar_usb2_clock_sel_driver
);
183 MODULE_DESCRIPTION("Renesas R-Car USB2 clock selector Driver");
184 MODULE_LICENSE("GPL v2");