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 -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEhb3UXAyxp6UQ0v6khtxQDvusFVQFAl3LKJwACgkQhtxQDvus FVSjJg/+OwVrpMoQiECdLkfAH80lAJpYf76nKXeoOpUWtb2L19Ze4VWWiKxzKbu5 vD5RR35x/w/DcrUSClTq9mH6yUMdXoIX2rosLykfHXa2SwEbVWWzfUKrTIFoTIIE qUOYrk7u3OMMAUJDbDdMBK/Jr6UX59QtzsfBSRacilfTCVZsK2JAZKwlEbmwdowV mfxe/BcCA+bYm9Uku5cwgKQdW82kBvdYkLdQQ1XGZMrdtn8J9NQKBBsLNA/EvK6h 7DqBiRFbtjd0/fFy0D60Jtnojf8cx+34VvWOX60WGzObUhFQVQsVRpUTy2vnoP4s YrOx76p3xGruwvpzAjcF966vt6WOgShkW/yZgYaY7O47VjJav7rs/yMptoMcHcSB awokC4Rjzqnv2+R1gnFyk5/bKgtWa63e4tmxy8bBhaQphN1lwUSreULLqCB2MLoV VoOlzksUAVEW2iz+Afaltdhzzh6hh2cxOUKFC0amog/6HcoX51Kx+3KCOYEHGhMw XGFqKQtw51KCGHH+oKNQEGtbIizAO84nWVK6c/gVe2ChAs4uE5z536ECdZ3blNyp a0bT9xRkJ/+mEnyKXOH/9tyFQSd6Imnq0I+pk7uVqzA95W7Bd3/GqyDDij5rCoNC 3bIhfEescyJFY78WSgt4LLgMpbTqMF6ZS0RDBnd49jjc8zQg31E= =H6+G -----END PGP SIGNATURE----- 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: Olof Johansson <olof@lixom.net>
This commit is contained in:
Коммит
84a1b6e1d9
|
@ -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.
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -0,0 +1,151 @@
|
|||
// 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);
|
|
@ -63,6 +63,11 @@ struct wakeup_source {
|
|||
bool autosleep_enabled:1;
|
||||
};
|
||||
|
||||
#define for_each_wakeup_source(ws) \
|
||||
for ((ws) = wakeup_sources_walk_start(); \
|
||||
(ws); \
|
||||
(ws) = wakeup_sources_walk_next((ws)))
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
/*
|
||||
|
@ -92,6 +97,10 @@ extern void wakeup_source_remove(struct wakeup_source *ws);
|
|||
extern struct wakeup_source *wakeup_source_register(struct device *dev,
|
||||
const char *name);
|
||||
extern void wakeup_source_unregister(struct wakeup_source *ws);
|
||||
extern int wakeup_sources_read_lock(void);
|
||||
extern void wakeup_sources_read_unlock(int idx);
|
||||
extern struct wakeup_source *wakeup_sources_walk_start(void);
|
||||
extern struct wakeup_source *wakeup_sources_walk_next(struct wakeup_source *ws);
|
||||
extern int device_wakeup_enable(struct device *dev);
|
||||
extern int device_wakeup_disable(struct device *dev);
|
||||
extern void device_set_wakeup_capable(struct device *dev, bool capable);
|
||||
|
|
Загрузка…
Ссылка в новой задаче