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 : 3 : module_platform_driver(clk_dvp_driver);