Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-or-later
2 : : // Copyright 2020 Cerno
3 : :
4 : : #include <linux/clk-provider.h>
5 : : #include <linux/module.h>
6 : : #include <linux/platform_device.h>
7 : : #include <linux/reset-controller.h>
8 : : #include <linux/reset/reset-simple.h>
9 : :
10 : : #define DVP_HT_RPI_SW_INIT 0x04
11 : : #define DVP_HT_RPI_MISC_CONFIG 0x08
12 : :
13 : : #define NR_CLOCKS 2
14 : : #define NR_RESETS 6
15 : :
16 : : struct clk_dvp {
17 : : struct clk_hw_onecell_data *data;
18 : : struct reset_simple_data reset;
19 : : };
20 : :
21 : 0 : static int clk_dvp_probe(struct platform_device *pdev)
22 : : {
23 : : struct clk_hw_onecell_data *data;
24 : : struct resource *res;
25 : : struct clk_dvp *dvp;
26 : : void __iomem *base;
27 : : const char *parent;
28 : : int ret;
29 : :
30 : 0 : dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL);
31 [ # # ]: 0 : if (!dvp)
32 : : return -ENOMEM;
33 : : platform_set_drvdata(pdev, dvp);
34 : :
35 : 0 : dvp->data = devm_kzalloc(&pdev->dev,
36 : : struct_size(dvp->data, hws, NR_CLOCKS),
37 : : GFP_KERNEL);
38 [ # # ]: 0 : if (!dvp->data)
39 : : return -ENOMEM;
40 : : data = dvp->data;
41 : :
42 : 0 : res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
43 : 0 : base = devm_ioremap_resource(&pdev->dev, res);
44 [ # # ]: 0 : if (IS_ERR(base))
45 : 0 : return PTR_ERR(base);
46 : :
47 : 0 : dvp->reset.rcdev.owner = THIS_MODULE;
48 : 0 : dvp->reset.rcdev.nr_resets = NR_RESETS;
49 : 0 : dvp->reset.rcdev.ops = &reset_simple_ops;
50 : 0 : dvp->reset.rcdev.of_node = pdev->dev.of_node;
51 : 0 : dvp->reset.membase = base + DVP_HT_RPI_SW_INIT;
52 : 0 : spin_lock_init(&dvp->reset.lock);
53 : :
54 : 0 : ret = reset_controller_register(&dvp->reset.rcdev);
55 [ # # ]: 0 : if (ret)
56 : : return ret;
57 : :
58 : 0 : parent = of_clk_get_parent_name(pdev->dev.of_node, 0);
59 [ # # ]: 0 : if (!parent)
60 : : goto unregister_reset;
61 : :
62 : 0 : data->hws[0] = clk_hw_register_gate(&pdev->dev, "hdmi0-108MHz",
63 : : parent, 0,
64 : : base + DVP_HT_RPI_MISC_CONFIG, 3,
65 : : CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock);
66 [ # # ]: 0 : if (IS_ERR(data->hws[0])) {
67 : : ret = PTR_ERR(data->hws[0]);
68 : 0 : goto unregister_reset;
69 : : }
70 : :
71 : 0 : data->hws[1] = clk_hw_register_gate(&pdev->dev, "hdmi1-108MHz",
72 : : parent, 0,
73 : : base + DVP_HT_RPI_MISC_CONFIG, 4,
74 : : CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock);
75 [ # # ]: 0 : if (IS_ERR(data->hws[1])) {
76 : : ret = PTR_ERR(data->hws[1]);
77 : 0 : goto unregister_clk0;
78 : : }
79 : :
80 : 0 : data->num = NR_CLOCKS;
81 : 0 : ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get,
82 : : data);
83 [ # # ]: 0 : if (ret)
84 : : goto unregister_clk1;
85 : :
86 : : return 0;
87 : :
88 : :
89 : : unregister_clk1:
90 : 0 : clk_hw_unregister_gate(data->hws[1]);
91 : :
92 : : unregister_clk0:
93 : 0 : clk_hw_unregister_gate(data->hws[0]);
94 : :
95 : : unregister_reset:
96 : 0 : reset_controller_unregister(&dvp->reset.rcdev);
97 : 0 : return ret;
98 : : };
99 : :
100 : 0 : static int clk_dvp_remove(struct platform_device *pdev)
101 : : {
102 : : struct clk_dvp *dvp = platform_get_drvdata(pdev);
103 : 0 : struct clk_hw_onecell_data *data = dvp->data;
104 : :
105 : 0 : clk_hw_unregister_gate(data->hws[1]);
106 : 0 : clk_hw_unregister_gate(data->hws[0]);
107 : 0 : reset_controller_unregister(&dvp->reset.rcdev);
108 : :
109 : 0 : return 0;
110 : : }
111 : :
112 : : static const struct of_device_id clk_dvp_dt_ids[] = {
113 : : { .compatible = "brcm,brcm2711-dvp", },
114 : : { /* sentinel */ }
115 : : };
116 : :
117 : : static struct platform_driver clk_dvp_driver = {
118 : : .probe = clk_dvp_probe,
119 : : .remove = clk_dvp_remove,
120 : : .driver = {
121 : : .name = "brcm2711-dvp",
122 : : .of_match_table = clk_dvp_dt_ids,
123 : : },
124 : : };
125 : 207 : module_platform_driver(clk_dvp_driver);
|