brintos

brintos / linux-shallow public Read only

0
0
Text · 5.8 KiB · 35d1541 Raw
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