cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

rcpm.c (4595B)


      1// SPDX-License-Identifier: GPL-2.0
      2//
      3// rcpm.c - Freescale QorIQ RCPM driver
      4//
      5// Copyright 2019-2020 NXP
      6//
      7// Author: Ran Wang <ran.wang_1@nxp.com>
      8
      9#include <linux/init.h>
     10#include <linux/module.h>
     11#include <linux/platform_device.h>
     12#include <linux/of_address.h>
     13#include <linux/slab.h>
     14#include <linux/suspend.h>
     15#include <linux/kernel.h>
     16#include <linux/acpi.h>
     17
     18#define RCPM_WAKEUP_CELL_MAX_SIZE	7
     19
     20struct rcpm {
     21	unsigned int	wakeup_cells;
     22	void __iomem	*ippdexpcr_base;
     23	bool		little_endian;
     24};
     25
     26#define  SCFG_SPARECR8	0x051c
     27
     28static void copy_ippdexpcr1_setting(u32 val)
     29{
     30	struct device_node *np;
     31	void __iomem *regs;
     32	u32 reg_val;
     33
     34	np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
     35	if (!np)
     36		return;
     37
     38	regs = of_iomap(np, 0);
     39	if (!regs)
     40		return;
     41
     42	reg_val = ioread32be(regs + SCFG_SPARECR8);
     43	iowrite32be(val | reg_val, regs + SCFG_SPARECR8);
     44
     45	iounmap(regs);
     46}
     47
     48/**
     49 * rcpm_pm_prepare - performs device-level tasks associated with power
     50 * management, such as programming related to the wakeup source control.
     51 * @dev: Device to handle.
     52 *
     53 */
     54static int rcpm_pm_prepare(struct device *dev)
     55{
     56	int i, ret, idx;
     57	void __iomem *base;
     58	struct wakeup_source	*ws;
     59	struct rcpm		*rcpm;
     60	struct device_node	*np = dev->of_node;
     61	u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
     62	u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
     63
     64	rcpm = dev_get_drvdata(dev);
     65	if (!rcpm)
     66		return -EINVAL;
     67
     68	base = rcpm->ippdexpcr_base;
     69	idx = wakeup_sources_read_lock();
     70
     71	/* Begin with first registered wakeup source */
     72	for_each_wakeup_source(ws) {
     73
     74		/* skip object which is not attached to device */
     75		if (!ws->dev || !ws->dev->parent)
     76			continue;
     77
     78		ret = device_property_read_u32_array(ws->dev->parent,
     79				"fsl,rcpm-wakeup", value,
     80				rcpm->wakeup_cells + 1);
     81
     82		if (ret)
     83			continue;
     84
     85		/*
     86		 * For DT mode, would handle devices with "fsl,rcpm-wakeup"
     87		 * pointing to the current RCPM node.
     88		 *
     89		 * For ACPI mode, currently we assume there is only one
     90		 * RCPM controller existing.
     91		 */
     92		if (is_of_node(dev->fwnode))
     93			if (np->phandle != value[0])
     94				continue;
     95
     96		/* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
     97		 * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
     98		 * of wakeup source IP contains an integer array: <phandle to
     99		 * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
    100		 * IPPDEXPCR2 setting, etc>.
    101		 *
    102		 * So we will go thought them to collect setting data.
    103		 */
    104		for (i = 0; i < rcpm->wakeup_cells; i++)
    105			setting[i] |= value[i + 1];
    106	}
    107
    108	wakeup_sources_read_unlock(idx);
    109
    110	/* Program all IPPDEXPCRn once */
    111	for (i = 0; i < rcpm->wakeup_cells; i++) {
    112		u32 tmp = setting[i];
    113		void __iomem *address = base + i * 4;
    114
    115		if (!tmp)
    116			continue;
    117
    118		/* We can only OR related bits */
    119		if (rcpm->little_endian) {
    120			tmp |= ioread32(address);
    121			iowrite32(tmp, address);
    122		} else {
    123			tmp |= ioread32be(address);
    124			iowrite32be(tmp, address);
    125		}
    126		/*
    127		 * Workaround of errata A-008646 on SoC LS1021A:
    128		 * There is a bug of register ippdexpcr1.
    129		 * Reading configuration register RCPM_IPPDEXPCR1
    130		 * always return zero. So save ippdexpcr1's value
    131		 * to register SCFG_SPARECR8.And the value of
    132		 * ippdexpcr1 will be read from SCFG_SPARECR8.
    133		 */
    134		if (dev_of_node(dev) && (i == 1))
    135			if (of_device_is_compatible(np, "fsl,ls1021a-rcpm"))
    136				copy_ippdexpcr1_setting(tmp);
    137	}
    138
    139	return 0;
    140}
    141
    142static const struct dev_pm_ops rcpm_pm_ops = {
    143	.prepare =  rcpm_pm_prepare,
    144};
    145
    146static int rcpm_probe(struct platform_device *pdev)
    147{
    148	struct device	*dev = &pdev->dev;
    149	struct rcpm	*rcpm;
    150	int ret;
    151
    152	rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
    153	if (!rcpm)
    154		return -ENOMEM;
    155
    156	rcpm->ippdexpcr_base = devm_platform_ioremap_resource(pdev, 0);
    157	if (IS_ERR(rcpm->ippdexpcr_base)) {
    158		ret =  PTR_ERR(rcpm->ippdexpcr_base);
    159		return ret;
    160	}
    161
    162	rcpm->little_endian = device_property_read_bool(
    163			&pdev->dev, "little-endian");
    164
    165	ret = device_property_read_u32(&pdev->dev,
    166			"#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
    167	if (ret)
    168		return ret;
    169
    170	dev_set_drvdata(&pdev->dev, rcpm);
    171
    172	return 0;
    173}
    174
    175static const struct of_device_id rcpm_of_match[] = {
    176	{ .compatible = "fsl,qoriq-rcpm-2.1+", },
    177	{}
    178};
    179MODULE_DEVICE_TABLE(of, rcpm_of_match);
    180
    181#ifdef CONFIG_ACPI
    182static const struct acpi_device_id rcpm_acpi_ids[] = {
    183	{"NXP0015",},
    184	{ }
    185};
    186MODULE_DEVICE_TABLE(acpi, rcpm_acpi_ids);
    187#endif
    188
    189static struct platform_driver rcpm_driver = {
    190	.driver = {
    191		.name = "rcpm",
    192		.of_match_table = rcpm_of_match,
    193		.acpi_match_table = ACPI_PTR(rcpm_acpi_ids),
    194		.pm	= &rcpm_pm_ops,
    195	},
    196	.probe = rcpm_probe,
    197};
    198
    199module_platform_driver(rcpm_driver);