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/delay.h>
20 #include <linux/init.h>
22 #include <linux/of_device.h>
23 #include <linux/reset.h>
24 #include <linux/platform_device.h>
25 #include <linux/reset-controller.h>
26 #include <linux/slab.h>
27 #include <linux/spinlock.h>
29 #define SUN9I_MMC_WIDTH 4
31 #define SUN9I_MMC_GATE_BIT 16
32 #define SUN9I_MMC_RESET_BIT 18
34 struct sun9i_mmc_clk_data
{
36 void __iomem
*membase
;
38 struct reset_control
*reset
;
39 struct clk_onecell_data clk_data
;
40 struct reset_controller_dev rcdev
;
43 static int sun9i_mmc_reset_assert(struct reset_controller_dev
*rcdev
,
46 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
47 struct sun9i_mmc_clk_data
,
50 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
53 clk_prepare_enable(data
->clk
);
54 spin_lock_irqsave(&data
->lock
, flags
);
57 writel(val
& ~BIT(SUN9I_MMC_RESET_BIT
), reg
);
59 spin_unlock_irqrestore(&data
->lock
, flags
);
60 clk_disable_unprepare(data
->clk
);
65 static int sun9i_mmc_reset_deassert(struct reset_controller_dev
*rcdev
,
68 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
69 struct sun9i_mmc_clk_data
,
72 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
75 clk_prepare_enable(data
->clk
);
76 spin_lock_irqsave(&data
->lock
, flags
);
79 writel(val
| BIT(SUN9I_MMC_RESET_BIT
), reg
);
81 spin_unlock_irqrestore(&data
->lock
, flags
);
82 clk_disable_unprepare(data
->clk
);
87 static int sun9i_mmc_reset_reset(struct reset_controller_dev
*rcdev
,
90 sun9i_mmc_reset_assert(rcdev
, id
);
92 sun9i_mmc_reset_deassert(rcdev
, id
);
97 static const struct reset_control_ops sun9i_mmc_reset_ops
= {
98 .assert = sun9i_mmc_reset_assert
,
99 .deassert
= sun9i_mmc_reset_deassert
,
100 .reset
= sun9i_mmc_reset_reset
,
103 static int sun9i_a80_mmc_config_clk_probe(struct platform_device
*pdev
)
105 struct device_node
*np
= pdev
->dev
.of_node
;
106 struct sun9i_mmc_clk_data
*data
;
107 struct clk_onecell_data
*clk_data
;
108 const char *clk_name
= np
->name
;
109 const char *clk_parent
;
113 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
117 spin_lock_init(&data
->lock
);
119 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
120 /* one clock/reset pair per word */
121 count
= DIV_ROUND_UP((resource_size(r
)), SUN9I_MMC_WIDTH
);
122 data
->membase
= devm_ioremap_resource(&pdev
->dev
, r
);
123 if (IS_ERR(data
->membase
))
124 return PTR_ERR(data
->membase
);
126 clk_data
= &data
->clk_data
;
127 clk_data
->clk_num
= count
;
128 clk_data
->clks
= devm_kcalloc(&pdev
->dev
, count
, sizeof(struct clk
*),
133 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
134 if (IS_ERR(data
->clk
)) {
135 dev_err(&pdev
->dev
, "Could not get clock\n");
136 return PTR_ERR(data
->clk
);
139 data
->reset
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
140 if (IS_ERR(data
->reset
)) {
141 dev_err(&pdev
->dev
, "Could not get reset control\n");
142 return PTR_ERR(data
->reset
);
145 ret
= reset_control_deassert(data
->reset
);
147 dev_err(&pdev
->dev
, "Reset deassert err %d\n", ret
);
151 clk_parent
= __clk_get_name(data
->clk
);
152 for (i
= 0; i
< count
; i
++) {
153 of_property_read_string_index(np
, "clock-output-names",
156 clk_data
->clks
[i
] = clk_register_gate(&pdev
->dev
, clk_name
,
158 data
->membase
+ SUN9I_MMC_WIDTH
* i
,
159 SUN9I_MMC_GATE_BIT
, 0,
162 if (IS_ERR(clk_data
->clks
[i
])) {
163 ret
= PTR_ERR(clk_data
->clks
[i
]);
164 goto err_clk_register
;
168 ret
= of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
170 goto err_clk_provider
;
172 data
->rcdev
.owner
= THIS_MODULE
;
173 data
->rcdev
.nr_resets
= count
;
174 data
->rcdev
.ops
= &sun9i_mmc_reset_ops
;
175 data
->rcdev
.of_node
= pdev
->dev
.of_node
;
177 ret
= reset_controller_register(&data
->rcdev
);
181 platform_set_drvdata(pdev
, data
);
186 of_clk_del_provider(np
);
189 for (i
= 0; i
< count
; i
++)
190 clk_unregister(clk_data
->clks
[i
]);
193 reset_control_assert(data
->reset
);
198 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids
[] = {
199 { .compatible
= "allwinner,sun9i-a80-mmc-config-clk" },
203 static struct platform_driver sun9i_a80_mmc_config_clk_driver
= {
205 .name
= "sun9i-a80-mmc-config-clk",
206 .suppress_bind_attrs
= true,
207 .of_match_table
= sun9i_a80_mmc_config_clk_dt_ids
,
209 .probe
= sun9i_a80_mmc_config_clk_probe
,
211 builtin_platform_driver(sun9i_a80_mmc_config_clk_driver
);