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