Commit 912c9fbe authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'imx-drivers-4.12' of...

Merge tag 'imx-drivers-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux into next/drivers

i.MX drivers updates for 4.12:
 - A series from Lucas Stach which partly rewrites the imx gpc driver
   to support multiple power domains, and moves the related code from
   imx platform into drivers folder.
 - A series from Dong Aisheng which fixes the issues with Lucas' code
   changes and improves things.
 - Add workaround for i.MX6QP hardware erratum ERR009619 that is PRE
   clocks may be stalled during the power up sequencing of the PU power
   domain.
 - Add imx-gpcv2 driver to support power domains managed by GPCv2 IP
   block found on i.MX7 series of SoCs.

* tag 'imx-drivers-4.12' of git://git.kernel.org/pub/scm/linux/kernel/git/shawnguo/linux

:
  soc: imx: gpc: add workaround for i.MX6QP to the GPC PD driver
  dt-bindings: imx-gpc: add i.MX6 QuadPlus compatible
  soc: imx: gpc: add defines for domain index
  soc: imx: Add GPCv2 power gating driver
  dt-bindings: Add GPCv2 power gating driver
  soc: imx: gpc: remove unnecessary readable_reg callback
  dt-bindings: imx-gpc: correct the DOMAIN_INDEX using
  soc: imx: gpc: keep PGC_X_CTRL name align with reference manual
  soc: imx: gpc: fix comment when power up domain
  soc: imx: gpc: fix imx6sl gpc power domain regression
  soc: imx: gpc: fix domain_index sanity check issue
  soc: imx: gpc: fix the wrong using of regmap cache
  soc: imx: gpc: fix gpc clk get error handling
  soc: imx: move PGC handling to a new GPC driver
  dt-bindings: add multidomain support to i.MX GPC DT binding

Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 7b020654 44c43c98
Loading
Loading
Loading
Loading
+57 −28
Original line number Diff line number Diff line
Freescale i.MX General Power Controller
=======================================

The i.MX6Q General Power Control (GPC) block contains DVFS load tracking
counters and Power Gating Control (PGC) for the CPU and PU (GPU/VPU) power
domains.
The i.MX6 General Power Control (GPC) block contains DVFS load tracking
counters and Power Gating Control (PGC).

Required properties:
- compatible: Should be "fsl,imx6q-gpc" or "fsl,imx6sl-gpc"
- compatible: Should be one of the following:
  - fsl,imx6q-gpc
  - fsl,imx6qp-gpc
  - fsl,imx6sl-gpc
- reg: should be register base and length as documented in the
  datasheet
- interrupts: Should contain GPC interrupt request 1
- pu-supply: Link to the LDO regulator powering the PU power domain
- clocks: Clock phandles to devices in the PU power domain that need
	  to be enabled during domain power-up for reset propagation.
- #power-domain-cells: Should be 1, see below:
- interrupts: Should contain one interrupt specifier for the GPC interrupt
- clocks: Must contain an entry for each entry in clock-names.
  See Documentation/devicetree/bindings/clocks/clock-bindings.txt for details.
- clock-names: Must include the following entries:
  - ipg

The gpc node is a power-controller as documented by the generic power domain
bindings in Documentation/devicetree/bindings/power/power_domain.txt.
The power domains are generic power domain providers as documented in
Documentation/devicetree/bindings/power/power_domain.txt. They are described as
subnodes of the power gating controller 'pgc' node of the GPC and should
contain the following:

Required properties:
- reg: Must contain the DOMAIN_INDEX of this power domain
  The following DOMAIN_INDEX values are valid for i.MX6Q:
  ARM_DOMAIN     0
  PU_DOMAIN      1
  The following additional DOMAIN_INDEX value is valid for i.MX6SL:
  DISPLAY_DOMAIN 2

- #power-domain-cells: Should be 0

Optional properties:
- clocks: a number of phandles to clocks that need to be enabled during domain
  power-up sequencing to ensure reset propagation into devices located inside
  this power domain
- power-supply: a phandle to the regulator powering this domain

Example:

@@ -25,14 +45,30 @@ Example:
		reg = <0x020dc000 0x4000>;
		interrupts = <0 89 IRQ_TYPE_LEVEL_HIGH>,
			     <0 90 IRQ_TYPE_LEVEL_HIGH>;
		pu-supply = <&reg_pu>;
		clocks = <&clks IMX6QDL_CLK_IPG>;
		clock-names = "ipg";

		pgc {
			#address-cells = <1>;
			#size-cells = <0>;

			power-domain@0 {
				reg = <0>;
				#power-domain-cells = <0>;
			};

			pd_pu: power-domain@1 {
				reg = <1>;
				#power-domain-cells = <0>;
				power-supply = <&reg_pu>;
				clocks = <&clks IMX6QDL_CLK_GPU3D_CORE>,
				         <&clks IMX6QDL_CLK_GPU3D_SHADER>,
				         <&clks IMX6QDL_CLK_GPU2D_CORE>,
				         <&clks IMX6QDL_CLK_GPU2D_AXI>,
				         <&clks IMX6QDL_CLK_OPENVG_AXI>,
				         <&clks IMX6QDL_CLK_VPU_AXI>;
		#power-domain-cells = <1>;
			};
		};
	};


@@ -40,20 +76,13 @@ Specifying power domain for IP modules
======================================

IP cores belonging to a power domain should contain a 'power-domains' property
that is a phandle pointing to the gpc device node and a DOMAIN_INDEX specifying
the power domain the device belongs to.
that is a phandle pointing to the power domain the device belongs to.

Example of a device that is part of the PU power domain:

	vpu: vpu@02040000 {
		reg = <0x02040000 0x3c000>;
		/* ... */
		power-domains = <&gpc 1>;
		power-domains = <&pd_pu>;
		/* ... */
	};

The following DOMAIN_INDEX values are valid for i.MX6Q:
ARM_DOMAIN     0
PU_DOMAIN      1
The following additional DOMAIN_INDEX value is valid for i.MX6SL:
DISPLAY_DOMAIN 2
+71 −0
Original line number Diff line number Diff line
Freescale i.MX General Power Controller v2
==========================================

The i.MX7S/D General Power Control (GPC) block contains Power Gating
Control (PGC) for various power domains.

Required properties:

- compatible: Should be "fsl,imx7d-gpc"

- reg: should be register base and length as documented in the
  datasheet

- interrupts: Should contain GPC interrupt request 1

Power domains contained within GPC node are generic power domain
providers, documented in
Documentation/devicetree/bindings/power/power_domain.txt, which are
described as subnodes of the power gating controller 'pgc' node,
which, in turn, is expected to contain the following:

Required properties:

- reg: Power domain index. Valid values are defined in
  include/dt-bindings/power/imx7-power.h

- #power-domain-cells: Should be 0

Optional properties:

- power-supply: Power supply used to power the domain

Example:

	gpc: gpc@303a0000 {
		compatible = "fsl,imx7d-gpc";
		reg = <0x303a0000 0x1000>;
		interrupt-controller;
		interrupts = <GIC_SPI 87 IRQ_TYPE_LEVEL_HIGH>;
		#interrupt-cells = <3>;
		interrupt-parent = <&intc>;

		pgc {
			#address-cells = <1>;
			#size-cells = <0>;

			pgc_pcie_phy: power-domain@3 {
				#power-domain-cells = <0>;

				reg = <IMX7_POWER_DOMAIN_PCIE_PHY>;
				power-supply = <&reg_1p0d>;
			};
		};
	};


Specifying power domain for IP modules
======================================

IP cores belonging to a power domain should contain a 'power-domains'
property that is a phandle for PGC node representing the domain.

Example of a device that is part of the PCIE_PHY power domain:

	pcie: pcie@33800000 {
	      reg = <0x33800000 0x4000>,
	            <0x4ff00000 0x80000>;
		/* ... */
		power-domains = <&pgc_pcie_phy>;
		/* ... */
	};
+1 −0
Original line number Diff line number Diff line
@@ -1268,6 +1268,7 @@ F: arch/arm/mach-mxs/
F:	arch/arm/boot/dts/imx*
F:	arch/arm/configs/imx*_defconfig
F:	drivers/clk/imx/
F:	drivers/soc/imx/
F:	include/soc/imx/

ARM/FREESCALE VYBRID ARM ARCHITECTURE
+0 −217
Original line number Diff line number Diff line
@@ -10,26 +10,17 @@
 * http://www.gnu.org/copyleft/gpl.html
 */

#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/irqchip.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/regulator/consumer.h>
#include <linux/irqchip/arm-gic.h>
#include "common.h"
#include "hardware.h"

#define GPC_CNTR		0x000
#define GPC_IMR1		0x008
#define GPC_PGC_GPU_PDN		0x260
#define GPC_PGC_GPU_PUPSCR	0x264
#define GPC_PGC_GPU_PDNSCR	0x268
#define GPC_PGC_CPU_PDN		0x2a0
#define GPC_PGC_CPU_PUPSCR	0x2a4
#define GPC_PGC_CPU_PDNSCR	0x2a8
@@ -39,18 +30,6 @@
#define IMR_NUM			4
#define GPC_MAX_IRQS		(IMR_NUM * 32)

#define GPU_VPU_PUP_REQ		BIT(1)
#define GPU_VPU_PDN_REQ		BIT(0)

#define GPC_CLK_MAX		6

struct pu_domain {
	struct generic_pm_domain base;
	struct regulator *reg;
	struct clk *clk[GPC_CLK_MAX];
	int num_clks;
};

static void __iomem *gpc_base;
static u32 gpc_wake_irqs[IMR_NUM];
static u32 gpc_saved_imrs[IMR_NUM];
@@ -296,199 +275,3 @@ void __init imx_gpc_check_dt(void)
		gpc_base = of_iomap(np, 0);
	}
}

static void _imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
{
	int iso, iso2sw;
	u32 val;

	/* Read ISO and ISO2SW power down delays */
	val = readl_relaxed(gpc_base + GPC_PGC_GPU_PDNSCR);
	iso = val & 0x3f;
	iso2sw = (val >> 8) & 0x3f;

	/* Gate off PU domain when GPU/VPU when powered down */
	writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);

	/* Request GPC to power down GPU/VPU */
	val = readl_relaxed(gpc_base + GPC_CNTR);
	val |= GPU_VPU_PDN_REQ;
	writel_relaxed(val, gpc_base + GPC_CNTR);

	/* Wait ISO + ISO2SW IPG clock cycles */
	ndelay((iso + iso2sw) * 1000 / 66);
}

static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd)
{
	struct pu_domain *pu = container_of(genpd, struct pu_domain, base);

	_imx6q_pm_pu_power_off(genpd);

	if (pu->reg)
		regulator_disable(pu->reg);

	return 0;
}

static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd)
{
	struct pu_domain *pu = container_of(genpd, struct pu_domain, base);
	int i, ret, sw, sw2iso;
	u32 val;

	if (pu->reg)
		ret = regulator_enable(pu->reg);
	if (pu->reg && ret) {
		pr_err("%s: failed to enable regulator: %d\n", __func__, ret);
		return ret;
	}

	/* Enable reset clocks for all devices in the PU domain */
	for (i = 0; i < pu->num_clks; i++)
		clk_prepare_enable(pu->clk[i]);

	/* Gate off PU domain when GPU/VPU when powered down */
	writel_relaxed(0x1, gpc_base + GPC_PGC_GPU_PDN);

	/* Read ISO and ISO2SW power down delays */
	val = readl_relaxed(gpc_base + GPC_PGC_GPU_PUPSCR);
	sw = val & 0x3f;
	sw2iso = (val >> 8) & 0x3f;

	/* Request GPC to power up GPU/VPU */
	val = readl_relaxed(gpc_base + GPC_CNTR);
	val |= GPU_VPU_PUP_REQ;
	writel_relaxed(val, gpc_base + GPC_CNTR);

	/* Wait ISO + ISO2SW IPG clock cycles */
	ndelay((sw + sw2iso) * 1000 / 66);

	/* Disable reset clocks for all devices in the PU domain */
	for (i = 0; i < pu->num_clks; i++)
		clk_disable_unprepare(pu->clk[i]);

	return 0;
}

static struct generic_pm_domain imx6q_arm_domain = {
	.name = "ARM",
};

static struct pu_domain imx6q_pu_domain = {
	.base = {
		.name = "PU",
		.power_off = imx6q_pm_pu_power_off,
		.power_on = imx6q_pm_pu_power_on,
	},
};

static struct generic_pm_domain imx6sl_display_domain = {
	.name = "DISPLAY",
};

static struct generic_pm_domain *imx_gpc_domains[] = {
	&imx6q_arm_domain,
	&imx6q_pu_domain.base,
	&imx6sl_display_domain,
};

static struct genpd_onecell_data imx_gpc_onecell_data = {
	.domains = imx_gpc_domains,
	.num_domains = ARRAY_SIZE(imx_gpc_domains),
};

static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg)
{
	struct clk *clk;
	int i, ret;

	imx6q_pu_domain.reg = pu_reg;

	for (i = 0; ; i++) {
		clk = of_clk_get(dev->of_node, i);
		if (IS_ERR(clk))
			break;
		if (i >= GPC_CLK_MAX) {
			dev_err(dev, "more than %d clocks\n", GPC_CLK_MAX);
			goto clk_err;
		}
		imx6q_pu_domain.clk[i] = clk;
	}
	imx6q_pu_domain.num_clks = i;

	/* Enable power always in case bootloader disabled it. */
	imx6q_pm_pu_power_on(&imx6q_pu_domain.base);

	if (!IS_ENABLED(CONFIG_PM_GENERIC_DOMAINS))
		return 0;

	imx6q_pu_domain.base.states = devm_kzalloc(dev,
					sizeof(*imx6q_pu_domain.base.states),
					GFP_KERNEL);
	if (!imx6q_pu_domain.base.states)
		return -ENOMEM;

	imx6q_pu_domain.base.states[0].power_off_latency_ns = 25000;
	imx6q_pu_domain.base.states[0].power_on_latency_ns = 2000000;
	imx6q_pu_domain.base.state_count = 1;

	for (i = 0; i < ARRAY_SIZE(imx_gpc_domains); i++)
		pm_genpd_init(imx_gpc_domains[i], NULL, false);

	ret =  of_genpd_add_provider_onecell(dev->of_node,
					     &imx_gpc_onecell_data);
	if (ret)
		goto power_off;

	return 0;

power_off:
	imx6q_pm_pu_power_off(&imx6q_pu_domain.base);
clk_err:
	while (i--)
		clk_put(imx6q_pu_domain.clk[i]);
	imx6q_pu_domain.reg = NULL;
	return -EINVAL;
}

static int imx_gpc_probe(struct platform_device *pdev)
{
	struct regulator *pu_reg;
	int ret;

	/* bail out if DT too old and doesn't provide the necessary info */
	if (!of_property_read_bool(pdev->dev.of_node, "#power-domain-cells"))
		return 0;

	pu_reg = devm_regulator_get_optional(&pdev->dev, "pu");
	if (PTR_ERR(pu_reg) == -ENODEV)
		pu_reg = NULL;
	if (IS_ERR(pu_reg)) {
		ret = PTR_ERR(pu_reg);
		dev_err(&pdev->dev, "failed to get pu regulator: %d\n", ret);
		return ret;
	}

	return imx_gpc_genpd_init(&pdev->dev, pu_reg);
}

static const struct of_device_id imx_gpc_dt_ids[] = {
	{ .compatible = "fsl,imx6q-gpc" },
	{ .compatible = "fsl,imx6sl-gpc" },
	{ }
};

static struct platform_driver imx_gpc_driver = {
	.driver = {
		.name = "imx-gpc",
		.of_match_table = imx_gpc_dt_ids,
	},
	.probe = imx_gpc_probe,
};

static int __init imx_pgc_init(void)
{
	return platform_driver_register(&imx_gpc_driver);
}
subsys_initcall(imx_pgc_init);
+1 −0
Original line number Diff line number Diff line
@@ -2,6 +2,7 @@ menu "SOC (System On Chip) specific Drivers"

source "drivers/soc/bcm/Kconfig"
source "drivers/soc/fsl/Kconfig"
source "drivers/soc/imx/Kconfig"
source "drivers/soc/mediatek/Kconfig"
source "drivers/soc/qcom/Kconfig"
source "drivers/soc/rockchip/Kconfig"
Loading