diff options
-rw-r--r-- | drivers/clk/sunxi/Makefile | 1 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-sunxi.c | 88 | ||||
-rw-r--r-- | drivers/clk/sunxi/clk-usb.c | 190 |
3 files changed, 191 insertions, 88 deletions
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile index 3a5292e3fcf8..058f273d6154 100644 --- a/drivers/clk/sunxi/Makefile +++ b/drivers/clk/sunxi/Makefile | |||
@@ -9,6 +9,7 @@ obj-y += clk-mod0.o | |||
9 | obj-y += clk-sun8i-mbus.o | 9 | obj-y += clk-sun8i-mbus.o |
10 | obj-y += clk-sun9i-core.o | 10 | obj-y += clk-sun9i-core.o |
11 | obj-y += clk-sun9i-mmc.o | 11 | obj-y += clk-sun9i-mmc.o |
12 | obj-y += clk-usb.o | ||
12 | 13 | ||
13 | obj-$(CONFIG_MFD_SUN6I_PRCM) += \ | 14 | obj-$(CONFIG_MFD_SUN6I_PRCM) += \ |
14 | clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ | 15 | clk-sun6i-ar100.o clk-sun6i-apb0.o clk-sun6i-apb0-gates.o \ |
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 379324eb5486..b6f28ac4f9d5 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c | |||
@@ -838,59 +838,6 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, | |||
838 | 838 | ||
839 | 839 | ||
840 | /** | 840 | /** |
841 | * sunxi_gates_reset... - reset bits in leaf gate clk registers handling | ||
842 | */ | ||
843 | |||
844 | struct gates_reset_data { | ||
845 | void __iomem *reg; | ||
846 | spinlock_t *lock; | ||
847 | struct reset_controller_dev rcdev; | ||
848 | }; | ||
849 | |||
850 | static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev, | ||
851 | unsigned long id) | ||
852 | { | ||
853 | struct gates_reset_data *data = container_of(rcdev, | ||
854 | struct gates_reset_data, | ||
855 | rcdev); | ||
856 | unsigned long flags; | ||
857 | u32 reg; | ||
858 | |||
859 | spin_lock_irqsave(data->lock, flags); | ||
860 | |||
861 | reg = readl(data->reg); | ||
862 | writel(reg & ~BIT(id), data->reg); | ||
863 | |||
864 | spin_unlock_irqrestore(data->lock, flags); | ||
865 | |||
866 | return 0; | ||
867 | } | ||
868 | |||
869 | static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev, | ||
870 | unsigned long id) | ||
871 | { | ||
872 | struct gates_reset_data *data = container_of(rcdev, | ||
873 | struct gates_reset_data, | ||
874 | rcdev); | ||
875 | unsigned long flags; | ||
876 | u32 reg; | ||
877 | |||
878 | spin_lock_irqsave(data->lock, flags); | ||
879 | |||
880 | reg = readl(data->reg); | ||
881 | writel(reg | BIT(id), data->reg); | ||
882 | |||
883 | spin_unlock_irqrestore(data->lock, flags); | ||
884 | |||
885 | return 0; | ||
886 | } | ||
887 | |||
888 | static struct reset_control_ops sunxi_gates_reset_ops = { | ||
889 | .assert = sunxi_gates_reset_assert, | ||
890 | .deassert = sunxi_gates_reset_deassert, | ||
891 | }; | ||
892 | |||
893 | /** | ||
894 | * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks | 841 | * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks |
895 | */ | 842 | */ |
896 | 843 | ||
@@ -898,7 +845,6 @@ static struct reset_control_ops sunxi_gates_reset_ops = { | |||
898 | 845 | ||
899 | struct gates_data { | 846 | struct gates_data { |
900 | DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); | 847 | DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); |
901 | u32 reset_mask; | ||
902 | }; | 848 | }; |
903 | 849 | ||
904 | static const struct gates_data sun4i_axi_gates_data __initconst = { | 850 | static const struct gates_data sun4i_axi_gates_data __initconst = { |
@@ -997,26 +943,10 @@ static const struct gates_data sun8i_a23_apb2_gates_data __initconst = { | |||
997 | .mask = {0x1F0007}, | 943 | .mask = {0x1F0007}, |
998 | }; | 944 | }; |
999 | 945 | ||
1000 | static const struct gates_data sun4i_a10_usb_gates_data __initconst = { | ||
1001 | .mask = {0x1C0}, | ||
1002 | .reset_mask = 0x07, | ||
1003 | }; | ||
1004 | |||
1005 | static const struct gates_data sun5i_a13_usb_gates_data __initconst = { | ||
1006 | .mask = {0x140}, | ||
1007 | .reset_mask = 0x03, | ||
1008 | }; | ||
1009 | |||
1010 | static const struct gates_data sun6i_a31_usb_gates_data __initconst = { | ||
1011 | .mask = { BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8) }, | ||
1012 | .reset_mask = BIT(2) | BIT(1) | BIT(0), | ||
1013 | }; | ||
1014 | |||
1015 | static void __init sunxi_gates_clk_setup(struct device_node *node, | 946 | static void __init sunxi_gates_clk_setup(struct device_node *node, |
1016 | struct gates_data *data) | 947 | struct gates_data *data) |
1017 | { | 948 | { |
1018 | struct clk_onecell_data *clk_data; | 949 | struct clk_onecell_data *clk_data; |
1019 | struct gates_reset_data *reset_data; | ||
1020 | const char *clk_parent; | 950 | const char *clk_parent; |
1021 | const char *clk_name; | 951 | const char *clk_name; |
1022 | void __iomem *reg; | 952 | void __iomem *reg; |
@@ -1057,21 +987,6 @@ static void __init sunxi_gates_clk_setup(struct device_node *node, | |||
1057 | clk_data->clk_num = i; | 987 | clk_data->clk_num = i; |
1058 | 988 | ||
1059 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | 989 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); |
1060 | |||
1061 | /* Register a reset controler for gates with reset bits */ | ||
1062 | if (data->reset_mask == 0) | ||
1063 | return; | ||
1064 | |||
1065 | reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); | ||
1066 | if (!reset_data) | ||
1067 | return; | ||
1068 | |||
1069 | reset_data->reg = reg; | ||
1070 | reset_data->lock = &clk_lock; | ||
1071 | reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; | ||
1072 | reset_data->rcdev.ops = &sunxi_gates_reset_ops; | ||
1073 | reset_data->rcdev.of_node = node; | ||
1074 | reset_controller_register(&reset_data->rcdev); | ||
1075 | } | 990 | } |
1076 | 991 | ||
1077 | 992 | ||
@@ -1324,9 +1239,6 @@ static const struct of_device_id clk_gates_match[] __initconst = { | |||
1324 | {.compatible = "allwinner,sun9i-a80-apb1-gates-clk", .data = &sun9i_a80_apb1_gates_data,}, | 1239 | {.compatible = "allwinner,sun9i-a80-apb1-gates-clk", .data = &sun9i_a80_apb1_gates_data,}, |
1325 | {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, | 1240 | {.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,}, |
1326 | {.compatible = "allwinner,sun8i-a23-apb2-gates-clk", .data = &sun8i_a23_apb2_gates_data,}, | 1241 | {.compatible = "allwinner,sun8i-a23-apb2-gates-clk", .data = &sun8i_a23_apb2_gates_data,}, |
1327 | {.compatible = "allwinner,sun4i-a10-usb-clk", .data = &sun4i_a10_usb_gates_data,}, | ||
1328 | {.compatible = "allwinner,sun5i-a13-usb-clk", .data = &sun5i_a13_usb_gates_data,}, | ||
1329 | {.compatible = "allwinner,sun6i-a31-usb-clk", .data = &sun6i_a31_usb_gates_data,}, | ||
1330 | {} | 1242 | {} |
1331 | }; | 1243 | }; |
1332 | 1244 | ||
diff --git a/drivers/clk/sunxi/clk-usb.c b/drivers/clk/sunxi/clk-usb.c new file mode 100644 index 000000000000..f1dcc8fb5a7d --- /dev/null +++ b/drivers/clk/sunxi/clk-usb.c | |||
@@ -0,0 +1,190 @@ | |||
1 | /* | ||
2 | * Copyright 2013-2015 Emilio López | ||
3 | * | ||
4 | * Emilio López <emilio@elopez.com.ar> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License as published by | ||
8 | * the Free Software Foundation; either version 2 of the License, or | ||
9 | * (at your option) any later version. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/clk-provider.h> | ||
18 | #include <linux/clkdev.h> | ||
19 | #include <linux/of.h> | ||
20 | #include <linux/of_address.h> | ||
21 | #include <linux/reset-controller.h> | ||
22 | #include <linux/spinlock.h> | ||
23 | |||
24 | |||
25 | /** | ||
26 | * sunxi_usb_reset... - reset bits in usb clk registers handling | ||
27 | */ | ||
28 | |||
29 | struct usb_reset_data { | ||
30 | void __iomem *reg; | ||
31 | spinlock_t *lock; | ||
32 | struct reset_controller_dev rcdev; | ||
33 | }; | ||
34 | |||
35 | static int sunxi_usb_reset_assert(struct reset_controller_dev *rcdev, | ||
36 | unsigned long id) | ||
37 | { | ||
38 | struct usb_reset_data *data = container_of(rcdev, | ||
39 | struct usb_reset_data, | ||
40 | rcdev); | ||
41 | unsigned long flags; | ||
42 | u32 reg; | ||
43 | |||
44 | spin_lock_irqsave(data->lock, flags); | ||
45 | |||
46 | reg = readl(data->reg); | ||
47 | writel(reg & ~BIT(id), data->reg); | ||
48 | |||
49 | spin_unlock_irqrestore(data->lock, flags); | ||
50 | |||
51 | return 0; | ||
52 | } | ||
53 | |||
54 | static int sunxi_usb_reset_deassert(struct reset_controller_dev *rcdev, | ||
55 | unsigned long id) | ||
56 | { | ||
57 | struct usb_reset_data *data = container_of(rcdev, | ||
58 | struct usb_reset_data, | ||
59 | rcdev); | ||
60 | unsigned long flags; | ||
61 | u32 reg; | ||
62 | |||
63 | spin_lock_irqsave(data->lock, flags); | ||
64 | |||
65 | reg = readl(data->reg); | ||
66 | writel(reg | BIT(id), data->reg); | ||
67 | |||
68 | spin_unlock_irqrestore(data->lock, flags); | ||
69 | |||
70 | return 0; | ||
71 | } | ||
72 | |||
73 | static struct reset_control_ops sunxi_usb_reset_ops = { | ||
74 | .assert = sunxi_usb_reset_assert, | ||
75 | .deassert = sunxi_usb_reset_deassert, | ||
76 | }; | ||
77 | |||
78 | /** | ||
79 | * sunxi_usb_clk_setup() - Setup function for usb gate clocks | ||
80 | */ | ||
81 | |||
82 | #define SUNXI_USB_MAX_SIZE 32 | ||
83 | |||
84 | struct usb_clk_data { | ||
85 | u32 clk_mask; | ||
86 | u32 reset_mask; | ||
87 | }; | ||
88 | |||
89 | static void __init sunxi_usb_clk_setup(struct device_node *node, | ||
90 | const struct usb_clk_data *data, | ||
91 | spinlock_t *lock) | ||
92 | { | ||
93 | struct clk_onecell_data *clk_data; | ||
94 | struct usb_reset_data *reset_data; | ||
95 | const char *clk_parent; | ||
96 | const char *clk_name; | ||
97 | void __iomem *reg; | ||
98 | int qty; | ||
99 | int i = 0; | ||
100 | int j = 0; | ||
101 | |||
102 | reg = of_io_request_and_map(node, 0, of_node_full_name(node)); | ||
103 | if (IS_ERR(reg)) | ||
104 | return; | ||
105 | |||
106 | clk_parent = of_clk_get_parent_name(node, 0); | ||
107 | if (!clk_parent) | ||
108 | return; | ||
109 | |||
110 | /* Worst-case size approximation and memory allocation */ | ||
111 | qty = find_last_bit((unsigned long *)&data->clk_mask, | ||
112 | SUNXI_USB_MAX_SIZE); | ||
113 | |||
114 | clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); | ||
115 | if (!clk_data) | ||
116 | return; | ||
117 | |||
118 | clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); | ||
119 | if (!clk_data->clks) { | ||
120 | kfree(clk_data); | ||
121 | return; | ||
122 | } | ||
123 | |||
124 | for_each_set_bit(i, (unsigned long *)&data->clk_mask, | ||
125 | SUNXI_USB_MAX_SIZE) { | ||
126 | of_property_read_string_index(node, "clock-output-names", | ||
127 | j, &clk_name); | ||
128 | clk_data->clks[i] = clk_register_gate(NULL, clk_name, | ||
129 | clk_parent, 0, | ||
130 | reg, i, 0, lock); | ||
131 | WARN_ON(IS_ERR(clk_data->clks[i])); | ||
132 | |||
133 | j++; | ||
134 | } | ||
135 | |||
136 | /* Adjust to the real max */ | ||
137 | clk_data->clk_num = i; | ||
138 | |||
139 | of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); | ||
140 | |||
141 | /* Register a reset controller for usb with reset bits */ | ||
142 | if (data->reset_mask == 0) | ||
143 | return; | ||
144 | |||
145 | reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); | ||
146 | if (!reset_data) | ||
147 | return; | ||
148 | |||
149 | reset_data->reg = reg; | ||
150 | reset_data->lock = lock; | ||
151 | reset_data->rcdev.nr_resets = __fls(data->reset_mask) + 1; | ||
152 | reset_data->rcdev.ops = &sunxi_usb_reset_ops; | ||
153 | reset_data->rcdev.of_node = node; | ||
154 | reset_controller_register(&reset_data->rcdev); | ||
155 | } | ||
156 | |||
157 | static const struct usb_clk_data sun4i_a10_usb_clk_data __initconst = { | ||
158 | .clk_mask = BIT(8) | BIT(7) | BIT(6), | ||
159 | .reset_mask = BIT(2) | BIT(1) | BIT(0), | ||
160 | }; | ||
161 | |||
162 | static DEFINE_SPINLOCK(sun4i_a10_usb_lock); | ||
163 | |||
164 | static void __init sun4i_a10_usb_setup(struct device_node *node) | ||
165 | { | ||
166 | sunxi_usb_clk_setup(node, &sun4i_a10_usb_clk_data, &sun4i_a10_usb_lock); | ||
167 | } | ||
168 | CLK_OF_DECLARE(sun4i_a10_usb, "allwinner,sun4i-a10-usb-clk", sun4i_a10_usb_setup); | ||
169 | |||
170 | static const struct usb_clk_data sun5i_a13_usb_clk_data __initconst = { | ||
171 | .clk_mask = BIT(8) | BIT(6), | ||
172 | .reset_mask = BIT(1) | BIT(0), | ||
173 | }; | ||
174 | |||
175 | static void __init sun5i_a13_usb_setup(struct device_node *node) | ||
176 | { | ||
177 | sunxi_usb_clk_setup(node, &sun5i_a13_usb_clk_data, &sun4i_a10_usb_lock); | ||
178 | } | ||
179 | CLK_OF_DECLARE(sun5i_a13_usb, "allwinner,sun5i-a13-usb-clk", sun5i_a13_usb_setup); | ||
180 | |||
181 | static const struct usb_clk_data sun6i_a31_usb_clk_data __initconst = { | ||
182 | .clk_mask = BIT(18) | BIT(17) | BIT(16) | BIT(10) | BIT(9) | BIT(8), | ||
183 | .reset_mask = BIT(2) | BIT(1) | BIT(0), | ||
184 | }; | ||
185 | |||
186 | static void __init sun6i_a31_usb_setup(struct device_node *node) | ||
187 | { | ||
188 | sunxi_usb_clk_setup(node, &sun6i_a31_usb_clk_data, &sun4i_a10_usb_lock); | ||
189 | } | ||
190 | CLK_OF_DECLARE(sun6i_a31_usb, "allwinner,sun6i-a31-usb-clk", sun6i_a31_usb_setup); | ||