1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * StarFive JH7110 Always-On Clock Driver |
4 | * |
5 | * Copyright (C) 2022 Emil Renner Berthing <kernel@esmil.dk> |
6 | * Copyright (C) 2022 StarFive Technology Co., Ltd. |
7 | */ |
8 | |
9 | #include <linux/clk-provider.h> |
10 | #include <linux/io.h> |
11 | #include <linux/platform_device.h> |
12 | |
13 | #include <dt-bindings/clock/starfive,jh7110-crg.h> |
14 | |
15 | #include "clk-starfive-jh7110.h" |
16 | |
17 | /* external clocks */ |
18 | #define JH7110_AONCLK_OSC (JH7110_AONCLK_END + 0) |
19 | #define JH7110_AONCLK_GMAC0_RMII_REFIN (JH7110_AONCLK_END + 1) |
20 | #define JH7110_AONCLK_GMAC0_RGMII_RXIN (JH7110_AONCLK_END + 2) |
21 | #define JH7110_AONCLK_STG_AXIAHB (JH7110_AONCLK_END + 3) |
22 | #define JH7110_AONCLK_APB_BUS (JH7110_AONCLK_END + 4) |
23 | #define JH7110_AONCLK_GMAC0_GTXCLK (JH7110_AONCLK_END + 5) |
24 | #define JH7110_AONCLK_RTC_OSC (JH7110_AONCLK_END + 6) |
25 | |
26 | static const struct jh71x0_clk_data jh7110_aonclk_data[] = { |
27 | /* source */ |
28 | JH71X0__DIV(JH7110_AONCLK_OSC_DIV4, "osc_div4" , 4, JH7110_AONCLK_OSC), |
29 | JH71X0__MUX(JH7110_AONCLK_APB_FUNC, "apb_func" , 0, 2, |
30 | JH7110_AONCLK_OSC_DIV4, |
31 | JH7110_AONCLK_OSC), |
32 | /* gmac0 */ |
33 | JH71X0_GATE(JH7110_AONCLK_GMAC0_AHB, "gmac0_ahb" , 0, JH7110_AONCLK_STG_AXIAHB), |
34 | JH71X0_GATE(JH7110_AONCLK_GMAC0_AXI, "gmac0_axi" , 0, JH7110_AONCLK_STG_AXIAHB), |
35 | JH71X0__DIV(JH7110_AONCLK_GMAC0_RMII_RTX, "gmac0_rmii_rtx" , 30, |
36 | JH7110_AONCLK_GMAC0_RMII_REFIN), |
37 | JH71X0_GMUX(JH7110_AONCLK_GMAC0_TX, "gmac0_tx" , |
38 | CLK_SET_RATE_PARENT | CLK_SET_RATE_NO_REPARENT, 2, |
39 | JH7110_AONCLK_GMAC0_GTXCLK, |
40 | JH7110_AONCLK_GMAC0_RMII_RTX), |
41 | JH71X0__INV(JH7110_AONCLK_GMAC0_TX_INV, "gmac0_tx_inv" , JH7110_AONCLK_GMAC0_TX), |
42 | JH71X0__MUX(JH7110_AONCLK_GMAC0_RX, "gmac0_rx" , 0, 2, |
43 | JH7110_AONCLK_GMAC0_RGMII_RXIN, |
44 | JH7110_AONCLK_GMAC0_RMII_RTX), |
45 | JH71X0__INV(JH7110_AONCLK_GMAC0_RX_INV, "gmac0_rx_inv" , JH7110_AONCLK_GMAC0_RX), |
46 | /* otpc */ |
47 | JH71X0_GATE(JH7110_AONCLK_OTPC_APB, "otpc_apb" , 0, JH7110_AONCLK_APB_BUS), |
48 | /* rtc */ |
49 | JH71X0_GATE(JH7110_AONCLK_RTC_APB, "rtc_apb" , 0, JH7110_AONCLK_APB_BUS), |
50 | JH71X0__DIV(JH7110_AONCLK_RTC_INTERNAL, "rtc_internal" , 1022, JH7110_AONCLK_OSC), |
51 | JH71X0__MUX(JH7110_AONCLK_RTC_32K, "rtc_32k" , 0, 2, |
52 | JH7110_AONCLK_RTC_OSC, |
53 | JH7110_AONCLK_RTC_INTERNAL), |
54 | JH71X0_GATE(JH7110_AONCLK_RTC_CAL, "rtc_cal" , 0, JH7110_AONCLK_OSC), |
55 | }; |
56 | |
57 | static struct clk_hw *jh7110_aonclk_get(struct of_phandle_args *clkspec, void *data) |
58 | { |
59 | struct jh71x0_clk_priv *priv = data; |
60 | unsigned int idx = clkspec->args[0]; |
61 | |
62 | if (idx < JH7110_AONCLK_END) |
63 | return &priv->reg[idx].hw; |
64 | |
65 | return ERR_PTR(error: -EINVAL); |
66 | } |
67 | |
68 | static int jh7110_aoncrg_probe(struct platform_device *pdev) |
69 | { |
70 | struct jh71x0_clk_priv *priv; |
71 | unsigned int idx; |
72 | int ret; |
73 | |
74 | priv = devm_kzalloc(dev: &pdev->dev, |
75 | struct_size(priv, reg, JH7110_AONCLK_END), |
76 | GFP_KERNEL); |
77 | if (!priv) |
78 | return -ENOMEM; |
79 | |
80 | spin_lock_init(&priv->rmw_lock); |
81 | priv->dev = &pdev->dev; |
82 | priv->base = devm_platform_ioremap_resource(pdev, index: 0); |
83 | if (IS_ERR(ptr: priv->base)) |
84 | return PTR_ERR(ptr: priv->base); |
85 | |
86 | for (idx = 0; idx < JH7110_AONCLK_END; idx++) { |
87 | u32 max = jh7110_aonclk_data[idx].max; |
88 | struct clk_parent_data parents[4] = {}; |
89 | struct clk_init_data init = { |
90 | .name = jh7110_aonclk_data[idx].name, |
91 | .ops = starfive_jh71x0_clk_ops(max), |
92 | .parent_data = parents, |
93 | .num_parents = |
94 | ((max & JH71X0_CLK_MUX_MASK) >> JH71X0_CLK_MUX_SHIFT) + 1, |
95 | .flags = jh7110_aonclk_data[idx].flags, |
96 | }; |
97 | struct jh71x0_clk *clk = &priv->reg[idx]; |
98 | unsigned int i; |
99 | |
100 | for (i = 0; i < init.num_parents; i++) { |
101 | unsigned int pidx = jh7110_aonclk_data[idx].parents[i]; |
102 | |
103 | if (pidx < JH7110_AONCLK_END) |
104 | parents[i].hw = &priv->reg[pidx].hw; |
105 | else if (pidx == JH7110_AONCLK_OSC) |
106 | parents[i].fw_name = "osc" ; |
107 | else if (pidx == JH7110_AONCLK_GMAC0_RMII_REFIN) |
108 | parents[i].fw_name = "gmac0_rmii_refin" ; |
109 | else if (pidx == JH7110_AONCLK_GMAC0_RGMII_RXIN) |
110 | parents[i].fw_name = "gmac0_rgmii_rxin" ; |
111 | else if (pidx == JH7110_AONCLK_STG_AXIAHB) |
112 | parents[i].fw_name = "stg_axiahb" ; |
113 | else if (pidx == JH7110_AONCLK_APB_BUS) |
114 | parents[i].fw_name = "apb_bus" ; |
115 | else if (pidx == JH7110_AONCLK_GMAC0_GTXCLK) |
116 | parents[i].fw_name = "gmac0_gtxclk" ; |
117 | else if (pidx == JH7110_AONCLK_RTC_OSC) |
118 | parents[i].fw_name = "rtc_osc" ; |
119 | } |
120 | |
121 | clk->hw.init = &init; |
122 | clk->idx = idx; |
123 | clk->max_div = max & JH71X0_CLK_DIV_MASK; |
124 | |
125 | ret = devm_clk_hw_register(dev: &pdev->dev, hw: &clk->hw); |
126 | if (ret) |
127 | return ret; |
128 | } |
129 | |
130 | ret = devm_of_clk_add_hw_provider(dev: &pdev->dev, get: jh7110_aonclk_get, data: priv); |
131 | if (ret) |
132 | return ret; |
133 | |
134 | return jh7110_reset_controller_register(priv, adev_name: "rst-aon" , adev_id: 1); |
135 | } |
136 | |
137 | static const struct of_device_id jh7110_aoncrg_match[] = { |
138 | { .compatible = "starfive,jh7110-aoncrg" }, |
139 | { /* sentinel */ } |
140 | }; |
141 | MODULE_DEVICE_TABLE(of, jh7110_aoncrg_match); |
142 | |
143 | static struct platform_driver jh7110_aoncrg_driver = { |
144 | .probe = jh7110_aoncrg_probe, |
145 | .driver = { |
146 | .name = "clk-starfive-jh7110-aon" , |
147 | .of_match_table = jh7110_aoncrg_match, |
148 | }, |
149 | }; |
150 | module_platform_driver(jh7110_aoncrg_driver); |
151 | |
152 | MODULE_AUTHOR("Emil Renner Berthing" ); |
153 | MODULE_DESCRIPTION("StarFive JH7110 always-on clock driver" ); |
154 | MODULE_LICENSE("GPL" ); |
155 | |