1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2015 Chen-Yu Tsai
5 * Chen-Yu Tsai <wens@csie.org>
9 #include <linux/clk-provider.h>
10 #include <linux/delay.h>
11 #include <linux/init.h>
14 #include <linux/of_device.h>
15 #include <linux/reset.h>
16 #include <linux/platform_device.h>
17 #include <linux/reset-controller.h>
18 #include <linux/slab.h>
19 #include <linux/spinlock.h>
21 #define SUN9I_MMC_WIDTH 4
23 #define SUN9I_MMC_GATE_BIT 16
24 #define SUN9I_MMC_RESET_BIT 18
26 struct sun9i_mmc_clk_data
{
28 void __iomem
*membase
;
30 struct reset_control
*reset
;
31 struct clk_onecell_data clk_data
;
32 struct reset_controller_dev rcdev
;
35 static int sun9i_mmc_reset_assert(struct reset_controller_dev
*rcdev
,
38 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
39 struct sun9i_mmc_clk_data
,
42 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
45 clk_prepare_enable(data
->clk
);
46 spin_lock_irqsave(&data
->lock
, flags
);
49 writel(val
& ~BIT(SUN9I_MMC_RESET_BIT
), reg
);
51 spin_unlock_irqrestore(&data
->lock
, flags
);
52 clk_disable_unprepare(data
->clk
);
57 static int sun9i_mmc_reset_deassert(struct reset_controller_dev
*rcdev
,
60 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
61 struct sun9i_mmc_clk_data
,
64 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
67 clk_prepare_enable(data
->clk
);
68 spin_lock_irqsave(&data
->lock
, flags
);
71 writel(val
| BIT(SUN9I_MMC_RESET_BIT
), reg
);
73 spin_unlock_irqrestore(&data
->lock
, flags
);
74 clk_disable_unprepare(data
->clk
);
79 static int sun9i_mmc_reset_reset(struct reset_controller_dev
*rcdev
,
82 sun9i_mmc_reset_assert(rcdev
, id
);
84 sun9i_mmc_reset_deassert(rcdev
, id
);
89 static const struct reset_control_ops sun9i_mmc_reset_ops
= {
90 .assert = sun9i_mmc_reset_assert
,
91 .deassert
= sun9i_mmc_reset_deassert
,
92 .reset
= sun9i_mmc_reset_reset
,
95 static int sun9i_a80_mmc_config_clk_probe(struct platform_device
*pdev
)
97 struct device_node
*np
= pdev
->dev
.of_node
;
98 struct sun9i_mmc_clk_data
*data
;
99 struct clk_onecell_data
*clk_data
;
100 const char *clk_name
= np
->name
;
101 const char *clk_parent
;
105 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
109 spin_lock_init(&data
->lock
);
111 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
112 /* one clock/reset pair per word */
113 count
= DIV_ROUND_UP((resource_size(r
)), SUN9I_MMC_WIDTH
);
114 data
->membase
= devm_ioremap_resource(&pdev
->dev
, r
);
115 if (IS_ERR(data
->membase
))
116 return PTR_ERR(data
->membase
);
118 clk_data
= &data
->clk_data
;
119 clk_data
->clk_num
= count
;
120 clk_data
->clks
= devm_kcalloc(&pdev
->dev
, count
, sizeof(struct clk
*),
125 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
126 if (IS_ERR(data
->clk
)) {
127 dev_err(&pdev
->dev
, "Could not get clock\n");
128 return PTR_ERR(data
->clk
);
131 data
->reset
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
132 if (IS_ERR(data
->reset
)) {
133 dev_err(&pdev
->dev
, "Could not get reset control\n");
134 return PTR_ERR(data
->reset
);
137 ret
= reset_control_deassert(data
->reset
);
139 dev_err(&pdev
->dev
, "Reset deassert err %d\n", ret
);
143 clk_parent
= __clk_get_name(data
->clk
);
144 for (i
= 0; i
< count
; i
++) {
145 of_property_read_string_index(np
, "clock-output-names",
148 clk_data
->clks
[i
] = clk_register_gate(&pdev
->dev
, clk_name
,
150 data
->membase
+ SUN9I_MMC_WIDTH
* i
,
151 SUN9I_MMC_GATE_BIT
, 0,
154 if (IS_ERR(clk_data
->clks
[i
])) {
155 ret
= PTR_ERR(clk_data
->clks
[i
]);
156 goto err_clk_register
;
160 ret
= of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
162 goto err_clk_provider
;
164 data
->rcdev
.owner
= THIS_MODULE
;
165 data
->rcdev
.nr_resets
= count
;
166 data
->rcdev
.ops
= &sun9i_mmc_reset_ops
;
167 data
->rcdev
.of_node
= pdev
->dev
.of_node
;
169 ret
= reset_controller_register(&data
->rcdev
);
173 platform_set_drvdata(pdev
, data
);
178 of_clk_del_provider(np
);
181 for (i
= 0; i
< count
; i
++)
182 clk_unregister(clk_data
->clks
[i
]);
185 reset_control_assert(data
->reset
);
190 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids
[] = {
191 { .compatible
= "allwinner,sun9i-a80-mmc-config-clk" },
195 static struct platform_driver sun9i_a80_mmc_config_clk_driver
= {
197 .name
= "sun9i-a80-mmc-config-clk",
198 .suppress_bind_attrs
= true,
199 .of_match_table
= sun9i_a80_mmc_config_clk_dt_ids
,
201 .probe
= sun9i_a80_mmc_config_clk_probe
,
203 builtin_platform_driver(sun9i_a80_mmc_config_clk_driver
);