Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright 2015 Chen-Yu Tsai
0004  *
0005  * Chen-Yu Tsai <wens@csie.org>
0006  */
0007 
0008 #include <linux/clk.h>
0009 #include <linux/clk-provider.h>
0010 #include <linux/delay.h>
0011 #include <linux/init.h>
0012 #include <linux/io.h>
0013 #include <linux/of.h>
0014 #include <linux/of_device.h>
0015 #include <linux/reset.h>
0016 #include <linux/platform_device.h>
0017 #include <linux/reset-controller.h>
0018 #include <linux/slab.h>
0019 #include <linux/spinlock.h>
0020 
0021 #define SUN9I_MMC_WIDTH     4
0022 
0023 #define SUN9I_MMC_GATE_BIT  16
0024 #define SUN9I_MMC_RESET_BIT 18
0025 
0026 struct sun9i_mmc_clk_data {
0027     spinlock_t          lock;
0028     void __iomem            *membase;
0029     struct clk          *clk;
0030     struct reset_control        *reset;
0031     struct clk_onecell_data     clk_data;
0032     struct reset_controller_dev rcdev;
0033 };
0034 
0035 static int sun9i_mmc_reset_assert(struct reset_controller_dev *rcdev,
0036                   unsigned long id)
0037 {
0038     struct sun9i_mmc_clk_data *data = container_of(rcdev,
0039                                struct sun9i_mmc_clk_data,
0040                                rcdev);
0041     unsigned long flags;
0042     void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
0043     u32 val;
0044 
0045     clk_prepare_enable(data->clk);
0046     spin_lock_irqsave(&data->lock, flags);
0047 
0048     val = readl(reg);
0049     writel(val & ~BIT(SUN9I_MMC_RESET_BIT), reg);
0050 
0051     spin_unlock_irqrestore(&data->lock, flags);
0052     clk_disable_unprepare(data->clk);
0053 
0054     return 0;
0055 }
0056 
0057 static int sun9i_mmc_reset_deassert(struct reset_controller_dev *rcdev,
0058                 unsigned long id)
0059 {
0060     struct sun9i_mmc_clk_data *data = container_of(rcdev,
0061                                struct sun9i_mmc_clk_data,
0062                                rcdev);
0063     unsigned long flags;
0064     void __iomem *reg = data->membase + SUN9I_MMC_WIDTH * id;
0065     u32 val;
0066 
0067     clk_prepare_enable(data->clk);
0068     spin_lock_irqsave(&data->lock, flags);
0069 
0070     val = readl(reg);
0071     writel(val | BIT(SUN9I_MMC_RESET_BIT), reg);
0072 
0073     spin_unlock_irqrestore(&data->lock, flags);
0074     clk_disable_unprepare(data->clk);
0075 
0076     return 0;
0077 }
0078 
0079 static int sun9i_mmc_reset_reset(struct reset_controller_dev *rcdev,
0080                  unsigned long id)
0081 {
0082     sun9i_mmc_reset_assert(rcdev, id);
0083     udelay(10);
0084     sun9i_mmc_reset_deassert(rcdev, id);
0085 
0086     return 0;
0087 }
0088 
0089 static const struct reset_control_ops sun9i_mmc_reset_ops = {
0090     .assert     = sun9i_mmc_reset_assert,
0091     .deassert   = sun9i_mmc_reset_deassert,
0092     .reset      = sun9i_mmc_reset_reset,
0093 };
0094 
0095 static int sun9i_a80_mmc_config_clk_probe(struct platform_device *pdev)
0096 {
0097     struct device_node *np = pdev->dev.of_node;
0098     struct sun9i_mmc_clk_data *data;
0099     struct clk_onecell_data *clk_data;
0100     const char *clk_name = np->name;
0101     const char *clk_parent;
0102     struct resource *r;
0103     int count, i, ret;
0104 
0105     data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
0106     if (!data)
0107         return -ENOMEM;
0108 
0109     spin_lock_init(&data->lock);
0110 
0111     r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0112     if (!r)
0113         return -EINVAL;
0114     /* one clock/reset pair per word */
0115     count = DIV_ROUND_UP((resource_size(r)), SUN9I_MMC_WIDTH);
0116     data->membase = devm_ioremap_resource(&pdev->dev, r);
0117     if (IS_ERR(data->membase))
0118         return PTR_ERR(data->membase);
0119 
0120     clk_data = &data->clk_data;
0121     clk_data->clk_num = count;
0122     clk_data->clks = devm_kcalloc(&pdev->dev, count, sizeof(struct clk *),
0123                       GFP_KERNEL);
0124     if (!clk_data->clks)
0125         return -ENOMEM;
0126 
0127     data->clk = devm_clk_get(&pdev->dev, NULL);
0128     if (IS_ERR(data->clk)) {
0129         dev_err(&pdev->dev, "Could not get clock\n");
0130         return PTR_ERR(data->clk);
0131     }
0132 
0133     data->reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
0134     if (IS_ERR(data->reset)) {
0135         dev_err(&pdev->dev, "Could not get reset control\n");
0136         return PTR_ERR(data->reset);
0137     }
0138 
0139     ret = reset_control_deassert(data->reset);
0140     if (ret) {
0141         dev_err(&pdev->dev, "Reset deassert err %d\n", ret);
0142         return ret;
0143     }
0144 
0145     clk_parent = __clk_get_name(data->clk);
0146     for (i = 0; i < count; i++) {
0147         of_property_read_string_index(np, "clock-output-names",
0148                           i, &clk_name);
0149 
0150         clk_data->clks[i] = clk_register_gate(&pdev->dev, clk_name,
0151                               clk_parent, 0,
0152                               data->membase + SUN9I_MMC_WIDTH * i,
0153                               SUN9I_MMC_GATE_BIT, 0,
0154                               &data->lock);
0155 
0156         if (IS_ERR(clk_data->clks[i])) {
0157             ret = PTR_ERR(clk_data->clks[i]);
0158             goto err_clk_register;
0159         }
0160     }
0161 
0162     ret = of_clk_add_provider(np, of_clk_src_onecell_get, clk_data);
0163     if (ret)
0164         goto err_clk_provider;
0165 
0166     data->rcdev.owner = THIS_MODULE;
0167     data->rcdev.nr_resets = count;
0168     data->rcdev.ops = &sun9i_mmc_reset_ops;
0169     data->rcdev.of_node = pdev->dev.of_node;
0170 
0171     ret = reset_controller_register(&data->rcdev);
0172     if (ret)
0173         goto err_rc_reg;
0174 
0175     platform_set_drvdata(pdev, data);
0176 
0177     return 0;
0178 
0179 err_rc_reg:
0180     of_clk_del_provider(np);
0181 
0182 err_clk_provider:
0183     for (i = 0; i < count; i++)
0184         clk_unregister(clk_data->clks[i]);
0185 
0186 err_clk_register:
0187     reset_control_assert(data->reset);
0188 
0189     return ret;
0190 }
0191 
0192 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids[] = {
0193     { .compatible = "allwinner,sun9i-a80-mmc-config-clk" },
0194     { /* sentinel */ }
0195 };
0196 
0197 static struct platform_driver sun9i_a80_mmc_config_clk_driver = {
0198     .driver = {
0199         .name = "sun9i-a80-mmc-config-clk",
0200         .suppress_bind_attrs = true,
0201         .of_match_table = sun9i_a80_mmc_config_clk_dt_ids,
0202     },
0203     .probe = sun9i_a80_mmc_config_clk_probe,
0204 };
0205 builtin_platform_driver(sun9i_a80_mmc_config_clk_driver);