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