-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
clk: sunxi: add PRCM (Power/Reset/Clock Management) clks support
The PRCM (Power/Reset/Clock Management) unit provides several clock devices: - AR100 clk: used to clock the Power Management co-processor - AHB0 clk: used to clock the AHB0 bus - APB0 clk and gates: used to clk peripherals connected to the APB0 bus Add support for these clks in a separate driver so that they can be probed as platform devices instead of registered during early init. This is needed to be able to probe PRCM MFD subdevices. Signed-off-by: Boris BREZILLON <boris.brezillon@free-electrons.com> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Emilio López <emilio@elopez.com.ar>
- Loading branch information
Boris BREZILLON
authored and
Maxime Ripard
committed
Jun 11, 2014
1 parent
efb3184
commit c8a76ca
Showing
4 changed files
with
411 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
/* | ||
* Copyright (C) 2014 Free Electrons | ||
* | ||
* License Terms: GNU General Public License v2 | ||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | ||
* | ||
* Allwinner A31 APB0 clock gates driver | ||
* | ||
*/ | ||
|
||
#include <linux/clk-provider.h> | ||
#include <linux/module.h> | ||
#include <linux/of.h> | ||
#include <linux/platform_device.h> | ||
|
||
#define SUN6I_APB0_GATES_MAX_SIZE 32 | ||
|
||
static int sun6i_a31_apb0_gates_clk_probe(struct platform_device *pdev) | ||
{ | ||
struct device_node *np = pdev->dev.of_node; | ||
struct clk_onecell_data *clk_data; | ||
const char *clk_parent; | ||
const char *clk_name; | ||
struct resource *r; | ||
void __iomem *reg; | ||
int gate_id; | ||
int ngates; | ||
int i; | ||
|
||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
reg = devm_ioremap_resource(&pdev->dev, r); | ||
if (!reg) | ||
return PTR_ERR(reg); | ||
|
||
clk_parent = of_clk_get_parent_name(np, 0); | ||
if (!clk_parent) | ||
return -EINVAL; | ||
|
||
ngates = of_property_count_strings(np, "clock-output-names"); | ||
if (ngates < 0) | ||
return ngates; | ||
|
||
if (!ngates || ngates > SUN6I_APB0_GATES_MAX_SIZE) | ||
return -EINVAL; | ||
|
||
clk_data = devm_kzalloc(&pdev->dev, sizeof(struct clk_onecell_data), | ||
GFP_KERNEL); | ||
if (!clk_data) | ||
return -ENOMEM; | ||
|
||
clk_data->clks = devm_kzalloc(&pdev->dev, | ||
SUN6I_APB0_GATES_MAX_SIZE * | ||
sizeof(struct clk *), | ||
GFP_KERNEL); | ||
if (!clk_data->clks) | ||
return -ENOMEM; | ||
|
||
for (i = 0; i < ngates; i++) { | ||
of_property_read_string_index(np, "clock-output-names", | ||
i, &clk_name); | ||
|
||
gate_id = i; | ||
of_property_read_u32_index(np, "clock-indices", i, &gate_id); | ||
|
||
WARN_ON(gate_id >= SUN6I_APB0_GATES_MAX_SIZE); | ||
if (gate_id >= SUN6I_APB0_GATES_MAX_SIZE) | ||
continue; | ||
|
||
clk_data->clks[gate_id] = clk_register_gate(&pdev->dev, | ||
clk_name, | ||
clk_parent, 0, | ||
reg, gate_id, | ||
0, NULL); | ||
WARN_ON(IS_ERR(clk_data->clks[gate_id])); | ||
} | ||
|
||
clk_data->clk_num = ngates; | ||
|
||
return of_clk_add_provider(np, of_clk_src_onecell_get, clk_data); | ||
} | ||
|
||
const struct of_device_id sun6i_a31_apb0_gates_clk_dt_ids[] = { | ||
{ .compatible = "allwinner,sun6i-a31-apb0-gates-clk" }, | ||
{ /* sentinel */ } | ||
}; | ||
|
||
static struct platform_driver sun6i_a31_apb0_gates_clk_driver = { | ||
.driver = { | ||
.name = "sun6i-a31-apb0-gates-clk", | ||
.owner = THIS_MODULE, | ||
.of_match_table = sun6i_a31_apb0_gates_clk_dt_ids, | ||
}, | ||
.probe = sun6i_a31_apb0_gates_clk_probe, | ||
}; | ||
module_platform_driver(sun6i_a31_apb0_gates_clk_driver); | ||
|
||
MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); | ||
MODULE_DESCRIPTION("Allwinner A31 APB0 gate clocks driver"); | ||
MODULE_LICENSE("GPL v2"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* Copyright (C) 2014 Free Electrons | ||
* | ||
* License Terms: GNU General Public License v2 | ||
* Author: Boris BREZILLON <boris.brezillon@free-electrons.com> | ||
* | ||
* Allwinner A31 APB0 clock driver | ||
* | ||
*/ | ||
|
||
#include <linux/clk-provider.h> | ||
#include <linux/module.h> | ||
#include <linux/of.h> | ||
#include <linux/platform_device.h> | ||
|
||
/* | ||
* The APB0 clk has a configurable divisor. | ||
* | ||
* We must use a clk_div_table and not a regular power of 2 | ||
* divisor here, because the first 2 values divide the clock | ||
* by 2. | ||
*/ | ||
static const struct clk_div_table sun6i_a31_apb0_divs[] = { | ||
{ .val = 0, .div = 2, }, | ||
{ .val = 1, .div = 2, }, | ||
{ .val = 2, .div = 4, }, | ||
{ .val = 3, .div = 8, }, | ||
{ /* sentinel */ }, | ||
}; | ||
|
||
static int sun6i_a31_apb0_clk_probe(struct platform_device *pdev) | ||
{ | ||
struct device_node *np = pdev->dev.of_node; | ||
const char *clk_name = np->name; | ||
const char *clk_parent; | ||
struct resource *r; | ||
void __iomem *reg; | ||
struct clk *clk; | ||
|
||
r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
reg = devm_ioremap_resource(&pdev->dev, r); | ||
if (IS_ERR(reg)) | ||
return PTR_ERR(reg); | ||
|
||
clk_parent = of_clk_get_parent_name(np, 0); | ||
if (!clk_parent) | ||
return -EINVAL; | ||
|
||
of_property_read_string(np, "clock-output-names", &clk_name); | ||
|
||
clk = clk_register_divider_table(&pdev->dev, clk_name, clk_parent, | ||
0, reg, 0, 2, 0, sun6i_a31_apb0_divs, | ||
NULL); | ||
if (IS_ERR(clk)) | ||
return PTR_ERR(clk); | ||
|
||
return of_clk_add_provider(np, of_clk_src_simple_get, clk); | ||
} | ||
|
||
const struct of_device_id sun6i_a31_apb0_clk_dt_ids[] = { | ||
{ .compatible = "allwinner,sun6i-a31-apb0-clk" }, | ||
{ /* sentinel */ } | ||
}; | ||
|
||
static struct platform_driver sun6i_a31_apb0_clk_driver = { | ||
.driver = { | ||
.name = "sun6i-a31-apb0-clk", | ||
.owner = THIS_MODULE, | ||
.of_match_table = sun6i_a31_apb0_clk_dt_ids, | ||
}, | ||
.probe = sun6i_a31_apb0_clk_probe, | ||
}; | ||
module_platform_driver(sun6i_a31_apb0_clk_driver); | ||
|
||
MODULE_AUTHOR("Boris BREZILLON <boris.brezillon@free-electrons.com>"); | ||
MODULE_DESCRIPTION("Allwinner A31 APB0 clock Driver"); | ||
MODULE_LICENSE("GPL v2"); |
Oops, something went wrong.