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.h>
18 #include <linux/clk-provider.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/slab.h>
26 #include <linux/spinlock.h>
28 #define SUN9I_MMC_WIDTH 4
30 #define SUN9I_MMC_GATE_BIT 16
31 #define SUN9I_MMC_RESET_BIT 18
33 struct sun9i_mmc_clk_data
{
35 void __iomem
*membase
;
37 struct reset_control
*reset
;
38 struct clk_onecell_data clk_data
;
39 struct reset_controller_dev rcdev
;
42 static int sun9i_mmc_reset_assert(struct reset_controller_dev
*rcdev
,
45 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
46 struct sun9i_mmc_clk_data
,
49 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
52 clk_prepare_enable(data
->clk
);
53 spin_lock_irqsave(&data
->lock
, flags
);
56 writel(val
& ~BIT(SUN9I_MMC_RESET_BIT
), reg
);
58 spin_unlock_irqrestore(&data
->lock
, flags
);
59 clk_disable_unprepare(data
->clk
);
64 static int sun9i_mmc_reset_deassert(struct reset_controller_dev
*rcdev
,
67 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
68 struct sun9i_mmc_clk_data
,
71 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
74 clk_prepare_enable(data
->clk
);
75 spin_lock_irqsave(&data
->lock
, flags
);
78 writel(val
| BIT(SUN9I_MMC_RESET_BIT
), reg
);
80 spin_unlock_irqrestore(&data
->lock
, flags
);
81 clk_disable_unprepare(data
->clk
);
86 static const struct reset_control_ops sun9i_mmc_reset_ops
= {
87 .assert = sun9i_mmc_reset_assert
,
88 .deassert
= sun9i_mmc_reset_deassert
,
91 static int sun9i_a80_mmc_config_clk_probe(struct platform_device
*pdev
)
93 struct device_node
*np
= pdev
->dev
.of_node
;
94 struct sun9i_mmc_clk_data
*data
;
95 struct clk_onecell_data
*clk_data
;
96 const char *clk_name
= np
->name
;
97 const char *clk_parent
;
101 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
105 spin_lock_init(&data
->lock
);
107 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
108 /* one clock/reset pair per word */
109 count
= DIV_ROUND_UP((r
->end
- r
->start
+ 1), SUN9I_MMC_WIDTH
);
110 data
->membase
= devm_ioremap_resource(&pdev
->dev
, r
);
111 if (IS_ERR(data
->membase
))
112 return PTR_ERR(data
->membase
);
114 clk_data
= &data
->clk_data
;
115 clk_data
->clk_num
= count
;
116 clk_data
->clks
= devm_kcalloc(&pdev
->dev
, count
, sizeof(struct clk
*),
121 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
122 if (IS_ERR(data
->clk
)) {
123 dev_err(&pdev
->dev
, "Could not get clock\n");
124 return PTR_ERR(data
->clk
);
127 data
->reset
= devm_reset_control_get(&pdev
->dev
, NULL
);
128 if (IS_ERR(data
->reset
)) {
129 dev_err(&pdev
->dev
, "Could not get reset control\n");
130 return PTR_ERR(data
->reset
);
133 ret
= reset_control_deassert(data
->reset
);
135 dev_err(&pdev
->dev
, "Reset deassert err %d\n", ret
);
139 clk_parent
= __clk_get_name(data
->clk
);
140 for (i
= 0; i
< count
; i
++) {
141 of_property_read_string_index(np
, "clock-output-names",
144 clk_data
->clks
[i
] = clk_register_gate(&pdev
->dev
, clk_name
,
146 data
->membase
+ SUN9I_MMC_WIDTH
* i
,
147 SUN9I_MMC_GATE_BIT
, 0,
150 if (IS_ERR(clk_data
->clks
[i
])) {
151 ret
= PTR_ERR(clk_data
->clks
[i
]);
152 goto err_clk_register
;
156 ret
= of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
158 goto err_clk_provider
;
160 data
->rcdev
.owner
= THIS_MODULE
;
161 data
->rcdev
.nr_resets
= count
;
162 data
->rcdev
.ops
= &sun9i_mmc_reset_ops
;
163 data
->rcdev
.of_node
= pdev
->dev
.of_node
;
165 ret
= reset_controller_register(&data
->rcdev
);
169 platform_set_drvdata(pdev
, data
);
174 of_clk_del_provider(np
);
177 for (i
= 0; i
< count
; i
++)
178 clk_unregister(clk_data
->clks
[i
]);
181 reset_control_assert(data
->reset
);
186 static int sun9i_a80_mmc_config_clk_remove(struct platform_device
*pdev
)
188 struct device_node
*np
= pdev
->dev
.of_node
;
189 struct sun9i_mmc_clk_data
*data
= platform_get_drvdata(pdev
);
190 struct clk_onecell_data
*clk_data
= &data
->clk_data
;
193 reset_controller_unregister(&data
->rcdev
);
194 of_clk_del_provider(np
);
195 for (i
= 0; i
< clk_data
->clk_num
; i
++)
196 clk_unregister(clk_data
->clks
[i
]);
198 reset_control_assert(data
->reset
);
203 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids
[] = {
204 { .compatible
= "allwinner,sun9i-a80-mmc-config-clk" },
207 MODULE_DEVICE_TABLE(of
, sun9i_a80_mmc_config_clk_dt_ids
);
209 static struct platform_driver sun9i_a80_mmc_config_clk_driver
= {
211 .name
= "sun9i-a80-mmc-config-clk",
212 .of_match_table
= sun9i_a80_mmc_config_clk_dt_ids
,
214 .probe
= sun9i_a80_mmc_config_clk_probe
,
215 .remove
= sun9i_a80_mmc_config_clk_remove
,
217 module_platform_driver(sun9i_a80_mmc_config_clk_driver
);
219 MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
220 MODULE_DESCRIPTION("Allwinner A80 MMC clock/reset Driver");
221 MODULE_LICENSE("GPL v2");