// SPDX-License-Identifier: GPL-2.0-or-later // Copyright 2020 Cerno #include #include #include #include #include #define DVP_HT_RPI_SW_INIT 0x04 #define DVP_HT_RPI_MISC_CONFIG 0x08 #define NR_CLOCKS 2 #define NR_RESETS 6 struct clk_dvp { struct clk_hw_onecell_data *data; struct reset_simple_data reset; }; static const struct clk_parent_data clk_dvp_parent = { .index = 0, }; static int clk_dvp_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *data; struct resource *res; struct clk_dvp *dvp; void __iomem *base; int ret; dvp = devm_kzalloc(&pdev->dev, sizeof(*dvp), GFP_KERNEL); if (!dvp) return -ENOMEM; platform_set_drvdata(pdev, dvp); dvp->data = devm_kzalloc(&pdev->dev, struct_size(dvp->data, hws, NR_CLOCKS), GFP_KERNEL); if (!dvp->data) return -ENOMEM; data = dvp->data; base = devm_platform_get_and_ioremap_resource(pdev, 0, &res); if (IS_ERR(base)) return PTR_ERR(base); dvp->reset.rcdev.owner = THIS_MODULE; dvp->reset.rcdev.nr_resets = NR_RESETS; dvp->reset.rcdev.ops = &reset_simple_ops; dvp->reset.rcdev.of_node = pdev->dev.of_node; dvp->reset.membase = base + DVP_HT_RPI_SW_INIT; spin_lock_init(&dvp->reset.lock); ret = devm_reset_controller_register(&pdev->dev, &dvp->reset.rcdev); if (ret) return ret; data->hws[0] = clk_hw_register_gate_parent_data(&pdev->dev, "hdmi0-108MHz", &clk_dvp_parent, 0, base + DVP_HT_RPI_MISC_CONFIG, 3, CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock); if (IS_ERR(data->hws[0])) return PTR_ERR(data->hws[0]); data->hws[1] = clk_hw_register_gate_parent_data(&pdev->dev, "hdmi1-108MHz", &clk_dvp_parent, 0, base + DVP_HT_RPI_MISC_CONFIG, 4, CLK_GATE_SET_TO_DISABLE, &dvp->reset.lock); if (IS_ERR(data->hws[1])) { ret = PTR_ERR(data->hws[1]); goto unregister_clk0; } data->num = NR_CLOCKS; ret = of_clk_add_hw_provider(pdev->dev.of_node, of_clk_hw_onecell_get, data); if (ret) goto unregister_clk1; return 0; unregister_clk1: clk_hw_unregister_gate(data->hws[1]); unregister_clk0: clk_hw_unregister_gate(data->hws[0]); return ret; }; static int clk_dvp_remove(struct platform_device *pdev) { struct clk_dvp *dvp = platform_get_drvdata(pdev); struct clk_hw_onecell_data *data = dvp->data; clk_hw_unregister_gate(data->hws[1]); clk_hw_unregister_gate(data->hws[0]); return 0; } static const struct of_device_id clk_dvp_dt_ids[] = { { .compatible = "brcm,brcm2711-dvp", }, { /* sentinel */ } }; static struct platform_driver clk_dvp_driver = { .probe = clk_dvp_probe, .remove = clk_dvp_remove, .driver = { .name = "brcm2711-dvp", .of_match_table = clk_dvp_dt_ids, }, }; module_platform_driver(clk_dvp_driver); MODULE_AUTHOR("Maxime Ripard "); MODULE_DESCRIPTION("BCM2711 DVP clock driver"); MODULE_LICENSE("GPL");