0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/clk-provider.h>
0009 #include <linux/io.h>
0010 #include <linux/kernel.h>
0011 #include <linux/of_address.h>
0012 #include <linux/reset-controller.h>
0013 #include <linux/slab.h>
0014 #include <linux/spinlock.h>
0015
0016 struct sun4i_a10_display_clk_data {
0017 bool has_div;
0018 u8 num_rst;
0019 u8 parents;
0020
0021 u8 offset_en;
0022 u8 offset_div;
0023 u8 offset_mux;
0024 u8 offset_rst;
0025
0026 u8 width_div;
0027 u8 width_mux;
0028
0029 u32 flags;
0030 };
0031
0032 struct reset_data {
0033 void __iomem *reg;
0034 spinlock_t *lock;
0035 struct reset_controller_dev rcdev;
0036 u8 offset;
0037 };
0038
0039 static DEFINE_SPINLOCK(sun4i_a10_display_lock);
0040
0041 static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev)
0042 {
0043 return container_of(rcdev, struct reset_data, rcdev);
0044 };
0045
0046 static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev,
0047 unsigned long id)
0048 {
0049 struct reset_data *data = rcdev_to_reset_data(rcdev);
0050 unsigned long flags;
0051 u32 reg;
0052
0053 spin_lock_irqsave(data->lock, flags);
0054
0055 reg = readl(data->reg);
0056 writel(reg & ~BIT(data->offset + id), data->reg);
0057
0058 spin_unlock_irqrestore(data->lock, flags);
0059
0060 return 0;
0061 }
0062
0063 static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev,
0064 unsigned long id)
0065 {
0066 struct reset_data *data = rcdev_to_reset_data(rcdev);
0067 unsigned long flags;
0068 u32 reg;
0069
0070 spin_lock_irqsave(data->lock, flags);
0071
0072 reg = readl(data->reg);
0073 writel(reg | BIT(data->offset + id), data->reg);
0074
0075 spin_unlock_irqrestore(data->lock, flags);
0076
0077 return 0;
0078 }
0079
0080 static int sun4i_a10_display_status(struct reset_controller_dev *rcdev,
0081 unsigned long id)
0082 {
0083 struct reset_data *data = rcdev_to_reset_data(rcdev);
0084
0085 return !(readl(data->reg) & BIT(data->offset + id));
0086 }
0087
0088 static const struct reset_control_ops sun4i_a10_display_reset_ops = {
0089 .assert = sun4i_a10_display_assert,
0090 .deassert = sun4i_a10_display_deassert,
0091 .status = sun4i_a10_display_status,
0092 };
0093
0094 static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev,
0095 const struct of_phandle_args *spec)
0096 {
0097
0098 return 0;
0099 }
0100
0101 static void __init sun4i_a10_display_init(struct device_node *node,
0102 const struct sun4i_a10_display_clk_data *data)
0103 {
0104 const char *parents[4];
0105 const char *clk_name = node->name;
0106 struct reset_data *reset_data;
0107 struct clk_divider *div = NULL;
0108 struct clk_gate *gate;
0109 struct resource res;
0110 struct clk_mux *mux;
0111 void __iomem *reg;
0112 struct clk *clk;
0113 int ret;
0114
0115 of_property_read_string(node, "clock-output-names", &clk_name);
0116
0117 reg = of_io_request_and_map(node, 0, of_node_full_name(node));
0118 if (IS_ERR(reg)) {
0119 pr_err("%s: Could not map the clock registers\n", clk_name);
0120 return;
0121 }
0122
0123 ret = of_clk_parent_fill(node, parents, data->parents);
0124 if (ret != data->parents) {
0125 pr_err("%s: Could not retrieve the parents\n", clk_name);
0126 goto unmap;
0127 }
0128
0129 mux = kzalloc(sizeof(*mux), GFP_KERNEL);
0130 if (!mux)
0131 goto unmap;
0132
0133 mux->reg = reg;
0134 mux->shift = data->offset_mux;
0135 mux->mask = (1 << data->width_mux) - 1;
0136 mux->lock = &sun4i_a10_display_lock;
0137
0138 gate = kzalloc(sizeof(*gate), GFP_KERNEL);
0139 if (!gate)
0140 goto free_mux;
0141
0142 gate->reg = reg;
0143 gate->bit_idx = data->offset_en;
0144 gate->lock = &sun4i_a10_display_lock;
0145
0146 if (data->has_div) {
0147 div = kzalloc(sizeof(*div), GFP_KERNEL);
0148 if (!div)
0149 goto free_gate;
0150
0151 div->reg = reg;
0152 div->shift = data->offset_div;
0153 div->width = data->width_div;
0154 div->lock = &sun4i_a10_display_lock;
0155 }
0156
0157 clk = clk_register_composite(NULL, clk_name,
0158 parents, data->parents,
0159 &mux->hw, &clk_mux_ops,
0160 data->has_div ? &div->hw : NULL,
0161 data->has_div ? &clk_divider_ops : NULL,
0162 &gate->hw, &clk_gate_ops,
0163 data->flags);
0164 if (IS_ERR(clk)) {
0165 pr_err("%s: Couldn't register the clock\n", clk_name);
0166 goto free_div;
0167 }
0168
0169 ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
0170 if (ret) {
0171 pr_err("%s: Couldn't register DT provider\n", clk_name);
0172 goto free_clk;
0173 }
0174
0175 if (!data->num_rst)
0176 return;
0177
0178 reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
0179 if (!reset_data)
0180 goto free_of_clk;
0181
0182 reset_data->reg = reg;
0183 reset_data->offset = data->offset_rst;
0184 reset_data->lock = &sun4i_a10_display_lock;
0185 reset_data->rcdev.nr_resets = data->num_rst;
0186 reset_data->rcdev.ops = &sun4i_a10_display_reset_ops;
0187 reset_data->rcdev.of_node = node;
0188
0189 if (data->num_rst == 1) {
0190 reset_data->rcdev.of_reset_n_cells = 0;
0191 reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate;
0192 } else {
0193 reset_data->rcdev.of_reset_n_cells = 1;
0194 }
0195
0196 if (reset_controller_register(&reset_data->rcdev)) {
0197 pr_err("%s: Couldn't register the reset controller\n",
0198 clk_name);
0199 goto free_reset;
0200 }
0201
0202 return;
0203
0204 free_reset:
0205 kfree(reset_data);
0206 free_of_clk:
0207 of_clk_del_provider(node);
0208 free_clk:
0209 clk_unregister_composite(clk);
0210 free_div:
0211 kfree(div);
0212 free_gate:
0213 kfree(gate);
0214 free_mux:
0215 kfree(mux);
0216 unmap:
0217 iounmap(reg);
0218 of_address_to_resource(node, 0, &res);
0219 release_mem_region(res.start, resource_size(&res));
0220 }
0221
0222 static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = {
0223 .num_rst = 2,
0224 .parents = 4,
0225 .offset_en = 31,
0226 .offset_rst = 29,
0227 .offset_mux = 24,
0228 .width_mux = 2,
0229 .flags = CLK_SET_RATE_PARENT,
0230 };
0231
0232 static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node)
0233 {
0234 sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data);
0235 }
0236 CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk",
0237 sun4i_a10_tcon_ch0_setup);
0238
0239 static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = {
0240 .has_div = true,
0241 .num_rst = 1,
0242 .parents = 3,
0243 .offset_en = 31,
0244 .offset_rst = 30,
0245 .offset_mux = 24,
0246 .offset_div = 0,
0247 .width_mux = 2,
0248 .width_div = 4,
0249 };
0250
0251 static void __init sun4i_a10_display_setup(struct device_node *node)
0252 {
0253 sun4i_a10_display_init(node, &sun4i_a10_display_data);
0254 }
0255 CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk",
0256 sun4i_a10_display_setup);