1 /* 2 * Copyright 2015 Maxime Ripard 3 * 4 * Maxime Ripard <maxime.ripard@free-electrons.com> 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/io.h> 19 #include <linux/kernel.h> 20 #include <linux/of_address.h> 21 #include <linux/reset-controller.h> 22 #include <linux/slab.h> 23 #include <linux/spinlock.h> 24 25 struct sun4i_a10_display_clk_data { 26 bool has_div; 27 u8 num_rst; 28 u8 parents; 29 30 u8 offset_en; 31 u8 offset_div; 32 u8 offset_mux; 33 u8 offset_rst; 34 35 u8 width_div; 36 u8 width_mux; 37 38 u32 flags; 39 }; 40 41 struct reset_data { 42 void __iomem *reg; 43 spinlock_t *lock; 44 struct reset_controller_dev rcdev; 45 u8 offset; 46 }; 47 48 static DEFINE_SPINLOCK(sun4i_a10_display_lock); 49 50 static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev) 51 { 52 return container_of(rcdev, struct reset_data, rcdev); 53 }; 54 55 static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev, 56 unsigned long id) 57 { 58 struct reset_data *data = rcdev_to_reset_data(rcdev); 59 unsigned long flags; 60 u32 reg; 61 62 spin_lock_irqsave(data->lock, flags); 63 64 reg = readl(data->reg); 65 writel(reg & ~BIT(data->offset + id), data->reg); 66 67 spin_unlock_irqrestore(data->lock, flags); 68 69 return 0; 70 } 71 72 static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev, 73 unsigned long id) 74 { 75 struct reset_data *data = rcdev_to_reset_data(rcdev); 76 unsigned long flags; 77 u32 reg; 78 79 spin_lock_irqsave(data->lock, flags); 80 81 reg = readl(data->reg); 82 writel(reg | BIT(data->offset + id), data->reg); 83 84 spin_unlock_irqrestore(data->lock, flags); 85 86 return 0; 87 } 88 89 static int sun4i_a10_display_status(struct reset_controller_dev *rcdev, 90 unsigned long id) 91 { 92 struct reset_data *data = rcdev_to_reset_data(rcdev); 93 94 return !(readl(data->reg) & BIT(data->offset + id)); 95 } 96 97 static const struct reset_control_ops sun4i_a10_display_reset_ops = { 98 .assert = sun4i_a10_display_assert, 99 .deassert = sun4i_a10_display_deassert, 100 .status = sun4i_a10_display_status, 101 }; 102 103 static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev, 104 const struct of_phandle_args *spec) 105 { 106 /* We only have a single reset signal */ 107 return 0; 108 } 109 110 static void __init sun4i_a10_display_init(struct device_node *node, 111 const struct sun4i_a10_display_clk_data *data) 112 { 113 const char *parents[4]; 114 const char *clk_name = node->name; 115 struct reset_data *reset_data; 116 struct clk_divider *div = NULL; 117 struct clk_gate *gate; 118 struct resource res; 119 struct clk_mux *mux; 120 void __iomem *reg; 121 struct clk *clk; 122 int ret; 123 124 of_property_read_string(node, "clock-output-names", &clk_name); 125 126 reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 127 if (IS_ERR(reg)) { 128 pr_err("%s: Could not map the clock registers\n", clk_name); 129 return; 130 } 131 132 ret = of_clk_parent_fill(node, parents, data->parents); 133 if (ret != data->parents) { 134 pr_err("%s: Could not retrieve the parents\n", clk_name); 135 goto unmap; 136 } 137 138 mux = kzalloc(sizeof(*mux), GFP_KERNEL); 139 if (!mux) 140 goto unmap; 141 142 mux->reg = reg; 143 mux->shift = data->offset_mux; 144 mux->mask = (1 << data->width_mux) - 1; 145 mux->lock = &sun4i_a10_display_lock; 146 147 gate = kzalloc(sizeof(*gate), GFP_KERNEL); 148 if (!gate) 149 goto free_mux; 150 151 gate->reg = reg; 152 gate->bit_idx = data->offset_en; 153 gate->lock = &sun4i_a10_display_lock; 154 155 if (data->has_div) { 156 div = kzalloc(sizeof(*div), GFP_KERNEL); 157 if (!div) 158 goto free_gate; 159 160 div->reg = reg; 161 div->shift = data->offset_div; 162 div->width = data->width_div; 163 div->lock = &sun4i_a10_display_lock; 164 } 165 166 clk = clk_register_composite(NULL, clk_name, 167 parents, data->parents, 168 &mux->hw, &clk_mux_ops, 169 data->has_div ? &div->hw : NULL, 170 data->has_div ? &clk_divider_ops : NULL, 171 &gate->hw, &clk_gate_ops, 172 data->flags); 173 if (IS_ERR(clk)) { 174 pr_err("%s: Couldn't register the clock\n", clk_name); 175 goto free_div; 176 } 177 178 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 179 if (ret) { 180 pr_err("%s: Couldn't register DT provider\n", clk_name); 181 goto free_clk; 182 } 183 184 if (!data->num_rst) 185 return; 186 187 reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 188 if (!reset_data) 189 goto free_of_clk; 190 191 reset_data->reg = reg; 192 reset_data->offset = data->offset_rst; 193 reset_data->lock = &sun4i_a10_display_lock; 194 reset_data->rcdev.nr_resets = data->num_rst; 195 reset_data->rcdev.ops = &sun4i_a10_display_reset_ops; 196 reset_data->rcdev.of_node = node; 197 198 if (data->num_rst == 1) { 199 reset_data->rcdev.of_reset_n_cells = 0; 200 reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate; 201 } else { 202 reset_data->rcdev.of_reset_n_cells = 1; 203 } 204 205 if (reset_controller_register(&reset_data->rcdev)) { 206 pr_err("%s: Couldn't register the reset controller\n", 207 clk_name); 208 goto free_reset; 209 } 210 211 return; 212 213 free_reset: 214 kfree(reset_data); 215 free_of_clk: 216 of_clk_del_provider(node); 217 free_clk: 218 clk_unregister_composite(clk); 219 free_div: 220 kfree(div); 221 free_gate: 222 kfree(gate); 223 free_mux: 224 kfree(mux); 225 unmap: 226 iounmap(reg); 227 of_address_to_resource(node, 0, &res); 228 release_mem_region(res.start, resource_size(&res)); 229 } 230 231 static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = { 232 .num_rst = 2, 233 .parents = 4, 234 .offset_en = 31, 235 .offset_rst = 29, 236 .offset_mux = 24, 237 .width_mux = 2, 238 .flags = CLK_SET_RATE_PARENT, 239 }; 240 241 static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node) 242 { 243 sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data); 244 } 245 CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk", 246 sun4i_a10_tcon_ch0_setup); 247 248 static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = { 249 .has_div = true, 250 .num_rst = 1, 251 .parents = 3, 252 .offset_en = 31, 253 .offset_rst = 30, 254 .offset_mux = 24, 255 .offset_div = 0, 256 .width_mux = 2, 257 .width_div = 4, 258 }; 259 260 static void __init sun4i_a10_display_setup(struct device_node *node) 261 { 262 sun4i_a10_display_init(node, &sun4i_a10_display_data); 263 } 264 CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk", 265 sun4i_a10_display_setup); 266