forked from mirrors/linux
		
	 889c2b7ec4
			
		
	
	
		889c2b7ec4
		
	
	
	
	
		
			
			Initially, the meson clock directory only hosted 2 controllers drivers, for meson8 and gxbb. At the time, both used the same set of clock drivers so managing the dependencies was not a big concern. Since this ancient time, entropy did its job, controllers with different requirement and specific clock drivers have been added. Unfortunately, we did not do a great job at managing the dependencies between the controllers and the different clock drivers. Some drivers, such as clk-phase or vid-pll-div, are compiled even if they are useless on the target (meson8). As we are adding new controllers, we need to be able to pick a driver w/o pulling the whole thing. The patch aims to clean things up by: * providing a dedicated CONFIG_ for each clock drivers * allowing clock drivers to be compiled as a modules, if possible * stating explicitly which drivers are required by each controller. Signed-off-by: Jerome Brunet <jbrunet@baylibre.com> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com> Link: https://lkml.kernel.org/r/20190201125841.26785-5-jbrunet@baylibre.com
		
			
				
	
	
		
			123 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			123 lines
		
	
	
	
		
			2.9 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0+
 | |
| /*
 | |
|  * Amlogic Meson-AXG Clock Controller Driver
 | |
|  *
 | |
|  * Copyright (c) 2016 BayLibre, SAS.
 | |
|  * Author: Neil Armstrong <narmstrong@baylibre.com>
 | |
|  *
 | |
|  * Copyright (c) 2018 Amlogic, inc.
 | |
|  * Author: Qiufang Dai <qiufang.dai@amlogic.com>
 | |
|  * Author: Yixun Lan <yixun.lan@amlogic.com>
 | |
|  */
 | |
| 
 | |
| #include <linux/platform_device.h>
 | |
| #include <linux/reset-controller.h>
 | |
| #include <linux/mfd/syscon.h>
 | |
| #include <linux/of_device.h>
 | |
| #include <linux/slab.h>
 | |
| #include "meson-aoclk.h"
 | |
| 
 | |
| #include "clk-input.h"
 | |
| 
 | |
| static int meson_aoclk_do_reset(struct reset_controller_dev *rcdev,
 | |
| 			       unsigned long id)
 | |
| {
 | |
| 	struct meson_aoclk_reset_controller *rstc =
 | |
| 		container_of(rcdev, struct meson_aoclk_reset_controller, reset);
 | |
| 
 | |
| 	return regmap_write(rstc->regmap, rstc->data->reset_reg,
 | |
| 			    BIT(rstc->data->reset[id]));
 | |
| }
 | |
| 
 | |
| static const struct reset_control_ops meson_aoclk_reset_ops = {
 | |
| 	.reset = meson_aoclk_do_reset,
 | |
| };
 | |
| 
 | |
| static int meson_aoclkc_register_inputs(struct device *dev,
 | |
| 					struct meson_aoclk_data *data)
 | |
| {
 | |
| 	struct clk_hw *hw;
 | |
| 	char *str;
 | |
| 	int i;
 | |
| 
 | |
| 	for (i = 0; i < data->num_inputs; i++) {
 | |
| 		const struct meson_aoclk_input *in = &data->inputs[i];
 | |
| 
 | |
| 		str = kasprintf(GFP_KERNEL, "%s%s", data->input_prefix,
 | |
| 				in->name);
 | |
| 		if (!str)
 | |
| 			return -ENOMEM;
 | |
| 
 | |
| 		hw = meson_clk_hw_register_input(dev, in->name, str, 0);
 | |
| 		kfree(str);
 | |
| 
 | |
| 		if (IS_ERR(hw)) {
 | |
| 			if (!in->required && PTR_ERR(hw) == -ENOENT)
 | |
| 				continue;
 | |
| 			else if (PTR_ERR(hw) != -EPROBE_DEFER)
 | |
| 				dev_err(dev, "failed to register input %s\n",
 | |
| 					in->name);
 | |
| 			return PTR_ERR(hw);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| int meson_aoclkc_probe(struct platform_device *pdev)
 | |
| {
 | |
| 	struct meson_aoclk_reset_controller *rstc;
 | |
| 	struct meson_aoclk_data *data;
 | |
| 	struct device *dev = &pdev->dev;
 | |
| 	struct regmap *regmap;
 | |
| 	int ret, clkid;
 | |
| 
 | |
| 	data = (struct meson_aoclk_data *) of_device_get_match_data(dev);
 | |
| 	if (!data)
 | |
| 		return -ENODEV;
 | |
| 
 | |
| 	rstc = devm_kzalloc(dev, sizeof(*rstc), GFP_KERNEL);
 | |
| 	if (!rstc)
 | |
| 		return -ENOMEM;
 | |
| 
 | |
| 	regmap = syscon_node_to_regmap(of_get_parent(dev->of_node));
 | |
| 	if (IS_ERR(regmap)) {
 | |
| 		dev_err(dev, "failed to get regmap\n");
 | |
| 		return PTR_ERR(regmap);
 | |
| 	}
 | |
| 
 | |
| 	ret = meson_aoclkc_register_inputs(dev, data);
 | |
| 	if (ret)
 | |
| 		return ret;
 | |
| 
 | |
| 	/* Reset Controller */
 | |
| 	rstc->data = data;
 | |
| 	rstc->regmap = regmap;
 | |
| 	rstc->reset.ops = &meson_aoclk_reset_ops;
 | |
| 	rstc->reset.nr_resets = data->num_reset,
 | |
| 	rstc->reset.of_node = dev->of_node;
 | |
| 	ret = devm_reset_controller_register(dev, &rstc->reset);
 | |
| 	if (ret) {
 | |
| 		dev_err(dev, "failed to register reset controller\n");
 | |
| 		return ret;
 | |
| 	}
 | |
| 
 | |
| 	/* Populate regmap */
 | |
| 	for (clkid = 0; clkid < data->num_clks; clkid++)
 | |
| 		data->clks[clkid]->map = regmap;
 | |
| 
 | |
| 	/* Register all clks */
 | |
| 	for (clkid = 0; clkid < data->hw_data->num; clkid++) {
 | |
| 		if (!data->hw_data->hws[clkid])
 | |
| 			continue;
 | |
| 
 | |
| 		ret = devm_clk_hw_register(dev, data->hw_data->hws[clkid]);
 | |
| 		if (ret) {
 | |
| 			dev_err(dev, "Clock registration failed\n");
 | |
| 			return ret;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return devm_of_clk_add_hw_provider(dev, of_clk_hw_onecell_get,
 | |
| 		(void *) data->hw_data);
 | |
| }
 |