2 * Copyright 2015 Chen-Yu Tsai
4 * Chen-Yu Tsai <wens@csie.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
17 #include <linux/clk-provider.h>
18 #include <linux/clkdev.h>
19 #include <linux/module.h>
21 #include <linux/of_device.h>
22 #include <linux/reset.h>
23 #include <linux/platform_device.h>
24 #include <linux/reset-controller.h>
25 #include <linux/spinlock.h>
27 #define SUN9I_MMC_WIDTH 4
29 #define SUN9I_MMC_GATE_BIT 16
30 #define SUN9I_MMC_RESET_BIT 18
32 struct sun9i_mmc_clk_data
{
34 void __iomem
*membase
;
36 struct reset_control
*reset
;
37 struct clk_onecell_data clk_data
;
38 struct reset_controller_dev rcdev
;
41 static int sun9i_mmc_reset_assert(struct reset_controller_dev
*rcdev
,
44 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
45 struct sun9i_mmc_clk_data
,
48 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
51 clk_prepare_enable(data
->clk
);
52 spin_lock_irqsave(&data
->lock
, flags
);
55 writel(val
& ~BIT(SUN9I_MMC_RESET_BIT
), reg
);
57 spin_unlock_irqrestore(&data
->lock
, flags
);
58 clk_disable_unprepare(data
->clk
);
63 static int sun9i_mmc_reset_deassert(struct reset_controller_dev
*rcdev
,
66 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
67 struct sun9i_mmc_clk_data
,
70 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
73 clk_prepare_enable(data
->clk
);
74 spin_lock_irqsave(&data
->lock
, flags
);
77 writel(val
| BIT(SUN9I_MMC_RESET_BIT
), reg
);
79 spin_unlock_irqrestore(&data
->lock
, flags
);
80 clk_disable_unprepare(data
->clk
);
85 static struct reset_control_ops sun9i_mmc_reset_ops
= {
86 .assert = sun9i_mmc_reset_assert
,
87 .deassert
= sun9i_mmc_reset_deassert
,
90 static int sun9i_a80_mmc_config_clk_probe(struct platform_device
*pdev
)
92 struct device_node
*np
= pdev
->dev
.of_node
;
93 struct sun9i_mmc_clk_data
*data
;
94 struct clk_onecell_data
*clk_data
;
95 const char *clk_name
= np
->name
;
96 const char *clk_parent
;
100 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
104 spin_lock_init(&data
->lock
);
106 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
107 /* one clock/reset pair per word */
108 count
= DIV_ROUND_UP((r
->end
- r
->start
+ 1), SUN9I_MMC_WIDTH
);
109 data
->membase
= devm_ioremap_resource(&pdev
->dev
, r
);
110 if (IS_ERR(data
->membase
))
111 return PTR_ERR(data
->membase
);
113 clk_data
= &data
->clk_data
;
114 clk_data
->clk_num
= count
;
115 clk_data
->clks
= devm_kcalloc(&pdev
->dev
, count
, sizeof(struct clk
*),
120 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
121 if (IS_ERR(data
->clk
)) {
122 dev_err(&pdev
->dev
, "Could not get clock\n");
123 return PTR_ERR(data
->clk
);
126 data
->reset
= devm_reset_control_get(&pdev
->dev
, NULL
);
127 if (IS_ERR(data
->reset
)) {
128 dev_err(&pdev
->dev
, "Could not get reset control\n");
129 return PTR_ERR(data
->reset
);
132 ret
= reset_control_deassert(data
->reset
);
134 dev_err(&pdev
->dev
, "Reset deassert err %d\n", ret
);
138 clk_parent
= __clk_get_name(data
->clk
);
139 for (i
= 0; i
< count
; i
++) {
140 of_property_read_string_index(np
, "clock-output-names",
143 clk_data
->clks
[i
] = clk_register_gate(&pdev
->dev
, clk_name
,
145 data
->membase
+ SUN9I_MMC_WIDTH
* i
,
146 SUN9I_MMC_GATE_BIT
, 0,
149 if (IS_ERR(clk_data
->clks
[i
])) {
150 ret
= PTR_ERR(clk_data
->clks
[i
]);
151 goto err_clk_register
;
155 ret
= of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
157 goto err_clk_provider
;
159 data
->rcdev
.owner
= THIS_MODULE
;
160 data
->rcdev
.nr_resets
= count
;
161 data
->rcdev
.ops
= &sun9i_mmc_reset_ops
;
162 data
->rcdev
.of_node
= pdev
->dev
.of_node
;
164 ret
= reset_controller_register(&data
->rcdev
);
168 platform_set_drvdata(pdev
, data
);
173 of_clk_del_provider(np
);
176 for (i
= 0; i
< count
; i
++)
177 clk_unregister(clk_data
->clks
[i
]);
180 reset_control_assert(data
->reset
);
185 static int sun9i_a80_mmc_config_clk_remove(struct platform_device
*pdev
)
187 struct device_node
*np
= pdev
->dev
.of_node
;
188 struct sun9i_mmc_clk_data
*data
= platform_get_drvdata(pdev
);
189 struct clk_onecell_data
*clk_data
= &data
->clk_data
;
192 reset_controller_unregister(&data
->rcdev
);
193 of_clk_del_provider(np
);
194 for (i
= 0; i
< clk_data
->clk_num
; i
++)
195 clk_unregister(clk_data
->clks
[i
]);
197 reset_control_assert(data
->reset
);
202 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids
[] = {
203 { .compatible
= "allwinner,sun9i-a80-mmc-config-clk" },
207 static struct platform_driver sun9i_a80_mmc_config_clk_driver
= {
209 .name
= "sun9i-a80-mmc-config-clk",
210 .of_match_table
= sun9i_a80_mmc_config_clk_dt_ids
,
212 .probe
= sun9i_a80_mmc_config_clk_probe
,
213 .remove
= sun9i_a80_mmc_config_clk_remove
,
215 module_platform_driver(sun9i_a80_mmc_config_clk_driver
);
217 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
218 MODULE_DESCRIPTION("Allwinner A80 MMC clock/reset Driver");
219 MODULE_LICENSE("GPL v2");