257 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/*3 * Copyright 2015 Maxime Ripard4 *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);257