198b8525aSMaxime Ripard /* 298b8525aSMaxime Ripard * Copyright 2015 Maxime Ripard 398b8525aSMaxime Ripard * 498b8525aSMaxime Ripard * Maxime Ripard <maxime.ripard@free-electrons.com> 598b8525aSMaxime Ripard * 698b8525aSMaxime Ripard * This program is free software; you can redistribute it and/or modify 798b8525aSMaxime Ripard * it under the terms of the GNU General Public License as published by 898b8525aSMaxime Ripard * the Free Software Foundation; either version 2 of the License, or 998b8525aSMaxime Ripard * (at your option) any later version. 1098b8525aSMaxime Ripard * 1198b8525aSMaxime Ripard * This program is distributed in the hope that it will be useful, 1298b8525aSMaxime Ripard * but WITHOUT ANY WARRANTY; without even the implied warranty of 1398b8525aSMaxime Ripard * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1498b8525aSMaxime Ripard * GNU General Public License for more details. 1598b8525aSMaxime Ripard */ 1698b8525aSMaxime Ripard 1798b8525aSMaxime Ripard #include <linux/clk-provider.h> 1898b8525aSMaxime Ripard #include <linux/kernel.h> 1998b8525aSMaxime Ripard #include <linux/of_address.h> 2098b8525aSMaxime Ripard #include <linux/reset-controller.h> 2198b8525aSMaxime Ripard #include <linux/slab.h> 2298b8525aSMaxime Ripard #include <linux/spinlock.h> 2398b8525aSMaxime Ripard 2498b8525aSMaxime Ripard struct sun4i_a10_display_clk_data { 2598b8525aSMaxime Ripard bool has_div; 2698b8525aSMaxime Ripard u8 num_rst; 2798b8525aSMaxime Ripard u8 parents; 2898b8525aSMaxime Ripard 2998b8525aSMaxime Ripard u8 offset_en; 3098b8525aSMaxime Ripard u8 offset_div; 3198b8525aSMaxime Ripard u8 offset_mux; 3298b8525aSMaxime Ripard u8 offset_rst; 3398b8525aSMaxime Ripard 3498b8525aSMaxime Ripard u8 width_div; 3598b8525aSMaxime Ripard u8 width_mux; 3607ea0b4dSMaxime Ripard 3707ea0b4dSMaxime Ripard u32 flags; 3898b8525aSMaxime Ripard }; 3998b8525aSMaxime Ripard 4098b8525aSMaxime Ripard struct reset_data { 4198b8525aSMaxime Ripard void __iomem *reg; 4298b8525aSMaxime Ripard spinlock_t *lock; 4398b8525aSMaxime Ripard struct reset_controller_dev rcdev; 4498b8525aSMaxime Ripard u8 offset; 4598b8525aSMaxime Ripard }; 4698b8525aSMaxime Ripard 4798b8525aSMaxime Ripard static DEFINE_SPINLOCK(sun4i_a10_display_lock); 4898b8525aSMaxime Ripard 4998b8525aSMaxime Ripard static inline struct reset_data *rcdev_to_reset_data(struct reset_controller_dev *rcdev) 5098b8525aSMaxime Ripard { 5198b8525aSMaxime Ripard return container_of(rcdev, struct reset_data, rcdev); 5298b8525aSMaxime Ripard }; 5398b8525aSMaxime Ripard 5498b8525aSMaxime Ripard static int sun4i_a10_display_assert(struct reset_controller_dev *rcdev, 5598b8525aSMaxime Ripard unsigned long id) 5698b8525aSMaxime Ripard { 5798b8525aSMaxime Ripard struct reset_data *data = rcdev_to_reset_data(rcdev); 5898b8525aSMaxime Ripard unsigned long flags; 5998b8525aSMaxime Ripard u32 reg; 6098b8525aSMaxime Ripard 6198b8525aSMaxime Ripard spin_lock_irqsave(data->lock, flags); 6298b8525aSMaxime Ripard 6398b8525aSMaxime Ripard reg = readl(data->reg); 6498b8525aSMaxime Ripard writel(reg & ~BIT(data->offset + id), data->reg); 6598b8525aSMaxime Ripard 6698b8525aSMaxime Ripard spin_unlock_irqrestore(data->lock, flags); 6798b8525aSMaxime Ripard 6898b8525aSMaxime Ripard return 0; 6998b8525aSMaxime Ripard } 7098b8525aSMaxime Ripard 7198b8525aSMaxime Ripard static int sun4i_a10_display_deassert(struct reset_controller_dev *rcdev, 7298b8525aSMaxime Ripard unsigned long id) 7398b8525aSMaxime Ripard { 7498b8525aSMaxime Ripard struct reset_data *data = rcdev_to_reset_data(rcdev); 7598b8525aSMaxime Ripard unsigned long flags; 7698b8525aSMaxime Ripard u32 reg; 7798b8525aSMaxime Ripard 7898b8525aSMaxime Ripard spin_lock_irqsave(data->lock, flags); 7998b8525aSMaxime Ripard 8098b8525aSMaxime Ripard reg = readl(data->reg); 8198b8525aSMaxime Ripard writel(reg | BIT(data->offset + id), data->reg); 8298b8525aSMaxime Ripard 8398b8525aSMaxime Ripard spin_unlock_irqrestore(data->lock, flags); 8498b8525aSMaxime Ripard 8598b8525aSMaxime Ripard return 0; 8698b8525aSMaxime Ripard } 8798b8525aSMaxime Ripard 8898b8525aSMaxime Ripard static int sun4i_a10_display_status(struct reset_controller_dev *rcdev, 8998b8525aSMaxime Ripard unsigned long id) 9098b8525aSMaxime Ripard { 9198b8525aSMaxime Ripard struct reset_data *data = rcdev_to_reset_data(rcdev); 9298b8525aSMaxime Ripard 9398b8525aSMaxime Ripard return !(readl(data->reg) & BIT(data->offset + id)); 9498b8525aSMaxime Ripard } 9598b8525aSMaxime Ripard 9698b8525aSMaxime Ripard static const struct reset_control_ops sun4i_a10_display_reset_ops = { 9798b8525aSMaxime Ripard .assert = sun4i_a10_display_assert, 9898b8525aSMaxime Ripard .deassert = sun4i_a10_display_deassert, 9998b8525aSMaxime Ripard .status = sun4i_a10_display_status, 10098b8525aSMaxime Ripard }; 10198b8525aSMaxime Ripard 10298b8525aSMaxime Ripard static int sun4i_a10_display_reset_xlate(struct reset_controller_dev *rcdev, 10398b8525aSMaxime Ripard const struct of_phandle_args *spec) 10498b8525aSMaxime Ripard { 10598b8525aSMaxime Ripard /* We only have a single reset signal */ 10698b8525aSMaxime Ripard return 0; 10798b8525aSMaxime Ripard } 10898b8525aSMaxime Ripard 10998b8525aSMaxime Ripard static void __init sun4i_a10_display_init(struct device_node *node, 11098b8525aSMaxime Ripard const struct sun4i_a10_display_clk_data *data) 11198b8525aSMaxime Ripard { 11298b8525aSMaxime Ripard const char *parents[4]; 11398b8525aSMaxime Ripard const char *clk_name = node->name; 11498b8525aSMaxime Ripard struct reset_data *reset_data; 11598b8525aSMaxime Ripard struct clk_divider *div = NULL; 11698b8525aSMaxime Ripard struct clk_gate *gate; 11798b8525aSMaxime Ripard struct resource res; 11898b8525aSMaxime Ripard struct clk_mux *mux; 11998b8525aSMaxime Ripard void __iomem *reg; 12098b8525aSMaxime Ripard struct clk *clk; 12198b8525aSMaxime Ripard int ret; 12298b8525aSMaxime Ripard 12398b8525aSMaxime Ripard of_property_read_string(node, "clock-output-names", &clk_name); 12498b8525aSMaxime Ripard 12598b8525aSMaxime Ripard reg = of_io_request_and_map(node, 0, of_node_full_name(node)); 12698b8525aSMaxime Ripard if (IS_ERR(reg)) { 12798b8525aSMaxime Ripard pr_err("%s: Could not map the clock registers\n", clk_name); 12898b8525aSMaxime Ripard return; 12998b8525aSMaxime Ripard } 13098b8525aSMaxime Ripard 13198b8525aSMaxime Ripard ret = of_clk_parent_fill(node, parents, data->parents); 13298b8525aSMaxime Ripard if (ret != data->parents) { 13398b8525aSMaxime Ripard pr_err("%s: Could not retrieve the parents\n", clk_name); 13498b8525aSMaxime Ripard goto unmap; 13598b8525aSMaxime Ripard } 13698b8525aSMaxime Ripard 13798b8525aSMaxime Ripard mux = kzalloc(sizeof(*mux), GFP_KERNEL); 13898b8525aSMaxime Ripard if (!mux) 13998b8525aSMaxime Ripard goto unmap; 14098b8525aSMaxime Ripard 14198b8525aSMaxime Ripard mux->reg = reg; 14298b8525aSMaxime Ripard mux->shift = data->offset_mux; 14398b8525aSMaxime Ripard mux->mask = (1 << data->width_mux) - 1; 14498b8525aSMaxime Ripard mux->lock = &sun4i_a10_display_lock; 14598b8525aSMaxime Ripard 14698b8525aSMaxime Ripard gate = kzalloc(sizeof(*gate), GFP_KERNEL); 14798b8525aSMaxime Ripard if (!gate) 14898b8525aSMaxime Ripard goto free_mux; 14998b8525aSMaxime Ripard 15098b8525aSMaxime Ripard gate->reg = reg; 15198b8525aSMaxime Ripard gate->bit_idx = data->offset_en; 15298b8525aSMaxime Ripard gate->lock = &sun4i_a10_display_lock; 15398b8525aSMaxime Ripard 15498b8525aSMaxime Ripard if (data->has_div) { 15598b8525aSMaxime Ripard div = kzalloc(sizeof(*div), GFP_KERNEL); 15698b8525aSMaxime Ripard if (!div) 15798b8525aSMaxime Ripard goto free_gate; 15898b8525aSMaxime Ripard 15998b8525aSMaxime Ripard div->reg = reg; 16098b8525aSMaxime Ripard div->shift = data->offset_div; 16198b8525aSMaxime Ripard div->width = data->width_div; 16298b8525aSMaxime Ripard div->lock = &sun4i_a10_display_lock; 16398b8525aSMaxime Ripard } 16498b8525aSMaxime Ripard 16598b8525aSMaxime Ripard clk = clk_register_composite(NULL, clk_name, 16698b8525aSMaxime Ripard parents, data->parents, 16798b8525aSMaxime Ripard &mux->hw, &clk_mux_ops, 16898b8525aSMaxime Ripard data->has_div ? &div->hw : NULL, 16998b8525aSMaxime Ripard data->has_div ? &clk_divider_ops : NULL, 17098b8525aSMaxime Ripard &gate->hw, &clk_gate_ops, 17107ea0b4dSMaxime Ripard data->flags); 17298b8525aSMaxime Ripard if (IS_ERR(clk)) { 17398b8525aSMaxime Ripard pr_err("%s: Couldn't register the clock\n", clk_name); 17498b8525aSMaxime Ripard goto free_div; 17598b8525aSMaxime Ripard } 17698b8525aSMaxime Ripard 17798b8525aSMaxime Ripard ret = of_clk_add_provider(node, of_clk_src_simple_get, clk); 17898b8525aSMaxime Ripard if (ret) { 17998b8525aSMaxime Ripard pr_err("%s: Couldn't register DT provider\n", clk_name); 18098b8525aSMaxime Ripard goto free_clk; 18198b8525aSMaxime Ripard } 18298b8525aSMaxime Ripard 18398b8525aSMaxime Ripard if (!data->num_rst) 18498b8525aSMaxime Ripard return; 18598b8525aSMaxime Ripard 18698b8525aSMaxime Ripard reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL); 18798b8525aSMaxime Ripard if (!reset_data) 18898b8525aSMaxime Ripard goto free_of_clk; 18998b8525aSMaxime Ripard 19098b8525aSMaxime Ripard reset_data->reg = reg; 19198b8525aSMaxime Ripard reset_data->offset = data->offset_rst; 19298b8525aSMaxime Ripard reset_data->lock = &sun4i_a10_display_lock; 19398b8525aSMaxime Ripard reset_data->rcdev.nr_resets = data->num_rst; 19498b8525aSMaxime Ripard reset_data->rcdev.ops = &sun4i_a10_display_reset_ops; 19598b8525aSMaxime Ripard reset_data->rcdev.of_node = node; 19698b8525aSMaxime Ripard 19798b8525aSMaxime Ripard if (data->num_rst == 1) { 19898b8525aSMaxime Ripard reset_data->rcdev.of_reset_n_cells = 0; 19998b8525aSMaxime Ripard reset_data->rcdev.of_xlate = &sun4i_a10_display_reset_xlate; 20098b8525aSMaxime Ripard } else { 20198b8525aSMaxime Ripard reset_data->rcdev.of_reset_n_cells = 1; 20298b8525aSMaxime Ripard } 20398b8525aSMaxime Ripard 20498b8525aSMaxime Ripard if (reset_controller_register(&reset_data->rcdev)) { 20598b8525aSMaxime Ripard pr_err("%s: Couldn't register the reset controller\n", 20698b8525aSMaxime Ripard clk_name); 20798b8525aSMaxime Ripard goto free_reset; 20898b8525aSMaxime Ripard } 20998b8525aSMaxime Ripard 21098b8525aSMaxime Ripard return; 21198b8525aSMaxime Ripard 21298b8525aSMaxime Ripard free_reset: 21398b8525aSMaxime Ripard kfree(reset_data); 21498b8525aSMaxime Ripard free_of_clk: 21598b8525aSMaxime Ripard of_clk_del_provider(node); 21698b8525aSMaxime Ripard free_clk: 21798b8525aSMaxime Ripard clk_unregister_composite(clk); 21898b8525aSMaxime Ripard free_div: 21998b8525aSMaxime Ripard kfree(div); 22098b8525aSMaxime Ripard free_gate: 22198b8525aSMaxime Ripard kfree(gate); 22298b8525aSMaxime Ripard free_mux: 22398b8525aSMaxime Ripard kfree(mux); 22498b8525aSMaxime Ripard unmap: 22598b8525aSMaxime Ripard iounmap(reg); 22698b8525aSMaxime Ripard of_address_to_resource(node, 0, &res); 22798b8525aSMaxime Ripard release_mem_region(res.start, resource_size(&res)); 22898b8525aSMaxime Ripard } 22998b8525aSMaxime Ripard 23098b8525aSMaxime Ripard static const struct sun4i_a10_display_clk_data sun4i_a10_tcon_ch0_data __initconst = { 23198b8525aSMaxime Ripard .num_rst = 2, 23298b8525aSMaxime Ripard .parents = 4, 23398b8525aSMaxime Ripard .offset_en = 31, 23498b8525aSMaxime Ripard .offset_rst = 29, 23598b8525aSMaxime Ripard .offset_mux = 24, 23698b8525aSMaxime Ripard .width_mux = 2, 23707ea0b4dSMaxime Ripard .flags = CLK_SET_RATE_PARENT, 23898b8525aSMaxime Ripard }; 23998b8525aSMaxime Ripard 24098b8525aSMaxime Ripard static void __init sun4i_a10_tcon_ch0_setup(struct device_node *node) 24198b8525aSMaxime Ripard { 24298b8525aSMaxime Ripard sun4i_a10_display_init(node, &sun4i_a10_tcon_ch0_data); 24398b8525aSMaxime Ripard } 24498b8525aSMaxime Ripard CLK_OF_DECLARE(sun4i_a10_tcon_ch0, "allwinner,sun4i-a10-tcon-ch0-clk", 24598b8525aSMaxime Ripard sun4i_a10_tcon_ch0_setup); 24698b8525aSMaxime Ripard 24798b8525aSMaxime Ripard static const struct sun4i_a10_display_clk_data sun4i_a10_display_data __initconst = { 24898b8525aSMaxime Ripard .has_div = true, 24998b8525aSMaxime Ripard .num_rst = 1, 25098b8525aSMaxime Ripard .parents = 3, 25198b8525aSMaxime Ripard .offset_en = 31, 25298b8525aSMaxime Ripard .offset_rst = 30, 25398b8525aSMaxime Ripard .offset_mux = 24, 25498b8525aSMaxime Ripard .offset_div = 0, 25598b8525aSMaxime Ripard .width_mux = 2, 25698b8525aSMaxime Ripard .width_div = 4, 25798b8525aSMaxime Ripard }; 25898b8525aSMaxime Ripard 25998b8525aSMaxime Ripard static void __init sun4i_a10_display_setup(struct device_node *node) 26098b8525aSMaxime Ripard { 26198b8525aSMaxime Ripard sun4i_a10_display_init(node, &sun4i_a10_display_data); 26298b8525aSMaxime Ripard } 26398b8525aSMaxime Ripard CLK_OF_DECLARE(sun4i_a10_display, "allwinner,sun4i-a10-display-clk", 26498b8525aSMaxime Ripard sun4i_a10_display_setup); 265