From 15160f6de0bba712fcea078c5ac7571fe33fcd5d Mon Sep 17 00:00:00 2001 From: Takeshi Kihara Date: Thu, 28 Feb 2019 12:00:48 +0100 Subject: soc: renesas: Identify R-Car M3-W ES1.3 The Product Register of R-Car M3-W ES1.3 incorrectly identifies the SoC revision as ES2.1. Add a workaround to fix this. Signed-off-by: Takeshi Kihara Signed-off-by: Geert Uytterhoeven Signed-off-by: Simon Horman --- drivers/soc/renesas/renesas-soc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/renesas/renesas-soc.c b/drivers/soc/renesas/renesas-soc.c index 4af96e668a2f..3299cf5365f3 100644 --- a/drivers/soc/renesas/renesas-soc.c +++ b/drivers/soc/renesas/renesas-soc.c @@ -335,6 +335,9 @@ static int __init renesas_soc_init(void) /* R-Car M3-W ES1.1 incorrectly identifies as ES2.0 */ if ((product & 0x7fff) == 0x5210) product ^= 0x11; + /* R-Car M3-W ES1.3 incorrectly identifies as ES2.1 */ + if ((product & 0x7fff) == 0x5211) + product ^= 0x12; if (soc->id && ((product >> 8) & 0xff) != soc->id) { pr_warn("SoC mismatch (product = 0x%x)\n", product); return -ENODEV; -- cgit v1.2.3 From b9472f7d8224460499dd7128ec944735ed5345a0 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Mon, 18 Feb 2019 22:43:09 +0100 Subject: firmware: xilinx: fix debugfs write handler - Userspace wants to write a string with `len` bytes, not counting the terminating NULL, so we should allocate `len+1` bytes. It looks like the current code relied on having a nullbyte directly behind `kern_buff`, which happens to work reliably as long as `len` isn't one of the kmalloc size classes. - strncpy_from_user() is completely wrong here; userspace is giving us a (not necessarily null-terminated) buffer and its length. strncpy_from_user() is for cases in which we don't know the length. - Don't let broken userspace allocate arbitrarily big kmalloc allocations. Just use memdup_user_nul(), which is designed precisely for things like this. Signed-off-by: Jann Horn Acked-by: Jolly Shah Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp-debug.c | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 2771df6df379..90b66cdbfd58 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -163,21 +163,14 @@ static ssize_t zynqmp_pm_debugfs_api_write(struct file *file, strcpy(debugfs_buf, ""); - if (*off != 0 || len == 0) + if (*off != 0 || len <= 1 || len > PAGE_SIZE - 1) return -EINVAL; - kern_buff = kzalloc(len, GFP_KERNEL); - if (!kern_buff) - return -ENOMEM; - + kern_buff = memdup_user_nul(ptr, len); + if (IS_ERR(kern_buff)) + return PTR_ERR(kern_buff); tmp_buff = kern_buff; - ret = strncpy_from_user(kern_buff, ptr, len); - if (ret < 0) { - ret = -EFAULT; - goto err; - } - /* Read the API name from a user request */ pm_api_req = strsep(&kern_buff, " "); -- cgit v1.2.3 From 3d0313786470acb414b7d5fdd2202f061acffb02 Mon Sep 17 00:00:00 2001 From: Rajan Vaja Date: Mon, 4 Mar 2019 15:18:08 -0800 Subject: drivers: Defer probe if firmware is not ready Driver needs ZynqMP firmware interface to call EEMI APIs. In case firmware is not ready, dependent drivers should wait until the firmware is ready. Signed-off-by: Rajan Vaja Signed-off-by: Jolly Shah Signed-off-by: Michal Simek --- drivers/clk/zynqmp/clkc.c | 4 ++-- drivers/firmware/xilinx/zynqmp-debug.c | 3 --- drivers/firmware/xilinx/zynqmp.c | 11 ++++++++++- drivers/nvmem/zynqmp_nvmem.c | 10 +++++++--- drivers/reset/reset-zynqmp.c | 8 ++++---- drivers/soc/xilinx/zynqmp_pm_domains.c | 18 ++++++++++-------- drivers/soc/xilinx/zynqmp_power.c | 10 ++++++---- drivers/spi/spi-zynqmp-gqspi.c | 5 +++++ 8 files changed, 44 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c index b0908ec62f73..2b1d43457f09 100644 --- a/drivers/clk/zynqmp/clkc.c +++ b/drivers/clk/zynqmp/clkc.c @@ -695,8 +695,8 @@ static int zynqmp_clock_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops) - return -ENXIO; + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); ret = zynqmp_clk_setup(dev->of_node); diff --git a/drivers/firmware/xilinx/zynqmp-debug.c b/drivers/firmware/xilinx/zynqmp-debug.c index 90b66cdbfd58..c6d0724da4db 100644 --- a/drivers/firmware/xilinx/zynqmp-debug.c +++ b/drivers/firmware/xilinx/zynqmp-debug.c @@ -90,9 +90,6 @@ static int process_api_request(u32 pm_id, u64 *pm_api_arg, u32 *pm_api_ret) int ret; struct zynqmp_pm_query_data qdata = {0}; - if (!eemi_ops) - return -ENXIO; - switch (pm_id) { case PM_GET_API_VERSION: ret = eemi_ops->get_api_version(&pm_api_version); diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 98f936125643..87a1d636c0dc 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -24,6 +24,8 @@ #include #include "zynqmp-debug.h" +static const struct zynqmp_eemi_ops *eemi_ops_tbl; + static const struct mfd_cell firmware_devs[] = { { .name = "zynqmp_power_controller", @@ -649,7 +651,11 @@ static const struct zynqmp_eemi_ops eemi_ops = { */ const struct zynqmp_eemi_ops *zynqmp_pm_get_eemi_ops(void) { - return &eemi_ops; + if (eemi_ops_tbl) + return eemi_ops_tbl; + else + return ERR_PTR(-EPROBE_DEFER); + } EXPORT_SYMBOL_GPL(zynqmp_pm_get_eemi_ops); @@ -694,6 +700,9 @@ static int zynqmp_firmware_probe(struct platform_device *pdev) pr_info("%s Trustzone version v%d.%d\n", __func__, pm_tz_version >> 16, pm_tz_version & 0xFFFF); + /* Assign eemi_ops_table */ + eemi_ops_tbl = &eemi_ops; + zynqmp_pm_api_debugfs_init(); ret = mfd_add_devices(&pdev->dev, PLATFORM_DEVID_NONE, firmware_devs, diff --git a/drivers/nvmem/zynqmp_nvmem.c b/drivers/nvmem/zynqmp_nvmem.c index 490c8fcaec80..5893543918c8 100644 --- a/drivers/nvmem/zynqmp_nvmem.c +++ b/drivers/nvmem/zynqmp_nvmem.c @@ -16,6 +16,8 @@ struct zynqmp_nvmem_data { struct nvmem_device *nvmem; }; +static const struct zynqmp_eemi_ops *eemi_ops; + static int zynqmp_nvmem_read(void *context, unsigned int offset, void *val, size_t bytes) { @@ -23,9 +25,7 @@ static int zynqmp_nvmem_read(void *context, unsigned int offset, int idcode, version; struct zynqmp_nvmem_data *priv = context; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - - if (!eemi_ops || !eemi_ops->get_chipid) + if (!eemi_ops->get_chipid) return -ENXIO; ret = eemi_ops->get_chipid(&idcode, &version); @@ -61,6 +61,10 @@ static int zynqmp_nvmem_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + priv->dev = dev; econfig.dev = dev; econfig.reg_read = zynqmp_nvmem_read; diff --git a/drivers/reset/reset-zynqmp.c b/drivers/reset/reset-zynqmp.c index 2ef1f13aa47b..99e75d92dada 100644 --- a/drivers/reset/reset-zynqmp.c +++ b/drivers/reset/reset-zynqmp.c @@ -79,11 +79,11 @@ static int zynqmp_reset_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - platform_set_drvdata(pdev, priv); - priv->eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!priv->eemi_ops) - return -ENXIO; + if (IS_ERR(priv->eemi_ops)) + return PTR_ERR(priv->eemi_ops); + + platform_set_drvdata(pdev, priv); priv->rcdev.ops = &zynqmp_reset_ops; priv->rcdev.owner = THIS_MODULE; diff --git a/drivers/soc/xilinx/zynqmp_pm_domains.c b/drivers/soc/xilinx/zynqmp_pm_domains.c index 354d256e6e00..600f57cf0c2e 100644 --- a/drivers/soc/xilinx/zynqmp_pm_domains.c +++ b/drivers/soc/xilinx/zynqmp_pm_domains.c @@ -23,6 +23,8 @@ /* Flag stating if PM nodes mapped to the PM domain has been requested */ #define ZYNQMP_PM_DOMAIN_REQUESTED BIT(0) +static const struct zynqmp_eemi_ops *eemi_ops; + /** * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain * @gpd: Generic power domain @@ -71,9 +73,8 @@ static int zynqmp_gpd_power_on(struct generic_pm_domain *domain) { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_requirement) + if (!eemi_ops->set_requirement) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -107,9 +108,8 @@ static int zynqmp_gpd_power_off(struct generic_pm_domain *domain) struct zynqmp_pm_domain *pd; u32 capabilities = 0; bool may_wakeup; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_requirement) + if (!eemi_ops->set_requirement) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -160,9 +160,8 @@ static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain, { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->request_node) + if (!eemi_ops->request_node) return -ENXIO; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -197,9 +196,8 @@ static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain, { int ret; struct zynqmp_pm_domain *pd; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->release_node) + if (!eemi_ops->release_node) return; pd = container_of(domain, struct zynqmp_pm_domain, gpd); @@ -266,6 +264,10 @@ static int zynqmp_gpd_probe(struct platform_device *pdev) struct zynqmp_pm_domain *pd; struct device *dev = &pdev->dev; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + pd = devm_kcalloc(dev, ZYNQMP_NUM_DOMAINS, sizeof(*pd), GFP_KERNEL); if (!pd) return -ENOMEM; diff --git a/drivers/soc/xilinx/zynqmp_power.c b/drivers/soc/xilinx/zynqmp_power.c index 771cb59b9d22..1b9d14411a15 100644 --- a/drivers/soc/xilinx/zynqmp_power.c +++ b/drivers/soc/xilinx/zynqmp_power.c @@ -31,6 +31,7 @@ static const char *const suspend_modes[] = { }; static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD; +static const struct zynqmp_eemi_ops *eemi_ops; enum pm_api_cb_id { PM_INIT_SUSPEND_CB = 30, @@ -92,9 +93,8 @@ static ssize_t suspend_mode_store(struct device *dev, const char *buf, size_t count) { int md, ret = -EINVAL; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); - if (!eemi_ops || !eemi_ops->set_suspend_mode) + if (!eemi_ops->set_suspend_mode) return ret; for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++) @@ -120,9 +120,11 @@ static int zynqmp_pm_probe(struct platform_device *pdev) int ret, irq; u32 pm_api_version; - const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); - if (!eemi_ops || !eemi_ops->get_api_version || !eemi_ops->init_finalize) + if (!eemi_ops->get_api_version || !eemi_ops->init_finalize) return -ENXIO; eemi_ops->init_finalize(); diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index 9f83e1b17aa1..d07b6f940f9f 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -138,6 +138,7 @@ #define SPI_AUTOSUSPEND_TIMEOUT 3000 enum mode_type {GQSPI_MODE_IO, GQSPI_MODE_DMA}; +static const struct zynqmp_eemi_ops *eemi_ops; /** * struct zynqmp_qspi - Defines qspi driver instance @@ -1021,6 +1022,10 @@ static int zynqmp_qspi_probe(struct platform_device *pdev) struct resource *res; struct device *dev = &pdev->dev; + eemi_ops = zynqmp_pm_get_eemi_ops(); + if (IS_ERR(eemi_ops)) + return PTR_ERR(eemi_ops); + master = spi_alloc_master(&pdev->dev, sizeof(*xqspi)); if (!master) return -ENOMEM; -- cgit v1.2.3 From dce47aed20c7de3ee2011b7a63e67f08e9dcfb5e Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 7 Mar 2019 15:01:45 +0100 Subject: soc: amlogic: gx-socinfo: Add mask for each SoC packages When updated IDs on f842c41adc04 ("amlogic: meson-gx-socinfo: Update soc ids") we introduced packages ids using the full 8bit value, but in the function socinfo_to_package_id() the id was filtered with the 0xf0 mask. While the 0xf0 mask is valid for most board, it filters out the lower 4 bits which encodes some characteristics of the chip. This patch moves the mask into the meson_gx_package_id table to be applied on each package name independently and add the correct mask for some specific entries. An example is the S905, in the vendor code the S905 is package_id different from 0x20, and S905M is exactly 0x20. Another example are the The Wetek Hub & Play2 boards using a S905-H variant, which is the S905 SoC with some licence bits enabled. These licence bits are encoded in the lower 4bits, so to detect the -H variant, we must detect the id == 0x3 with the 0xf mask. Fixes: f842c41adc04 ("amlogic: meson-gx-socinfo: Update soc ids") Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 37ea0a1c24c8..1ae339f5eadb 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -43,20 +43,21 @@ static const struct meson_gx_package_id { const char *name; unsigned int major_id; unsigned int pack_id; + unsigned int pack_mask; } soc_packages[] = { - { "S905", 0x1f, 0 }, - { "S905H", 0x1f, 0x13 }, - { "S905M", 0x1f, 0x20 }, - { "S905D", 0x21, 0 }, - { "S905X", 0x21, 0x80 }, - { "S905W", 0x21, 0xa0 }, - { "S905L", 0x21, 0xc0 }, - { "S905M2", 0x21, 0xe0 }, - { "S912", 0x22, 0 }, - { "962X", 0x24, 0x10 }, - { "962E", 0x24, 0x20 }, - { "A113X", 0x25, 0x37 }, - { "A113D", 0x25, 0x22 }, + { "S905", 0x1f, 0, 0x20 }, /* pack_id != 0x20 */ + { "S905H", 0x1f, 0x3, 0xf }, /* pack_id & 0xf == 0x3 */ + { "S905M", 0x1f, 0x20, 0xf0 }, /* pack_id == 0x20 */ + { "S905D", 0x21, 0, 0xf0 }, + { "S905X", 0x21, 0x80, 0xf0 }, + { "S905W", 0x21, 0xa0, 0xf0 }, + { "S905L", 0x21, 0xc0, 0xf0 }, + { "S905M2", 0x21, 0xe0, 0xf0 }, + { "S912", 0x22, 0, 0x0 }, /* Only S912 is known for GXM */ + { "962X", 0x24, 0x10, 0xf0 }, + { "962E", 0x24, 0x20, 0xf0 }, + { "A113X", 0x25, 0x37, 0xff }, + { "A113D", 0x25, 0x22, 0xff }, }; static inline unsigned int socinfo_to_major(u32 socinfo) @@ -81,13 +82,14 @@ static inline unsigned int socinfo_to_misc(u32 socinfo) static const char *socinfo_to_package_id(u32 socinfo) { - unsigned int pack = socinfo_to_pack(socinfo) & 0xf0; + unsigned int pack = socinfo_to_pack(socinfo); unsigned int major = socinfo_to_major(socinfo); int i; for (i = 0 ; i < ARRAY_SIZE(soc_packages) ; ++i) { if (soc_packages[i].major_id == major && - soc_packages[i].pack_id == pack) + soc_packages[i].pack_id == + (pack & soc_packages[i].pack_mask)) return soc_packages[i].name; } -- cgit v1.2.3 From 65f80df58eb770b2b687c35142c113d1ad6fa415 Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Thu, 7 Mar 2019 15:01:46 +0100 Subject: soc: amlogic: gx-socinfo: Add new SoC IDs and Packages IDs This adds the: - G12A SoC ID and S905X2, S905D2 package IDs, found booting the X96 Max and U200 Reference Board - G12B SoC ID and S922X package ID, found booting the Odroid-N2 - S805X, S805Y package IDs found in the vendor U-Boot source Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index 1ae339f5eadb..c8d138d85f34 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -37,6 +37,8 @@ static const struct meson_gx_soc_id { { "AXG", 0x25 }, { "GXLX", 0x26 }, { "TXHD", 0x27 }, + { "G12A", 0x28 }, + { "G12B", 0x29 }, }; static const struct meson_gx_package_id { @@ -53,11 +55,16 @@ static const struct meson_gx_package_id { { "S905W", 0x21, 0xa0, 0xf0 }, { "S905L", 0x21, 0xc0, 0xf0 }, { "S905M2", 0x21, 0xe0, 0xf0 }, + { "S805X", 0x21, 0x30, 0xf0 }, + { "S805Y", 0x21, 0xb0, 0xf0 }, { "S912", 0x22, 0, 0x0 }, /* Only S912 is known for GXM */ { "962X", 0x24, 0x10, 0xf0 }, { "962E", 0x24, 0x20, 0xf0 }, { "A113X", 0x25, 0x37, 0xff }, { "A113D", 0x25, 0x22, 0xff }, + { "S905D2", 0x28, 0x10, 0xf0 }, + { "S905X2", 0x28, 0x40, 0xf0 }, + { "S922X", 0x29, 0x40, 0xf0 }, }; static inline unsigned int socinfo_to_major(u32 socinfo) -- cgit v1.2.3 From fdda0a6adc33536ad468f07db27325423703c5bc Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 23 Feb 2019 14:20:40 +0100 Subject: meson-gx-socinfo: add missing of_node_put after of_device_is_available Add an of_node_put when a tested device node is not available. The semantic patch that fixes this problem is as follows (http://coccinelle.lip6.fr): // @@ identifier f; local idexpression e; expression x; @@ e = f(...); ... when != of_node_put(e) when != x = e when != e = x when any if (<+...of_device_is_available(e)...+>) { ... when != of_node_put(e) ( return e; | + of_node_put(e); return ...; ) } // Fixes: a9daaba2965e8 ("soc: Add Amlogic SoC Information driver") Signed-off-by: Julia Lawall Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-socinfo.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/amlogic/meson-gx-socinfo.c b/drivers/soc/amlogic/meson-gx-socinfo.c index c8d138d85f34..bca34954518e 100644 --- a/drivers/soc/amlogic/meson-gx-socinfo.c +++ b/drivers/soc/amlogic/meson-gx-socinfo.c @@ -132,8 +132,10 @@ static int __init meson_gx_socinfo_init(void) return -ENODEV; /* check if interface is enabled */ - if (!of_device_is_available(np)) + if (!of_device_is_available(np)) { + of_node_put(np); return -ENODEV; + } /* check if chip-id is available */ if (!of_property_read_bool(np, "amlogic,has-chip-id")) -- cgit v1.2.3 From 8217a7a2c76259ff1f2623b60c3bcede052cdb49 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Tue, 19 Feb 2019 18:14:29 -0800 Subject: soc: imx: gpcv2: Make use of regmap_read_poll_timeout() Replace explicit polling loop with a call to regmap_read_poll_timeout() to avoid code repetition. Also fix misspelled "failed" while at it. Signed-off-by: Andrey Smirnov Cc: Lucas Stach Cc: Chris Healy Cc: linux-arm-kernel@lists.infradead.org Cc: linux-kernel@vger.kernel.org Reviewed-by: Lucas Stach Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 39 ++++++++++++++------------------------- 1 file changed, 14 insertions(+), 25 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index 176f473127b6..e06bf12a1e4b 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -136,8 +136,8 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, GPC_PU_PGC_SW_PUP_REQ : GPC_PU_PGC_SW_PDN_REQ; const bool enable_power_control = !on; const bool has_regulator = !IS_ERR(domain->regulator); - unsigned long deadline; int i, ret = 0; + u32 pxx_req; regmap_update_bits(domain->regmap, GPC_PGC_CPU_MAPPING, domain->bits.map, domain->bits.map); @@ -169,30 +169,19 @@ static int imx_gpc_pu_pgc_sw_pxx_req(struct generic_pm_domain *genpd, * As per "5.5.9.4 Example Code 4" in IMX7DRM.pdf wait * for PUP_REQ/PDN_REQ bit to be cleared */ - deadline = jiffies + msecs_to_jiffies(1); - while (true) { - u32 pxx_req; - - regmap_read(domain->regmap, offset, &pxx_req); - - if (!(pxx_req & domain->bits.pxx)) - break; - - if (time_after(jiffies, deadline)) { - dev_err(domain->dev, "falied to command PGC\n"); - ret = -ETIMEDOUT; - /* - * If we were in a process of enabling a - * domain and failed we might as well disable - * the regulator we just enabled. And if it - * was the opposite situation and we failed to - * power down -- keep the regulator on - */ - on = !on; - break; - } - - cpu_relax(); + ret = regmap_read_poll_timeout(domain->regmap, offset, pxx_req, + !(pxx_req & domain->bits.pxx), + 0, USEC_PER_MSEC); + if (ret) { + dev_err(domain->dev, "failed to command PGC\n"); + /* + * If we were in a process of enabling a + * domain and failed we might as well disable + * the regulator we just enabled. And if it + * was the opposite situation and we failed to + * power down -- keep the regulator on + */ + on = !on; } if (enable_power_control) -- cgit v1.2.3 From 9d616d62faefd573f6eaf687f6c83a872708afcf Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:27 +0000 Subject: firmware: imx: scu-pd: use bool to set postfix Using bool instead 0/1 to indicate whether adding a postfix for domain names which can improve the code readability and less confusing. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 102 +++++++++++++++++++++--------------------- 1 file changed, 51 insertions(+), 51 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 39a94c7177fc..e7802ec591c2 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -84,71 +84,71 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, 1 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, 1 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, 1 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, 1 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, 1 }, + { "lsio-pwm", IMX_SC_R_PWM_0, 8, true }, + { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true }, + { "lsio-gpt", IMX_SC_R_GPT_0, 5, true }, + { "lsio-kpp", IMX_SC_R_KPP, 1, false }, + { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true }, + { "lsio-mu", IMX_SC_R_MU_0A, 14, true }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, 1 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, 1 }, - { "con-enet", IMX_SC_R_ENET_0, 2, 1 }, - { "con-nand", IMX_SC_R_NAND, 1, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, 1 }, + { "con-usb", IMX_SC_R_USB_0, 2, true }, + { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false }, + { "con-usb2", IMX_SC_R_USB_2, 1, false }, + { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false }, + { "con-sdhc", IMX_SC_R_SDHC_0, 3, true }, + { "con-enet", IMX_SC_R_ENET_0, 2, true }, + { "con-nand", IMX_SC_R_NAND, 1, false }, + { "con-mlb", IMX_SC_R_MLB_0, 1, true }, /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, 1 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, 1 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, 1 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, 1 }, - { "adma-amix", IMX_SC_R_AMIX, 1, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, 1 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, 1 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, 1 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, 1 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, 1 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, 1 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, 1 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, 1 }, + { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false }, + { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false }, + { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false }, + { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true }, + { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true }, + { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true }, + { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false }, + { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false }, + { "adma-esai0", IMX_SC_R_ESAI_0, 1, false }, + { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false }, + { "adma-sai", IMX_SC_R_SAI_0, 3, true }, + { "adma-amix", IMX_SC_R_AMIX, 1, false }, + { "adma-mqs0", IMX_SC_R_MQS_0, 1, false }, + { "adma-dsp", IMX_SC_R_DSP, 1, false }, + { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false }, + { "adma-can", IMX_SC_R_CAN_0, 3, true }, + { "adma-ftm", IMX_SC_R_FTM_0, 2, true }, + { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true }, + { "adma-adc", IMX_SC_R_ADC_0, 1, true }, + { "adma-lcd", IMX_SC_R_LCD_0, 1, true }, + { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true }, + { "adma-lpuart", IMX_SC_R_UART_0, 4, true }, + { "adma-lpspi", IMX_SC_R_SPI_0, 4, true }, /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, 0 }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, 1 }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, 0 }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, 0 }, + { "vpu", IMX_SC_R_VPU, 1, false }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, 1 }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, 0 }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, 0 }, + { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false }, + { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false }, /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, 0 }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, 0 }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, 1 }, - { "lvds0", IMX_SC_R_LVDS_0, 1, 0 }, + { "mipi0", IMX_SC_R_MIPI_0, 1, false }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true }, + { "lvds0", IMX_SC_R_LVDS_0, 1, false }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, 0 }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, 1 }, + { "dc0", IMX_SC_R_DC_0, 1, false }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { -- cgit v1.2.3 From ad8cc071c557b075b923bf27aee8a7dae7338f5e Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:32 +0000 Subject: firmware: imx: scu-pd: add specifying the base of domain name index support As the domain resource id in the same type may not be continuous, so it's hard to describe all such power domains with current struct imx_sc_pd_range. Adding the optional base for domain name index to address this issue. Then we can add the discrete domains easily later. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 107 ++++++++++++++++++++++-------------------- 1 file changed, 55 insertions(+), 52 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index e7802ec591c2..8a2b6ada58ad 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -74,7 +74,10 @@ struct imx_sc_pd_range { char *name; u32 rsrc; u8 num; + + /* add domain index */ bool postfix; + u8 start_from; }; struct imx_sc_pd_soc { @@ -84,71 +87,71 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, true }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, true }, - { "lsio-kpp", IMX_SC_R_KPP, 1, false }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, true }, + { "lsio-pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "lsio-gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "lsio-kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "lsio-mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, true }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false }, - { "con-usb2", IMX_SC_R_USB_2, 1, false }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, true }, - { "con-enet", IMX_SC_R_ENET_0, 2, true }, - { "con-nand", IMX_SC_R_NAND, 1, false }, - { "con-mlb", IMX_SC_R_MLB_0, 1, true }, + { "con-usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "con-usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "con-sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "con-enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "con-nand", IMX_SC_R_NAND, 1, false, 0 }, + { "con-mlb", IMX_SC_R_MLB_0, 1, true, 0 }, /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, false }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false }, - { "adma-sai", IMX_SC_R_SAI_0, 3, true }, - { "adma-amix", IMX_SC_R_AMIX, 1, false }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, false }, - { "adma-dsp", IMX_SC_R_DSP, 1, false }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false }, - { "adma-can", IMX_SC_R_CAN_0, 3, true }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, true }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true }, - { "adma-adc", IMX_SC_R_ADC_0, 1, true }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, true }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, true }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, true }, + { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "adma-esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "adma-sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "adma-amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "adma-mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "adma-dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + { "adma-can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "adma-ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adma-adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "adma-lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "adma-lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "adma-lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, /* VPU SS */ - { "vpu", IMX_SC_R_VPU, 1, false }, - { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true }, - { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false }, - { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false }, + { "vpu", IMX_SC_R_VPU, 1, false, 0 }, + { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, + { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, + { "vpu-enc0", IMX_SC_R_VPU_ENC_0, 1, false, 0 }, /* GPU SS */ - { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true }, + { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false }, - { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false }, + { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, /* MIPI/LVDS SS */ - { "mipi0", IMX_SC_R_MIPI_0, 1, false }, - { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false }, - { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true }, - { "lvds0", IMX_SC_R_LVDS_0, 1, false }, + { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, + { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, + { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, + { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ - { "dc0", IMX_SC_R_DC_0, 1, false }, - { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true }, + { "dc0", IMX_SC_R_DC_0, 1, false, 0 }, + { "dc0-pll", IMX_SC_R_DC_0_PLL_0, 2, true, 0 }, }; static const struct imx_sc_pd_soc imx8qxp_scu_pd = { @@ -236,7 +239,7 @@ imx_scu_add_pm_domain(struct device *dev, int idx, if (pd_ranges->postfix) snprintf(sc_pd->name, sizeof(sc_pd->name), - "%s%i", pd_ranges->name, idx); + "%s%i", pd_ranges->name, pd_ranges->start_from + idx); else snprintf(sc_pd->name, sizeof(sc_pd->name), "%s", pd_ranges->name); -- cgit v1.2.3 From 32654dad06e24e2909c64b4fc3d61689f5522975 Mon Sep 17 00:00:00 2001 From: Aisheng Dong Date: Wed, 20 Feb 2019 14:38:36 +0000 Subject: firmware: imx: scu-pd: decouple the SS information from domain names As resource power domain service is provided by SCU firmware, no SS information required. So we can remove the SS indicator from the domain names, then the domains defined can be better shared among different SCU based platforms. Cc: Ulf Hansson Cc: Shawn Guo Cc: Sascha Hauer Cc: "Rafael J. Wysocki" Cc: Kevin Hilman Cc: linux-pm@vger.kernel.org Signed-off-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/scu-pd.c | 92 ++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/imx/scu-pd.c b/drivers/firmware/imx/scu-pd.c index 8a2b6ada58ad..480cec69e2c9 100644 --- a/drivers/firmware/imx/scu-pd.c +++ b/drivers/firmware/imx/scu-pd.c @@ -87,49 +87,51 @@ struct imx_sc_pd_soc { static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { /* LSIO SS */ - { "lsio-pwm", IMX_SC_R_PWM_0, 8, true, 0 }, - { "lsio-gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, - { "lsio-gpt", IMX_SC_R_GPT_0, 5, true, 0 }, - { "lsio-kpp", IMX_SC_R_KPP, 1, false, 0 }, - { "lsio-fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, - { "lsio-mu", IMX_SC_R_MU_0A, 14, true, 0 }, + { "pwm", IMX_SC_R_PWM_0, 8, true, 0 }, + { "gpio", IMX_SC_R_GPIO_0, 8, true, 0 }, + { "gpt", IMX_SC_R_GPT_0, 5, true, 0 }, + { "kpp", IMX_SC_R_KPP, 1, false, 0 }, + { "fspi", IMX_SC_R_FSPI_0, 2, true, 0 }, + { "mu", IMX_SC_R_MU_0A, 14, true, 0 }, /* CONN SS */ - { "con-usb", IMX_SC_R_USB_0, 2, true, 0 }, - { "con-usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, - { "con-usb2", IMX_SC_R_USB_2, 1, false, 0 }, - { "con-usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, - { "con-sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, - { "con-enet", IMX_SC_R_ENET_0, 2, true, 0 }, - { "con-nand", IMX_SC_R_NAND, 1, false, 0 }, - { "con-mlb", IMX_SC_R_MLB_0, 1, true, 0 }, - - /* Audio DMA SS */ - { "adma-audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, - { "adma-audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, - { "adma-audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, - { "adma-dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, - { "adma-dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, - { "adma-dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, - { "adma-asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, - { "adma-asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, - { "adma-esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, - { "adma-spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, - { "adma-sai", IMX_SC_R_SAI_0, 3, true, 0 }, - { "adma-amix", IMX_SC_R_AMIX, 1, false, 0 }, - { "adma-mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, - { "adma-dsp", IMX_SC_R_DSP, 1, false, 0 }, - { "adma-dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, - { "adma-can", IMX_SC_R_CAN_0, 3, true, 0 }, - { "adma-ftm", IMX_SC_R_FTM_0, 2, true, 0 }, - { "adma-lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, - { "adma-adc", IMX_SC_R_ADC_0, 1, true, 0 }, - { "adma-lcd", IMX_SC_R_LCD_0, 1, true, 0 }, - { "adma-lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, - { "adma-lpuart", IMX_SC_R_UART_0, 4, true, 0 }, - { "adma-lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, - - /* VPU SS */ + { "usb", IMX_SC_R_USB_0, 2, true, 0 }, + { "usb0phy", IMX_SC_R_USB_0_PHY, 1, false, 0 }, + { "usb2", IMX_SC_R_USB_2, 1, false, 0 }, + { "usb2phy", IMX_SC_R_USB_2_PHY, 1, false, 0 }, + { "sdhc", IMX_SC_R_SDHC_0, 3, true, 0 }, + { "enet", IMX_SC_R_ENET_0, 2, true, 0 }, + { "nand", IMX_SC_R_NAND, 1, false, 0 }, + { "mlb", IMX_SC_R_MLB_0, 1, true, 0 }, + + /* AUDIO SS */ + { "audio-pll0", IMX_SC_R_AUDIO_PLL_0, 1, false, 0 }, + { "audio-pll1", IMX_SC_R_AUDIO_PLL_1, 1, false, 0 }, + { "audio-clk-0", IMX_SC_R_AUDIO_CLK_0, 1, false, 0 }, + { "dma0-ch", IMX_SC_R_DMA_0_CH0, 16, true, 0 }, + { "dma1-ch", IMX_SC_R_DMA_1_CH0, 16, true, 0 }, + { "dma2-ch", IMX_SC_R_DMA_2_CH0, 5, true, 0 }, + { "asrc0", IMX_SC_R_ASRC_0, 1, false, 0 }, + { "asrc1", IMX_SC_R_ASRC_1, 1, false, 0 }, + { "esai0", IMX_SC_R_ESAI_0, 1, false, 0 }, + { "spdif0", IMX_SC_R_SPDIF_0, 1, false, 0 }, + { "sai", IMX_SC_R_SAI_0, 3, true, 0 }, + { "amix", IMX_SC_R_AMIX, 1, false, 0 }, + { "mqs0", IMX_SC_R_MQS_0, 1, false, 0 }, + { "dsp", IMX_SC_R_DSP, 1, false, 0 }, + { "dsp-ram", IMX_SC_R_DSP_RAM, 1, false, 0 }, + + /* DMA SS */ + { "can", IMX_SC_R_CAN_0, 3, true, 0 }, + { "ftm", IMX_SC_R_FTM_0, 2, true, 0 }, + { "lpi2c", IMX_SC_R_I2C_0, 4, true, 0 }, + { "adc", IMX_SC_R_ADC_0, 1, true, 0 }, + { "lcd", IMX_SC_R_LCD_0, 1, true, 0 }, + { "lcd0-pwm", IMX_SC_R_LCD_0_PWM_0, 1, true, 0 }, + { "lpuart", IMX_SC_R_UART_0, 4, true, 0 }, + { "lpspi", IMX_SC_R_SPI_0, 4, true, 0 }, + + /* VPU SS */ { "vpu", IMX_SC_R_VPU, 1, false, 0 }, { "vpu-pid", IMX_SC_R_VPU_PID0, 8, true, 0 }, { "vpu-dec0", IMX_SC_R_VPU_DEC_0, 1, false, 0 }, @@ -139,14 +141,16 @@ static const struct imx_sc_pd_range imx8qxp_scu_pd_ranges[] = { { "gpu0-pid", IMX_SC_R_GPU_0_PID0, 4, true, 0 }, /* HSIO SS */ - { "hsio-pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, - { "hsio-serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, + { "pcie-b", IMX_SC_R_PCIE_B, 1, false, 0 }, + { "serdes-1", IMX_SC_R_SERDES_1, 1, false, 0 }, { "hsio-gpio", IMX_SC_R_HSIO_GPIO, 1, false, 0 }, - /* MIPI/LVDS SS */ + /* MIPI SS */ { "mipi0", IMX_SC_R_MIPI_0, 1, false, 0 }, { "mipi0-pwm0", IMX_SC_R_MIPI_0_PWM_0, 1, false, 0 }, { "mipi0-i2c", IMX_SC_R_MIPI_0_I2C_0, 2, true, 0 }, + + /* LVDS SS */ { "lvds0", IMX_SC_R_LVDS_0, 1, false, 0 }, /* DC SS */ -- cgit v1.2.3 From 7fe5719b4364fe9b673c3763007915877f3922c0 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 18 Feb 2019 17:36:43 +0100 Subject: soc/tegra: pmc: Implement acquire/release for resets By implementing the acquire/release protocol, the resets can be shared with other drivers that also adhere to this protocol. This will be used for example by the SOR driver to put hardware into a known good state, irrespective of whether or not the power domain can be reset. Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 0df258518693..0c5f79528e5f 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -656,10 +656,15 @@ static int tegra_genpd_power_on(struct generic_pm_domain *domain) int err; err = tegra_powergate_power_up(pg, true); - if (err) + if (err) { dev_err(dev, "failed to turn on PM domain %s: %d\n", pg->genpd.name, err); + goto out; + } + reset_control_release(pg->reset); + +out: return err; } @@ -669,10 +674,18 @@ static int tegra_genpd_power_off(struct generic_pm_domain *domain) struct device *dev = pg->pmc->dev; int err; + err = reset_control_acquire(pg->reset); + if (err < 0) { + pr_err("failed to acquire resets: %d\n", err); + return err; + } + err = tegra_powergate_power_down(pg); - if (err) + if (err) { dev_err(dev, "failed to turn off PM domain %s: %d\n", pg->genpd.name, err); + reset_control_release(pg->reset); + } return err; } @@ -937,20 +950,34 @@ static int tegra_powergate_of_get_resets(struct tegra_powergate *pg, struct device *dev = pg->pmc->dev; int err; - pg->reset = of_reset_control_array_get_exclusive(np); + pg->reset = of_reset_control_array_get_exclusive_released(np); if (IS_ERR(pg->reset)) { err = PTR_ERR(pg->reset); dev_err(dev, "failed to get device resets: %d\n", err); return err; } - if (off) + err = reset_control_acquire(pg->reset); + if (err < 0) { + pr_err("failed to acquire resets: %d\n", err); + goto out; + } + + if (off) { err = reset_control_assert(pg->reset); - else + } else { err = reset_control_deassert(pg->reset); + if (err < 0) + goto out; - if (err) + reset_control_release(pg->reset); + } + +out: + if (err) { + reset_control_release(pg->reset); reset_control_put(pg->reset); + } return err; } -- cgit v1.2.3 From 8da3daaa097171edd9658a82bbd3f335bc2e03ad Mon Sep 17 00:00:00 2001 From: Ankit Jain Date: Mon, 4 Feb 2019 10:15:43 +0530 Subject: soc: qcom: rmtfs: Add support for mmap functionality This change adds mmap functionality to rmtfs_mem driver. Userspace application can map the address and use this mapped address directly as buffer for read/write call to disk. and avoid the read/write call to the shared path to copy the buffer to userspace application. Reviewed-by: Bjorn Andersson Signed-off-by: Ankit Jain Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/rmtfs_mem.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/qcom/rmtfs_mem.c b/drivers/soc/qcom/rmtfs_mem.c index 7200d762a951..6f5e8be9689c 100644 --- a/drivers/soc/qcom/rmtfs_mem.c +++ b/drivers/soc/qcom/rmtfs_mem.c @@ -137,6 +137,26 @@ static struct class rmtfs_class = { .name = "rmtfs", }; +static int qcom_rmtfs_mem_mmap(struct file *filep, struct vm_area_struct *vma) +{ + struct qcom_rmtfs_mem *rmtfs_mem = filep->private_data; + + if (vma->vm_end - vma->vm_start > rmtfs_mem->size) { + dev_dbg(&rmtfs_mem->dev, + "vm_end[%lu] - vm_start[%lu] [%lu] > mem->size[%pa]\n", + vma->vm_end, vma->vm_start, + (vma->vm_end - vma->vm_start), &rmtfs_mem->size); + return -EINVAL; + } + + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + return remap_pfn_range(vma, + vma->vm_start, + rmtfs_mem->addr >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot); +} + static const struct file_operations qcom_rmtfs_mem_fops = { .owner = THIS_MODULE, .open = qcom_rmtfs_mem_open, @@ -144,6 +164,7 @@ static const struct file_operations qcom_rmtfs_mem_fops = { .write = qcom_rmtfs_mem_write, .release = qcom_rmtfs_mem_release, .llseek = default_llseek, + .mmap = qcom_rmtfs_mem_mmap, }; static void qcom_rmtfs_mem_release_device(struct device *dev) -- cgit v1.2.3 From 9324df5817c01f55fcde24da0da9df2c2f9392ac Mon Sep 17 00:00:00 2001 From: Chris Lew Date: Thu, 21 Feb 2019 18:33:39 -0800 Subject: soc: qcom: qmi: Change txn wait to non-interruptible Current QMI clients are not userspace facing, if their threads are signaled, they do not do any signal checking or propagate the ERESTARTSYS return code up. Remove the interruptible option so clients can finish their QMI transactions even if the thread is signaled. Reviewed-by: Bjorn Andersson Signed-off-by: Chris Lew Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/qmi_interface.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/qmi_interface.c b/drivers/soc/qcom/qmi_interface.c index c239a28e503f..f9e309f0acd3 100644 --- a/drivers/soc/qcom/qmi_interface.c +++ b/drivers/soc/qcom/qmi_interface.c @@ -345,8 +345,7 @@ int qmi_txn_wait(struct qmi_txn *txn, unsigned long timeout) struct qmi_handle *qmi = txn->qmi; int ret; - ret = wait_for_completion_interruptible_timeout(&txn->completion, - timeout); + ret = wait_for_completion_timeout(&txn->completion, timeout); mutex_lock(&qmi->txn_lock); mutex_lock(&txn->lock); @@ -354,9 +353,7 @@ int qmi_txn_wait(struct qmi_txn *txn, unsigned long timeout) mutex_unlock(&txn->lock); mutex_unlock(&qmi->txn_lock); - if (ret < 0) - return ret; - else if (ret == 0) + if (ret == 0) return -ETIMEDOUT; else return txn->result; -- cgit v1.2.3 From 93b260528020792032e50725383f27a27897bb0f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Thu, 28 Feb 2019 08:48:49 +0300 Subject: soc: qcom: cmd-db: Fix an error code in cmd_db_dev_probe() The memremap() function doesn't return error pointers, it returns NULL. This code is returning "ret = PTR_ERR(NULL);" which is success, but it should return -ENOMEM. Fixes: 312416d9171a ("drivers: qcom: add command DB driver") Signed-off-by: Dan Carpenter Signed-off-by: Bjorn Andersson Signed-off-by: Andy Gross --- drivers/soc/qcom/cmd-db.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c index c701b3b010f1..f6c3d17b05c7 100644 --- a/drivers/soc/qcom/cmd-db.c +++ b/drivers/soc/qcom/cmd-db.c @@ -248,8 +248,8 @@ static int cmd_db_dev_probe(struct platform_device *pdev) } cmd_db_header = memremap(rmem->base, rmem->size, MEMREMAP_WB); - if (IS_ERR_OR_NULL(cmd_db_header)) { - ret = PTR_ERR(cmd_db_header); + if (!cmd_db_header) { + ret = -ENOMEM; cmd_db_header = NULL; return ret; } -- cgit v1.2.3 From 0d7dab926130e194decf1b4dabbf66de6e68bf0c Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Wed, 13 Mar 2019 10:11:58 +0530 Subject: bus: tegra-aconnect: use devm_clk_*() helpers aconnect bus driver is using pm_clk_*() interface for managing clocks. With this, clocks seem to be always ON. This happens on Tegra devices which use BPMP co-processor to manage clock resources, where clocks are enabled during prepare phase. This is necessary because calls to BPMP are always blocking. When pm_clk_*() interface is used on such Tegra devices, clock prepare count is not balanced till driver remove() gets executed and hence clocks are seen ON always. Thus this patch replaces pm_clk_*() with devm_clk_*() framework. Suggested-by: Mohan Kumar D Reviewed-by: Jonathan Hunter Signed-off-by: Sameer Pujar Signed-off-by: Thierry Reding --- drivers/bus/tegra-aconnect.c | 64 ++++++++++++++++++++++++++++++-------------- 1 file changed, 44 insertions(+), 20 deletions(-) (limited to 'drivers') diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c index 084ae286fa23..9349157b3a5b 100644 --- a/drivers/bus/tegra-aconnect.c +++ b/drivers/bus/tegra-aconnect.c @@ -12,28 +12,38 @@ #include #include #include -#include #include +struct tegra_aconnect { + struct clk *ape_clk; + struct clk *apb2ape_clk; +}; + static int tegra_aconnect_probe(struct platform_device *pdev) { - int ret; + struct tegra_aconnect *aconnect; if (!pdev->dev.of_node) return -EINVAL; - ret = pm_clk_create(&pdev->dev); - if (ret) - return ret; + aconnect = devm_kzalloc(&pdev->dev, sizeof(struct tegra_aconnect), + GFP_KERNEL); + if (!aconnect) + return -ENOMEM; - ret = of_pm_clk_add_clk(&pdev->dev, "ape"); - if (ret) - goto clk_destroy; + aconnect->ape_clk = devm_clk_get(&pdev->dev, "ape"); + if (IS_ERR(aconnect->ape_clk)) { + dev_err(&pdev->dev, "Can't retrieve ape clock\n"); + return PTR_ERR(aconnect->ape_clk); + } - ret = of_pm_clk_add_clk(&pdev->dev, "apb2ape"); - if (ret) - goto clk_destroy; + aconnect->apb2ape_clk = devm_clk_get(&pdev->dev, "apb2ape"); + if (IS_ERR(aconnect->apb2ape_clk)) { + dev_err(&pdev->dev, "Can't retrieve apb2ape clock\n"); + return PTR_ERR(aconnect->apb2ape_clk); + } + dev_set_drvdata(&pdev->dev, aconnect); pm_runtime_enable(&pdev->dev); of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev); @@ -41,30 +51,44 @@ static int tegra_aconnect_probe(struct platform_device *pdev) dev_info(&pdev->dev, "Tegra ACONNECT bus registered\n"); return 0; - -clk_destroy: - pm_clk_destroy(&pdev->dev); - - return ret; } static int tegra_aconnect_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); - pm_clk_destroy(&pdev->dev); - return 0; } static int tegra_aconnect_runtime_resume(struct device *dev) { - return pm_clk_resume(dev); + struct tegra_aconnect *aconnect = dev_get_drvdata(dev); + int ret; + + ret = clk_prepare_enable(aconnect->ape_clk); + if (ret) { + dev_err(dev, "ape clk_enable failed: %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(aconnect->apb2ape_clk); + if (ret) { + clk_disable_unprepare(aconnect->ape_clk); + dev_err(dev, "apb2ape clk_enable failed: %d\n", ret); + return ret; + } + + return 0; } static int tegra_aconnect_runtime_suspend(struct device *dev) { - return pm_clk_suspend(dev); + struct tegra_aconnect *aconnect = dev_get_drvdata(dev); + + clk_disable_unprepare(aconnect->ape_clk); + clk_disable_unprepare(aconnect->apb2ape_clk); + + return 0; } static const struct dev_pm_ops tegra_aconnect_pm_ops = { -- cgit v1.2.3 From 1427736e64c3a8b513cd984332f596055a7ca196 Mon Sep 17 00:00:00 2001 From: Sameer Pujar Date: Wed, 13 Mar 2019 10:11:59 +0530 Subject: bus: tegra-aconnect: add system sleep callbacks pm_runtime_force_suspend() and pm_runtime_force_resume() are used as system sleep noirq suspend and resume callbacks. If the driver is active till late suspend, where runtime PM cannot run, force suspend is essential for the device. This makes sure that the device is put into low power state during system wide PM transitions to sleep states. Signed-off-by: Sameer Pujar Acked-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/bus/tegra-aconnect.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/bus/tegra-aconnect.c b/drivers/bus/tegra-aconnect.c index 9349157b3a5b..ac58142301f4 100644 --- a/drivers/bus/tegra-aconnect.c +++ b/drivers/bus/tegra-aconnect.c @@ -94,6 +94,8 @@ static int tegra_aconnect_runtime_suspend(struct device *dev) static const struct dev_pm_ops tegra_aconnect_pm_ops = { SET_RUNTIME_PM_OPS(tegra_aconnect_runtime_suspend, tegra_aconnect_runtime_resume, NULL) + SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) }; static const struct of_device_id tegra_aconnect_of_match[] = { -- cgit v1.2.3 From 6256f7f7f217b2216fcb73929508325f4ee98237 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 3 Apr 2019 10:27:39 +0530 Subject: rtc: OMAP: Add support for rtc-only mode Prepare rtc driver for rtc-only with DDR in self-refresh mode. omap_rtc_power_off now should cater to two features: 1) RTC plus DDR in self-refresh is power a saving mode where in the entire system including the different voltage rails from PMIC are shutdown except the ones feeding on to RTC and DDR. DDR is kept in self-refresh hence the contents are preserved. RTC ALARM2 is connected to PMIC_EN line once we the ALARM2 is triggered we enter the mode with DDR in self-refresh and RTC Ticking. After a predetermined time an RTC ALARM1 triggers waking up the system[1]. The control goes to bootloader. The bootloader then checks RTC scratchpad registers to confirm it was an rtc_only wakeup and follows a different path, configure bare minimal clocks for ddr and then jumps to the resume address in another RTC scratchpad registers and transfers the control to Kernel. Kernel then restores the saved context. omap_rtc_power_off_program does the ALARM2 programming part. [1] http://www.ti.com/lit/ug/spruhl7h/spruhl7h.pdf Page 2884 2) Power-off: This is usual poweroff mode. omap_rtc_power_off calls the above omap_rtc_power_off_program function and in addition to that programs the OMAP_RTC_PMIC_REG for any external wake ups for PMIC like the pushbutton and shuts off the PMIC. Hence the split in omap_rtc_power_off. Signed-off-by: Keerthy Acked-by: Alexandre Belloni [tony@atomide.com: folded in a fix for compile warning] Signed-off-by: Tony Lindgren --- drivers/rtc/rtc-omap.c | 51 +++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/rtc/rtc-omap.c b/drivers/rtc/rtc-omap.c index bbff0e2deb84..19b11b824d9c 100644 --- a/drivers/rtc/rtc-omap.c +++ b/drivers/rtc/rtc-omap.c @@ -415,15 +415,12 @@ static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) static struct omap_rtc *omap_rtc_power_off_rtc; -/* - * omap_rtc_poweroff: RTC-controlled power off - * - * The RTC can be used to control an external PMIC via the pmic_power_en pin, - * which can be configured to transition to OFF on ALARM2 events. - * - * Called with local interrupts disabled. +/** + * omap_rtc_power_off_program: Set the pmic power off sequence. The RTC + * generates pmic_pwr_enable control, which can be used to control an external + * PMIC. */ -static void omap_rtc_power_off(void) +int omap_rtc_power_off_program(struct device *dev) { struct omap_rtc *rtc = omap_rtc_power_off_rtc; struct rtc_time tm; @@ -437,6 +434,9 @@ static void omap_rtc_power_off(void) rtc_writel(rtc, OMAP_RTC_PMIC_REG, val | OMAP_RTC_PMIC_POWER_EN_EN); again: + /* Clear any existing ALARM2 event */ + rtc_writel(rtc, OMAP_RTC_STATUS_REG, OMAP_RTC_STATUS_ALARM2); + /* set alarm one second from now */ omap_rtc_read_time_raw(rtc, &tm); seconds = tm.tm_sec; @@ -447,7 +447,7 @@ again: if (tm2bcd(&tm) < 0) { dev_err(&rtc->rtc->dev, "power off failed\n"); rtc->type->lock(rtc); - return; + return -EINVAL; } rtc_wait_not_busy(rtc); @@ -477,6 +477,39 @@ again: rtc->type->lock(rtc); + return 0; +} +EXPORT_SYMBOL(omap_rtc_power_off_program); + +/* + * omap_rtc_poweroff: RTC-controlled power off + * + * The RTC can be used to control an external PMIC via the pmic_power_en pin, + * which can be configured to transition to OFF on ALARM2 events. + * + * Notes: + * The one-second alarm offset is the shortest offset possible as the alarm + * registers must be set before the next timer update and the offset + * calculation is too heavy for everything to be done within a single access + * period (~15 us). + * + * Called with local interrupts disabled. + */ +static void omap_rtc_power_off(void) +{ + struct rtc_device *rtc = omap_rtc_power_off_rtc->rtc; + u32 val; + + omap_rtc_power_off_program(rtc->dev.parent); + + /* Set PMIC power enable and EXT_WAKEUP in case PB power on is used */ + omap_rtc_power_off_rtc->type->unlock(omap_rtc_power_off_rtc); + val = rtc_readl(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG); + val |= OMAP_RTC_PMIC_POWER_EN_EN | OMAP_RTC_PMIC_EXT_WKUP_POL(0) | + OMAP_RTC_PMIC_EXT_WKUP_EN(0); + rtc_writel(omap_rtc_power_off_rtc, OMAP_RTC_PMIC_REG, val); + omap_rtc_power_off_rtc->type->lock(omap_rtc_power_off_rtc); + /* * Wait for alarm to trigger (within one second) and external PMIC to * power off the system. Add a 500 ms margin for external latencies -- cgit v1.2.3 From 1c6c03545089ef13a5dd6ef85900765be64aea49 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 3 Apr 2019 10:27:41 +0530 Subject: soc: ti: pm33xx: Move the am33xx_push_sram_idle to the top Move the am33xx_push_sram_idle function to the top as a preparation for rtc+ddr mode as the function will be called by multiple functions currently present before it. No functional changes. Signed-off-by: Keerthy Signed-off-by: Tony Lindgren --- drivers/soc/ti/pm33xx.c | 100 ++++++++++++++++++++++++------------------------ 1 file changed, 50 insertions(+), 50 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index d0dab323651f..69128993776d 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -49,6 +49,56 @@ static u32 sram_suspend_address(unsigned long addr) AMX3_PM_SRAM_SYMBOL_OFFSET(addr)); } +static int am33xx_push_sram_idle(void) +{ + struct am33xx_pm_ro_sram_data ro_sram_data; + int ret; + u32 table_addr, ro_data_addr; + void *copy_addr; + + ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; + ro_sram_data.amx3_pm_sram_data_phys = + gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); + ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr(); + + /* Save physical address to calculate resume offset during pm init */ + am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, + ocmcram_location); + + am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, + pm_sram->do_wfi, + *pm_sram->do_wfi_sz); + if (!am33xx_do_wfi_sram) { + dev_err(pm33xx_dev, + "PM: %s: am33xx_do_wfi copy to sram failed\n", + __func__); + return -ENODEV; + } + + table_addr = + sram_suspend_address((unsigned long)pm_sram->emif_sram_table); + ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); + if (ret) { + dev_dbg(pm33xx_dev, + "PM: %s: EMIF function copy failed\n", __func__); + return -EPROBE_DEFER; + } + + ro_data_addr = + sram_suspend_address((unsigned long)pm_sram->ro_sram_data); + copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, + &ro_sram_data, + sizeof(ro_sram_data)); + if (!copy_addr) { + dev_err(pm33xx_dev, + "PM: %s: ro_sram_data copy to sram failed\n", + __func__); + return -ENODEV; + } + + return 0; +} + #ifdef CONFIG_SUSPEND static int am33xx_pm_suspend(suspend_state_t suspend_state) { @@ -219,56 +269,6 @@ mpu_put_node: return ret; } -static int am33xx_push_sram_idle(void) -{ - struct am33xx_pm_ro_sram_data ro_sram_data; - int ret; - u32 table_addr, ro_data_addr; - void *copy_addr; - - ro_sram_data.amx3_pm_sram_data_virt = ocmcram_location_data; - ro_sram_data.amx3_pm_sram_data_phys = - gen_pool_virt_to_phys(sram_pool_data, ocmcram_location_data); - ro_sram_data.rtc_base_virt = pm_ops->get_rtc_base_addr(); - - /* Save physical address to calculate resume offset during pm init */ - am33xx_do_wfi_sram_phys = gen_pool_virt_to_phys(sram_pool, - ocmcram_location); - - am33xx_do_wfi_sram = sram_exec_copy(sram_pool, (void *)ocmcram_location, - pm_sram->do_wfi, - *pm_sram->do_wfi_sz); - if (!am33xx_do_wfi_sram) { - dev_err(pm33xx_dev, - "PM: %s: am33xx_do_wfi copy to sram failed\n", - __func__); - return -ENODEV; - } - - table_addr = - sram_suspend_address((unsigned long)pm_sram->emif_sram_table); - ret = ti_emif_copy_pm_function_table(sram_pool, (void *)table_addr); - if (ret) { - dev_dbg(pm33xx_dev, - "PM: %s: EMIF function copy failed\n", __func__); - return -EPROBE_DEFER; - } - - ro_data_addr = - sram_suspend_address((unsigned long)pm_sram->ro_sram_data); - copy_addr = sram_exec_copy(sram_pool, (void *)ro_data_addr, - &ro_sram_data, - sizeof(ro_sram_data)); - if (!copy_addr) { - dev_err(pm33xx_dev, - "PM: %s: ro_sram_data copy to sram failed\n", - __func__); - return -ENODEV; - } - - return 0; -} - static int am33xx_pm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; -- cgit v1.2.3 From 5a99ae0092fe24fd581fdb6b9c2b48f94f92cf32 Mon Sep 17 00:00:00 2001 From: Keerthy Date: Wed, 3 Apr 2019 10:27:42 +0530 Subject: soc: ti: pm33xx: AM437X: Add rtc_only with ddr in self-refresh support During RTC-only suspend, power is lost to the wkup domain, so we need to save and restore the state of that domain. We also need to store some information within the RTC registers so that u-boot can do the right thing at powerup. The state is entered by getting the RTC to bring the pmic_power_en line low which will instruct the PMIC to disable the appropriate power rails after putting DDR into self-refresh mode. To bring pmic_power_en low, we need to get an ALARM2 event. Since we are running from SRAM at that point, it means calculating what the next second is (via ASM) and programming that into the RTC. This patch also adds support for wake up source detection. Signed-off-by: Keerthy Signed-off-by: Dave Gerlach Signed-off-by: Tony Lindgren --- drivers/soc/ti/Kconfig | 5 +- drivers/soc/ti/pm33xx.c | 191 +++++++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 185 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig index be4570baad96..57960e92ebe0 100644 --- a/drivers/soc/ti/Kconfig +++ b/drivers/soc/ti/Kconfig @@ -45,11 +45,12 @@ config KEYSTONE_NAVIGATOR_DMA config AMX3_PM tristate "AMx3 Power Management" depends on SOC_AM33XX || SOC_AM43XX - depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM + depends on WKUP_M3_IPC && TI_EMIF_SRAM && SRAM && RTC_DRV_OMAP help Enable power management on AM335x and AM437x. Required for suspend to mem and standby states on both AM335x and AM437x platforms and for deeper cpuidle - c-states on AM335x. + c-states on AM335x. Also required for rtc and ddr in self-refresh low + power mode on AM437x platforms. config WKUP_M3_IPC tristate "TI AMx3 Wkup-M3 IPC Driver" diff --git a/drivers/soc/ti/pm33xx.c b/drivers/soc/ti/pm33xx.c index 69128993776d..fc5802ccb1c0 100644 --- a/drivers/soc/ti/pm33xx.c +++ b/drivers/soc/ti/pm33xx.c @@ -6,6 +6,7 @@ * Vaibhav Bedia, Dave Gerlach */ +#include #include #include #include @@ -13,9 +14,12 @@ #include #include #include +#include #include #include #include +#include +#include #include #include #include @@ -29,20 +33,47 @@ #define AMX3_PM_SRAM_SYMBOL_OFFSET(sym) ((unsigned long)(sym) - \ (unsigned long)pm_sram->do_wfi) +#define RTC_SCRATCH_RESUME_REG 0 +#define RTC_SCRATCH_MAGIC_REG 1 +#define RTC_REG_BOOT_MAGIC 0x8cd0 /* RTC */ +#define GIC_INT_SET_PENDING_BASE 0x200 +#define AM43XX_GIC_DIST_BASE 0x48241000 + +static u32 rtc_magic_val; + static int (*am33xx_do_wfi_sram)(unsigned long unused); static phys_addr_t am33xx_do_wfi_sram_phys; static struct gen_pool *sram_pool, *sram_pool_data; static unsigned long ocmcram_location, ocmcram_location_data; +static struct rtc_device *omap_rtc; +static void __iomem *gic_dist_base; + static struct am33xx_pm_platform_data *pm_ops; static struct am33xx_pm_sram_addr *pm_sram; static struct device *pm33xx_dev; static struct wkup_m3_ipc *m3_ipc; +#ifdef CONFIG_SUSPEND +static int rtc_only_idle; +static int retrigger_irq; static unsigned long suspend_wfi_flags; +static struct wkup_m3_wakeup_src wakeup_src = {.irq_nr = 0, + .src = "Unknown", +}; + +static struct wkup_m3_wakeup_src rtc_alarm_wakeup = { + .irq_nr = 108, .src = "RTC Alarm", +}; + +static struct wkup_m3_wakeup_src rtc_ext_wakeup = { + .irq_nr = 0, .src = "Ext wakeup", +}; +#endif + static u32 sram_suspend_address(unsigned long addr) { return ((unsigned long)am33xx_do_wfi_sram + @@ -99,13 +130,65 @@ static int am33xx_push_sram_idle(void) return 0; } +static int __init am43xx_map_gic(void) +{ + gic_dist_base = ioremap(AM43XX_GIC_DIST_BASE, SZ_4K); + + if (!gic_dist_base) + return -ENOMEM; + + return 0; +} + #ifdef CONFIG_SUSPEND +struct wkup_m3_wakeup_src rtc_wake_src(void) +{ + u32 i; + + i = __raw_readl(pm_ops->get_rtc_base_addr() + 0x44) & 0x40; + + if (i) { + retrigger_irq = rtc_alarm_wakeup.irq_nr; + return rtc_alarm_wakeup; + } + + retrigger_irq = rtc_ext_wakeup.irq_nr; + + return rtc_ext_wakeup; +} + +int am33xx_rtc_only_idle(unsigned long wfi_flags) +{ + omap_rtc_power_off_program(&omap_rtc->dev); + am33xx_do_wfi_sram(wfi_flags); + return 0; +} + static int am33xx_pm_suspend(suspend_state_t suspend_state) { int i, ret = 0; - ret = pm_ops->soc_suspend((unsigned long)suspend_state, - am33xx_do_wfi_sram, suspend_wfi_flags); + if (suspend_state == PM_SUSPEND_MEM && + pm_ops->check_off_mode_enable()) { + pm_ops->prepare_rtc_suspend(); + pm_ops->save_context(); + suspend_wfi_flags |= WFI_FLAG_RTC_ONLY; + clk_save_context(); + ret = pm_ops->soc_suspend(suspend_state, am33xx_rtc_only_idle, + suspend_wfi_flags); + + suspend_wfi_flags &= ~WFI_FLAG_RTC_ONLY; + + if (!ret) { + clk_restore_context(); + pm_ops->restore_context(); + m3_ipc->ops->set_rtc_only(m3_ipc); + am33xx_push_sram_idle(); + } + } else { + ret = pm_ops->soc_suspend(suspend_state, am33xx_do_wfi_sram, + suspend_wfi_flags); + } if (ret) { dev_err(pm33xx_dev, "PM: Kernel suspend failure\n"); @@ -127,8 +210,20 @@ static int am33xx_pm_suspend(suspend_state_t suspend_state) "PM: CM3 returned unknown result = %d\n", i); ret = -1; } + + /* print the wakeup reason */ + if (rtc_only_idle) { + wakeup_src = rtc_wake_src(); + pr_info("PM: Wakeup source %s\n", wakeup_src.src); + } else { + pr_info("PM: Wakeup source %s\n", + m3_ipc->ops->request_wake_src(m3_ipc)); + } } + if (suspend_state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) + pm_ops->prepare_rtc_resume(); + return ret; } @@ -151,6 +246,18 @@ static int am33xx_pm_enter(suspend_state_t suspend_state) static int am33xx_pm_begin(suspend_state_t state) { int ret = -EINVAL; + struct nvmem_device *nvmem; + + if (state == PM_SUSPEND_MEM && pm_ops->check_off_mode_enable()) { + nvmem = devm_nvmem_device_get(&omap_rtc->dev, + "omap_rtc_scratch0"); + if (nvmem) + nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, + (void *)&rtc_magic_val); + rtc_only_idle = 1; + } else { + rtc_only_idle = 0; + } switch (state) { case PM_SUSPEND_MEM: @@ -166,7 +273,28 @@ static int am33xx_pm_begin(suspend_state_t state) static void am33xx_pm_end(void) { + u32 val = 0; + struct nvmem_device *nvmem; + + nvmem = devm_nvmem_device_get(&omap_rtc->dev, "omap_rtc_scratch0"); m3_ipc->ops->finish_low_power(m3_ipc); + if (rtc_only_idle) { + if (retrigger_irq) + /* + * 32 bits of Interrupt Set-Pending correspond to 32 + * 32 interrupts. Compute the bit offset of the + * Interrupt and set that particular bit + * Compute the register offset by dividing interrupt + * number by 32 and mutiplying by 4 + */ + writel_relaxed(1 << (retrigger_irq & 31), + gic_dist_base + GIC_INT_SET_PENDING_BASE + + retrigger_irq / 32 * 4); + nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, 4, + (void *)&val); + } + + rtc_only_idle = 0; } static int am33xx_pm_valid(suspend_state_t state) @@ -269,6 +397,42 @@ mpu_put_node: return ret; } +static int am33xx_pm_rtc_setup(void) +{ + struct device_node *np; + unsigned long val = 0; + struct nvmem_device *nvmem; + + np = of_find_node_by_name(NULL, "rtc"); + + if (of_device_is_available(np)) { + omap_rtc = rtc_class_open("rtc0"); + if (!omap_rtc) { + pr_warn("PM: rtc0 not available"); + return -EPROBE_DEFER; + } + + nvmem = devm_nvmem_device_get(&omap_rtc->dev, + "omap_rtc_scratch0"); + if (nvmem) { + nvmem_device_read(nvmem, RTC_SCRATCH_MAGIC_REG * 4, + 4, (void *)&rtc_magic_val); + if ((rtc_magic_val & 0xffff) != RTC_REG_BOOT_MAGIC) + pr_warn("PM: bootloader does not support rtc-only!\n"); + + nvmem_device_write(nvmem, RTC_SCRATCH_MAGIC_REG * 4, + 4, (void *)&val); + val = pm_sram->resume_address; + nvmem_device_write(nvmem, RTC_SCRATCH_RESUME_REG * 4, + 4, (void *)&val); + } + } else { + pr_warn("PM: no-rtc available, rtc-only mode disabled.\n"); + } + + return 0; +} + static int am33xx_pm_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -284,34 +448,42 @@ static int am33xx_pm_probe(struct platform_device *pdev) return -ENODEV; } + ret = am43xx_map_gic(); + if (ret) { + pr_err("PM: Could not ioremap GIC base\n"); + return ret; + } + pm_sram = pm_ops->get_sram_addrs(); if (!pm_sram) { dev_err(dev, "PM: Cannot get PM asm function addresses!!\n"); return -ENODEV; } + m3_ipc = wkup_m3_ipc_get(); + if (!m3_ipc) { + pr_err("PM: Cannot get wkup_m3_ipc handle\n"); + return -EPROBE_DEFER; + } + pm33xx_dev = dev; ret = am33xx_pm_alloc_sram(); if (ret) return ret; - ret = am33xx_push_sram_idle(); + ret = am33xx_pm_rtc_setup(); if (ret) goto err_free_sram; - m3_ipc = wkup_m3_ipc_get(); - if (!m3_ipc) { - dev_dbg(dev, "PM: Cannot get wkup_m3_ipc handle\n"); - ret = -EPROBE_DEFER; + ret = am33xx_push_sram_idle(); + if (ret) goto err_free_sram; - } am33xx_pm_set_ipc_ops(); #ifdef CONFIG_SUSPEND suspend_set_ops(&am33xx_pm_ops); -#endif /* CONFIG_SUSPEND */ /* * For a system suspend we must flush the caches, we want @@ -323,6 +495,7 @@ static int am33xx_pm_probe(struct platform_device *pdev) suspend_wfi_flags |= WFI_FLAG_SELF_REFRESH; suspend_wfi_flags |= WFI_FLAG_SAVE_EMIF; suspend_wfi_flags |= WFI_FLAG_WAKE_M3; +#endif /* CONFIG_SUSPEND */ ret = pm_ops->init(); if (ret) { -- cgit v1.2.3 From 6c110561eb2d4d1496961c13a92f96f29eea7c72 Mon Sep 17 00:00:00 2001 From: Dave Gerlach Date: Tue, 2 Apr 2019 11:57:42 -0500 Subject: memory: ti-emif-sram: Add ti_emif_run_hw_leveling for DDR3 hardware leveling In certain situations, such as when returning from low power modes, the EMIF must re-run hardware leveling to properly restore DDR3 access. This is accomplished by introducing a new ti-emif-sram-pm call, ti_emif_run_hw_leveling, to check if DDR3 is in use and if so, trigger the full write and read leveling processes. Suggested-by: Brad Griffis Signed-off-by: Dave Gerlach Acked-by: Santosh Shilimkar Signed-off-by: Tony Lindgren --- drivers/memory/emif.h | 4 ++++ drivers/memory/ti-emif-pm.c | 3 +++ drivers/memory/ti-emif-sram-pm.S | 41 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/memory/emif.h b/drivers/memory/emif.h index 9e9f8037955d..6b71fadb3cfa 100644 --- a/drivers/memory/emif.h +++ b/drivers/memory/emif.h @@ -537,6 +537,9 @@ #define MCONNID_SHIFT 0 #define MCONNID_MASK (0xff << 0) +/* READ_WRITE_LEVELING_CONTROL */ +#define RDWRLVLFULL_START 0x80000000 + /* DDR_PHY_CTRL_1 - EMIF4D */ #define DLL_SLAVE_DLY_CTRL_SHIFT_4D 4 #define DLL_SLAVE_DLY_CTRL_MASK_4D (0xFF << 4) @@ -598,6 +601,7 @@ extern struct emif_regs_amx3 ti_emif_regs_amx3; void ti_emif_save_context(void); void ti_emif_restore_context(void); +void ti_emif_run_hw_leveling(void); void ti_emif_enter_sr(void); void ti_emif_exit_sr(void); void ti_emif_abort_sr(void); diff --git a/drivers/memory/ti-emif-pm.c b/drivers/memory/ti-emif-pm.c index 2250d03ea17f..ab07aa163138 100644 --- a/drivers/memory/ti-emif-pm.c +++ b/drivers/memory/ti-emif-pm.c @@ -138,6 +138,9 @@ static int ti_emif_alloc_sram(struct device *dev, emif_data->pm_functions.exit_sr = sram_resume_address(emif_data, (unsigned long)ti_emif_exit_sr); + emif_data->pm_functions.run_hw_leveling = + sram_resume_address(emif_data, + (unsigned long)ti_emif_run_hw_leveling); emif_data->pm_data.regs_virt = (struct emif_regs_amx3 *)emif_data->ti_emif_sram_data_virt; diff --git a/drivers/memory/ti-emif-sram-pm.S b/drivers/memory/ti-emif-sram-pm.S index a5369181e5c2..d75ae18efa7d 100644 --- a/drivers/memory/ti-emif-sram-pm.S +++ b/drivers/memory/ti-emif-sram-pm.S @@ -27,6 +27,7 @@ #define EMIF_POWER_MGMT_SELF_REFRESH_MODE_MASK 0x0700 #define EMIF_SDCFG_TYPE_DDR2 0x2 << SDRAM_TYPE_SHIFT +#define EMIF_SDCFG_TYPE_DDR3 0x3 << SDRAM_TYPE_SHIFT #define EMIF_STATUS_READY 0x4 #define AM43XX_EMIF_PHY_CTRL_REG_COUNT 0x120 @@ -244,6 +245,46 @@ emif_skip_restore_extra_regs: mov pc, lr ENDPROC(ti_emif_restore_context) +/* + * void ti_emif_run_hw_leveling(void) + * + * Used during resume to run hardware leveling again and restore the + * configuration of the EMIF PHY, only for DDR3. + */ +ENTRY(ti_emif_run_hw_leveling) + adr r4, ti_emif_pm_sram_data + ldr r0, [r4, #EMIF_PM_BASE_ADDR_PHYS_OFFSET] + + ldr r3, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL] + orr r3, r3, #RDWRLVLFULL_START + ldr r2, [r0, #EMIF_SDRAM_CONFIG] + and r2, r2, #SDRAM_TYPE_MASK + cmp r2, #EMIF_SDCFG_TYPE_DDR3 + bne skip_hwlvl + + str r3, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL] + + /* + * If EMIF registers are touched during initial stage of HW + * leveling sequence there will be an L3 NOC timeout error issued + * as the EMIF will not respond, which is not fatal, but it is + * avoidable. This small wait loop is enough time for this condition + * to clear, even at worst case of CPU running at max speed of 1Ghz. + */ + mov r2, #0x2000 +1: + subs r2, r2, #0x1 + bne 1b + + /* Bit clears when operation is complete */ +2: ldr r1, [r0, #EMIF_READ_WRITE_LEVELING_CONTROL] + tst r1, #RDWRLVLFULL_START + bne 2b + +skip_hwlvl: + mov pc, lr +ENDPROC(ti_emif_run_hw_leveling) + /* * void ti_emif_enter_sr(void) * -- cgit v1.2.3 From bbeac60f064158aa92b4c72fb50b12231e62cbcd Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Thu, 21 Feb 2019 18:10:36 +0530 Subject: drivers: soc: qcom: rpmh-rsc: Correct check for slot number The return index value from bitmap_find_next_zero_area can be higher than available slot. So correct the check to return error in such case. Signed-off-by: Maulik Shah Signed-off-by: Raju P.L.S.S.S.N Reviewed-by: Lina Iyer Signed-off-by: Andy Gross --- drivers/soc/qcom/rpmh-rsc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 75bd9a83aef0..e278fc11fe5c 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -459,7 +459,7 @@ static int find_slots(struct tcs_group *tcs, const struct tcs_request *msg, do { slot = bitmap_find_next_zero_area(tcs->slots, MAX_TCS_SLOTS, i, msg->num_cmds, 0); - if (slot == tcs->num_tcs * tcs->ncpt) + if (slot >= tcs->num_tcs * tcs->ncpt) return -ENOMEM; i += tcs->ncpt; } while (slot + msg->num_cmds - 1 >= i); -- cgit v1.2.3 From ccc1de31ab54bbf3e9fe2df1f562ea92d8fe8e44 Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 1 Apr 2019 06:07:08 +0000 Subject: soc: imx: gpc: use devm_platform_ioremap_resource() to simplify code Use the new helper devm_platform_ioremap_resource() which wraps the platform_get_resource() and devm_ioremap_resource() together, to simplify the code. Signed-off-by: Anson Huang Reviewed-by: Mukesh Ojha Signed-off-by: Shawn Guo --- drivers/soc/imx/gpc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c index 7d14a4b4e82a..a8f1e47ce698 100644 --- a/drivers/soc/imx/gpc.c +++ b/drivers/soc/imx/gpc.c @@ -406,7 +406,6 @@ static int imx_gpc_probe(struct platform_device *pdev) const struct imx_gpc_dt_data *of_id_data = of_id->data; struct device_node *pgc_node; struct regmap *regmap; - struct resource *res; void __iomem *base; int ret; @@ -417,8 +416,7 @@ static int imx_gpc_probe(struct platform_device *pdev) !pgc_node) return 0; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); -- cgit v1.2.3 From 9f735c4e94fcbec66aa282eebb92c4aa3de88c6d Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Mon, 1 Apr 2019 06:07:13 +0000 Subject: soc: imx: gpcv2: use devm_platform_ioremap_resource() to simplify code Use the new helper devm_platform_ioremap_resource() which wraps the platform_get_resource() and devm_ioremap_resource() together, to simplify the code. Signed-off-by: Anson Huang Reviewed-by: Mukesh Ojha Signed-off-by: Shawn Guo --- drivers/soc/imx/gpcv2.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/imx/gpcv2.c b/drivers/soc/imx/gpcv2.c index e06bf12a1e4b..31b8d002d855 100644 --- a/drivers/soc/imx/gpcv2.c +++ b/drivers/soc/imx/gpcv2.c @@ -563,7 +563,6 @@ static int imx_gpcv2_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *pgc_np, *np; struct regmap *regmap; - struct resource *res; void __iomem *base; int ret; @@ -573,8 +572,7 @@ static int imx_gpcv2_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(dev, res); + base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(base)) return PTR_ERR(base); -- cgit v1.2.3 From 851826c7566e9bb4d03eb050634031ecc802affb Mon Sep 17 00:00:00 2001 From: Anson Huang Date: Tue, 9 Apr 2019 04:59:55 +0000 Subject: firmware: imx: enable imx scu general irq function The System Controller Firmware (SCFW) controls RTC, thermal and WDOG etc., these resources' interrupt function are managed by SCU. When any IRQ pending, SCU will notify Linux via MU general interrupt channel #3, and Linux kernel needs to call SCU APIs to get IRQ status and notify each module to handle the interrupt. Since there is no data transmission for SCU IRQ notification, so doorbell mode is used for this MU channel, and SCU driver will use notifier mechanism to broadcast to every module which registers the SCU block notifier. Signed-off-by: Anson Huang Reviewed-by: Dong Aisheng Signed-off-by: Shawn Guo --- drivers/firmware/imx/Makefile | 2 +- drivers/firmware/imx/imx-scu-irq.c | 168 +++++++++++++++++++++++++++++++++++++ drivers/firmware/imx/imx-scu.c | 6 ++ 3 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 drivers/firmware/imx/imx-scu-irq.c (limited to 'drivers') diff --git a/drivers/firmware/imx/Makefile b/drivers/firmware/imx/Makefile index 1b2e15b3c9ca..802c4ad8e8f9 100644 --- a/drivers/firmware/imx/Makefile +++ b/drivers/firmware/imx/Makefile @@ -1,3 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 -obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o +obj-$(CONFIG_IMX_SCU) += imx-scu.o misc.o imx-scu-irq.o obj-$(CONFIG_IMX_SCU_PD) += scu-pd.o diff --git a/drivers/firmware/imx/imx-scu-irq.c b/drivers/firmware/imx/imx-scu-irq.c new file mode 100644 index 000000000000..043833ad3c1a --- /dev/null +++ b/drivers/firmware/imx/imx-scu-irq.c @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2019 NXP + * + * Implementation of the SCU IRQ functions using MU. + * + */ + +#include +#include +#include + +#define IMX_SC_IRQ_FUNC_ENABLE 1 +#define IMX_SC_IRQ_FUNC_STATUS 2 +#define IMX_SC_IRQ_NUM_GROUP 4 + +static u32 mu_resource_id; + +struct imx_sc_msg_irq_get_status { + struct imx_sc_rpc_msg hdr; + union { + struct { + u16 resource; + u8 group; + u8 reserved; + } __packed req; + struct { + u32 status; + } resp; + } data; +}; + +struct imx_sc_msg_irq_enable { + struct imx_sc_rpc_msg hdr; + u32 mask; + u16 resource; + u8 group; + u8 enable; +} __packed; + +static struct imx_sc_ipc *imx_sc_irq_ipc_handle; +static struct work_struct imx_sc_irq_work; +static ATOMIC_NOTIFIER_HEAD(imx_scu_irq_notifier_chain); + +int imx_scu_irq_register_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_register( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_register_notifier); + +int imx_scu_irq_unregister_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister( + &imx_scu_irq_notifier_chain, nb); +} +EXPORT_SYMBOL(imx_scu_irq_unregister_notifier); + +static int imx_scu_irq_notifier_call_chain(unsigned long status, u8 *group) +{ + return atomic_notifier_call_chain(&imx_scu_irq_notifier_chain, + status, (void *)group); +} + +static void imx_scu_irq_work_handler(struct work_struct *work) +{ + struct imx_sc_msg_irq_get_status msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + u32 irq_status; + int ret; + u8 i; + + for (i = 0; i < IMX_SC_IRQ_NUM_GROUP; i++) { + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_STATUS; + hdr->size = 2; + + msg.data.req.resource = mu_resource_id; + msg.data.req.group = i; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) { + pr_err("get irq group %d status failed, ret %d\n", + i, ret); + return; + } + + irq_status = msg.data.resp.status; + if (!irq_status) + continue; + + imx_scu_irq_notifier_call_chain(irq_status, &i); + } +} + +int imx_scu_irq_group_enable(u8 group, u32 mask, u8 enable) +{ + struct imx_sc_msg_irq_enable msg; + struct imx_sc_rpc_msg *hdr = &msg.hdr; + int ret; + + hdr->ver = IMX_SC_RPC_VERSION; + hdr->svc = IMX_SC_RPC_SVC_IRQ; + hdr->func = IMX_SC_IRQ_FUNC_ENABLE; + hdr->size = 3; + + msg.resource = mu_resource_id; + msg.group = group; + msg.mask = mask; + msg.enable = enable; + + ret = imx_scu_call_rpc(imx_sc_irq_ipc_handle, &msg, true); + if (ret) + pr_err("enable irq failed, group %d, mask %d, ret %d\n", + group, mask, ret); + + return ret; +} +EXPORT_SYMBOL(imx_scu_irq_group_enable); + +static void imx_scu_irq_callback(struct mbox_client *c, void *msg) +{ + schedule_work(&imx_sc_irq_work); +} + +int imx_scu_enable_general_irq_channel(struct device *dev) +{ + struct of_phandle_args spec; + struct mbox_client *cl; + struct mbox_chan *ch; + int ret = 0, i = 0; + + ret = imx_scu_get_handle(&imx_sc_irq_ipc_handle); + if (ret) + return ret; + + cl = devm_kzalloc(dev, sizeof(*cl), GFP_KERNEL); + if (!cl) + return -ENOMEM; + + cl->dev = dev; + cl->rx_callback = imx_scu_irq_callback; + + /* SCU general IRQ uses general interrupt channel 3 */ + ch = mbox_request_channel_byname(cl, "gip3"); + if (IS_ERR(ch)) { + ret = PTR_ERR(ch); + dev_err(dev, "failed to request mbox chan gip3, ret %d\n", ret); + devm_kfree(dev, cl); + return ret; + } + + INIT_WORK(&imx_sc_irq_work, imx_scu_irq_work_handler); + + if (!of_parse_phandle_with_args(dev->of_node, "mboxes", + "#mbox-cells", 0, &spec)) + i = of_alias_get_id(spec.np, "mu"); + + /* use mu1 as general mu irq channel if failed */ + if (i < 0) + i = 1; + + mu_resource_id = IMX_SC_R_MU_0A + i; + + return ret; +} +EXPORT_SYMBOL(imx_scu_enable_general_irq_channel); diff --git a/drivers/firmware/imx/imx-scu.c b/drivers/firmware/imx/imx-scu.c index 2bb1a19c413f..04a24a863d6e 100644 --- a/drivers/firmware/imx/imx-scu.c +++ b/drivers/firmware/imx/imx-scu.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -246,6 +247,11 @@ static int imx_scu_probe(struct platform_device *pdev) imx_sc_ipc_handle = sc_ipc; + ret = imx_scu_enable_general_irq_channel(dev); + if (ret) + dev_warn(dev, + "failed to enable general irq channel: %d\n", ret); + dev_info(dev, "NXP i.MX SCU Initialized\n"); return devm_of_platform_populate(dev); -- cgit v1.2.3 From f2222a9e8033ceca2ed8b1addea1d541b536ab7b Mon Sep 17 00:00:00 2001 From: Yue Haibing Date: Wed, 10 Apr 2019 22:04:50 +0800 Subject: memory: tegra: Make terga20_mc_reset_ops static Fix sparse warning: drivers/memory/tegra/tegra20.c:277:33: warning: symbol 'terga20_mc_reset_ops' was not declared. Should it be static? Reported-by: Hulk Robot Signed-off-by: Yue Haibing Signed-off-by: Thierry Reding --- drivers/memory/tegra/tegra20.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index 7119e532471c..b786aec089e7 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -274,7 +274,7 @@ static int terga20_mc_unblock_dma(struct tegra_mc *mc, return 0; } -const struct tegra_mc_reset_ops terga20_mc_reset_ops = { +static const struct tegra_mc_reset_ops terga20_mc_reset_ops = { .hotreset_assert = terga20_mc_hotreset_assert, .hotreset_deassert = terga20_mc_hotreset_deassert, .block_dma = terga20_mc_block_dma, -- cgit v1.2.3 From bbdc00a7de24cc90315b1775fb74841373fe12f7 Mon Sep 17 00:00:00 2001 From: Douglas Anderson Date: Tue, 9 Apr 2019 13:49:05 -0700 Subject: soc: rockchip: Set the proper PWM for rk3288 The rk3288 SoC has two PWM implementations available, the "old" implementation and the "new" one. You can switch between the two of them by flipping a bit in the grf. The "old" implementation is the default at chip power up but isn't the one that's officially supposed to be used. ...and, in fact, the driver that gets selected in Linux using the rk3288 device tree only supports the "new" implementation. Long ago I tried to get a switch to the right IP block landed in the PWM driver (search for "rk3288: Switch to use the proper PWM IP") but that got rejected. In the mean time the grf has grown a full-fledged driver that already sets other random bits like this. That means we can now get the fix landed. For those wondering how things could have possibly worked for the last 4.5 years, folks have mostly been relying on the bootloader to set this bit. ...but occasionally folks have pointed back to my old patch series [1] in downstream kernels. [1] https://www.mail-archive.com/linux-kernel@vger.kernel.org/msg1391597.html Signed-off-by: Douglas Anderson Signed-off-by: Heiko Stuebner --- drivers/soc/rockchip/grf.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/rockchip/grf.c b/drivers/soc/rockchip/grf.c index 96882ffde67e..3b81e1d75a97 100644 --- a/drivers/soc/rockchip/grf.c +++ b/drivers/soc/rockchip/grf.c @@ -66,9 +66,11 @@ static const struct rockchip_grf_info rk3228_grf __initconst = { }; #define RK3288_GRF_SOC_CON0 0x244 +#define RK3288_GRF_SOC_CON2 0x24c static const struct rockchip_grf_value rk3288_defaults[] __initconst = { { "jtag switching", RK3288_GRF_SOC_CON0, HIWORD_UPDATE(0, 1, 12) }, + { "pwm select", RK3288_GRF_SOC_CON2, HIWORD_UPDATE(1, 1, 0) }, }; static const struct rockchip_grf_info rk3288_grf __initconst = { -- cgit v1.2.3 From cb2b58391e40d0877b1f60023c8be72696ff8511 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 11 Apr 2019 10:48:25 +0200 Subject: memory: tegra: Properly spell "tegra" Rename all occurrences of "terga" to "tegra". It's an easy typo to make and a difficult one to spot. Reviewed-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/memory/tegra/mc.c | 18 +++++++++--------- drivers/memory/tegra/mc.h | 2 +- drivers/memory/tegra/tegra114.c | 2 +- drivers/memory/tegra/tegra124.c | 4 ++-- drivers/memory/tegra/tegra20.c | 28 ++++++++++++++-------------- drivers/memory/tegra/tegra210.c | 2 +- drivers/memory/tegra/tegra30.c | 2 +- 7 files changed, 29 insertions(+), 29 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 0a53598d982f..a08b61a86760 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -74,7 +74,7 @@ static const struct of_device_id tegra_mc_of_match[] = { }; MODULE_DEVICE_TABLE(of, tegra_mc_of_match); -static int terga_mc_block_dma_common(struct tegra_mc *mc, +static int tegra_mc_block_dma_common(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -90,13 +90,13 @@ static int terga_mc_block_dma_common(struct tegra_mc *mc, return 0; } -static bool terga_mc_dma_idling_common(struct tegra_mc *mc, +static bool tegra_mc_dma_idling_common(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { return (mc_readl(mc, rst->status) & BIT(rst->bit)) != 0; } -static int terga_mc_unblock_dma_common(struct tegra_mc *mc, +static int tegra_mc_unblock_dma_common(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -112,17 +112,17 @@ static int terga_mc_unblock_dma_common(struct tegra_mc *mc, return 0; } -static int terga_mc_reset_status_common(struct tegra_mc *mc, +static int tegra_mc_reset_status_common(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { return (mc_readl(mc, rst->control) & BIT(rst->bit)) != 0; } -const struct tegra_mc_reset_ops terga_mc_reset_ops_common = { - .block_dma = terga_mc_block_dma_common, - .dma_idling = terga_mc_dma_idling_common, - .unblock_dma = terga_mc_unblock_dma_common, - .reset_status = terga_mc_reset_status_common, +const struct tegra_mc_reset_ops tegra_mc_reset_ops_common = { + .block_dma = tegra_mc_block_dma_common, + .dma_idling = tegra_mc_dma_idling_common, + .unblock_dma = tegra_mc_unblock_dma_common, + .reset_status = tegra_mc_reset_status_common, }; static inline struct tegra_mc *reset_to_mc(struct reset_controller_dev *rcdev) diff --git a/drivers/memory/tegra/mc.h b/drivers/memory/tegra/mc.h index 887a3b07334f..392993955c93 100644 --- a/drivers/memory/tegra/mc.h +++ b/drivers/memory/tegra/mc.h @@ -35,7 +35,7 @@ static inline void mc_writel(struct tegra_mc *mc, u32 value, writel_relaxed(value, mc->regs + offset); } -extern const struct tegra_mc_reset_ops terga_mc_reset_ops_common; +extern const struct tegra_mc_reset_ops tegra_mc_reset_ops_common; #ifdef CONFIG_ARCH_TEGRA_2x_SOC extern const struct tegra_mc_soc tegra20_mc_soc; diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c index 6560a5101322..7247a46b71a8 100644 --- a/drivers/memory/tegra/tegra114.c +++ b/drivers/memory/tegra/tegra114.c @@ -975,7 +975,7 @@ const struct tegra_mc_soc tegra114_mc_soc = { .smmu = &tegra114_smmu_soc, .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, - .reset_ops = &terga_mc_reset_ops_common, + .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra114_mc_resets, .num_resets = ARRAY_SIZE(tegra114_mc_resets), }; diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c index b561a1fe7f46..8f8487bda642 100644 --- a/drivers/memory/tegra/tegra124.c +++ b/drivers/memory/tegra/tegra124.c @@ -1074,7 +1074,7 @@ const struct tegra_mc_soc tegra124_mc_soc = { .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, - .reset_ops = &terga_mc_reset_ops_common, + .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra124_mc_resets, .num_resets = ARRAY_SIZE(tegra124_mc_resets), }; @@ -1104,7 +1104,7 @@ const struct tegra_mc_soc tegra132_mc_soc = { .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, - .reset_ops = &terga_mc_reset_ops_common, + .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra124_mc_resets, .num_resets = ARRAY_SIZE(tegra124_mc_resets), }; diff --git a/drivers/memory/tegra/tegra20.c b/drivers/memory/tegra/tegra20.c index b786aec089e7..121237b16add 100644 --- a/drivers/memory/tegra/tegra20.c +++ b/drivers/memory/tegra/tegra20.c @@ -198,7 +198,7 @@ static const struct tegra_mc_reset tegra20_mc_resets[] = { TEGRA20_MC_RESET(VI, 0x100, 0x178, 0x104, 14), }; -static int terga20_mc_hotreset_assert(struct tegra_mc *mc, +static int tegra20_mc_hotreset_assert(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -214,7 +214,7 @@ static int terga20_mc_hotreset_assert(struct tegra_mc *mc, return 0; } -static int terga20_mc_hotreset_deassert(struct tegra_mc *mc, +static int tegra20_mc_hotreset_deassert(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -230,7 +230,7 @@ static int terga20_mc_hotreset_deassert(struct tegra_mc *mc, return 0; } -static int terga20_mc_block_dma(struct tegra_mc *mc, +static int tegra20_mc_block_dma(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -246,19 +246,19 @@ static int terga20_mc_block_dma(struct tegra_mc *mc, return 0; } -static bool terga20_mc_dma_idling(struct tegra_mc *mc, +static bool tegra20_mc_dma_idling(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { return mc_readl(mc, rst->status) == 0; } -static int terga20_mc_reset_status(struct tegra_mc *mc, +static int tegra20_mc_reset_status(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { return (mc_readl(mc, rst->reset) & BIT(rst->bit)) == 0; } -static int terga20_mc_unblock_dma(struct tegra_mc *mc, +static int tegra20_mc_unblock_dma(struct tegra_mc *mc, const struct tegra_mc_reset *rst) { unsigned long flags; @@ -274,13 +274,13 @@ static int terga20_mc_unblock_dma(struct tegra_mc *mc, return 0; } -static const struct tegra_mc_reset_ops terga20_mc_reset_ops = { - .hotreset_assert = terga20_mc_hotreset_assert, - .hotreset_deassert = terga20_mc_hotreset_deassert, - .block_dma = terga20_mc_block_dma, - .dma_idling = terga20_mc_dma_idling, - .unblock_dma = terga20_mc_unblock_dma, - .reset_status = terga20_mc_reset_status, +static const struct tegra_mc_reset_ops tegra20_mc_reset_ops = { + .hotreset_assert = tegra20_mc_hotreset_assert, + .hotreset_deassert = tegra20_mc_hotreset_deassert, + .block_dma = tegra20_mc_block_dma, + .dma_idling = tegra20_mc_dma_idling, + .unblock_dma = tegra20_mc_unblock_dma, + .reset_status = tegra20_mc_reset_status, }; const struct tegra_mc_soc tegra20_mc_soc = { @@ -290,7 +290,7 @@ const struct tegra_mc_soc tegra20_mc_soc = { .client_id_mask = 0x3f, .intmask = MC_INT_SECURITY_VIOLATION | MC_INT_INVALID_GART_PAGE | MC_INT_DECERR_EMEM, - .reset_ops = &terga20_mc_reset_ops, + .reset_ops = &tegra20_mc_reset_ops, .resets = tegra20_mc_resets, .num_resets = ARRAY_SIZE(tegra20_mc_resets), }; diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c index d00a77160407..aa22cda637eb 100644 --- a/drivers/memory/tegra/tegra210.c +++ b/drivers/memory/tegra/tegra210.c @@ -1132,7 +1132,7 @@ const struct tegra_mc_soc tegra210_mc_soc = { .intmask = MC_INT_DECERR_MTS | MC_INT_SECERR_SEC | MC_INT_DECERR_VPR | MC_INT_INVALID_APB_ASID_UPDATE | MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, - .reset_ops = &terga_mc_reset_ops_common, + .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra210_mc_resets, .num_resets = ARRAY_SIZE(tegra210_mc_resets), }; diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c index bee5314ed404..68499ffdf0f4 100644 --- a/drivers/memory/tegra/tegra30.c +++ b/drivers/memory/tegra/tegra30.c @@ -999,7 +999,7 @@ const struct tegra_mc_soc tegra30_mc_soc = { .smmu = &tegra30_smmu_soc, .intmask = MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION | MC_INT_DECERR_EMEM, - .reset_ops = &terga_mc_reset_ops_common, + .reset_ops = &tegra_mc_reset_ops_common, .resets = tegra30_mc_resets, .num_resets = ARRAY_SIZE(tegra30_mc_resets), }; -- cgit v1.2.3 From fa7fe29a645b4da08efe8ff2392898b88f9ded9f Mon Sep 17 00:00:00 2001 From: Steven Price Date: Mon, 25 Mar 2019 17:37:22 +0000 Subject: firmware: arm_scmi: fix of_node leak in scmi_mailbox_check of_parse_phandle_with_args() requires the caller to call of_node_put() on the returned args->np pointer. Otherwise the reference count will remain incremented. However, in this case, since we don't actually use the returned pointer, we can simply pass in NULL. Fixes: aa4f886f3893f ("firmware: arm_scmi: add basic driver infrastructure for SCMI") Signed-off-by: Steven Price Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index 8f952f2f1a29..dd967d675c08 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -654,9 +654,7 @@ static int scmi_xfer_info_init(struct scmi_info *sinfo) static int scmi_mailbox_check(struct device_node *np) { - struct of_phandle_args arg; - - return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, &arg); + return of_parse_phandle_with_args(np, "mboxes", "#mbox-cells", 0, NULL); } static int scmi_mbox_free_channel(int id, void *p, void *data) -- cgit v1.2.3 From d9350f21e5fe2614e1f78ef20c3a3e83c4a36391 Mon Sep 17 00:00:00 2001 From: Aditya Pakki Date: Fri, 22 Mar 2019 16:55:03 -0500 Subject: firmware: arm_scmi: replace of_match_device->data with of_device_get_match_data() of_match_device can return NULL if no matching device is found though it's highly unlikely to happen in scmi_probe as it's called only if a valid match is found. However we can use of_device_get_match_data() instead of of_match_device()->data to handle NULL pointer checks and return -EINVAL in such a scenario. Reviewed-by: Steven Price Signed-off-by: Aditya Pakki Signed-off-by: Sudeep Holla --- drivers/firmware/arm_scmi/driver.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/firmware/arm_scmi/driver.c b/drivers/firmware/arm_scmi/driver.c index dd967d675c08..b5bc4c7a8fab 100644 --- a/drivers/firmware/arm_scmi/driver.c +++ b/drivers/firmware/arm_scmi/driver.c @@ -796,7 +796,9 @@ static int scmi_probe(struct platform_device *pdev) return -EINVAL; } - desc = of_match_device(scmi_of_match, dev)->data; + desc = of_device_get_match_data(dev); + if (!desc) + return -EINVAL; info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL); if (!info) -- cgit v1.2.3 From a51f4c031de35bc30d56bede232ffd2ae9c5fd6d Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Sat, 23 Mar 2019 22:15:52 +0100 Subject: soc: mediatek: pwrap: add missing check on rstc The variable rstc is set only when the SoC PWRAP have the PWRAP_CAP_RESET capability. Check whether rstc is set before using it to avoid errors. Signed-off-by: Fabien Parent Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 8236a6c87e19..b3ba2301f569 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1478,7 +1478,8 @@ static int pwrap_init(struct pmic_wrapper *wrp) { int ret; - reset_control_reset(wrp->rstc); + if (wrp->rstc) + reset_control_reset(wrp->rstc); if (wrp->rstc_bridge) reset_control_reset(wrp->rstc_bridge); -- cgit v1.2.3 From 45a0686b91205ad4a83cfe8548e95a04f69565e0 Mon Sep 17 00:00:00 2001 From: Fabien Parent Date: Sat, 23 Mar 2019 22:15:53 +0100 Subject: soc: mediatek: pwrap: add support for MT8516 pwrap Add the code to support the pwrap IP on the MediaTek MT8516 SoC. Signed-off-by: Fabien Parent Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 106 +++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) (limited to 'drivers') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index b3ba2301f569..73f0be0567bd 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -381,6 +381,10 @@ enum pwrap_regs { PWRAP_EXT_GPS_AUXADC_RDATA_ADDR, PWRAP_GPSINF_0_STA, PWRAP_GPSINF_1_STA, + + /* MT8516 only regs */ + PWRAP_OP_TYPE, + PWRAP_MSB_FIRST, }; static int mt2701_regs[] = { @@ -852,6 +856,91 @@ static int mt8183_regs[] = { [PWRAP_WACS2_VLDCLR] = 0xC28, }; +static int mt8516_regs[] = { + [PWRAP_MUX_SEL] = 0x0, + [PWRAP_WRAP_EN] = 0x4, + [PWRAP_DIO_EN] = 0x8, + [PWRAP_SIDLY] = 0xc, + [PWRAP_RDDMY] = 0x10, + [PWRAP_SI_CK_CON] = 0x14, + [PWRAP_CSHEXT_WRITE] = 0x18, + [PWRAP_CSHEXT_READ] = 0x1c, + [PWRAP_CSLEXT_START] = 0x20, + [PWRAP_CSLEXT_END] = 0x24, + [PWRAP_STAUPD_PRD] = 0x28, + [PWRAP_STAUPD_GRPEN] = 0x2c, + [PWRAP_STAUPD_MAN_TRIG] = 0x40, + [PWRAP_STAUPD_STA] = 0x44, + [PWRAP_WRAP_STA] = 0x48, + [PWRAP_HARB_INIT] = 0x4c, + [PWRAP_HARB_HPRIO] = 0x50, + [PWRAP_HIPRIO_ARB_EN] = 0x54, + [PWRAP_HARB_STA0] = 0x58, + [PWRAP_HARB_STA1] = 0x5c, + [PWRAP_MAN_EN] = 0x60, + [PWRAP_MAN_CMD] = 0x64, + [PWRAP_MAN_RDATA] = 0x68, + [PWRAP_MAN_VLDCLR] = 0x6c, + [PWRAP_WACS0_EN] = 0x70, + [PWRAP_INIT_DONE0] = 0x74, + [PWRAP_WACS0_CMD] = 0x78, + [PWRAP_WACS0_RDATA] = 0x7c, + [PWRAP_WACS0_VLDCLR] = 0x80, + [PWRAP_WACS1_EN] = 0x84, + [PWRAP_INIT_DONE1] = 0x88, + [PWRAP_WACS1_CMD] = 0x8c, + [PWRAP_WACS1_RDATA] = 0x90, + [PWRAP_WACS1_VLDCLR] = 0x94, + [PWRAP_WACS2_EN] = 0x98, + [PWRAP_INIT_DONE2] = 0x9c, + [PWRAP_WACS2_CMD] = 0xa0, + [PWRAP_WACS2_RDATA] = 0xa4, + [PWRAP_WACS2_VLDCLR] = 0xa8, + [PWRAP_INT_EN] = 0xac, + [PWRAP_INT_FLG_RAW] = 0xb0, + [PWRAP_INT_FLG] = 0xb4, + [PWRAP_INT_CLR] = 0xb8, + [PWRAP_SIG_ADR] = 0xbc, + [PWRAP_SIG_MODE] = 0xc0, + [PWRAP_SIG_VALUE] = 0xc4, + [PWRAP_SIG_ERRVAL] = 0xc8, + [PWRAP_CRC_EN] = 0xcc, + [PWRAP_TIMER_EN] = 0xd0, + [PWRAP_TIMER_STA] = 0xd4, + [PWRAP_WDT_UNIT] = 0xd8, + [PWRAP_WDT_SRC_EN] = 0xdc, + [PWRAP_WDT_FLG] = 0xe0, + [PWRAP_DEBUG_INT_SEL] = 0xe4, + [PWRAP_DVFS_ADR0] = 0xe8, + [PWRAP_DVFS_WDATA0] = 0xec, + [PWRAP_DVFS_ADR1] = 0xf0, + [PWRAP_DVFS_WDATA1] = 0xf4, + [PWRAP_DVFS_ADR2] = 0xf8, + [PWRAP_DVFS_WDATA2] = 0xfc, + [PWRAP_DVFS_ADR3] = 0x100, + [PWRAP_DVFS_WDATA3] = 0x104, + [PWRAP_DVFS_ADR4] = 0x108, + [PWRAP_DVFS_WDATA4] = 0x10c, + [PWRAP_DVFS_ADR5] = 0x110, + [PWRAP_DVFS_WDATA5] = 0x114, + [PWRAP_DVFS_ADR6] = 0x118, + [PWRAP_DVFS_WDATA6] = 0x11c, + [PWRAP_DVFS_ADR7] = 0x120, + [PWRAP_DVFS_WDATA7] = 0x124, + [PWRAP_SPMINF_STA] = 0x128, + [PWRAP_CIPHER_KEY_SEL] = 0x12c, + [PWRAP_CIPHER_IV_SEL] = 0x130, + [PWRAP_CIPHER_EN] = 0x134, + [PWRAP_CIPHER_RDY] = 0x138, + [PWRAP_CIPHER_MODE] = 0x13c, + [PWRAP_CIPHER_SWRST] = 0x140, + [PWRAP_DCM_EN] = 0x144, + [PWRAP_DCM_DBC_PRD] = 0x148, + [PWRAP_SW_RST] = 0x168, + [PWRAP_OP_TYPE] = 0x16c, + [PWRAP_MSB_FIRST] = 0x170, +}; + enum pmic_type { PMIC_MT6323, PMIC_MT6351, @@ -869,6 +958,7 @@ enum pwrap_type { PWRAP_MT8135, PWRAP_MT8173, PWRAP_MT8183, + PWRAP_MT8516, }; struct pmic_wrapper; @@ -1297,6 +1387,7 @@ static int pwrap_init_cipher(struct pmic_wrapper *wrp) case PWRAP_MT6765: case PWRAP_MT6797: case PWRAP_MT8173: + case PWRAP_MT8516: pwrap_writel(wrp, 1, PWRAP_CIPHER_EN); break; case PWRAP_MT7622: @@ -1765,6 +1856,18 @@ static const struct pmic_wrapper_type pwrap_mt8183 = { .init_soc_specific = pwrap_mt8183_init_soc_specific, }; +static struct pmic_wrapper_type pwrap_mt8516 = { + .regs = mt8516_regs, + .type = PWRAP_MT8516, + .arb_en_all = 0xff, + .int_en_all = ~(u32)(BIT(31) | BIT(2)), + .spi_w = PWRAP_MAN_CMD_SPI_WRITE, + .wdt_src = PWRAP_WDT_SRC_MASK_ALL, + .caps = PWRAP_CAP_DCM, + .init_reg_clock = pwrap_mt2701_init_reg_clock, + .init_soc_specific = NULL, +}; + static const struct of_device_id of_pwrap_match_tbl[] = { { .compatible = "mediatek,mt2701-pwrap", @@ -1787,6 +1890,9 @@ static const struct of_device_id of_pwrap_match_tbl[] = { }, { .compatible = "mediatek,mt8183-pwrap", .data = &pwrap_mt8183, + }, { + .compatible = "mediatek,mt8516-pwrap", + .data = &pwrap_mt8516, }, { /* sentinel */ } -- cgit v1.2.3 From 89e28da82836530f1ac7a3a32fecc31f22d79b3e Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Thu, 7 Mar 2019 15:56:51 -0700 Subject: soc: mediatek: pwrap: Zero initialize rdata in pwrap_init_cipher When building with -Wsometimes-uninitialized, Clang warns: drivers/soc/mediatek/mtk-pmic-wrap.c:1358:6: error: variable 'rdata' is used uninitialized whenever '||' condition is true [-Werror,-Wsometimes-uninitialized] If pwrap_write returns non-zero, pwrap_read will not be called to initialize rdata, meaning that we will use some random uninitialized stack value in our print statement. Zero initialize rdata in case this happens. Link: https://github.com/ClangBuiltLinux/linux/issues/401 Signed-off-by: Nathan Chancellor Reviewed-by: Nick Desaulniers Reviewed-by: Arnd Bergmann Signed-off-by: Matthias Brugger --- drivers/soc/mediatek/mtk-pmic-wrap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/mediatek/mtk-pmic-wrap.c b/drivers/soc/mediatek/mtk-pmic-wrap.c index 73f0be0567bd..c4449a163991 100644 --- a/drivers/soc/mediatek/mtk-pmic-wrap.c +++ b/drivers/soc/mediatek/mtk-pmic-wrap.c @@ -1371,7 +1371,7 @@ static bool pwrap_is_pmic_cipher_ready(struct pmic_wrapper *wrp) static int pwrap_init_cipher(struct pmic_wrapper *wrp) { int ret; - u32 rdata; + u32 rdata = 0; pwrap_writel(wrp, 0x1, PWRAP_CIPHER_SWRST); pwrap_writel(wrp, 0x0, PWRAP_CIPHER_SWRST); -- cgit v1.2.3 From e840decc1954521ff73bbc87b5b3ea36af1704d2 Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Mon, 15 Apr 2019 12:47:46 +0530 Subject: firmware: xilinx: Add fpga API's This Patch Adds fpga API's to support the Bitstream loading by using firmware interface. Signed-off-by: Nava kishore Manne Reviewed-by: Moritz Fischer Signed-off-by: Michal Simek --- drivers/firmware/xilinx/zynqmp.c | 45 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers') diff --git a/drivers/firmware/xilinx/zynqmp.c b/drivers/firmware/xilinx/zynqmp.c index 87a1d636c0dc..fd3d83745208 100644 --- a/drivers/firmware/xilinx/zynqmp.c +++ b/drivers/firmware/xilinx/zynqmp.c @@ -539,6 +539,49 @@ static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset, return ret; } +/** + * zynqmp_pm_fpga_load - Perform the fpga load + * @address: Address to write to + * @size: pl bitstream size + * @flags: Bitstream type + * -XILINX_ZYNQMP_PM_FPGA_FULL: FPGA full reconfiguration + * -XILINX_ZYNQMP_PM_FPGA_PARTIAL: FPGA partial reconfiguration + * + * This function provides access to pmufw. To transfer + * the required bitstream into PL. + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_load(const u64 address, const u32 size, + const u32 flags) +{ + return zynqmp_pm_invoke_fn(PM_FPGA_LOAD, lower_32_bits(address), + upper_32_bits(address), size, flags, NULL); +} + +/** + * zynqmp_pm_fpga_get_status - Read value from PCAP status register + * @value: Value to read + * + * This function provides access to the pmufw to get the PCAP + * status + * + * Return: Returns status, either success or error+reason + */ +static int zynqmp_pm_fpga_get_status(u32 *value) +{ + u32 ret_payload[PAYLOAD_ARG_CNT]; + int ret; + + if (!value) + return -EINVAL; + + ret = zynqmp_pm_invoke_fn(PM_FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload); + *value = ret_payload[1]; + + return ret; +} + /** * zynqmp_pm_init_finalize() - PM call to inform firmware that the caller * master has initialized its own power management @@ -642,6 +685,8 @@ static const struct zynqmp_eemi_ops eemi_ops = { .request_node = zynqmp_pm_request_node, .release_node = zynqmp_pm_release_node, .set_requirement = zynqmp_pm_set_requirement, + .fpga_load = zynqmp_pm_fpga_load, + .fpga_get_status = zynqmp_pm_fpga_get_status, }; /** -- cgit v1.2.3 From c09f7471127e9debf3e814ffef4f4012c31a5a3d Mon Sep 17 00:00:00 2001 From: Nava kishore Manne Date: Mon, 15 Apr 2019 12:47:48 +0530 Subject: fpga manager: Adding FPGA Manager support for Xilinx zynqmp This patch adds FPGA Manager support for the Xilinx ZynqMP chip. Signed-off-by: Nava kishore Manne Reviewed-by: Moritz Fischer Acked-by: Alan Tull Signed-off-by: Michal Simek --- drivers/fpga/Kconfig | 9 +++ drivers/fpga/Makefile | 1 + drivers/fpga/zynqmp-fpga.c | 159 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 drivers/fpga/zynqmp-fpga.c (limited to 'drivers') diff --git a/drivers/fpga/Kconfig b/drivers/fpga/Kconfig index c20445b867ae..d892f3efcd76 100644 --- a/drivers/fpga/Kconfig +++ b/drivers/fpga/Kconfig @@ -204,4 +204,13 @@ config FPGA_DFL_PCI To compile this as a module, choose M here. +config FPGA_MGR_ZYNQMP_FPGA + tristate "Xilinx ZynqMP FPGA" + depends on ARCH_ZYNQMP || COMPILE_TEST + help + FPGA manager driver support for Xilinx ZynqMP FPGAs. + This driver uses the processor configuration port(PCAP) + to configure the programmable logic(PL) through PS + on ZynqMP SoC. + endif # FPGA diff --git a/drivers/fpga/Makefile b/drivers/fpga/Makefile index c0dd4c82fbdb..312b9371742f 100644 --- a/drivers/fpga/Makefile +++ b/drivers/fpga/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_FPGA_MGR_STRATIX10_SOC) += stratix10-soc.o obj-$(CONFIG_FPGA_MGR_TS73XX) += ts73xx-fpga.o obj-$(CONFIG_FPGA_MGR_XILINX_SPI) += xilinx-spi.o obj-$(CONFIG_FPGA_MGR_ZYNQ_FPGA) += zynq-fpga.o +obj-$(CONFIG_FPGA_MGR_ZYNQMP_FPGA) += zynqmp-fpga.o obj-$(CONFIG_ALTERA_PR_IP_CORE) += altera-pr-ip-core.o obj-$(CONFIG_ALTERA_PR_IP_CORE_PLAT) += altera-pr-ip-core-plat.o diff --git a/drivers/fpga/zynqmp-fpga.c b/drivers/fpga/zynqmp-fpga.c new file mode 100644 index 000000000000..f7cbaadf49ab --- /dev/null +++ b/drivers/fpga/zynqmp-fpga.c @@ -0,0 +1,159 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2019 Xilinx, Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Constant Definitions */ +#define IXR_FPGA_DONE_MASK BIT(3) + +/** + * struct zynqmp_fpga_priv - Private data structure + * @dev: Device data structure + * @flags: flags which is used to identify the bitfile type + */ +struct zynqmp_fpga_priv { + struct device *dev; + u32 flags; +}; + +static int zynqmp_fpga_ops_write_init(struct fpga_manager *mgr, + struct fpga_image_info *info, + const char *buf, size_t size) +{ + struct zynqmp_fpga_priv *priv; + + priv = mgr->priv; + priv->flags = info->flags; + + return 0; +} + +static int zynqmp_fpga_ops_write(struct fpga_manager *mgr, + const char *buf, size_t size) +{ + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); + struct zynqmp_fpga_priv *priv; + dma_addr_t dma_addr; + u32 eemi_flags = 0; + char *kbuf; + int ret; + + if (!eemi_ops || !eemi_ops->fpga_load) + return -ENXIO; + + priv = mgr->priv; + + kbuf = dma_alloc_coherent(priv->dev, size, &dma_addr, GFP_KERNEL); + if (!kbuf) + return -ENOMEM; + + memcpy(kbuf, buf, size); + + wmb(); /* ensure all writes are done before initiate FW call */ + + if (priv->flags & FPGA_MGR_PARTIAL_RECONFIG) + eemi_flags |= XILINX_ZYNQMP_PM_FPGA_PARTIAL; + + ret = eemi_ops->fpga_load(dma_addr, size, eemi_flags); + + dma_free_coherent(priv->dev, size, kbuf, dma_addr); + + return ret; +} + +static int zynqmp_fpga_ops_write_complete(struct fpga_manager *mgr, + struct fpga_image_info *info) +{ + return 0; +} + +static enum fpga_mgr_states zynqmp_fpga_ops_state(struct fpga_manager *mgr) +{ + const struct zynqmp_eemi_ops *eemi_ops = zynqmp_pm_get_eemi_ops(); + u32 status; + + if (!eemi_ops || !eemi_ops->fpga_get_status) + return FPGA_MGR_STATE_UNKNOWN; + + eemi_ops->fpga_get_status(&status); + if (status & IXR_FPGA_DONE_MASK) + return FPGA_MGR_STATE_OPERATING; + + return FPGA_MGR_STATE_UNKNOWN; +} + +static const struct fpga_manager_ops zynqmp_fpga_ops = { + .state = zynqmp_fpga_ops_state, + .write_init = zynqmp_fpga_ops_write_init, + .write = zynqmp_fpga_ops_write, + .write_complete = zynqmp_fpga_ops_write_complete, +}; + +static int zynqmp_fpga_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct zynqmp_fpga_priv *priv; + struct fpga_manager *mgr; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + + mgr = devm_fpga_mgr_create(dev, "Xilinx ZynqMP FPGA Manager", + &zynqmp_fpga_ops, priv); + if (!mgr) + return -ENOMEM; + + platform_set_drvdata(pdev, mgr); + + ret = fpga_mgr_register(mgr); + if (ret) { + dev_err(dev, "unable to register FPGA manager"); + return ret; + } + + return 0; +} + +static int zynqmp_fpga_remove(struct platform_device *pdev) +{ + struct fpga_manager *mgr = platform_get_drvdata(pdev); + + fpga_mgr_unregister(mgr); + + return 0; +} + +static const struct of_device_id zynqmp_fpga_of_match[] = { + { .compatible = "xlnx,zynqmp-pcap-fpga", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, zynqmp_fpga_of_match); + +static struct platform_driver zynqmp_fpga_driver = { + .probe = zynqmp_fpga_probe, + .remove = zynqmp_fpga_remove, + .driver = { + .name = "zynqmp_fpga_manager", + .of_match_table = of_match_ptr(zynqmp_fpga_of_match), + }, +}; + +module_platform_driver(zynqmp_fpga_driver); + +MODULE_AUTHOR("Nava kishore Manne "); +MODULE_DESCRIPTION("Xilinx ZynqMp FPGA Manager"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3 From 2fe3b4bbc93ec30a173ebae7d2b8c530416df3af Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 1 Apr 2019 09:48:01 +0200 Subject: soc: amlogic: meson-gx-pwrc-vpu: Fix power on/off register bitmask The register bitmask to power on/off the VPU memories was incorectly set to 0x2 instead of 0x3. While still working, let's use the recommended vendor value instead. Fixes: 75fcb5ca4b46 ("soc: amlogic: add Meson GX VPU Domains driver") Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 6289965c42e9..05421d029dff 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -54,12 +54,12 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) /* Power Down Memories */ for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x2 << i, 0x3 << i); + 0x3 << i, 0x3 << i); udelay(5); } for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x2 << i, 0x3 << i); + 0x3 << i, 0x3 << i); udelay(5); } for (i = 8; i < 16; i++) { @@ -108,13 +108,13 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) /* Power Up Memories */ for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, - 0x2 << i, 0); + 0x3 << i, 0); udelay(5); } for (i = 0; i < 32; i += 2) { regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, - 0x2 << i, 0); + 0x3 << i, 0); udelay(5); } -- cgit v1.2.3 From bb1dca3a3900a00b881286c96340d6ab85eafe0c Mon Sep 17 00:00:00 2001 From: Neil Armstrong Date: Mon, 1 Apr 2019 09:52:58 +0200 Subject: soc: amlogic: meson-gx-pwrc-vpu: Add support for G12A The Amlogic G12A SoC has a very similar VPU Power Controller setup than the older GXBB, GXL & GXm SoCs. This patch adds the variant support for G12A. Signed-off-by: Neil Armstrong Signed-off-by: Kevin Hilman --- drivers/soc/amlogic/meson-gx-pwrc-vpu.c | 152 +++++++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c index 05421d029dff..511b6856225d 100644 --- a/drivers/soc/amlogic/meson-gx-pwrc-vpu.c +++ b/drivers/soc/amlogic/meson-gx-pwrc-vpu.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -26,6 +27,7 @@ #define HHI_MEM_PD_REG0 (0x40 << 2) #define HHI_VPU_MEM_PD_REG0 (0x41 << 2) #define HHI_VPU_MEM_PD_REG1 (0x42 << 2) +#define HHI_VPU_MEM_PD_REG2 (0x4d << 2) struct meson_gx_pwrc_vpu { struct generic_pm_domain genpd; @@ -80,6 +82,49 @@ static int meson_gx_pwrc_vpu_power_off(struct generic_pm_domain *genpd) return 0; } +static int meson_g12a_pwrc_vpu_power_off(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, GEN_PWR_VPU_HDMI_ISO); + udelay(20); + + /* Power Down Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, + 0x3 << i, 0x3 << i); + udelay(5); + } + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), BIT(i)); + udelay(5); + } + udelay(20); + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, GEN_PWR_VPU_HDMI); + + msleep(20); + + clk_disable_unprepare(pd->vpu_clk); + clk_disable_unprepare(pd->vapb_clk); + + return 0; +} + static int meson_gx_pwrc_vpu_setup_clk(struct meson_gx_pwrc_vpu *pd) { int ret; @@ -143,6 +188,60 @@ static int meson_gx_pwrc_vpu_power_on(struct generic_pm_domain *genpd) return 0; } +static int meson_g12a_pwrc_vpu_power_on(struct generic_pm_domain *genpd) +{ + struct meson_gx_pwrc_vpu *pd = genpd_to_pd(genpd); + int ret; + int i; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI, 0); + udelay(20); + + /* Power Up Memories */ + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG0, + 0x3 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG1, + 0x3 << i, 0); + udelay(5); + } + + for (i = 0; i < 32; i += 2) { + regmap_update_bits(pd->regmap_hhi, HHI_VPU_MEM_PD_REG2, + 0x3 << i, 0); + udelay(5); + } + + for (i = 8; i < 16; i++) { + regmap_update_bits(pd->regmap_hhi, HHI_MEM_PD_REG0, + BIT(i), 0); + udelay(5); + } + udelay(20); + + ret = reset_control_assert(pd->rstc); + if (ret) + return ret; + + regmap_update_bits(pd->regmap_ao, AO_RTI_GEN_PWR_SLEEP0, + GEN_PWR_VPU_HDMI_ISO, 0); + + ret = reset_control_deassert(pd->rstc); + if (ret) + return ret; + + ret = meson_gx_pwrc_vpu_setup_clk(pd); + if (ret) + return ret; + + return 0; +} + static bool meson_gx_pwrc_vpu_get_power(struct meson_gx_pwrc_vpu *pd) { u32 reg; @@ -160,15 +259,37 @@ static struct meson_gx_pwrc_vpu vpu_hdmi_pd = { }, }; +static struct meson_gx_pwrc_vpu vpu_hdmi_pd_g12a = { + .genpd = { + .name = "vpu_hdmi", + .power_off = meson_g12a_pwrc_vpu_power_off, + .power_on = meson_g12a_pwrc_vpu_power_on, + }, +}; + static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) { + const struct meson_gx_pwrc_vpu *vpu_pd_match; struct regmap *regmap_ao, *regmap_hhi; + struct meson_gx_pwrc_vpu *vpu_pd; struct reset_control *rstc; struct clk *vpu_clk; struct clk *vapb_clk; bool powered_off; int ret; + vpu_pd_match = of_device_get_match_data(&pdev->dev); + if (!vpu_pd_match) { + dev_err(&pdev->dev, "failed to get match data\n"); + return -ENODEV; + } + + vpu_pd = devm_kzalloc(&pdev->dev, sizeof(*vpu_pd), GFP_KERNEL); + if (!vpu_pd) + return -ENOMEM; + + memcpy(vpu_pd, vpu_pd_match, sizeof(*vpu_pd)); + regmap_ao = syscon_node_to_regmap(of_get_parent(pdev->dev.of_node)); if (IS_ERR(regmap_ao)) { dev_err(&pdev->dev, "failed to get regmap\n"); @@ -201,39 +322,46 @@ static int meson_gx_pwrc_vpu_probe(struct platform_device *pdev) return PTR_ERR(vapb_clk); } - vpu_hdmi_pd.regmap_ao = regmap_ao; - vpu_hdmi_pd.regmap_hhi = regmap_hhi; - vpu_hdmi_pd.rstc = rstc; - vpu_hdmi_pd.vpu_clk = vpu_clk; - vpu_hdmi_pd.vapb_clk = vapb_clk; + vpu_pd->regmap_ao = regmap_ao; + vpu_pd->regmap_hhi = regmap_hhi; + vpu_pd->rstc = rstc; + vpu_pd->vpu_clk = vpu_clk; + vpu_pd->vapb_clk = vapb_clk; + + platform_set_drvdata(pdev, vpu_pd); - powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); /* If already powered, sync the clock states */ if (!powered_off) { - ret = meson_gx_pwrc_vpu_setup_clk(&vpu_hdmi_pd); + ret = meson_gx_pwrc_vpu_setup_clk(vpu_pd); if (ret) return ret; } - pm_genpd_init(&vpu_hdmi_pd.genpd, &pm_domain_always_on_gov, + pm_genpd_init(&vpu_pd->genpd, &pm_domain_always_on_gov, powered_off); return of_genpd_add_provider_simple(pdev->dev.of_node, - &vpu_hdmi_pd.genpd); + &vpu_pd->genpd); } static void meson_gx_pwrc_vpu_shutdown(struct platform_device *pdev) { + struct meson_gx_pwrc_vpu *vpu_pd = platform_get_drvdata(pdev); bool powered_off; - powered_off = meson_gx_pwrc_vpu_get_power(&vpu_hdmi_pd); + powered_off = meson_gx_pwrc_vpu_get_power(vpu_pd); if (!powered_off) - meson_gx_pwrc_vpu_power_off(&vpu_hdmi_pd.genpd); + vpu_pd->genpd.power_off(&vpu_pd->genpd); } static const struct of_device_id meson_gx_pwrc_vpu_match_table[] = { - { .compatible = "amlogic,meson-gx-pwrc-vpu" }, + { .compatible = "amlogic,meson-gx-pwrc-vpu", .data = &vpu_hdmi_pd }, + { + .compatible = "amlogic,meson-g12a-pwrc-vpu", + .data = &vpu_hdmi_pd_g12a + }, { /* sentinel */ } }; -- cgit v1.2.3 From 00cdaa1b811f36afa52a5956350c263ded4944a6 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 16 Apr 2019 17:48:06 +0100 Subject: soc/tegra: pmc: Fix reset sources and levels Commit 5f84bb1a4099 ("soc/tegra: pmc: Add sysfs entries for reset info") added support for reading the Tegra reset source and level from sysfs. However, there are a few issues with this commit which are ... 1. The number of reset sources for Tegra210 is defined as 5 but it should be 6. 2. The number of reset sources for Tegra186 is defined as 13 but it should be 15. 3. The SoC data variables num_reset_sources and num_reset_levels are defined but never used. Fix the above by ... 1. Removing the reset source 'AOTAG' from the tegra30_reset_sources because this is only applicable for Tegra210. 2. Adding a new tegra210_reset_sources structure for Tegra210 reset sources. 3. Correct the number of reset sources for Tegra210 and Tegra186 by using the ARRAY_SIZE macro. 4. Updating the functions reset_reason_show() and reset_level_show() to check whether the value read is valid. While we are at it clean-up these functions to remove an unnecessary u32 variable. Fixes: 5f84bb1a4099 ("soc/tegra: pmc: Add sysfs entries for reset info") Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 44 +++++++++++++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 0c5f79528e5f..7953aa7b11df 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -268,6 +268,14 @@ static const char * const tegra186_reset_levels[] = { }; static const char * const tegra30_reset_sources[] = { + "POWER_ON_RESET", + "WATCHDOG", + "SENSOR", + "SW_MAIN", + "LP0" +}; + +static const char * const tegra210_reset_sources[] = { "POWER_ON_RESET", "WATCHDOG", "SENSOR", @@ -1736,13 +1744,16 @@ static int tegra_pmc_pinctrl_init(struct tegra_pmc *pmc) static ssize_t reset_reason_show(struct device *dev, struct device_attribute *attr, char *buf) { - u32 value, rst_src; + u32 value; value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); - rst_src = (value & pmc->soc->regs->rst_source_mask) >> - pmc->soc->regs->rst_source_shift; + value &= pmc->soc->regs->rst_source_mask; + value >>= pmc->soc->regs->rst_source_shift; + + if (WARN_ON(value >= pmc->soc->num_reset_sources)) + return sprintf(buf, "%s\n", "UNKNOWN"); - return sprintf(buf, "%s\n", pmc->soc->reset_sources[rst_src]); + return sprintf(buf, "%s\n", pmc->soc->reset_sources[value]); } static DEVICE_ATTR_RO(reset_reason); @@ -1750,13 +1761,16 @@ static DEVICE_ATTR_RO(reset_reason); static ssize_t reset_level_show(struct device *dev, struct device_attribute *attr, char *buf) { - u32 value, rst_lvl; + u32 value; value = tegra_pmc_readl(pmc, pmc->soc->regs->rst_status); - rst_lvl = (value & pmc->soc->regs->rst_level_mask) >> - pmc->soc->regs->rst_level_shift; + value &= pmc->soc->regs->rst_level_mask; + value >>= pmc->soc->regs->rst_level_shift; - return sprintf(buf, "%s\n", pmc->soc->reset_levels[rst_lvl]); + if (WARN_ON(value >= pmc->soc->num_reset_levels)) + return sprintf(buf, "%s\n", "UNKNOWN"); + + return sprintf(buf, "%s\n", pmc->soc->reset_levels[value]); } static DEVICE_ATTR_RO(reset_level); @@ -2212,7 +2226,7 @@ static const struct tegra_pmc_soc tegra30_pmc_soc = { .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .reset_sources = tegra30_reset_sources, - .num_reset_sources = 5, + .num_reset_sources = ARRAY_SIZE(tegra30_reset_sources), .reset_levels = NULL, .num_reset_levels = 0, }; @@ -2263,7 +2277,7 @@ static const struct tegra_pmc_soc tegra114_pmc_soc = { .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .reset_sources = tegra30_reset_sources, - .num_reset_sources = 5, + .num_reset_sources = ARRAY_SIZE(tegra30_reset_sources), .reset_levels = NULL, .num_reset_levels = 0, }; @@ -2374,7 +2388,7 @@ static const struct tegra_pmc_soc tegra124_pmc_soc = { .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, .reset_sources = tegra30_reset_sources, - .num_reset_sources = 5, + .num_reset_sources = ARRAY_SIZE(tegra30_reset_sources), .reset_levels = NULL, .num_reset_levels = 0, }; @@ -2479,8 +2493,8 @@ static const struct tegra_pmc_soc tegra210_pmc_soc = { .regs = &tegra20_pmc_regs, .init = tegra20_pmc_init, .setup_irq_polarity = tegra20_pmc_setup_irq_polarity, - .reset_sources = tegra30_reset_sources, - .num_reset_sources = 5, + .reset_sources = tegra210_reset_sources, + .num_reset_sources = ARRAY_SIZE(tegra210_reset_sources), .reset_levels = NULL, .num_reset_levels = 0, }; @@ -2605,9 +2619,9 @@ static const struct tegra_pmc_soc tegra186_pmc_soc = { .init = NULL, .setup_irq_polarity = tegra186_pmc_setup_irq_polarity, .reset_sources = tegra186_reset_sources, - .num_reset_sources = 14, + .num_reset_sources = ARRAY_SIZE(tegra186_reset_sources), .reset_levels = tegra186_reset_levels, - .num_reset_levels = 3, + .num_reset_levels = ARRAY_SIZE(tegra186_reset_levels), .num_wake_events = ARRAY_SIZE(tegra186_wake_events), .wake_events = tegra186_wake_events, }; -- cgit v1.2.3 From a46b51cd2a57d52d5047e1d48240536243eeab34 Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 16 Apr 2019 17:48:07 +0100 Subject: soc/tegra: pmc: Remove reset sysfs entries on error Commit 5f84bb1a4099 ("soc/tegra: pmc: Add sysfs entries for reset info") added sysfs entries for Tegra reset source and level. However, these sysfs are not removed on error and so if the registering of PMC device is probe deferred, then the next time we attempt to probe the PMC device warnings such as the following will be displayed on boot ... sysfs: cannot create duplicate filename '/devices/platform/7000e400.pmc/reset_reason' Fix this by calling device_remove_file() for each sysfs entry added on failure. Note that we call device_remove_file() unconditionally without checking if the sysfs entry was created in the first place, but this should be OK because kernfs_remove_by_name_ns() will fail silently. Fixes: 5f84bb1a4099 ("soc/tegra: pmc: Add sysfs entries for reset info") Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 7953aa7b11df..741afb141887 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -2040,7 +2040,7 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_DEBUG_FS)) { err = tegra_powergate_debugfs_init(); if (err < 0) - return err; + goto cleanup_sysfs; } err = register_restart_handler(&tegra_pmc_restart_handler); @@ -2071,6 +2071,9 @@ cleanup_restart_handler: unregister_restart_handler(&tegra_pmc_restart_handler); cleanup_debugfs: debugfs_remove(pmc->debugfs); +cleanup_sysfs: + device_remove_file(&pdev->dev, &dev_attr_reset_reason); + device_remove_file(&pdev->dev, &dev_attr_reset_level); return err; } -- cgit v1.2.3 From 6ac2a01de1700c1b6d889f02f61c4c9602573a8d Mon Sep 17 00:00:00 2001 From: Jon Hunter Date: Tue, 16 Apr 2019 17:48:08 +0100 Subject: soc/tegra: pmc: Move powergate initialisation to probe Commit 8df127456f29 ("soc/tegra: pmc: Enable XUSB partitions on boot") was added as a workaround to ensure that the XUSB powergates or domains were turned on early during boot because as this time the Tegra XHCI driver did not handle the power domains at all. Now that the Tegra XHCI driver has been updated to properly managed the power domains, the workaround to enable the XUSB power domain early has been removed. This also means that we can now move the initialisation of the powergates into the PMC driver probe. Therefore, move the powergate initialisation into the PMC driver probe and return any errors detected. To handle any errors, functions to cleanup and remove any power-domains registered with the generic power-domain framework have been added. Finally the initialisation of the 'powergates_available' bitmask is kept in the PMC early init function to allow the legacy PMC powergate APIs to be called during early boot for enabling secondary CPUs on 32-bit Tegra devices. Signed-off-by: Jon Hunter Signed-off-by: Thierry Reding --- drivers/soc/tegra/pmc.c | 83 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 15 deletions(-) (limited to 'drivers') diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c index 741afb141887..5648e5c09ef5 100644 --- a/drivers/soc/tegra/pmc.c +++ b/drivers/soc/tegra/pmc.c @@ -990,20 +990,21 @@ out: return err; } -static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) +static int tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) { struct device *dev = pmc->dev; struct tegra_powergate *pg; - int id, err; + int id, err = 0; bool off; pg = kzalloc(sizeof(*pg), GFP_KERNEL); if (!pg) - return; + return -ENOMEM; id = tegra_powergate_lookup(pmc, np->name); if (id < 0) { dev_err(dev, "powergate lookup failed for %pOFn: %d\n", np, id); + err = -ENODEV; goto free_mem; } @@ -1056,7 +1057,7 @@ static void tegra_powergate_add(struct tegra_pmc *pmc, struct device_node *np) dev_dbg(dev, "added PM domain %s\n", pg->genpd.name); - return; + return 0; remove_genpd: pm_genpd_remove(&pg->genpd); @@ -1075,25 +1076,67 @@ set_available: free_mem: kfree(pg); + + return err; } -static void tegra_powergate_init(struct tegra_pmc *pmc, - struct device_node *parent) +static int tegra_powergate_init(struct tegra_pmc *pmc, + struct device_node *parent) { struct device_node *np, *child; - unsigned int i; + int err = 0; + + np = of_get_child_by_name(parent, "powergates"); + if (!np) + return 0; - /* Create a bitmap of the available and valid partitions */ - for (i = 0; i < pmc->soc->num_powergates; i++) - if (pmc->soc->powergates[i]) - set_bit(i, pmc->powergates_available); + for_each_child_of_node(np, child) { + err = tegra_powergate_add(pmc, child); + if (err < 0) { + of_node_put(child); + break; + } + } + + of_node_put(np); + + return err; +} + +static void tegra_powergate_remove(struct generic_pm_domain *genpd) +{ + struct tegra_powergate *pg = to_powergate(genpd); + + reset_control_put(pg->reset); + + while (pg->num_clks--) + clk_put(pg->clks[pg->num_clks]); + + kfree(pg->clks); + + set_bit(pg->id, pmc->powergates_available); + + kfree(pg); +} + +static void tegra_powergate_remove_all(struct device_node *parent) +{ + struct generic_pm_domain *genpd; + struct device_node *np, *child; np = of_get_child_by_name(parent, "powergates"); if (!np) return; - for_each_child_of_node(np, child) - tegra_powergate_add(pmc, child); + for_each_child_of_node(np, child) { + of_genpd_del_provider(child); + + genpd = of_genpd_remove_last(child); + if (IS_ERR(genpd)) + continue; + + tegra_powergate_remove(genpd); + } of_node_put(np); } @@ -2054,9 +2097,13 @@ static int tegra_pmc_probe(struct platform_device *pdev) if (err) goto cleanup_restart_handler; + err = tegra_powergate_init(pmc, pdev->dev.of_node); + if (err < 0) + goto cleanup_powergates; + err = tegra_pmc_irq_init(pmc); if (err < 0) - goto cleanup_restart_handler; + goto cleanup_powergates; mutex_lock(&pmc->powergates_lock); iounmap(pmc->base); @@ -2067,6 +2114,8 @@ static int tegra_pmc_probe(struct platform_device *pdev) return 0; +cleanup_powergates: + tegra_powergate_remove_all(pdev->dev.of_node); cleanup_restart_handler: unregister_restart_handler(&tegra_pmc_restart_handler); cleanup_debugfs: @@ -2763,6 +2812,7 @@ static int __init tegra_pmc_early_init(void) const struct of_device_id *match; struct device_node *np; struct resource regs; + unsigned int i; bool invert; mutex_init(&pmc->powergates_lock); @@ -2819,7 +2869,10 @@ static int __init tegra_pmc_early_init(void) if (pmc->soc->maybe_tz_only) pmc->tz_only = tegra_pmc_detect_tz_only(pmc); - tegra_powergate_init(pmc, np); + /* Create a bitmap of the available and valid partitions */ + for (i = 0; i < pmc->soc->num_powergates; i++) + if (pmc->soc->powergates[i]) + set_bit(i, pmc->powergates_available); /* * Invert the interrupt polarity if a PMC device tree node -- cgit v1.2.3 From 9733b072a12a422e2bf17bc7ba8b39769853d4a2 Mon Sep 17 00:00:00 2001 From: Volodymyr Babchuk Date: Fri, 7 Sep 2018 21:37:07 +0300 Subject: optee: allow to work without static shared memory On virtualized systems it is possible that OP-TEE will provide only dynamic shared memory support. So it is fine to boot without static SHM enabled if dymanic one is supported. Signed-off-by: Volodymyr Babchuk Signed-off-by: Jens Wiklander --- drivers/tee/optee/core.c | 80 +++++++++++++++++++++++++++++------------------- 1 file changed, 49 insertions(+), 31 deletions(-) (limited to 'drivers') diff --git a/drivers/tee/optee/core.c b/drivers/tee/optee/core.c index 947f9b28de9e..fdce6293e03d 100644 --- a/drivers/tee/optee/core.c +++ b/drivers/tee/optee/core.c @@ -419,9 +419,35 @@ static bool optee_msg_exchange_capabilities(optee_invoke_fn *invoke_fn, return true; } +static struct tee_shm_pool *optee_config_dyn_shm(void) +{ + struct tee_shm_pool_mgr *priv_mgr; + struct tee_shm_pool_mgr *dmabuf_mgr; + void *rc; + + rc = optee_shm_pool_alloc_pages(); + if (IS_ERR(rc)) + return rc; + priv_mgr = rc; + + rc = optee_shm_pool_alloc_pages(); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + return rc; + } + dmabuf_mgr = rc; + + rc = tee_shm_pool_alloc(priv_mgr, dmabuf_mgr); + if (IS_ERR(rc)) { + tee_shm_pool_mgr_destroy(priv_mgr); + tee_shm_pool_mgr_destroy(dmabuf_mgr); + } + + return rc; +} + static struct tee_shm_pool * -optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm, - u32 sec_caps) +optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm) { union { struct arm_smccc_res smccc; @@ -436,10 +462,11 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm, struct tee_shm_pool_mgr *priv_mgr; struct tee_shm_pool_mgr *dmabuf_mgr; void *rc; + const int sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc); if (res.result.status != OPTEE_SMC_RETURN_OK) { - pr_info("shm service not available\n"); + pr_err("static shm service not available\n"); return ERR_PTR(-ENOENT); } @@ -465,28 +492,15 @@ optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm, } vaddr = (unsigned long)va; - /* - * If OP-TEE can work with unregistered SHM, we will use own pool - * for private shm - */ - if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) { - rc = optee_shm_pool_alloc_pages(); - if (IS_ERR(rc)) - goto err_memunmap; - priv_mgr = rc; - } else { - const size_t sz = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE; - - rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, - 3 /* 8 bytes aligned */); - if (IS_ERR(rc)) - goto err_memunmap; - priv_mgr = rc; - - vaddr += sz; - paddr += sz; - size -= sz; - } + rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, sz, + 3 /* 8 bytes aligned */); + if (IS_ERR(rc)) + goto err_memunmap; + priv_mgr = rc; + + vaddr += sz; + paddr += sz; + size -= sz; rc = tee_shm_pool_mgr_alloc_res_mem(vaddr, paddr, size, PAGE_SHIFT); if (IS_ERR(rc)) @@ -552,7 +566,7 @@ static optee_invoke_fn *get_invoke_func(struct device_node *np) static struct optee *optee_probe(struct device_node *np) { optee_invoke_fn *invoke_fn; - struct tee_shm_pool *pool; + struct tee_shm_pool *pool = ERR_PTR(-EINVAL); struct optee *optee = NULL; void *memremaped_shm = NULL; struct tee_device *teedev; @@ -581,13 +595,17 @@ static struct optee *optee_probe(struct device_node *np) } /* - * We have no other option for shared memory, if secure world - * doesn't have any reserved memory we can use we can't continue. + * Try to use dynamic shared memory if possible */ - if (!(sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) - return ERR_PTR(-EINVAL); + if (sec_caps & OPTEE_SMC_SEC_CAP_DYNAMIC_SHM) + pool = optee_config_dyn_shm(); + + /* + * If dynamic shared memory is not available or failed - try static one + */ + if (IS_ERR(pool) && (sec_caps & OPTEE_SMC_SEC_CAP_HAVE_RESERVED_SHM)) + pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm); - pool = optee_config_shm_memremap(invoke_fn, &memremaped_shm, sec_caps); if (IS_ERR(pool)) return (void *)pool; -- cgit v1.2.3 From 76b959a44c0b9c60cd41627cecb022c78042ad74 Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Apr 2019 01:12:47 +0300 Subject: memory: tegra: Fix missed registers values latching Some of Memory Controller registers are shadowed and require latching in order to copy assembly state into the active, MC_EMEM_ARB_CFG is one of these registers. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/memory/tegra/mc.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'drivers') diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index a08b61a86760..1735e23dbc28 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -51,6 +51,9 @@ #define MC_EMEM_ADR_CFG 0x54 #define MC_EMEM_ADR_CFG_EMEM_NUMDEV BIT(0) +#define MC_TIMING_CONTROL 0xfc +#define MC_TIMING_UPDATE BIT(0) + static const struct of_device_id tegra_mc_of_match[] = { #ifdef CONFIG_ARCH_TEGRA_2x_SOC { .compatible = "nvidia,tegra20-mc-gart", .data = &tegra20_mc_soc }, @@ -301,6 +304,9 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) writel(value, mc->regs + la->reg); } + /* latch new values */ + writel(MC_TIMING_UPDATE, mc->regs + MC_TIMING_CONTROL); + return 0; } -- cgit v1.2.3 From b906c056b6023c390f18347169071193fda57dde Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Apr 2019 01:12:48 +0300 Subject: memory: tegra: Fix integer overflow on tick value calculation Multiplying the Memory Controller clock rate by the tick count results in an integer overflow and in result the truncated tick value is being programmed into hardware, such that the GR3D memory client performance is reduced by two times. Cc: stable Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/memory/tegra/mc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 1735e23dbc28..483ac3c1a762 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -285,7 +285,7 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) u32 value; /* compute the number of MC clock cycles per tick */ - tick = mc->tick * clk_get_rate(mc->clk); + tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk); do_div(tick, NSEC_PER_SEC); value = readl(mc->regs + MC_EMEM_ARB_CFG); -- cgit v1.2.3 From 48791f972234301a72e4c40745d0abafa0985d2d Mon Sep 17 00:00:00 2001 From: Dmitry Osipenko Date: Fri, 12 Apr 2019 01:12:49 +0300 Subject: memory: tegra: Replace readl-writel with mc_readl-mc_writel There is no need for a memory barriers on reading/writing of register values as we only care about the read/write order, hence let's use the common helpers. Signed-off-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/memory/tegra/mc.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/mc.c b/drivers/memory/tegra/mc.c index 483ac3c1a762..163b6c69e651 100644 --- a/drivers/memory/tegra/mc.c +++ b/drivers/memory/tegra/mc.c @@ -288,24 +288,24 @@ static int tegra_mc_setup_latency_allowance(struct tegra_mc *mc) tick = (unsigned long long)mc->tick * clk_get_rate(mc->clk); do_div(tick, NSEC_PER_SEC); - value = readl(mc->regs + MC_EMEM_ARB_CFG); + value = mc_readl(mc, MC_EMEM_ARB_CFG); value &= ~MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE_MASK; value |= MC_EMEM_ARB_CFG_CYCLES_PER_UPDATE(tick); - writel(value, mc->regs + MC_EMEM_ARB_CFG); + mc_writel(mc, value, MC_EMEM_ARB_CFG); /* write latency allowance defaults */ for (i = 0; i < mc->soc->num_clients; i++) { const struct tegra_mc_la *la = &mc->soc->clients[i].la; u32 value; - value = readl(mc->regs + la->reg); + value = mc_readl(mc, la->reg); value &= ~(la->mask << la->shift); value |= (la->def & la->mask) << la->shift; - writel(value, mc->regs + la->reg); + mc_writel(mc, value, la->reg); } /* latch new values */ - writel(MC_TIMING_UPDATE, mc->regs + MC_TIMING_CONTROL); + mc_writel(mc, MC_TIMING_UPDATE, MC_TIMING_CONTROL); return 0; } -- cgit v1.2.3 From 67a8d5b0fadfd931f7e6a78e9ee7b2792a7114aa Mon Sep 17 00:00:00 2001 From: Edward Cragg Date: Fri, 8 Mar 2019 11:21:55 +0000 Subject: memory: tegra: Fix a typos for "fdcdwr2" mc client Fix typo for fdcwr2 to fdcdwr2 to match the TRM. Signed-off-by: Edward Cragg Signed-off-by: Kejia Hu Acked-by: Jon Hunter Reviewed-by: Dmitry Osipenko Signed-off-by: Thierry Reding --- drivers/memory/tegra/tegra114.c | 2 +- drivers/memory/tegra/tegra30.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c index 7247a46b71a8..62305fafd641 100644 --- a/drivers/memory/tegra/tegra114.c +++ b/drivers/memory/tegra/tegra114.c @@ -572,7 +572,7 @@ static const struct tegra_mc_client tegra114_mc_clients[] = { }, }, { .id = 0x34, - .name = "fdcwr2", + .name = "fdcdwr2", .swgroup = TEGRA_SWGROUP_NV, .smmu = { .reg = 0x22c, diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c index 68499ffdf0f4..c9af0f682ead 100644 --- a/drivers/memory/tegra/tegra30.c +++ b/drivers/memory/tegra/tegra30.c @@ -726,7 +726,7 @@ static const struct tegra_mc_client tegra30_mc_clients[] = { }, }, { .id = 0x34, - .name = "fdcwr2", + .name = "fdcdwr2", .swgroup = TEGRA_SWGROUP_NV2, .smmu = { .reg = 0x22c, -- cgit v1.2.3 From a7e26f356ca12906a164d83c9e9f8527ee7da022 Mon Sep 17 00:00:00 2001 From: Abel Vesa Date: Fri, 22 Mar 2019 16:49:20 +0000 Subject: soc: imx: Add generic i.MX8 SoC driver Add generic i.MX8 SoC driver along with the i.MX8MQ SoC specific code. For now, only i.MX8MQ revision B1 is supported. For any other, i.MX8MQ revision it will print 'unknown'. Signed-off-by: Abel Vesa Reviewed-by: Leonard Crestez Signed-off-by: Shawn Guo --- drivers/soc/imx/Makefile | 1 + drivers/soc/imx/soc-imx8.c | 115 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 drivers/soc/imx/soc-imx8.c (limited to 'drivers') diff --git a/drivers/soc/imx/Makefile b/drivers/soc/imx/Makefile index 506a6f3c2b9b..d6b529e06d9a 100644 --- a/drivers/soc/imx/Makefile +++ b/drivers/soc/imx/Makefile @@ -1,2 +1,3 @@ obj-$(CONFIG_HAVE_IMX_GPC) += gpc.o obj-$(CONFIG_IMX_GPCV2_PM_DOMAINS) += gpcv2.o +obj-$(CONFIG_ARCH_MXC) += soc-imx8.o diff --git a/drivers/soc/imx/soc-imx8.c b/drivers/soc/imx/soc-imx8.c new file mode 100644 index 000000000000..fc6429f9170a --- /dev/null +++ b/drivers/soc/imx/soc-imx8.c @@ -0,0 +1,115 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2019 NXP. + */ + +#include +#include +#include +#include +#include +#include +#include + +#define REV_B1 0x21 + +#define IMX8MQ_SW_INFO_B1 0x40 +#define IMX8MQ_SW_MAGIC_B1 0xff0055aa + +struct imx8_soc_data { + char *name; + u32 (*soc_revision)(void); +}; + +static u32 __init imx8mq_soc_revision(void) +{ + struct device_node *np; + void __iomem *ocotp_base; + u32 magic; + u32 rev = 0; + + np = of_find_compatible_node(NULL, NULL, "fsl,imx8mq-ocotp"); + if (!np) + goto out; + + ocotp_base = of_iomap(np, 0); + WARN_ON(!ocotp_base); + + magic = readl_relaxed(ocotp_base + IMX8MQ_SW_INFO_B1); + if (magic == IMX8MQ_SW_MAGIC_B1) + rev = REV_B1; + + iounmap(ocotp_base); + +out: + of_node_put(np); + return rev; +} + +static const struct imx8_soc_data imx8mq_soc_data = { + .name = "i.MX8MQ", + .soc_revision = imx8mq_soc_revision, +}; + +static const struct of_device_id imx8_soc_match[] = { + { .compatible = "fsl,imx8mq", .data = &imx8mq_soc_data, }, + { } +}; + +#define imx8_revision(soc_rev) \ + soc_rev ? \ + kasprintf(GFP_KERNEL, "%d.%d", (soc_rev >> 4) & 0xf, soc_rev & 0xf) : \ + "unknown" + +static int __init imx8_soc_init(void) +{ + struct soc_device_attribute *soc_dev_attr; + struct soc_device *soc_dev; + struct device_node *root; + const struct of_device_id *id; + u32 soc_rev = 0; + const struct imx8_soc_data *data; + int ret; + + soc_dev_attr = kzalloc(sizeof(*soc_dev_attr), GFP_KERNEL); + if (!soc_dev_attr) + return -ENODEV; + + soc_dev_attr->family = "Freescale i.MX"; + + root = of_find_node_by_path("/"); + ret = of_property_read_string(root, "model", &soc_dev_attr->machine); + if (ret) + goto free_soc; + + id = of_match_node(imx8_soc_match, root); + if (!id) + goto free_soc; + + of_node_put(root); + + data = id->data; + if (data) { + soc_dev_attr->soc_id = data->name; + if (data->soc_revision) + soc_rev = data->soc_revision(); + } + + soc_dev_attr->revision = imx8_revision(soc_rev); + if (!soc_dev_attr->revision) + goto free_soc; + + soc_dev = soc_device_register(soc_dev_attr); + if (IS_ERR(soc_dev)) + goto free_rev; + + return 0; + +free_rev: + kfree(soc_dev_attr->revision); +free_soc: + kfree(soc_dev_attr); + of_node_put(root); + return -ENODEV; +} +device_initcall(imx8_soc_init); -- cgit v1.2.3 From ab7b7c715e9a3b74d4e2ee33e6cd5d1bde5332cc Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 29 Apr 2019 09:15:14 -0700 Subject: spi: zynqmp: Fix build break Missing include: drivers/spi/spi-zynqmp-gqspi.c:1025:13: error: implicit declaration of function 'zynqmp_pm_get_eemi_ops'; did you mean 'zynqmp_process_dma_irq'? [-Werror=implicit-function-declaration] Fixes: 3d0313786470a ('drivers: Defer probe if firmware is not ready') Cc: Rajan Vaja Cc: Jolly Shah Cc: Michal Simek Signed-off-by: Olof Johansson --- drivers/spi/spi-zynqmp-gqspi.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/spi/spi-zynqmp-gqspi.c b/drivers/spi/spi-zynqmp-gqspi.c index d07b6f940f9f..9850a0efe85a 100644 --- a/drivers/spi/spi-zynqmp-gqspi.c +++ b/drivers/spi/spi-zynqmp-gqspi.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3 From 524feb799408e5d45c6aa82763a9f52489d1e19f Mon Sep 17 00:00:00 2001 From: Patrick Venture Date: Mon, 22 Apr 2019 10:54:19 -0700 Subject: soc: add aspeed folder and misc drivers Create a SoC folder for the ASPEED parts and place the misc drivers currently present into this folder. These drivers are not generic part drivers, but rather only apply to the ASPEED SoCs. Signed-off-by: Patrick Venture Acked-by: Arnd Bergmann Signed-off-by: Olof Johansson --- drivers/misc/Kconfig | 16 -- drivers/misc/Makefile | 2 - drivers/misc/aspeed-lpc-ctrl.c | 300 ----------------------------- drivers/misc/aspeed-lpc-snoop.c | 349 ---------------------------------- drivers/soc/Kconfig | 1 + drivers/soc/Makefile | 1 + drivers/soc/aspeed/Kconfig | 19 ++ drivers/soc/aspeed/Makefile | 2 + drivers/soc/aspeed/aspeed-lpc-ctrl.c | 300 +++++++++++++++++++++++++++++ drivers/soc/aspeed/aspeed-lpc-snoop.c | 349 ++++++++++++++++++++++++++++++++++ 10 files changed, 672 insertions(+), 667 deletions(-) delete mode 100644 drivers/misc/aspeed-lpc-ctrl.c delete mode 100644 drivers/misc/aspeed-lpc-snoop.c create mode 100644 drivers/soc/aspeed/Kconfig create mode 100644 drivers/soc/aspeed/Makefile create mode 100644 drivers/soc/aspeed/aspeed-lpc-ctrl.c create mode 100644 drivers/soc/aspeed/aspeed-lpc-snoop.c (limited to 'drivers') diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 42ab8ec92a04..b80cb6af0cb4 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -496,22 +496,6 @@ config VEXPRESS_SYSCFG bus. System Configuration interface is one of the possible means of generating transactions on this bus. -config ASPEED_LPC_CTRL - depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON - tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" - ---help--- - Control Aspeed ast2400/2500 HOST LPC to BMC mappings through - ioctl()s, the driver also provides a read/write interface to a BMC ram - region where the host LPC read/write region can be buffered. - -config ASPEED_LPC_SNOOP - tristate "Aspeed ast2500 HOST LPC snoop support" - depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON - help - Provides a driver to control the LPC snoop interface which - allows the BMC to listen on and save the data written by - the host to an arbitrary LPC I/O port. - config PCI_ENDPOINT_TEST depends on PCI select CRC32 diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d5b7d3404dc7..b9affcdaa3d6 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -54,8 +54,6 @@ obj-$(CONFIG_GENWQE) += genwqe/ obj-$(CONFIG_ECHO) += echo/ obj-$(CONFIG_VEXPRESS_SYSCFG) += vexpress-syscfg.o obj-$(CONFIG_CXL_BASE) += cxl/ -obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o -obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o obj-$(CONFIG_PCI_ENDPOINT_TEST) += pci_endpoint_test.o obj-$(CONFIG_OCXL) += ocxl/ obj-y += cardreader/ diff --git a/drivers/misc/aspeed-lpc-ctrl.c b/drivers/misc/aspeed-lpc-ctrl.c deleted file mode 100644 index a024f8042259..000000000000 --- a/drivers/misc/aspeed-lpc-ctrl.c +++ /dev/null @@ -1,300 +0,0 @@ -/* - * Copyright 2017 IBM Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#define DEVICE_NAME "aspeed-lpc-ctrl" - -#define HICR5 0x0 -#define HICR5_ENL2H BIT(8) -#define HICR5_ENFWH BIT(10) - -#define HICR7 0x8 -#define HICR8 0xc - -struct aspeed_lpc_ctrl { - struct miscdevice miscdev; - struct regmap *regmap; - struct clk *clk; - phys_addr_t mem_base; - resource_size_t mem_size; - u32 pnor_size; - u32 pnor_base; -}; - -static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file) -{ - return container_of(file->private_data, struct aspeed_lpc_ctrl, - miscdev); -} - -static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); - unsigned long vsize = vma->vm_end - vma->vm_start; - pgprot_t prot = vma->vm_page_prot; - - if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size) - return -EINVAL; - - /* ast2400/2500 AHB accesses are not cache coherent */ - prot = pgprot_noncached(prot); - - if (remap_pfn_range(vma, vma->vm_start, - (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff, - vsize, prot)) - return -EAGAIN; - - return 0; -} - -static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd, - unsigned long param) -{ - struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); - void __user *p = (void __user *)param; - struct aspeed_lpc_ctrl_mapping map; - u32 addr; - u32 size; - long rc; - - if (copy_from_user(&map, p, sizeof(map))) - return -EFAULT; - - if (map.flags != 0) - return -EINVAL; - - switch (cmd) { - case ASPEED_LPC_CTRL_IOCTL_GET_SIZE: - /* The flash windows don't report their size */ - if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY) - return -EINVAL; - - /* Support more than one window id in the future */ - if (map.window_id != 0) - return -EINVAL; - - map.size = lpc_ctrl->mem_size; - - return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0; - case ASPEED_LPC_CTRL_IOCTL_MAP: - - /* - * The top half of HICR7 is the MSB of the BMC address of the - * mapping. - * The bottom half of HICR7 is the MSB of the HOST LPC - * firmware space address of the mapping. - * - * The 1 bits in the top of half of HICR8 represent the bits - * (in the requested address) that should be ignored and - * replaced with those from the top half of HICR7. - * The 1 bits in the bottom half of HICR8 represent the bits - * (in the requested address) that should be kept and pass - * into the BMC address space. - */ - - /* - * It doesn't make sense to talk about a size or offset with - * low 16 bits set. Both HICR7 and HICR8 talk about the top 16 - * bits of addresses and sizes. - */ - - if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff)) - return -EINVAL; - - /* - * Because of the way the masks work in HICR8 offset has to - * be a multiple of size. - */ - if (map.offset & (map.size - 1)) - return -EINVAL; - - if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) { - addr = lpc_ctrl->pnor_base; - size = lpc_ctrl->pnor_size; - } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) { - addr = lpc_ctrl->mem_base; - size = lpc_ctrl->mem_size; - } else { - return -EINVAL; - } - - /* Check overflow first! */ - if (map.offset + map.size < map.offset || - map.offset + map.size > size) - return -EINVAL; - - if (map.size == 0 || map.size > size) - return -EINVAL; - - addr += map.offset; - - /* - * addr (host lpc address) is safe regardless of values. This - * simply changes the address the host has to request on its - * side of the LPC bus. This cannot impact the hosts own - * memory space by surprise as LPC specific accessors are - * required. The only strange thing that could be done is - * setting the lower 16 bits but the shift takes care of that. - */ - - rc = regmap_write(lpc_ctrl->regmap, HICR7, - (addr | (map.addr >> 16))); - if (rc) - return rc; - - rc = regmap_write(lpc_ctrl->regmap, HICR8, - (~(map.size - 1)) | ((map.size >> 16) - 1)); - if (rc) - return rc; - - /* - * Enable LPC FHW cycles. This is required for the host to - * access the regions specified. - */ - return regmap_update_bits(lpc_ctrl->regmap, HICR5, - HICR5_ENFWH | HICR5_ENL2H, - HICR5_ENFWH | HICR5_ENL2H); - } - - return -EINVAL; -} - -static const struct file_operations aspeed_lpc_ctrl_fops = { - .owner = THIS_MODULE, - .mmap = aspeed_lpc_ctrl_mmap, - .unlocked_ioctl = aspeed_lpc_ctrl_ioctl, -}; - -static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) -{ - struct aspeed_lpc_ctrl *lpc_ctrl; - struct device_node *node; - struct resource resm; - struct device *dev; - int rc; - - dev = &pdev->dev; - - lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL); - if (!lpc_ctrl) - return -ENOMEM; - - node = of_parse_phandle(dev->of_node, "flash", 0); - if (!node) { - dev_err(dev, "Didn't find host pnor flash node\n"); - return -ENODEV; - } - - rc = of_address_to_resource(node, 1, &resm); - of_node_put(node); - if (rc) { - dev_err(dev, "Couldn't address to resource for flash\n"); - return rc; - } - - lpc_ctrl->pnor_size = resource_size(&resm); - lpc_ctrl->pnor_base = resm.start; - - dev_set_drvdata(&pdev->dev, lpc_ctrl); - - node = of_parse_phandle(dev->of_node, "memory-region", 0); - if (!node) { - dev_err(dev, "Didn't find reserved memory\n"); - return -EINVAL; - } - - rc = of_address_to_resource(node, 0, &resm); - of_node_put(node); - if (rc) { - dev_err(dev, "Couldn't address to resource for reserved memory\n"); - return -ENOMEM; - } - - lpc_ctrl->mem_size = resource_size(&resm); - lpc_ctrl->mem_base = resm.start; - - lpc_ctrl->regmap = syscon_node_to_regmap( - pdev->dev.parent->of_node); - if (IS_ERR(lpc_ctrl->regmap)) { - dev_err(dev, "Couldn't get regmap\n"); - return -ENODEV; - } - - lpc_ctrl->clk = devm_clk_get(dev, NULL); - if (IS_ERR(lpc_ctrl->clk)) { - dev_err(dev, "couldn't get clock\n"); - return PTR_ERR(lpc_ctrl->clk); - } - rc = clk_prepare_enable(lpc_ctrl->clk); - if (rc) { - dev_err(dev, "couldn't enable clock\n"); - return rc; - } - - lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; - lpc_ctrl->miscdev.name = DEVICE_NAME; - lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; - lpc_ctrl->miscdev.parent = dev; - rc = misc_register(&lpc_ctrl->miscdev); - if (rc) { - dev_err(dev, "Unable to register device\n"); - goto err; - } - - dev_info(dev, "Loaded at %pr\n", &resm); - - return 0; - -err: - clk_disable_unprepare(lpc_ctrl->clk); - return rc; -} - -static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) -{ - struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); - - misc_deregister(&lpc_ctrl->miscdev); - clk_disable_unprepare(lpc_ctrl->clk); - - return 0; -} - -static const struct of_device_id aspeed_lpc_ctrl_match[] = { - { .compatible = "aspeed,ast2400-lpc-ctrl" }, - { .compatible = "aspeed,ast2500-lpc-ctrl" }, - { }, -}; - -static struct platform_driver aspeed_lpc_ctrl_driver = { - .driver = { - .name = DEVICE_NAME, - .of_match_table = aspeed_lpc_ctrl_match, - }, - .probe = aspeed_lpc_ctrl_probe, - .remove = aspeed_lpc_ctrl_remove, -}; - -module_platform_driver(aspeed_lpc_ctrl_driver); - -MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Cyril Bur "); -MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings"); diff --git a/drivers/misc/aspeed-lpc-snoop.c b/drivers/misc/aspeed-lpc-snoop.c deleted file mode 100644 index 2feb4347d67f..000000000000 --- a/drivers/misc/aspeed-lpc-snoop.c +++ /dev/null @@ -1,349 +0,0 @@ -/* - * Copyright 2017 Google Inc - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - * - * Provides a simple driver to control the ASPEED LPC snoop interface which - * allows the BMC to listen on and save the data written by - * the host to an arbitrary LPC I/O port. - * - * Typically used by the BMC to "watch" host boot progress via port - * 0x80 writes made by the BIOS during the boot process. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define DEVICE_NAME "aspeed-lpc-snoop" - -#define NUM_SNOOP_CHANNELS 2 -#define SNOOP_FIFO_SIZE 2048 - -#define HICR5 0x0 -#define HICR5_EN_SNP0W BIT(0) -#define HICR5_ENINT_SNP0W BIT(1) -#define HICR5_EN_SNP1W BIT(2) -#define HICR5_ENINT_SNP1W BIT(3) - -#define HICR6 0x4 -#define HICR6_STR_SNP0W BIT(0) -#define HICR6_STR_SNP1W BIT(1) -#define SNPWADR 0x10 -#define SNPWADR_CH0_MASK GENMASK(15, 0) -#define SNPWADR_CH0_SHIFT 0 -#define SNPWADR_CH1_MASK GENMASK(31, 16) -#define SNPWADR_CH1_SHIFT 16 -#define SNPWDR 0x14 -#define SNPWDR_CH0_MASK GENMASK(7, 0) -#define SNPWDR_CH0_SHIFT 0 -#define SNPWDR_CH1_MASK GENMASK(15, 8) -#define SNPWDR_CH1_SHIFT 8 -#define HICRB 0x80 -#define HICRB_ENSNP0D BIT(14) -#define HICRB_ENSNP1D BIT(15) - -struct aspeed_lpc_snoop_model_data { - /* The ast2400 has bits 14 and 15 as reserved, whereas the ast2500 - * can use them. - */ - unsigned int has_hicrb_ensnp; -}; - -struct aspeed_lpc_snoop_channel { - struct kfifo fifo; - wait_queue_head_t wq; - struct miscdevice miscdev; -}; - -struct aspeed_lpc_snoop { - struct regmap *regmap; - int irq; - struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; -}; - -static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file) -{ - return container_of(file->private_data, - struct aspeed_lpc_snoop_channel, - miscdev); -} - -static ssize_t snoop_file_read(struct file *file, char __user *buffer, - size_t count, loff_t *ppos) -{ - struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); - unsigned int copied; - int ret = 0; - - if (kfifo_is_empty(&chan->fifo)) { - if (file->f_flags & O_NONBLOCK) - return -EAGAIN; - ret = wait_event_interruptible(chan->wq, - !kfifo_is_empty(&chan->fifo)); - if (ret == -ERESTARTSYS) - return -EINTR; - } - ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); - - return ret ? ret : copied; -} - -static unsigned int snoop_file_poll(struct file *file, - struct poll_table_struct *pt) -{ - struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); - - poll_wait(file, &chan->wq, pt); - return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; -} - -static const struct file_operations snoop_fops = { - .owner = THIS_MODULE, - .read = snoop_file_read, - .poll = snoop_file_poll, - .llseek = noop_llseek, -}; - -/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ -static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val) -{ - if (!kfifo_initialized(&chan->fifo)) - return; - if (kfifo_is_full(&chan->fifo)) - kfifo_skip(&chan->fifo); - kfifo_put(&chan->fifo, val); - wake_up_interruptible(&chan->wq); -} - -static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg) -{ - struct aspeed_lpc_snoop *lpc_snoop = arg; - u32 reg, data; - - if (regmap_read(lpc_snoop->regmap, HICR6, ®)) - return IRQ_NONE; - - /* Check if one of the snoop channels is interrupting */ - reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W); - if (!reg) - return IRQ_NONE; - - /* Ack pending IRQs */ - regmap_write(lpc_snoop->regmap, HICR6, reg); - - /* Read and save most recent snoop'ed data byte to FIFO */ - regmap_read(lpc_snoop->regmap, SNPWDR, &data); - - if (reg & HICR6_STR_SNP0W) { - u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT; - - put_fifo_with_discard(&lpc_snoop->chan[0], val); - } - if (reg & HICR6_STR_SNP1W) { - u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT; - - put_fifo_with_discard(&lpc_snoop->chan[1], val); - } - - return IRQ_HANDLED; -} - -static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop, - struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - int rc; - - lpc_snoop->irq = platform_get_irq(pdev, 0); - if (!lpc_snoop->irq) - return -ENODEV; - - rc = devm_request_irq(dev, lpc_snoop->irq, - aspeed_lpc_snoop_irq, IRQF_SHARED, - DEVICE_NAME, lpc_snoop); - if (rc < 0) { - dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); - lpc_snoop->irq = 0; - return rc; - } - - return 0; -} - -static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, - struct device *dev, - int channel, u16 lpc_port) -{ - int rc = 0; - u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; - const struct aspeed_lpc_snoop_model_data *model_data = - of_device_get_match_data(dev); - - init_waitqueue_head(&lpc_snoop->chan[channel].wq); - /* Create FIFO datastructure */ - rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, - SNOOP_FIFO_SIZE, GFP_KERNEL); - if (rc) - return rc; - - lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; - lpc_snoop->chan[channel].miscdev.name = - devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); - lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; - lpc_snoop->chan[channel].miscdev.parent = dev; - rc = misc_register(&lpc_snoop->chan[channel].miscdev); - if (rc) - return rc; - - /* Enable LPC snoop channel at requested port */ - switch (channel) { - case 0: - hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; - snpwadr_mask = SNPWADR_CH0_MASK; - snpwadr_shift = SNPWADR_CH0_SHIFT; - hicrb_en = HICRB_ENSNP0D; - break; - case 1: - hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W; - snpwadr_mask = SNPWADR_CH1_MASK; - snpwadr_shift = SNPWADR_CH1_SHIFT; - hicrb_en = HICRB_ENSNP1D; - break; - default: - return -EINVAL; - } - - regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); - regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, - lpc_port << snpwadr_shift); - if (model_data->has_hicrb_ensnp) - regmap_update_bits(lpc_snoop->regmap, HICRB, - hicrb_en, hicrb_en); - - return rc; -} - -static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop, - int channel) -{ - switch (channel) { - case 0: - regmap_update_bits(lpc_snoop->regmap, HICR5, - HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, - 0); - break; - case 1: - regmap_update_bits(lpc_snoop->regmap, HICR5, - HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, - 0); - break; - default: - return; - } - - kfifo_free(&lpc_snoop->chan[channel].fifo); - misc_deregister(&lpc_snoop->chan[channel].miscdev); -} - -static int aspeed_lpc_snoop_probe(struct platform_device *pdev) -{ - struct aspeed_lpc_snoop *lpc_snoop; - struct device *dev; - u32 port; - int rc; - - dev = &pdev->dev; - - lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); - if (!lpc_snoop) - return -ENOMEM; - - lpc_snoop->regmap = syscon_node_to_regmap( - pdev->dev.parent->of_node); - if (IS_ERR(lpc_snoop->regmap)) { - dev_err(dev, "Couldn't get regmap\n"); - return -ENODEV; - } - - dev_set_drvdata(&pdev->dev, lpc_snoop); - - rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); - if (rc) { - dev_err(dev, "no snoop ports configured\n"); - return -ENODEV; - } - - rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); - if (rc) - return rc; - - rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); - if (rc) - return rc; - - /* Configuration of 2nd snoop channel port is optional */ - if (of_property_read_u32_index(dev->of_node, "snoop-ports", - 1, &port) == 0) { - rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); - if (rc) - aspeed_lpc_disable_snoop(lpc_snoop, 0); - } - - return rc; -} - -static int aspeed_lpc_snoop_remove(struct platform_device *pdev) -{ - struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); - - /* Disable both snoop channels */ - aspeed_lpc_disable_snoop(lpc_snoop, 0); - aspeed_lpc_disable_snoop(lpc_snoop, 1); - - return 0; -} - -static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { - .has_hicrb_ensnp = 0, -}; - -static const struct aspeed_lpc_snoop_model_data ast2500_model_data = { - .has_hicrb_ensnp = 1, -}; - -static const struct of_device_id aspeed_lpc_snoop_match[] = { - { .compatible = "aspeed,ast2400-lpc-snoop", - .data = &ast2400_model_data }, - { .compatible = "aspeed,ast2500-lpc-snoop", - .data = &ast2500_model_data }, - { }, -}; - -static struct platform_driver aspeed_lpc_snoop_driver = { - .driver = { - .name = DEVICE_NAME, - .of_match_table = aspeed_lpc_snoop_match, - }, - .probe = aspeed_lpc_snoop_probe, - .remove = aspeed_lpc_snoop_remove, -}; - -module_platform_driver(aspeed_lpc_snoop_driver); - -MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Robert Lippert "); -MODULE_DESCRIPTION("Linux driver to control Aspeed LPC snoop functionality"); diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig index c07b4a85253f..b750a88547c7 100644 --- a/drivers/soc/Kconfig +++ b/drivers/soc/Kconfig @@ -2,6 +2,7 @@ menu "SOC (System On Chip) specific Drivers" source "drivers/soc/actions/Kconfig" source "drivers/soc/amlogic/Kconfig" +source "drivers/soc/aspeed/Kconfig" source "drivers/soc/atmel/Kconfig" source "drivers/soc/bcm/Kconfig" source "drivers/soc/fsl/Kconfig" diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile index 90b686e586c6..814128fe479f 100644 --- a/drivers/soc/Makefile +++ b/drivers/soc/Makefile @@ -4,6 +4,7 @@ # obj-$(CONFIG_ARCH_ACTIONS) += actions/ +obj-$(CONFIG_ARCH_ASPEED) += aspeed/ obj-$(CONFIG_ARCH_AT91) += atmel/ obj-y += bcm/ obj-$(CONFIG_ARCH_DOVE) += dove/ diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig new file mode 100644 index 000000000000..457282cd1da5 --- /dev/null +++ b/drivers/soc/aspeed/Kconfig @@ -0,0 +1,19 @@ +menu "Aspeed SoC drivers" + +config ASPEED_LPC_CTRL + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + tristate "Aspeed ast2400/2500 HOST LPC to BMC bridge control" + ---help--- + Control Aspeed ast2400/2500 HOST LPC to BMC mappings through + ioctl()s, the driver also provides a read/write interface to a BMC ram + region where the host LPC read/write region can be buffered. + +config ASPEED_LPC_SNOOP + tristate "Aspeed ast2500 HOST LPC snoop support" + depends on (ARCH_ASPEED || COMPILE_TEST) && REGMAP && MFD_SYSCON + help + Provides a driver to control the LPC snoop interface which + allows the BMC to listen on and save the data written by + the host to an arbitrary LPC I/O port. + + diff --git a/drivers/soc/aspeed/Makefile b/drivers/soc/aspeed/Makefile new file mode 100644 index 000000000000..cfaa9adc67b5 --- /dev/null +++ b/drivers/soc/aspeed/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_ASPEED_LPC_CTRL) += aspeed-lpc-ctrl.o +obj-$(CONFIG_ASPEED_LPC_SNOOP) += aspeed-lpc-snoop.o diff --git a/drivers/soc/aspeed/aspeed-lpc-ctrl.c b/drivers/soc/aspeed/aspeed-lpc-ctrl.c new file mode 100644 index 000000000000..a024f8042259 --- /dev/null +++ b/drivers/soc/aspeed/aspeed-lpc-ctrl.c @@ -0,0 +1,300 @@ +/* + * Copyright 2017 IBM Corporation + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define DEVICE_NAME "aspeed-lpc-ctrl" + +#define HICR5 0x0 +#define HICR5_ENL2H BIT(8) +#define HICR5_ENFWH BIT(10) + +#define HICR7 0x8 +#define HICR8 0xc + +struct aspeed_lpc_ctrl { + struct miscdevice miscdev; + struct regmap *regmap; + struct clk *clk; + phys_addr_t mem_base; + resource_size_t mem_size; + u32 pnor_size; + u32 pnor_base; +}; + +static struct aspeed_lpc_ctrl *file_aspeed_lpc_ctrl(struct file *file) +{ + return container_of(file->private_data, struct aspeed_lpc_ctrl, + miscdev); +} + +static int aspeed_lpc_ctrl_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); + unsigned long vsize = vma->vm_end - vma->vm_start; + pgprot_t prot = vma->vm_page_prot; + + if (vma->vm_pgoff + vsize > lpc_ctrl->mem_base + lpc_ctrl->mem_size) + return -EINVAL; + + /* ast2400/2500 AHB accesses are not cache coherent */ + prot = pgprot_noncached(prot); + + if (remap_pfn_range(vma, vma->vm_start, + (lpc_ctrl->mem_base >> PAGE_SHIFT) + vma->vm_pgoff, + vsize, prot)) + return -EAGAIN; + + return 0; +} + +static long aspeed_lpc_ctrl_ioctl(struct file *file, unsigned int cmd, + unsigned long param) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = file_aspeed_lpc_ctrl(file); + void __user *p = (void __user *)param; + struct aspeed_lpc_ctrl_mapping map; + u32 addr; + u32 size; + long rc; + + if (copy_from_user(&map, p, sizeof(map))) + return -EFAULT; + + if (map.flags != 0) + return -EINVAL; + + switch (cmd) { + case ASPEED_LPC_CTRL_IOCTL_GET_SIZE: + /* The flash windows don't report their size */ + if (map.window_type != ASPEED_LPC_CTRL_WINDOW_MEMORY) + return -EINVAL; + + /* Support more than one window id in the future */ + if (map.window_id != 0) + return -EINVAL; + + map.size = lpc_ctrl->mem_size; + + return copy_to_user(p, &map, sizeof(map)) ? -EFAULT : 0; + case ASPEED_LPC_CTRL_IOCTL_MAP: + + /* + * The top half of HICR7 is the MSB of the BMC address of the + * mapping. + * The bottom half of HICR7 is the MSB of the HOST LPC + * firmware space address of the mapping. + * + * The 1 bits in the top of half of HICR8 represent the bits + * (in the requested address) that should be ignored and + * replaced with those from the top half of HICR7. + * The 1 bits in the bottom half of HICR8 represent the bits + * (in the requested address) that should be kept and pass + * into the BMC address space. + */ + + /* + * It doesn't make sense to talk about a size or offset with + * low 16 bits set. Both HICR7 and HICR8 talk about the top 16 + * bits of addresses and sizes. + */ + + if ((map.size & 0x0000ffff) || (map.offset & 0x0000ffff)) + return -EINVAL; + + /* + * Because of the way the masks work in HICR8 offset has to + * be a multiple of size. + */ + if (map.offset & (map.size - 1)) + return -EINVAL; + + if (map.window_type == ASPEED_LPC_CTRL_WINDOW_FLASH) { + addr = lpc_ctrl->pnor_base; + size = lpc_ctrl->pnor_size; + } else if (map.window_type == ASPEED_LPC_CTRL_WINDOW_MEMORY) { + addr = lpc_ctrl->mem_base; + size = lpc_ctrl->mem_size; + } else { + return -EINVAL; + } + + /* Check overflow first! */ + if (map.offset + map.size < map.offset || + map.offset + map.size > size) + return -EINVAL; + + if (map.size == 0 || map.size > size) + return -EINVAL; + + addr += map.offset; + + /* + * addr (host lpc address) is safe regardless of values. This + * simply changes the address the host has to request on its + * side of the LPC bus. This cannot impact the hosts own + * memory space by surprise as LPC specific accessors are + * required. The only strange thing that could be done is + * setting the lower 16 bits but the shift takes care of that. + */ + + rc = regmap_write(lpc_ctrl->regmap, HICR7, + (addr | (map.addr >> 16))); + if (rc) + return rc; + + rc = regmap_write(lpc_ctrl->regmap, HICR8, + (~(map.size - 1)) | ((map.size >> 16) - 1)); + if (rc) + return rc; + + /* + * Enable LPC FHW cycles. This is required for the host to + * access the regions specified. + */ + return regmap_update_bits(lpc_ctrl->regmap, HICR5, + HICR5_ENFWH | HICR5_ENL2H, + HICR5_ENFWH | HICR5_ENL2H); + } + + return -EINVAL; +} + +static const struct file_operations aspeed_lpc_ctrl_fops = { + .owner = THIS_MODULE, + .mmap = aspeed_lpc_ctrl_mmap, + .unlocked_ioctl = aspeed_lpc_ctrl_ioctl, +}; + +static int aspeed_lpc_ctrl_probe(struct platform_device *pdev) +{ + struct aspeed_lpc_ctrl *lpc_ctrl; + struct device_node *node; + struct resource resm; + struct device *dev; + int rc; + + dev = &pdev->dev; + + lpc_ctrl = devm_kzalloc(dev, sizeof(*lpc_ctrl), GFP_KERNEL); + if (!lpc_ctrl) + return -ENOMEM; + + node = of_parse_phandle(dev->of_node, "flash", 0); + if (!node) { + dev_err(dev, "Didn't find host pnor flash node\n"); + return -ENODEV; + } + + rc = of_address_to_resource(node, 1, &resm); + of_node_put(node); + if (rc) { + dev_err(dev, "Couldn't address to resource for flash\n"); + return rc; + } + + lpc_ctrl->pnor_size = resource_size(&resm); + lpc_ctrl->pnor_base = resm.start; + + dev_set_drvdata(&pdev->dev, lpc_ctrl); + + node = of_parse_phandle(dev->of_node, "memory-region", 0); + if (!node) { + dev_err(dev, "Didn't find reserved memory\n"); + return -EINVAL; + } + + rc = of_address_to_resource(node, 0, &resm); + of_node_put(node); + if (rc) { + dev_err(dev, "Couldn't address to resource for reserved memory\n"); + return -ENOMEM; + } + + lpc_ctrl->mem_size = resource_size(&resm); + lpc_ctrl->mem_base = resm.start; + + lpc_ctrl->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_ctrl->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + lpc_ctrl->clk = devm_clk_get(dev, NULL); + if (IS_ERR(lpc_ctrl->clk)) { + dev_err(dev, "couldn't get clock\n"); + return PTR_ERR(lpc_ctrl->clk); + } + rc = clk_prepare_enable(lpc_ctrl->clk); + if (rc) { + dev_err(dev, "couldn't enable clock\n"); + return rc; + } + + lpc_ctrl->miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_ctrl->miscdev.name = DEVICE_NAME; + lpc_ctrl->miscdev.fops = &aspeed_lpc_ctrl_fops; + lpc_ctrl->miscdev.parent = dev; + rc = misc_register(&lpc_ctrl->miscdev); + if (rc) { + dev_err(dev, "Unable to register device\n"); + goto err; + } + + dev_info(dev, "Loaded at %pr\n", &resm); + + return 0; + +err: + clk_disable_unprepare(lpc_ctrl->clk); + return rc; +} + +static int aspeed_lpc_ctrl_remove(struct platform_device *pdev) +{ + struct aspeed_lpc_ctrl *lpc_ctrl = dev_get_drvdata(&pdev->dev); + + misc_deregister(&lpc_ctrl->miscdev); + clk_disable_unprepare(lpc_ctrl->clk); + + return 0; +} + +static const struct of_device_id aspeed_lpc_ctrl_match[] = { + { .compatible = "aspeed,ast2400-lpc-ctrl" }, + { .compatible = "aspeed,ast2500-lpc-ctrl" }, + { }, +}; + +static struct platform_driver aspeed_lpc_ctrl_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_lpc_ctrl_match, + }, + .probe = aspeed_lpc_ctrl_probe, + .remove = aspeed_lpc_ctrl_remove, +}; + +module_platform_driver(aspeed_lpc_ctrl_driver); + +MODULE_DEVICE_TABLE(of, aspeed_lpc_ctrl_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Cyril Bur "); +MODULE_DESCRIPTION("Control for aspeed 2400/2500 LPC HOST to BMC mappings"); diff --git a/drivers/soc/aspeed/aspeed-lpc-snoop.c b/drivers/soc/aspeed/aspeed-lpc-snoop.c new file mode 100644 index 000000000000..2feb4347d67f --- /dev/null +++ b/drivers/soc/aspeed/aspeed-lpc-snoop.c @@ -0,0 +1,349 @@ +/* + * Copyright 2017 Google Inc + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + * Provides a simple driver to control the ASPEED LPC snoop interface which + * allows the BMC to listen on and save the data written by + * the host to an arbitrary LPC I/O port. + * + * Typically used by the BMC to "watch" host boot progress via port + * 0x80 writes made by the BIOS during the boot process. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DEVICE_NAME "aspeed-lpc-snoop" + +#define NUM_SNOOP_CHANNELS 2 +#define SNOOP_FIFO_SIZE 2048 + +#define HICR5 0x0 +#define HICR5_EN_SNP0W BIT(0) +#define HICR5_ENINT_SNP0W BIT(1) +#define HICR5_EN_SNP1W BIT(2) +#define HICR5_ENINT_SNP1W BIT(3) + +#define HICR6 0x4 +#define HICR6_STR_SNP0W BIT(0) +#define HICR6_STR_SNP1W BIT(1) +#define SNPWADR 0x10 +#define SNPWADR_CH0_MASK GENMASK(15, 0) +#define SNPWADR_CH0_SHIFT 0 +#define SNPWADR_CH1_MASK GENMASK(31, 16) +#define SNPWADR_CH1_SHIFT 16 +#define SNPWDR 0x14 +#define SNPWDR_CH0_MASK GENMASK(7, 0) +#define SNPWDR_CH0_SHIFT 0 +#define SNPWDR_CH1_MASK GENMASK(15, 8) +#define SNPWDR_CH1_SHIFT 8 +#define HICRB 0x80 +#define HICRB_ENSNP0D BIT(14) +#define HICRB_ENSNP1D BIT(15) + +struct aspeed_lpc_snoop_model_data { + /* The ast2400 has bits 14 and 15 as reserved, whereas the ast2500 + * can use them. + */ + unsigned int has_hicrb_ensnp; +}; + +struct aspeed_lpc_snoop_channel { + struct kfifo fifo; + wait_queue_head_t wq; + struct miscdevice miscdev; +}; + +struct aspeed_lpc_snoop { + struct regmap *regmap; + int irq; + struct aspeed_lpc_snoop_channel chan[NUM_SNOOP_CHANNELS]; +}; + +static struct aspeed_lpc_snoop_channel *snoop_file_to_chan(struct file *file) +{ + return container_of(file->private_data, + struct aspeed_lpc_snoop_channel, + miscdev); +} + +static ssize_t snoop_file_read(struct file *file, char __user *buffer, + size_t count, loff_t *ppos) +{ + struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); + unsigned int copied; + int ret = 0; + + if (kfifo_is_empty(&chan->fifo)) { + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + ret = wait_event_interruptible(chan->wq, + !kfifo_is_empty(&chan->fifo)); + if (ret == -ERESTARTSYS) + return -EINTR; + } + ret = kfifo_to_user(&chan->fifo, buffer, count, &copied); + + return ret ? ret : copied; +} + +static unsigned int snoop_file_poll(struct file *file, + struct poll_table_struct *pt) +{ + struct aspeed_lpc_snoop_channel *chan = snoop_file_to_chan(file); + + poll_wait(file, &chan->wq, pt); + return !kfifo_is_empty(&chan->fifo) ? POLLIN : 0; +} + +static const struct file_operations snoop_fops = { + .owner = THIS_MODULE, + .read = snoop_file_read, + .poll = snoop_file_poll, + .llseek = noop_llseek, +}; + +/* Save a byte to a FIFO and discard the oldest byte if FIFO is full */ +static void put_fifo_with_discard(struct aspeed_lpc_snoop_channel *chan, u8 val) +{ + if (!kfifo_initialized(&chan->fifo)) + return; + if (kfifo_is_full(&chan->fifo)) + kfifo_skip(&chan->fifo); + kfifo_put(&chan->fifo, val); + wake_up_interruptible(&chan->wq); +} + +static irqreturn_t aspeed_lpc_snoop_irq(int irq, void *arg) +{ + struct aspeed_lpc_snoop *lpc_snoop = arg; + u32 reg, data; + + if (regmap_read(lpc_snoop->regmap, HICR6, ®)) + return IRQ_NONE; + + /* Check if one of the snoop channels is interrupting */ + reg &= (HICR6_STR_SNP0W | HICR6_STR_SNP1W); + if (!reg) + return IRQ_NONE; + + /* Ack pending IRQs */ + regmap_write(lpc_snoop->regmap, HICR6, reg); + + /* Read and save most recent snoop'ed data byte to FIFO */ + regmap_read(lpc_snoop->regmap, SNPWDR, &data); + + if (reg & HICR6_STR_SNP0W) { + u8 val = (data & SNPWDR_CH0_MASK) >> SNPWDR_CH0_SHIFT; + + put_fifo_with_discard(&lpc_snoop->chan[0], val); + } + if (reg & HICR6_STR_SNP1W) { + u8 val = (data & SNPWDR_CH1_MASK) >> SNPWDR_CH1_SHIFT; + + put_fifo_with_discard(&lpc_snoop->chan[1], val); + } + + return IRQ_HANDLED; +} + +static int aspeed_lpc_snoop_config_irq(struct aspeed_lpc_snoop *lpc_snoop, + struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + int rc; + + lpc_snoop->irq = platform_get_irq(pdev, 0); + if (!lpc_snoop->irq) + return -ENODEV; + + rc = devm_request_irq(dev, lpc_snoop->irq, + aspeed_lpc_snoop_irq, IRQF_SHARED, + DEVICE_NAME, lpc_snoop); + if (rc < 0) { + dev_warn(dev, "Unable to request IRQ %d\n", lpc_snoop->irq); + lpc_snoop->irq = 0; + return rc; + } + + return 0; +} + +static int aspeed_lpc_enable_snoop(struct aspeed_lpc_snoop *lpc_snoop, + struct device *dev, + int channel, u16 lpc_port) +{ + int rc = 0; + u32 hicr5_en, snpwadr_mask, snpwadr_shift, hicrb_en; + const struct aspeed_lpc_snoop_model_data *model_data = + of_device_get_match_data(dev); + + init_waitqueue_head(&lpc_snoop->chan[channel].wq); + /* Create FIFO datastructure */ + rc = kfifo_alloc(&lpc_snoop->chan[channel].fifo, + SNOOP_FIFO_SIZE, GFP_KERNEL); + if (rc) + return rc; + + lpc_snoop->chan[channel].miscdev.minor = MISC_DYNAMIC_MINOR; + lpc_snoop->chan[channel].miscdev.name = + devm_kasprintf(dev, GFP_KERNEL, "%s%d", DEVICE_NAME, channel); + lpc_snoop->chan[channel].miscdev.fops = &snoop_fops; + lpc_snoop->chan[channel].miscdev.parent = dev; + rc = misc_register(&lpc_snoop->chan[channel].miscdev); + if (rc) + return rc; + + /* Enable LPC snoop channel at requested port */ + switch (channel) { + case 0: + hicr5_en = HICR5_EN_SNP0W | HICR5_ENINT_SNP0W; + snpwadr_mask = SNPWADR_CH0_MASK; + snpwadr_shift = SNPWADR_CH0_SHIFT; + hicrb_en = HICRB_ENSNP0D; + break; + case 1: + hicr5_en = HICR5_EN_SNP1W | HICR5_ENINT_SNP1W; + snpwadr_mask = SNPWADR_CH1_MASK; + snpwadr_shift = SNPWADR_CH1_SHIFT; + hicrb_en = HICRB_ENSNP1D; + break; + default: + return -EINVAL; + } + + regmap_update_bits(lpc_snoop->regmap, HICR5, hicr5_en, hicr5_en); + regmap_update_bits(lpc_snoop->regmap, SNPWADR, snpwadr_mask, + lpc_port << snpwadr_shift); + if (model_data->has_hicrb_ensnp) + regmap_update_bits(lpc_snoop->regmap, HICRB, + hicrb_en, hicrb_en); + + return rc; +} + +static void aspeed_lpc_disable_snoop(struct aspeed_lpc_snoop *lpc_snoop, + int channel) +{ + switch (channel) { + case 0: + regmap_update_bits(lpc_snoop->regmap, HICR5, + HICR5_EN_SNP0W | HICR5_ENINT_SNP0W, + 0); + break; + case 1: + regmap_update_bits(lpc_snoop->regmap, HICR5, + HICR5_EN_SNP1W | HICR5_ENINT_SNP1W, + 0); + break; + default: + return; + } + + kfifo_free(&lpc_snoop->chan[channel].fifo); + misc_deregister(&lpc_snoop->chan[channel].miscdev); +} + +static int aspeed_lpc_snoop_probe(struct platform_device *pdev) +{ + struct aspeed_lpc_snoop *lpc_snoop; + struct device *dev; + u32 port; + int rc; + + dev = &pdev->dev; + + lpc_snoop = devm_kzalloc(dev, sizeof(*lpc_snoop), GFP_KERNEL); + if (!lpc_snoop) + return -ENOMEM; + + lpc_snoop->regmap = syscon_node_to_regmap( + pdev->dev.parent->of_node); + if (IS_ERR(lpc_snoop->regmap)) { + dev_err(dev, "Couldn't get regmap\n"); + return -ENODEV; + } + + dev_set_drvdata(&pdev->dev, lpc_snoop); + + rc = of_property_read_u32_index(dev->of_node, "snoop-ports", 0, &port); + if (rc) { + dev_err(dev, "no snoop ports configured\n"); + return -ENODEV; + } + + rc = aspeed_lpc_snoop_config_irq(lpc_snoop, pdev); + if (rc) + return rc; + + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 0, port); + if (rc) + return rc; + + /* Configuration of 2nd snoop channel port is optional */ + if (of_property_read_u32_index(dev->of_node, "snoop-ports", + 1, &port) == 0) { + rc = aspeed_lpc_enable_snoop(lpc_snoop, dev, 1, port); + if (rc) + aspeed_lpc_disable_snoop(lpc_snoop, 0); + } + + return rc; +} + +static int aspeed_lpc_snoop_remove(struct platform_device *pdev) +{ + struct aspeed_lpc_snoop *lpc_snoop = dev_get_drvdata(&pdev->dev); + + /* Disable both snoop channels */ + aspeed_lpc_disable_snoop(lpc_snoop, 0); + aspeed_lpc_disable_snoop(lpc_snoop, 1); + + return 0; +} + +static const struct aspeed_lpc_snoop_model_data ast2400_model_data = { + .has_hicrb_ensnp = 0, +}; + +static const struct aspeed_lpc_snoop_model_data ast2500_model_data = { + .has_hicrb_ensnp = 1, +}; + +static const struct of_device_id aspeed_lpc_snoop_match[] = { + { .compatible = "aspeed,ast2400-lpc-snoop", + .data = &ast2400_model_data }, + { .compatible = "aspeed,ast2500-lpc-snoop", + .data = &ast2500_model_data }, + { }, +}; + +static struct platform_driver aspeed_lpc_snoop_driver = { + .driver = { + .name = DEVICE_NAME, + .of_match_table = aspeed_lpc_snoop_match, + }, + .probe = aspeed_lpc_snoop_probe, + .remove = aspeed_lpc_snoop_remove, +}; + +module_platform_driver(aspeed_lpc_snoop_driver); + +MODULE_DEVICE_TABLE(of, aspeed_lpc_snoop_match); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Lippert "); +MODULE_DESCRIPTION("Linux driver to control Aspeed LPC snoop functionality"); -- cgit v1.2.3 From 80d0c649244253d8cb3ba32d708c1431e7ac8fbf Mon Sep 17 00:00:00 2001 From: Olof Johansson Date: Mon, 29 Apr 2019 12:25:41 -0700 Subject: soc: aspeed: fix Kconfig Fixes build break: scripts/kconfig/conf --allnoconfig Kconfig drivers/soc/Kconfig:24: 'menu' in different file than 'menu' drivers/soc/aspeed/Kconfig:1: location of the 'menu' drivers/Kconfig:233: 'menu' in different file than 'menu' drivers/soc/aspeed/Kconfig:1: location of the 'menu' :34: syntax error Signed-off-by: Patrick Venture Signed-off-by: Olof Johansson --- drivers/soc/aspeed/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/soc/aspeed/Kconfig b/drivers/soc/aspeed/Kconfig index 457282cd1da5..858b5e3f79c9 100644 --- a/drivers/soc/aspeed/Kconfig +++ b/drivers/soc/aspeed/Kconfig @@ -17,3 +17,4 @@ config ASPEED_LPC_SNOOP the host to an arbitrary LPC I/O port. +endmenu -- cgit v1.2.3