^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) //
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) // rcpm.c - Freescale QorIQ RCPM driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) //
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) // Copyright 2019 NXP
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) //
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) // Author: Ran Wang <ran.wang_1@nxp.com>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/of_address.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/slab.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/suspend.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #define RCPM_WAKEUP_CELL_MAX_SIZE 7
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) struct rcpm {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) unsigned int wakeup_cells;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) void __iomem *ippdexpcr_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) bool little_endian;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * rcpm_pm_prepare - performs device-level tasks associated with power
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) * management, such as programming related to the wakeup source control.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) * @dev: Device to handle.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) static int rcpm_pm_prepare(struct device *dev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) int i, ret, idx;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) void __iomem *base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) struct wakeup_source *ws;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) struct rcpm *rcpm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) struct device_node *np = dev->of_node;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) u32 value[RCPM_WAKEUP_CELL_MAX_SIZE + 1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) u32 setting[RCPM_WAKEUP_CELL_MAX_SIZE] = {0};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) rcpm = dev_get_drvdata(dev);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) if (!rcpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) base = rcpm->ippdexpcr_base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) idx = wakeup_sources_read_lock();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) /* Begin with first registered wakeup source */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) for_each_wakeup_source(ws) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) /* skip object which is not attached to device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) if (!ws->dev || !ws->dev->parent)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) ret = device_property_read_u32_array(ws->dev->parent,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) "fsl,rcpm-wakeup", value,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) rcpm->wakeup_cells + 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) /* Wakeup source should refer to current rcpm device */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) if (ret || (np->phandle != value[0]))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) /* Property "#fsl,rcpm-wakeup-cells" of rcpm node defines the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * number of IPPDEXPCR register cells, and "fsl,rcpm-wakeup"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * of wakeup source IP contains an integer array: <phandle to
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * RCPM node, IPPDEXPCR0 setting, IPPDEXPCR1 setting,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * IPPDEXPCR2 setting, etc>.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) * So we will go thought them to collect setting data.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) for (i = 0; i < rcpm->wakeup_cells; i++)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) setting[i] |= value[i + 1];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) wakeup_sources_read_unlock(idx);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) /* Program all IPPDEXPCRn once */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) for (i = 0; i < rcpm->wakeup_cells; i++) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) u32 tmp = setting[i];
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) void __iomem *address = base + i * 4;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) if (!tmp)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) continue;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) /* We can only OR related bits */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) if (rcpm->little_endian) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) tmp |= ioread32(address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) iowrite32(tmp, address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) tmp |= ioread32be(address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) iowrite32be(tmp, address);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) static const struct dev_pm_ops rcpm_pm_ops = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .prepare = rcpm_pm_prepare,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) static int rcpm_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) struct device *dev = &pdev->dev;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) struct resource *r;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) struct rcpm *rcpm;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) int ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) rcpm = devm_kzalloc(dev, sizeof(*rcpm), GFP_KERNEL);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) if (!rcpm)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) return -ENOMEM;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) if (!r)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115) return -ENODEV;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) rcpm->ippdexpcr_base = devm_ioremap_resource(&pdev->dev, r);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118) if (IS_ERR(rcpm->ippdexpcr_base)) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) ret = PTR_ERR(rcpm->ippdexpcr_base);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) rcpm->little_endian = device_property_read_bool(
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) &pdev->dev, "little-endian");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 125)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 126) ret = device_property_read_u32(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 127) "#fsl,rcpm-wakeup-cells", &rcpm->wakeup_cells);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 128) if (ret)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 129) return ret;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 130)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 131) dev_set_drvdata(&pdev->dev, rcpm);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 132)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 133) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 134) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 135)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 136) static const struct of_device_id rcpm_of_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 137) { .compatible = "fsl,qoriq-rcpm-2.1+", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 138) {}
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 139) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 140) MODULE_DEVICE_TABLE(of, rcpm_of_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 141)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 142) static struct platform_driver rcpm_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 143) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 144) .name = "rcpm",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 145) .of_match_table = rcpm_of_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 146) .pm = &rcpm_pm_ops,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 147) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 148) .probe = rcpm_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 149) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 150)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 151) module_platform_driver(rcpm_driver);