Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (C) 2014 Free Electrons
0004  *
0005  * Author: Boris BREZILLON <boris.brezillon@free-electrons.com>
0006  *
0007  * Allwinner A31 APB0 clock gates driver
0008  */
0009 
0010 #include <linux/clk-provider.h>
0011 #include <linux/init.h>
0012 #include <linux/of.h>
0013 #include <linux/of_device.h>
0014 #include <linux/platform_device.h>
0015 
0016 #define SUN6I_APB0_GATES_MAX_SIZE   32
0017 
0018 struct gates_data {
0019     DECLARE_BITMAP(mask, SUN6I_APB0_GATES_MAX_SIZE);
0020 };
0021 
0022 static const struct gates_data sun6i_a31_apb0_gates __initconst = {
0023     .mask = {0x7F},
0024 };
0025 
0026 static const struct gates_data sun8i_a23_apb0_gates __initconst = {
0027     .mask = {0x5D},
0028 };
0029 
0030 static const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = {
0031     { .compatible = "allwinner,sun6i-a31-apb0-gates-clk", .data = &sun6i_a31_apb0_gates },
0032     { .compatible = "allwinner,sun8i-a23-apb0-gates-clk", .data = &sun8i_a23_apb0_gates },
0033     { /* sentinel */ }
0034 };
0035 
0036 static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev)
0037 {
0038     struct device_node *np = pdev->dev.of_node;
0039     struct clk_onecell_data *clk_data;
0040     const struct gates_data *data;
0041     const char *clk_parent;
0042     const char *clk_name;
0043     void __iomem *reg;
0044     int ngates;
0045     int i;
0046     int j = 0;
0047 
0048     if (!np)
0049         return -ENODEV;
0050 
0051     data = of_device_get_match_data(&pdev->dev);
0052     if (!data)
0053         return -ENODEV;
0054 
0055     reg = devm_platform_ioremap_resource(pdev, 0);
0056     if (IS_ERR(reg))
0057         return PTR_ERR(reg);
0058 
0059     clk_parent = of_clk_get_parent_name(np, 0);
0060     if (!clk_parent)
0061         return -EINVAL;
0062 
0063     clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data),
0064                 GFP_KERNEL);
0065     if (!clk_data)
0066         return -ENOMEM;
0067 
0068     /* Worst-case size approximation and memory allocation */
0069     ngates = find_last_bit(data->mask, SUN6I_APB0_GATES_MAX_SIZE);
0070     clk_data->clks = devm_kcalloc(&pdev->dev, (ngates + 1),
0071                       sizeof(struct clk *), GFP_KERNEL);
0072     if (!clk_data->clks)
0073         return -ENOMEM;
0074 
0075     for_each_set_bit(i, data->mask, SUN6I_APB0_GATES_MAX_SIZE) {
0076         of_property_read_string_index(np, "clock-output-names",
0077                           j, &clk_name);
0078 
0079         clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name,
0080                               clk_parent, 0, reg, i,
0081                               0, NULL);
0082         WARN_ON(IS_ERR(clk_data->clks[i]));
0083 
0084         j++;
0085     }
0086 
0087     clk_data->clk_num = ngates + 1;
0088 
0089     return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
0090 }
0091 
0092 static struct platform_driver sun6i_a31_apb0_gates_clk_driver = {
0093     .driver = {
0094         .name = "sun6i-a31-apb0-gates-clk",
0095         .of_match_table = sun6i_a31_apb0_gates_clk_dt_ids,
0096     },
0097     .probe = sun6i_a31_apb0_gates_clk_probe,
0098 };
0099 builtin_platform_driver(sun6i_a31_apb0_gates_clk_driver);