1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * Copyright 2013 Emilio López |
4 | * Emilio López <emilio@elopez.com.ar> |
5 | * |
6 | * Copyright 2013 Chen-Yu Tsai |
7 | * Chen-Yu Tsai <wens@csie.org> |
8 | */ |
9 | |
10 | #include <linux/clk-provider.h> |
11 | #include <linux/io.h> |
12 | #include <linux/of.h> |
13 | #include <linux/of_address.h> |
14 | #include <linux/slab.h> |
15 | |
16 | static DEFINE_SPINLOCK(gmac_lock); |
17 | |
18 | /** |
19 | * sun7i_a20_gmac_clk_setup - Setup function for A20/A31 GMAC clock module |
20 | * |
21 | * This clock looks something like this |
22 | * ________________________ |
23 | * MII TX clock from PHY >-----|___________ _________|----> to GMAC core |
24 | * GMAC Int. RGMII TX clk >----|___________\__/__gate---|----> to PHY |
25 | * Ext. 125MHz RGMII TX clk >--|__divider__/ | |
26 | * |________________________| |
27 | * |
28 | * The external 125 MHz reference is optional, i.e. GMAC can use its |
29 | * internal TX clock just fine. The A31 GMAC clock module does not have |
30 | * the divider controls for the external reference. |
31 | * |
32 | * To keep it simple, let the GMAC use either the MII TX clock for MII mode, |
33 | * and its internal TX clock for GMII and RGMII modes. The GMAC driver should |
34 | * select the appropriate source and gate/ungate the output to the PHY. |
35 | * |
36 | * Only the GMAC should use this clock. Altering the clock so that it doesn't |
37 | * match the GMAC's operation parameters will result in the GMAC not being |
38 | * able to send traffic out. The GMAC driver should set the clock rate and |
39 | * enable/disable this clock to configure the required state. The clock |
40 | * driver then responds by auto-reparenting the clock. |
41 | */ |
42 | |
43 | #define SUN7I_A20_GMAC_GPIT 2 |
44 | #define SUN7I_A20_GMAC_MASK 0x3 |
45 | #define SUN7I_A20_GMAC_PARENTS 2 |
46 | |
47 | static u32 sun7i_a20_gmac_mux_table[SUN7I_A20_GMAC_PARENTS] = { |
48 | 0x00, /* Select mii_phy_tx_clk */ |
49 | 0x02, /* Select gmac_int_tx_clk */ |
50 | }; |
51 | |
52 | static void __init sun7i_a20_gmac_clk_setup(struct device_node *node) |
53 | { |
54 | struct clk *clk; |
55 | struct clk_mux *mux; |
56 | struct clk_gate *gate; |
57 | const char *clk_name = node->name; |
58 | const char *parents[SUN7I_A20_GMAC_PARENTS]; |
59 | void __iomem *reg; |
60 | |
61 | if (of_property_read_string(np: node, propname: "clock-output-names" , out_string: &clk_name)) |
62 | return; |
63 | |
64 | /* allocate mux and gate clock structs */ |
65 | mux = kzalloc(size: sizeof(struct clk_mux), GFP_KERNEL); |
66 | if (!mux) |
67 | return; |
68 | |
69 | gate = kzalloc(size: sizeof(struct clk_gate), GFP_KERNEL); |
70 | if (!gate) |
71 | goto free_mux; |
72 | |
73 | /* gmac clock requires exactly 2 parents */ |
74 | if (of_clk_parent_fill(np: node, parents, size: 2) != 2) |
75 | goto free_gate; |
76 | |
77 | reg = of_iomap(node, index: 0); |
78 | if (!reg) |
79 | goto free_gate; |
80 | |
81 | /* set up gate and fixed rate properties */ |
82 | gate->reg = reg; |
83 | gate->bit_idx = SUN7I_A20_GMAC_GPIT; |
84 | gate->lock = &gmac_lock; |
85 | mux->reg = reg; |
86 | mux->mask = SUN7I_A20_GMAC_MASK; |
87 | mux->table = sun7i_a20_gmac_mux_table; |
88 | mux->lock = &gmac_lock; |
89 | |
90 | clk = clk_register_composite(NULL, name: clk_name, |
91 | parent_names: parents, SUN7I_A20_GMAC_PARENTS, |
92 | mux_hw: &mux->hw, mux_ops: &clk_mux_ops, |
93 | NULL, NULL, |
94 | gate_hw: &gate->hw, gate_ops: &clk_gate_ops, |
95 | flags: 0); |
96 | |
97 | if (IS_ERR(ptr: clk)) |
98 | goto iounmap_reg; |
99 | |
100 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
101 | |
102 | return; |
103 | |
104 | iounmap_reg: |
105 | iounmap(addr: reg); |
106 | free_gate: |
107 | kfree(objp: gate); |
108 | free_mux: |
109 | kfree(objp: mux); |
110 | } |
111 | CLK_OF_DECLARE(sun7i_a20_gmac, "allwinner,sun7i-a20-gmac-clk" , |
112 | sun7i_a20_gmac_clk_setup); |
113 | |