Commit 84a1b6e1 authored by Olof Johansson's avatar Olof Johansson
Browse files

Merge tag 'soc-fsl-next-v5.5' of...

Merge tag 'soc-fsl-next-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/leo/linux into arm/drivers

NXP/FSL SoC driver updates for v5.5

RCPM driver for ARM SoCs
- add RCPM driver to manage the wakeup devices for QorIQ ARM SoCs (HW low
power states are supported in PSCI firmware)
- add API to PM wakeup framework to retrieve wakeup sources

* tag 'soc-fsl-next-v5.5' of git://git.kernel.org/pub/scm/linux/kernel/git/leo/linux:
  soc: fsl: add RCPM driver
  dt-bindings: fsl: rcpm: Add 'little-endian' and update Chassis definition
  PM: wakeup: Add routine to help fetch wakeup source object.

Link: https://lore.kernel.org/r/1573599595-31411-1-git-send-email-leoyang.li@nxp.com


Signed-off-by: default avatarOlof Johansson <olof@lixom.net>
parents 57a54dfe 3b8db034
Loading
Loading
Loading
Loading
+10 −4
Original line number Diff line number Diff line
@@ -5,7 +5,7 @@ and power management.

Required properites:
  - reg : Offset and length of the register set of the RCPM block.
  - fsl,#rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
  - #fsl,rcpm-wakeup-cells : The number of IPPDEXPCR register cells in the
	fsl,rcpm-wakeup property.
  - compatible : Must contain a chip-specific RCPM block compatible string
	and (if applicable) may contain a chassis-version RCPM compatible
@@ -20,6 +20,7 @@ Required properites:
	* "fsl,qoriq-rcpm-1.0": for chassis 1.0 rcpm
	* "fsl,qoriq-rcpm-2.0": for chassis 2.0 rcpm
	* "fsl,qoriq-rcpm-2.1": for chassis 2.1 rcpm
	* "fsl,qoriq-rcpm-2.1+": for chassis 2.1+ rcpm

All references to "1.0" and "2.0" refer to the QorIQ chassis version to
which the chip complies.
@@ -27,14 +28,19 @@ Chassis Version Example Chips
---------------		-------------------------------
1.0				p4080, p5020, p5040, p2041, p3041
2.0				t4240, b4860, b4420
2.1				t1040, ls1021
2.1				t1040,
2.1+				ls1021a, ls1012a, ls1043a, ls1046a

Optional properties:
 - little-endian : RCPM register block is Little Endian. Without it RCPM
   will be Big Endian (default case).

Example:
The RCPM node for T4240:
	rcpm: global-utilities@e2000 {
		compatible = "fsl,t4240-rcpm", "fsl,qoriq-rcpm-2.0";
		reg = <0xe2000 0x1000>;
		fsl,#rcpm-wakeup-cells = <2>;
		#fsl,rcpm-wakeup-cells = <2>;
	};

* Freescale RCPM Wakeup Source Device Tree Bindings
@@ -44,7 +50,7 @@ can be used as a wakeup source.

  - fsl,rcpm-wakeup: Consists of a phandle to the rcpm node and the IPPDEXPCR
	register cells. The number of IPPDEXPCR register cells is defined in
	"fsl,#rcpm-wakeup-cells" in the rcpm node. The first register cell is
	"#fsl,rcpm-wakeup-cells" in the rcpm node. The first register cell is
	the bit mask that should be set in IPPDEXPCR0, and the second register
	cell is for IPPDEXPCR1, and so on.

+54 −0
Original line number Diff line number Diff line
@@ -247,6 +247,60 @@ void wakeup_source_unregister(struct wakeup_source *ws)
}
EXPORT_SYMBOL_GPL(wakeup_source_unregister);

/**
 * wakeup_sources_read_lock - Lock wakeup source list for read.
 *
 * Returns an index of srcu lock for struct wakeup_srcu.
 * This index must be passed to the matching wakeup_sources_read_unlock().
 */
int wakeup_sources_read_lock(void)
{
	return srcu_read_lock(&wakeup_srcu);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_lock);

/**
 * wakeup_sources_read_unlock - Unlock wakeup source list.
 * @idx: return value from corresponding wakeup_sources_read_lock()
 */
void wakeup_sources_read_unlock(int idx)
{
	srcu_read_unlock(&wakeup_srcu, idx);
}
EXPORT_SYMBOL_GPL(wakeup_sources_read_unlock);

/**
 * wakeup_sources_walk_start - Begin a walk on wakeup source list
 *
 * Returns first object of the list of wakeup sources.
 *
 * Note that to be safe, wakeup sources list needs to be locked by calling
 * wakeup_source_read_lock() for this.
 */
struct wakeup_source *wakeup_sources_walk_start(void)
{
	struct list_head *ws_head = &wakeup_sources;

	return list_entry_rcu(ws_head->next, struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_start);

/**
 * wakeup_sources_walk_next - Get next wakeup source from the list
 * @ws: Previous wakeup source object
 *
 * Note that to be safe, wakeup sources list needs to be locked by calling
 * wakeup_source_read_lock() for this.
 */
struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws)
{
	struct list_head *ws_head = &wakeup_sources;

	return list_next_or_null_rcu(ws_head, &ws->entry,
				struct wakeup_source, entry);
}
EXPORT_SYMBOL_GPL(wakeup_sources_walk_next);

/**
 * device_wakeup_attach - Attach a wakeup source object to a device object.
 * @dev: Device to handle.
+10 −0
Original line number Diff line number Diff line
@@ -40,4 +40,14 @@ config DPAA2_CONSOLE
	  /dev/dpaa2_mc_console and /dev/dpaa2_aiop_console,
	  which can be used to dump the Management Complex and AIOP
	  firmware logs.

config FSL_RCPM
	bool "Freescale RCPM support"
	depends on PM_SLEEP && (ARM || ARM64)
	help
	  The NXP QorIQ Processors based on ARM Core have RCPM module
	  (Run Control and Power Management), which performs all device-level
	  tasks associated with power management, such as wakeup source control.
	  Note that currently this driver will not support PowerPC based
	  QorIQ processor.
endmenu
+1 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@
obj-$(CONFIG_FSL_DPAA)                 += qbman/
obj-$(CONFIG_QUICC_ENGINE)		+= qe/
obj-$(CONFIG_CPM)			+= qe/
obj-$(CONFIG_FSL_RCPM)			+= rcpm.o
obj-$(CONFIG_FSL_GUTS)			+= guts.o
obj-$(CONFIG_FSL_MC_DPIO) 		+= dpio/
obj-$(CONFIG_DPAA2_CONSOLE)		+= dpaa2-console.o

drivers/soc/fsl/rcpm.c

0 → 100644
+151 −0
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
//
// rcpm.c - Freescale QorIQ RCPM driver
//
// Copyright 2019 NXP
//
// Author: Ran Wang <ran.wang_1@nxp.com>

#include <linux/init.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of_address.h>
#include <linux/slab.h>
#include <linux/suspend.h>
#include <linux/kernel.h>

#define RCPM_WAKEUP_CELL_MAX_SIZE	7

struct rcpm {
	unsigned int	wakeup_cells;
	void __iomem	*ippdexpcr_base;
	bool		little_endian;
};

/**
 * rcpm_pm_prepare - performs device-level tasks associated with power
 * management, such as programming related to the wakeup source control.
 * @dev: Device to handle.
 *
 */
static int rcpm_pm_prepare(struct device *dev)
{
	int i, ret, idx;
	void __iomem *base;
	struct wakeup_source	*ws;
	struct rcpm		*rcpm;
	struct device_node	*np = dev->of_node;
	u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
	u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};

	rcpm = dev_get_drvdata(dev);
	if (!rcpm)
		return -EINVAL;

	base = rcpm->ippdexpcr_base;
	idx = wakeup_sources_read_lock();

	/* Begin with first registered wakeup source */
	for_each_wakeup_source(ws) {

		/* skip object which is not attached to device */
		if (!ws->dev || !ws->dev->parent)
			continue;

		ret = device_property_read_u32_array(ws->dev->parent,
				"fsl,rcpm-wakeup", value,
				rcpm->wakeup_cells + 1);

		/*  Wakeup source should refer to current rcpm device */
		if (ret || (np->phandle != value[0]))
			continue;

		/* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
		 * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
		 * of wakeup source IP contains an integer array: <phandle to
		 * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
		 * IPPDEXPCR2 setting, etc>.
		 *
		 * So we will go thought them to collect setting data.
		 */
		for (i = 0; i < rcpm->wakeup_cells; i++)
			setting[i] |= value[i + 1];
	}

	wakeup_sources_read_unlock(idx);

	/* Program all IPPDEXPCRn once */
	for (i = 0; i < rcpm->wakeup_cells; i++) {
		u32 tmp = setting[i];
		void __iomem *address = base + i * 4;

		if (!tmp)
			continue;

		/* We can only OR related bits */
		if (rcpm->little_endian) {
			tmp |= ioread32(address);
			iowrite32(tmp, address);
		} else {
			tmp |= ioread32be(address);
			iowrite32be(tmp, address);
		}
	}

	return 0;
}

static const struct dev_pm_ops rcpm_pm_ops = {
	.prepare =  rcpm_pm_prepare,
};

static int rcpm_probe(struct platform_device *pdev)
{
	struct device	*dev = &pdev->dev;
	struct resource *r;
	struct rcpm	*rcpm;
	int ret;

	rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
	if (!rcpm)
		return -ENOMEM;

	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	if (!r)
		return -ENODEV;

	rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
	if (IS_ERR(rcpm->ippdexpcr_base)) {
		ret =  PTR_ERR(rcpm->ippdexpcr_base);
		return ret;
	}

	rcpm->little_endian = device_property_read_bool(
			&pdev->dev, "little-endian");

	ret = device_property_read_u32(&pdev->dev,
			"#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
	if (ret)
		return ret;

	dev_set_drvdata(&pdev->dev, rcpm);

	return 0;
}

static const struct of_device_id rcpm_of_match[] = {
	{ .compatible = "fsl,qoriq-rcpm-2.1+", },
	{}
};
MODULE_DEVICE_TABLE(of, rcpm_of_match);

static struct platform_driver rcpm_driver = {
	.driver = {
		.name = "rcpm",
		.of_match_table = rcpm_of_match,
		.pm	= &rcpm_pm_ops,
	},
	.probe = rcpm_probe,
};

module_platform_driver(rcpm_driver);
Loading