Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 // Copyright 2020 Cerno
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     { /* sentinel */ }
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");