Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 //
0003 // rcpm.c - Freescale QorIQ RCPM driver
0004 //
0005 // Copyright 2019-2020 NXP
0006 //
0007 // Author: Ran Wang <ran.wang_1@nxp.com>
0008 
0009 #include <linux/init.h>
0010 #include <linux/module.h>
0011 #include <linux/platform_device.h>
0012 #include <linux/of_address.h>
0013 #include <linux/slab.h>
0014 #include <linux/suspend.h>
0015 #include <linux/kernel.h>
0016 #include <linux/acpi.h>
0017 
0018 #define RCPM_WAKEUP_CELL_MAX_SIZE   7
0019 
0020 struct rcpm {
0021     unsigned int    wakeup_cells;
0022     void __iomem    *ippdexpcr_base;
0023     bool        little_endian;
0024 };
0025 
0026 #define  SCFG_SPARECR8  0x051c
0027 
0028 static void copy_ippdexpcr1_setting(u32 val)
0029 {
0030     struct device_node *np;
0031     void __iomem *regs;
0032     u32 reg_val;
0033 
0034     np = of_find_compatible_node(NULL, NULL, "fsl,ls1021a-scfg");
0035     if (!np)
0036         return;
0037 
0038     regs = of_iomap(np, 0);
0039     if (!regs)
0040         return;
0041 
0042     reg_val = ioread32be(regs + SCFG_SPARECR8);
0043     iowrite32be(val | reg_val, regs + SCFG_SPARECR8);
0044 
0045     iounmap(regs);
0046 }
0047 
0048 /**
0049  * rcpm_pm_prepare - performs device-level tasks associated with power
0050  * management, such as programming related to the wakeup source control.
0051  * @dev: Device to handle.
0052  *
0053  */
0054 static int rcpm_pm_prepare(struct device *dev)
0055 {
0056     int i, ret, idx;
0057     void __iomem *base;
0058     struct wakeup_source    *ws;
0059     struct rcpm     *rcpm;
0060     struct device_node  *np = dev->of_node;
0061     u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
0062     u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
0063 
0064     rcpm = dev_get_drvdata(dev);
0065     if (!rcpm)
0066         return -EINVAL;
0067 
0068     base = rcpm->ippdexpcr_base;
0069     idx = wakeup_sources_read_lock();
0070 
0071     /* Begin with first registered wakeup source */
0072     for_each_wakeup_source(ws) {
0073 
0074         /* skip object which is not attached to device */
0075         if (!ws->dev || !ws->dev->parent)
0076             continue;
0077 
0078         ret = device_property_read_u32_array(ws->dev->parent,
0079                 "fsl,rcpm-wakeup", value,
0080                 rcpm->wakeup_cells + 1);
0081 
0082         if (ret)
0083             continue;
0084 
0085         /*
0086          * For DT mode, would handle devices with "fsl,rcpm-wakeup"
0087          * pointing to the current RCPM node.
0088          *
0089          * For ACPI mode, currently we assume there is only one
0090          * RCPM controller existing.
0091          */
0092         if (is_of_node(dev->fwnode))
0093             if (np->phandle != value[0])
0094                 continue;
0095 
0096         /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
0097          * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
0098          * of wakeup source IP contains an integer array: <phandle to
0099          * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
0100          * IPPDEXPCR2 setting, etc>.
0101          *
0102          * So we will go thought them to collect setting data.
0103          */
0104         for (i = 0; i < rcpm->wakeup_cells; i++)
0105             setting[i] |= value[i + 1];
0106     }
0107 
0108     wakeup_sources_read_unlock(idx);
0109 
0110     /* Program all IPPDEXPCRn once */
0111     for (i = 0; i < rcpm->wakeup_cells; i++) {
0112         u32 tmp = setting[i];
0113         void __iomem *address = base + i * 4;
0114 
0115         if (!tmp)
0116             continue;
0117 
0118         /* We can only OR related bits */
0119         if (rcpm->little_endian) {
0120             tmp |= ioread32(address);
0121             iowrite32(tmp, address);
0122         } else {
0123             tmp |= ioread32be(address);
0124             iowrite32be(tmp, address);
0125         }
0126         /*
0127          * Workaround of errata A-008646 on SoC LS1021A:
0128          * There is a bug of register ippdexpcr1.
0129          * Reading configuration register RCPM_IPPDEXPCR1
0130          * always return zero. So save ippdexpcr1's value
0131          * to register SCFG_SPARECR8.And the value of
0132          * ippdexpcr1 will be read from SCFG_SPARECR8.
0133          */
0134         if (dev_of_node(dev) && (i == 1))
0135             if (of_device_is_compatible(np, "fsl,ls1021a-rcpm"))
0136                 copy_ippdexpcr1_setting(tmp);
0137     }
0138 
0139     return 0;
0140 }
0141 
0142 static const struct dev_pm_ops rcpm_pm_ops = {
0143     .prepare =  rcpm_pm_prepare,
0144 };
0145 
0146 static int rcpm_probe(struct platform_device *pdev)
0147 {
0148     struct device   *dev = &pdev->dev;
0149     struct rcpm *rcpm;
0150     int ret;
0151 
0152     rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
0153     if (!rcpm)
0154         return -ENOMEM;
0155 
0156     rcpm->ippdexpcr_base = devm_platform_ioremap_resource(pdev, 0);
0157     if (IS_ERR(rcpm->ippdexpcr_base)) {
0158         ret =  PTR_ERR(rcpm->ippdexpcr_base);
0159         return ret;
0160     }
0161 
0162     rcpm->little_endian = device_property_read_bool(
0163             &pdev->dev, "little-endian");
0164 
0165     ret = device_property_read_u32(&pdev->dev,
0166             "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
0167     if (ret)
0168         return ret;
0169 
0170     dev_set_drvdata(&pdev->dev, rcpm);
0171 
0172     return 0;
0173 }
0174 
0175 static const struct of_device_id rcpm_of_match[] = {
0176     { .compatible = "fsl,qoriq-rcpm-2.1+", },
0177     {}
0178 };
0179 MODULE_DEVICE_TABLE(of, rcpm_of_match);
0180 
0181 #ifdef CONFIG_ACPI
0182 static const struct acpi_device_id rcpm_acpi_ids[] = {
0183     {"NXP0015",},
0184     { }
0185 };
0186 MODULE_DEVICE_TABLE(acpi, rcpm_acpi_ids);
0187 #endif
0188 
0189 static struct platform_driver rcpm_driver = {
0190     .driver = {
0191         .name = "rcpm",
0192         .of_match_table = rcpm_of_match,
0193         .acpi_match_table = ACPI_PTR(rcpm_acpi_ids),
0194         .pm = &rcpm_pm_ops,
0195     },
0196     .probe = rcpm_probe,
0197 };
0198 
0199 module_platform_driver(rcpm_driver);