Subject: [PATCH 4/8] ARM: dove: create a proper PMU driver for power domains, PMU IRQs and resets

From: [email-masked] (Sebastian Hesselbarth)
Date: Sat, 14 Feb 2015 18:02:11 +0100

On 14.02.2015 16:27, Russell King wrote:
> The PMU device contains an interrupt controller, power control and
> resets.  The interrupt controller is a little sub-standard in that
> there is no race free way to clear down pending interrupts, so we try
> to avoid problems by reducing the window as much as possible, and
> clearing as infrequently as possible.
>
> The interrupt support is implemented using an IRQ domain, and the
> parent interrupt referenced in the standard DT way.
>
> The power domains and reset support is closely related - there is a
> defined sequence for powering down a domain which is tightly coupled
> with asserting the reset.  Hence, it makes sense to group these two
> together.
>
> This patch adds the core PMU driver: power domains must be defined in
> the DT file in order to make use of them.  The reset controller can
> be referenced in the standard way for reset controllers.
>
> Signed-off-by: Russell King <[email-masked]>
> ---
[...]
> diff --git a/arch/arm/mach-dove/pmu.c b/arch/arm/mach-dove/pmu.c
> new file mode 100644
> index 000000000000..2d1325995a68
> --- /dev/null
> +++ b/arch/arm/mach-dove/pmu.c
> @@ -0,0 +1,410 @@
> +/*
> + * Marvell Dove PMU support
> + */
[...]
> +/*
> + * pmu {
> + *	compatible = "marvell,pmu";

Russell,

Should we be more precise and call it "marvell,dove-pmu" ?

Also, can we have an additional "syscon" compatible for that node?

That will allow us to get rid of the messy iomem stuff current
pinctrl-dove is doing to get access to the pmu pinctrl registers.

> + *	reg = <0xd0000 0x8000> <0xd8000 0x8000>;
> + *	interrupts = <33>;
> + *	#reset-cells = 1;
> + *	#power-domain-cells = <0>;
> + *	vpu_domain: vpu-domain {
> + *		marvell,pmu_pwr_mask = <0x00000008>;
> + *		marvell,pmu_iso_mask = <0x00000001>;
> + *		resets = <&pmu 16>;
> + *	};
> + *	gpu_domain: gpu-domain {
> + *		marvell,pmu_pwr_mask = <0x00000004>;
> + *		marvell,pmu_iso_mask = <0x00000002>;
> + *		resets = <&pmu 18>;
> + *	};
> + * };
> + */
> +int __init dove_init_pmu(void)
> +{

How about we copy the clk subsystem way of installing early probed
pm for DT here?

For example:

#define PM_OF_DECLARE(name, compat, fn) OF_DECLARE_1(clk, name, compat, fn)

and

static int __init dove_pmu_init(struct device_node *np) { ... }
PM_OF_DECLARE(dove_pmu, "marvell,dove-pmu", dove_init_pmu);

Sebastian

> +	struct device_node *np_pmu, *np;
> +	struct pmu_data *pmu;
> +	int ret, parent_irq;
> +
> +	/* Lookup the PMU node */
> +	np_pmu = of_find_compatible_node(NULL, NULL, "marvell,pmu");
> +	if (!np_pmu)
> +		return 0;
> +
> +	pmu = kzalloc(sizeof(*pmu), GFP_KERNEL);
> +	if (!pmu)
> +		return -ENOMEM;
> +
> +	spin_lock_init(&pmu->lock);
> +	pmu->of_node = np_pmu;
> +	pmu->pmc_base = of_iomap(pmu->of_node, 0);
> +	pmu->pmu_base = of_iomap(pmu->of_node, 1);
> +	if (!pmu->pmc_base || !pmu->pmu_base) {
> +		pr_err("%s: failed to map PMU\n", np_pmu->name);
> +		iounmap(pmu->pmu_base);
> +		iounmap(pmu->pmc_base);
> +		kfree(pmu);
> +		return -ENOMEM;
> +	}
> +
> +	parent_irq = irq_of_parse_and_map(pmu->of_node, 0);
> +	if (!parent_irq)
> +		pr_err("%s: no interrupt specified\n", np_pmu->name);
> +
> +	pmu_reset_init(pmu);
> +
> +	for_each_available_child_of_node(pmu->of_node, np) {
> +		struct of_phandle_args args;
> +		struct pmu_domain *domain;
> +
> +		domain = kzalloc(sizeof(*domain), GFP_KERNEL);
> +		if (!domain)
> +			break;
> +
> +		domain->pmu = pmu;
> +		domain->base.name = kstrdup(np->name, GFP_KERNEL);
> +		if (!domain->base.name) {
> +			kfree(domain);
> +			break;
> +		}
> +
> +		of_property_read_u32(np, "marvell,pmu_pwr_mask",
> +				     &domain->pwr_mask);
> +		of_property_read_u32(np, "marvell,pmu_iso_mask",
> +				     &domain->iso_mask);
> +
> +		ret = of_parse_phandle_with_args(np, "resets", "#reset-cells",
> +						 0, &args);
> +		if (ret == 0) {
> +			if (args.np == pmu->of_node)
> +				domain->rst_mask = BIT(args.args[0]);
> +			of_node_put(args.np);
> +		}
> +
> +		__pmu_domain_register(domain, np);
> +	}
> +	pm_genpd_poweroff_unused();
> +
> +	ret = dove_init_pmu_irq(pmu, parent_irq);
> +	if (ret)
> +		pr_err("dove_init_pmu_irq() failed: %d\n", ret);
> +
> +	return 0;
> +}
>