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>
23 #include <linux/of_device.h>
24 #include <linux/reset.h>
25 #include <linux/platform_device.h>
26 #include <linux/reset-controller.h>
27 #include <linux/slab.h>
28 #include <linux/spinlock.h>
30 #define SUN9I_MMC_WIDTH 4
32 #define SUN9I_MMC_GATE_BIT 16
33 #define SUN9I_MMC_RESET_BIT 18
35 struct sun9i_mmc_clk_data
{
37 void __iomem
*membase
;
39 struct reset_control
*reset
;
40 struct clk_onecell_data clk_data
;
41 struct reset_controller_dev rcdev
;
44 static int sun9i_mmc_reset_assert(struct reset_controller_dev
*rcdev
,
47 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
48 struct sun9i_mmc_clk_data
,
51 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
54 clk_prepare_enable(data
->clk
);
55 spin_lock_irqsave(&data
->lock
, flags
);
58 writel(val
& ~BIT(SUN9I_MMC_RESET_BIT
), reg
);
60 spin_unlock_irqrestore(&data
->lock
, flags
);
61 clk_disable_unprepare(data
->clk
);
66 static int sun9i_mmc_reset_deassert(struct reset_controller_dev
*rcdev
,
69 struct sun9i_mmc_clk_data
*data
= container_of(rcdev
,
70 struct sun9i_mmc_clk_data
,
73 void __iomem
*reg
= data
->membase
+ SUN9I_MMC_WIDTH
* id
;
76 clk_prepare_enable(data
->clk
);
77 spin_lock_irqsave(&data
->lock
, flags
);
80 writel(val
| BIT(SUN9I_MMC_RESET_BIT
), reg
);
82 spin_unlock_irqrestore(&data
->lock
, flags
);
83 clk_disable_unprepare(data
->clk
);
88 static int sun9i_mmc_reset_reset(struct reset_controller_dev
*rcdev
,
91 sun9i_mmc_reset_assert(rcdev
, id
);
93 sun9i_mmc_reset_deassert(rcdev
, id
);
98 static const struct reset_control_ops sun9i_mmc_reset_ops
= {
99 .assert = sun9i_mmc_reset_assert
,
100 .deassert
= sun9i_mmc_reset_deassert
,
101 .reset
= sun9i_mmc_reset_reset
,
104 static int sun9i_a80_mmc_config_clk_probe(struct platform_device
*pdev
)
106 struct device_node
*np
= pdev
->dev
.of_node
;
107 struct sun9i_mmc_clk_data
*data
;
108 struct clk_onecell_data
*clk_data
;
109 const char *clk_name
= np
->name
;
110 const char *clk_parent
;
114 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
118 spin_lock_init(&data
->lock
);
120 r
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
121 /* one clock/reset pair per word */
122 count
= DIV_ROUND_UP((resource_size(r
)), SUN9I_MMC_WIDTH
);
123 data
->membase
= devm_ioremap_resource(&pdev
->dev
, r
);
124 if (IS_ERR(data
->membase
))
125 return PTR_ERR(data
->membase
);
127 clk_data
= &data
->clk_data
;
128 clk_data
->clk_num
= count
;
129 clk_data
->clks
= devm_kcalloc(&pdev
->dev
, count
, sizeof(struct clk
*),
134 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
135 if (IS_ERR(data
->clk
)) {
136 dev_err(&pdev
->dev
, "Could not get clock\n");
137 return PTR_ERR(data
->clk
);
140 data
->reset
= devm_reset_control_get_exclusive(&pdev
->dev
, NULL
);
141 if (IS_ERR(data
->reset
)) {
142 dev_err(&pdev
->dev
, "Could not get reset control\n");
143 return PTR_ERR(data
->reset
);
146 ret
= reset_control_deassert(data
->reset
);
148 dev_err(&pdev
->dev
, "Reset deassert err %d\n", ret
);
152 clk_parent
= __clk_get_name(data
->clk
);
153 for (i
= 0; i
< count
; i
++) {
154 of_property_read_string_index(np
, "clock-output-names",
157 clk_data
->clks
[i
] = clk_register_gate(&pdev
->dev
, clk_name
,
159 data
->membase
+ SUN9I_MMC_WIDTH
* i
,
160 SUN9I_MMC_GATE_BIT
, 0,
163 if (IS_ERR(clk_data
->clks
[i
])) {
164 ret
= PTR_ERR(clk_data
->clks
[i
]);
165 goto err_clk_register
;
169 ret
= of_clk_add_provider(np
, of_clk_src_onecell_get
, clk_data
);
171 goto err_clk_provider
;
173 data
->rcdev
.owner
= THIS_MODULE
;
174 data
->rcdev
.nr_resets
= count
;
175 data
->rcdev
.ops
= &sun9i_mmc_reset_ops
;
176 data
->rcdev
.of_node
= pdev
->dev
.of_node
;
178 ret
= reset_controller_register(&data
->rcdev
);
182 platform_set_drvdata(pdev
, data
);
187 of_clk_del_provider(np
);
190 for (i
= 0; i
< count
; i
++)
191 clk_unregister(clk_data
->clks
[i
]);
194 reset_control_assert(data
->reset
);
199 static const struct of_device_id sun9i_a80_mmc_config_clk_dt_ids
[] = {
200 { .compatible
= "allwinner,sun9i-a80-mmc-config-clk" },
204 static struct platform_driver sun9i_a80_mmc_config_clk_driver
= {
206 .name
= "sun9i-a80-mmc-config-clk",
207 .suppress_bind_attrs
= true,
208 .of_match_table
= sun9i_a80_mmc_config_clk_dt_ids
,
210 .probe
= sun9i_a80_mmc_config_clk_probe
,
212 builtin_platform_driver(sun9i_a80_mmc_config_clk_driver
);