0001
0002
0003
0004 #include <linux/clk-provider.h>
0005 #include <linux/module.h>
0006 #include <linux/platform_device.h>
0007 #include <linux/reset-controller.h>
0008 #include <linux/reset/reset-simple.h>
0009
0010 #define DVP_HT_RPI_SW_INIT 0x04
0011 #define DVP_HT_RPI_MISC_CONFIG 0x08
0012
0013 #define NR_CLOCKS 2
0014 #define NR_RESETS 6
0015
0016 struct clk_dvp {
0017 struct clk_hw_onecell_data *data;
0018 struct reset_simple_data reset;
0019 };
0020
0021 static const struct clk_parent_data clk_dvp_parent = {
0022 .index = 0,
0023 };
0024
0025 static int clk_dvp_probe(struct platform_device *pdev)
0026 {
0027 struct clk_hw_onecell_data *data;
0028 struct clk_dvp *dvp;
0029 void __iomem *base;
0030 int ret;
0031
0032 dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL);
0033 if (!dvp)
0034 return -ENOMEM;
0035 platform_set_drvdata(pdev, dvp);
0036
0037 dvp->data = devm_kzalloc(&pdev->dev,
0038 struct_size(dvp->data, hws, NR_CLOCKS),
0039 GFP_KERNEL);
0040 if (!dvp->data)
0041 return -ENOMEM;
0042 data = dvp->data;
0043
0044 base = devm_platform_ioremap_resource(pdev, 0);
0045 if (IS_ERR(base))
0046 return PTR_ERR(base);
0047
0048 dvp->reset.rcdev.owner = THIS_MODULE;
0049 dvp->reset.rcdev.nr_resets = NR_RESETS;
0050 dvp->reset.rcdev.ops = &reset_simple_ops;
0051 dvp->reset.rcdev.of_node = pdev->dev.of_node;
0052 dvp->reset.membase = base + DVP_HT_RPI_SW_INIT;
0053 spin_lock_init(&dvp->reset.lock);
0054
0055 ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev);
0056 if (ret)
0057 return ret;
0058
0059 data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev,
0060 "hdmi0-108MHz",
0061 &clk_dvp_parent, 0,
0062 base + DVP_HT_RPI_MISC_CONFIG, 3,
0063 CLK_GATE_SET_TO_DISABLE,
0064 &dvp->reset.lock);
0065 if (IS_ERR(data->hws[0]))
0066 return PTR_ERR(data->hws[0]);
0067
0068 data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev,
0069 "hdmi1-108MHz",
0070 &clk_dvp_parent, 0,
0071 base + DVP_HT_RPI_MISC_CONFIG, 4,
0072 CLK_GATE_SET_TO_DISABLE,
0073 &dvp->reset.lock);
0074 if (IS_ERR(data->hws[1])) {
0075 ret = PTR_ERR(data->hws[1]);
0076 goto unregister_clk0;
0077 }
0078
0079 data->num = NR_CLOCKS;
0080 ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
0081 data);
0082 if (ret)
0083 goto unregister_clk1;
0084
0085 return 0;
0086
0087 unregister_clk1:
0088 clk_hw_unregister_gate(data->hws[1]);
0089
0090 unregister_clk0:
0091 clk_hw_unregister_gate(data->hws[0]);
0092 return ret;
0093 };
0094
0095 static int clk_dvp_remove(struct platform_device *pdev)
0096 {
0097 struct clk_dvp *dvp = platform_get_drvdata(pdev);
0098 struct clk_hw_onecell_data *data = dvp->data;
0099
0100 clk_hw_unregister_gate(data->hws[1]);
0101 clk_hw_unregister_gate(data->hws[0]);
0102
0103 return 0;
0104 }
0105
0106 static const struct of_device_id clk_dvp_dt_ids[] = {
0107 { .compatible = "brcm,brcm2711-dvp", },
0108 { }
0109 };
0110 MODULE_DEVICE_TABLE(of, clk_dvp_dt_ids);
0111
0112 static struct platform_driver clk_dvp_driver = {
0113 .probe = clk_dvp_probe,
0114 .remove = clk_dvp_remove,
0115 .driver = {
0116 .name = "brcm2711-dvp",
0117 .of_match_table = clk_dvp_dt_ids,
0118 },
0119 };
0120 module_platform_driver(clk_dvp_driver);
0121
0122 MODULE_AUTHOR("Maxime Ripard <maxime@cerno.tech>");
0123 MODULE_DESCRIPTION("BCM2711 DVP clock driver");
0124 MODULE_LICENSE("GPL");