18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci// Copyright 2020 Cerno
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci#include <linux/clk-provider.h>
58c2ecf20Sopenharmony_ci#include <linux/module.h>
68c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
78c2ecf20Sopenharmony_ci#include <linux/reset-controller.h>
88c2ecf20Sopenharmony_ci#include <linux/reset/reset-simple.h>
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define DVP_HT_RPI_SW_INIT	0x04
118c2ecf20Sopenharmony_ci#define DVP_HT_RPI_MISC_CONFIG	0x08
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#define NR_CLOCKS	2
148c2ecf20Sopenharmony_ci#define NR_RESETS	6
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_cistruct clk_dvp {
178c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data	*data;
188c2ecf20Sopenharmony_ci	struct reset_simple_data	reset;
198c2ecf20Sopenharmony_ci};
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic const struct clk_parent_data clk_dvp_parent = {
228c2ecf20Sopenharmony_ci	.index	= 0,
238c2ecf20Sopenharmony_ci};
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int clk_dvp_probe(struct platform_device *pdev)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data *data;
288c2ecf20Sopenharmony_ci	struct resource *res;
298c2ecf20Sopenharmony_ci	struct clk_dvp *dvp;
308c2ecf20Sopenharmony_ci	void __iomem *base;
318c2ecf20Sopenharmony_ci	int ret;
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci	dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL);
348c2ecf20Sopenharmony_ci	if (!dvp)
358c2ecf20Sopenharmony_ci		return -ENOMEM;
368c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, dvp);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	dvp->data = devm_kzalloc(&pdev->dev,
398c2ecf20Sopenharmony_ci				 struct_size(dvp->data, hws, NR_CLOCKS),
408c2ecf20Sopenharmony_ci				 GFP_KERNEL);
418c2ecf20Sopenharmony_ci	if (!dvp->data)
428c2ecf20Sopenharmony_ci		return -ENOMEM;
438c2ecf20Sopenharmony_ci	data = dvp->data;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
468c2ecf20Sopenharmony_ci	if (IS_ERR(base))
478c2ecf20Sopenharmony_ci		return PTR_ERR(base);
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	dvp->reset.rcdev.owner = THIS_MODULE;
508c2ecf20Sopenharmony_ci	dvp->reset.rcdev.nr_resets = NR_RESETS;
518c2ecf20Sopenharmony_ci	dvp->reset.rcdev.ops = &reset_simple_ops;
528c2ecf20Sopenharmony_ci	dvp->reset.rcdev.of_node = pdev->dev.of_node;
538c2ecf20Sopenharmony_ci	dvp->reset.membase = base + DVP_HT_RPI_SW_INIT;
548c2ecf20Sopenharmony_ci	spin_lock_init(&dvp->reset.lock);
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev);
578c2ecf20Sopenharmony_ci	if (ret)
588c2ecf20Sopenharmony_ci		return ret;
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev,
618c2ecf20Sopenharmony_ci							"hdmi0-108MHz",
628c2ecf20Sopenharmony_ci							&clk_dvp_parent, 0,
638c2ecf20Sopenharmony_ci							base + DVP_HT_RPI_MISC_CONFIG, 3,
648c2ecf20Sopenharmony_ci							CLK_GATE_SET_TO_DISABLE,
658c2ecf20Sopenharmony_ci							&dvp->reset.lock);
668c2ecf20Sopenharmony_ci	if (IS_ERR(data->hws[0]))
678c2ecf20Sopenharmony_ci		return PTR_ERR(data->hws[0]);
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev,
708c2ecf20Sopenharmony_ci							"hdmi1-108MHz",
718c2ecf20Sopenharmony_ci							&clk_dvp_parent, 0,
728c2ecf20Sopenharmony_ci							base + DVP_HT_RPI_MISC_CONFIG, 4,
738c2ecf20Sopenharmony_ci							CLK_GATE_SET_TO_DISABLE,
748c2ecf20Sopenharmony_ci							&dvp->reset.lock);
758c2ecf20Sopenharmony_ci	if (IS_ERR(data->hws[1])) {
768c2ecf20Sopenharmony_ci		ret = PTR_ERR(data->hws[1]);
778c2ecf20Sopenharmony_ci		goto unregister_clk0;
788c2ecf20Sopenharmony_ci	}
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	data->num = NR_CLOCKS;
818c2ecf20Sopenharmony_ci	ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
828c2ecf20Sopenharmony_ci				     data);
838c2ecf20Sopenharmony_ci	if (ret)
848c2ecf20Sopenharmony_ci		goto unregister_clk1;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return 0;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ciunregister_clk1:
898c2ecf20Sopenharmony_ci	clk_hw_unregister_gate(data->hws[1]);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ciunregister_clk0:
928c2ecf20Sopenharmony_ci	clk_hw_unregister_gate(data->hws[0]);
938c2ecf20Sopenharmony_ci	return ret;
948c2ecf20Sopenharmony_ci};
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int clk_dvp_remove(struct platform_device *pdev)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	struct clk_dvp *dvp = platform_get_drvdata(pdev);
998c2ecf20Sopenharmony_ci	struct clk_hw_onecell_data *data = dvp->data;
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci	clk_hw_unregister_gate(data->hws[1]);
1028c2ecf20Sopenharmony_ci	clk_hw_unregister_gate(data->hws[0]);
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	return 0;
1058c2ecf20Sopenharmony_ci}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_cistatic const struct of_device_id clk_dvp_dt_ids[] = {
1088c2ecf20Sopenharmony_ci	{ .compatible = "brcm,brcm2711-dvp", },
1098c2ecf20Sopenharmony_ci	{ /* sentinel */ }
1108c2ecf20Sopenharmony_ci};
1118c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, clk_dvp_dt_ids);
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_cistatic struct platform_driver clk_dvp_driver = {
1148c2ecf20Sopenharmony_ci	.probe	= clk_dvp_probe,
1158c2ecf20Sopenharmony_ci	.remove	= clk_dvp_remove,
1168c2ecf20Sopenharmony_ci	.driver	= {
1178c2ecf20Sopenharmony_ci		.name		= "brcm2711-dvp",
1188c2ecf20Sopenharmony_ci		.of_match_table	= clk_dvp_dt_ids,
1198c2ecf20Sopenharmony_ci	},
1208c2ecf20Sopenharmony_ci};
1218c2ecf20Sopenharmony_cimodule_platform_driver(clk_dvp_driver);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ciMODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
1248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("BCM2711 DVP clock driver");
1258c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
126