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