1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2016 Chen-Yu Tsai. All rights reserved. |
4 | */ |
5 | |
6 | #include <linux/clk.h> |
7 | #include <linux/clk-provider.h> |
8 | #include <linux/module.h> |
9 | #include <linux/platform_device.h> |
10 | |
11 | #include "ccu_common.h" |
12 | #include "ccu_gate.h" |
13 | #include "ccu_reset.h" |
14 | |
15 | #include "ccu-sun9i-a80-usb.h" |
16 | |
17 | static const struct clk_parent_data clk_parent_hosc[] = { |
18 | { .fw_name = "hosc" }, |
19 | }; |
20 | |
21 | static const struct clk_parent_data clk_parent_bus[] = { |
22 | { .fw_name = "bus" }, |
23 | }; |
24 | |
25 | static SUNXI_CCU_GATE_DATA(bus_hci0_clk, "bus-hci0" , clk_parent_bus, 0x0, BIT(1), 0); |
26 | static SUNXI_CCU_GATE_DATA(usb_ohci0_clk, "usb-ohci0" , clk_parent_hosc, 0x0, BIT(2), 0); |
27 | static SUNXI_CCU_GATE_DATA(bus_hci1_clk, "bus-hci1" , clk_parent_bus, 0x0, BIT(3), 0); |
28 | static SUNXI_CCU_GATE_DATA(bus_hci2_clk, "bus-hci2" , clk_parent_bus, 0x0, BIT(5), 0); |
29 | static SUNXI_CCU_GATE_DATA(usb_ohci2_clk, "usb-ohci2" , clk_parent_hosc, 0x0, BIT(6), 0); |
30 | |
31 | static SUNXI_CCU_GATE_DATA(usb0_phy_clk, "usb0-phy" , clk_parent_hosc, 0x4, BIT(1), 0); |
32 | static SUNXI_CCU_GATE_DATA(usb1_hsic_clk, "usb1-hsic" , clk_parent_hosc, 0x4, BIT(2), 0); |
33 | static SUNXI_CCU_GATE_DATA(usb1_phy_clk, "usb1-phy" , clk_parent_hosc, 0x4, BIT(3), 0); |
34 | static SUNXI_CCU_GATE_DATA(usb2_hsic_clk, "usb2-hsic" , clk_parent_hosc, 0x4, BIT(4), 0); |
35 | static SUNXI_CCU_GATE_DATA(usb2_phy_clk, "usb2-phy" , clk_parent_hosc, 0x4, BIT(5), 0); |
36 | static SUNXI_CCU_GATE_DATA(usb_hsic_clk, "usb-hsic" , clk_parent_hosc, 0x4, BIT(10), 0); |
37 | |
38 | static struct ccu_common *sun9i_a80_usb_clks[] = { |
39 | &bus_hci0_clk.common, |
40 | &usb_ohci0_clk.common, |
41 | &bus_hci1_clk.common, |
42 | &bus_hci2_clk.common, |
43 | &usb_ohci2_clk.common, |
44 | |
45 | &usb0_phy_clk.common, |
46 | &usb1_hsic_clk.common, |
47 | &usb1_phy_clk.common, |
48 | &usb2_hsic_clk.common, |
49 | &usb2_phy_clk.common, |
50 | &usb_hsic_clk.common, |
51 | }; |
52 | |
53 | static struct clk_hw_onecell_data sun9i_a80_usb_hw_clks = { |
54 | .hws = { |
55 | [CLK_BUS_HCI0] = &bus_hci0_clk.common.hw, |
56 | [CLK_USB_OHCI0] = &usb_ohci0_clk.common.hw, |
57 | [CLK_BUS_HCI1] = &bus_hci1_clk.common.hw, |
58 | [CLK_BUS_HCI2] = &bus_hci2_clk.common.hw, |
59 | [CLK_USB_OHCI2] = &usb_ohci2_clk.common.hw, |
60 | |
61 | [CLK_USB0_PHY] = &usb0_phy_clk.common.hw, |
62 | [CLK_USB1_HSIC] = &usb1_hsic_clk.common.hw, |
63 | [CLK_USB1_PHY] = &usb1_phy_clk.common.hw, |
64 | [CLK_USB2_HSIC] = &usb2_hsic_clk.common.hw, |
65 | [CLK_USB2_PHY] = &usb2_phy_clk.common.hw, |
66 | [CLK_USB_HSIC] = &usb_hsic_clk.common.hw, |
67 | }, |
68 | .num = CLK_NUMBER, |
69 | }; |
70 | |
71 | static struct ccu_reset_map sun9i_a80_usb_resets[] = { |
72 | [RST_USB0_HCI] = { 0x0, BIT(17) }, |
73 | [RST_USB1_HCI] = { 0x0, BIT(18) }, |
74 | [RST_USB2_HCI] = { 0x0, BIT(19) }, |
75 | |
76 | [RST_USB0_PHY] = { 0x4, BIT(17) }, |
77 | [RST_USB1_HSIC] = { 0x4, BIT(18) }, |
78 | [RST_USB1_PHY] = { 0x4, BIT(19) }, |
79 | [RST_USB2_HSIC] = { 0x4, BIT(20) }, |
80 | [RST_USB2_PHY] = { 0x4, BIT(21) }, |
81 | }; |
82 | |
83 | static const struct sunxi_ccu_desc sun9i_a80_usb_clk_desc = { |
84 | .ccu_clks = sun9i_a80_usb_clks, |
85 | .num_ccu_clks = ARRAY_SIZE(sun9i_a80_usb_clks), |
86 | |
87 | .hw_clks = &sun9i_a80_usb_hw_clks, |
88 | |
89 | .resets = sun9i_a80_usb_resets, |
90 | .num_resets = ARRAY_SIZE(sun9i_a80_usb_resets), |
91 | }; |
92 | |
93 | static int sun9i_a80_usb_clk_probe(struct platform_device *pdev) |
94 | { |
95 | struct clk *bus_clk; |
96 | void __iomem *reg; |
97 | int ret; |
98 | |
99 | reg = devm_platform_ioremap_resource(pdev, index: 0); |
100 | if (IS_ERR(ptr: reg)) |
101 | return PTR_ERR(ptr: reg); |
102 | |
103 | bus_clk = devm_clk_get(dev: &pdev->dev, id: "bus" ); |
104 | if (IS_ERR(ptr: bus_clk)) |
105 | return dev_err_probe(dev: &pdev->dev, err: PTR_ERR(ptr: bus_clk), |
106 | fmt: "Couldn't get bus clk\n" ); |
107 | |
108 | /* The bus clock needs to be enabled for us to access the registers */ |
109 | ret = clk_prepare_enable(clk: bus_clk); |
110 | if (ret) { |
111 | dev_err(&pdev->dev, "Couldn't enable bus clk: %d\n" , ret); |
112 | return ret; |
113 | } |
114 | |
115 | ret = devm_sunxi_ccu_probe(dev: &pdev->dev, reg, desc: &sun9i_a80_usb_clk_desc); |
116 | if (ret) |
117 | goto err_disable_clk; |
118 | |
119 | return 0; |
120 | |
121 | err_disable_clk: |
122 | clk_disable_unprepare(clk: bus_clk); |
123 | return ret; |
124 | } |
125 | |
126 | static const struct of_device_id sun9i_a80_usb_clk_ids[] = { |
127 | { .compatible = "allwinner,sun9i-a80-usb-clks" }, |
128 | { } |
129 | }; |
130 | |
131 | static struct platform_driver sun9i_a80_usb_clk_driver = { |
132 | .probe = sun9i_a80_usb_clk_probe, |
133 | .driver = { |
134 | .name = "sun9i-a80-usb-clks" , |
135 | .of_match_table = sun9i_a80_usb_clk_ids, |
136 | }, |
137 | }; |
138 | module_platform_driver(sun9i_a80_usb_clk_driver); |
139 | |
140 | MODULE_IMPORT_NS(SUNXI_CCU); |
141 | MODULE_LICENSE("GPL" ); |
142 | |