diff options
author | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2017-12-21 14:34:21 +0100 |
---|---|---|
committer | Marcel Ziswiler <marcel.ziswiler@toradex.com> | 2017-12-21 14:34:21 +0100 |
commit | a898b45a9c18bd9d723f82576a5b32a102808a09 (patch) | |
tree | e34c381ee55f0524bb2c69b9162901dd87f29d39 | |
parent | 9282ed58a8d84776f995225f1b3d2e793d0bdb50 (diff) | |
parent | 727301c4a23f798ab3f50f8ce6dc032f11dd75f3 (diff) |
Merge remote-tracking branch 'linux-fslc/4.9-1.0.x-imx' into toradex_4.9-1.0.x-imx-next
-rw-r--r-- | Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt | 38 | ||||
-rw-r--r-- | arch/arm/boot/dts/imx7ulp-evk.dts | 33 | ||||
-rw-r--r-- | arch/arm/configs/imx_v7_defconfig | 1 | ||||
-rw-r--r-- | arch/arm/mach-imx/anatop.c | 60 | ||||
-rw-r--r-- | arch/arm/mach-imx/cpu.c | 4 | ||||
-rw-r--r-- | arch/arm/mach-imx/gpc.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-imx/mach-imx6q.c | 5 | ||||
-rw-r--r-- | drivers/clk/imx/clk-imx6q.c | 7 | ||||
-rw-r--r-- | drivers/gpio/Kconfig | 6 | ||||
-rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
-rw-r--r-- | drivers/gpio/gpio-imx-rpmsg.c | 322 | ||||
-rw-r--r-- | drivers/rpmsg/imx_rpmsg.c | 2 | ||||
-rw-r--r-- | drivers/usb/phy/phy-mxs-usb.c | 35 | ||||
-rw-r--r-- | include/linux/imx_rpmsg.h | 1 |
14 files changed, 465 insertions, 61 deletions
diff --git a/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt b/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt new file mode 100644 index 000000000000..49c7d321e2bf --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt @@ -0,0 +1,38 @@ +Device-Tree bindings for drivers/gpio/gpio-imx-rpmsg.c gpio driver over +rpmsg. On i.mx7ULP PTA PTB are connected on M4 side, so rpmsg gpio driver +needed to get/set gpio status from M4 side by rpmsg. + +Required properties: +- compatible : Should be "fsl,imx-rpmsg-gpio". +- port_idx : Specify the GPIO PORT index, PTA:0, PTB:1. +- gpio-controller : Mark the device node as a gpio controller. +- #gpio-cells : Should be two. The first cell is the pin number and + the second cell is used to specify the gpio polarity: + 0 = active high + 1 = active low + +Note: Each GPIO port should have an alias correctly numbered in "aliases" +node. + +Examples: + +aliases { + gpio4 = &rpmsg_gpio0; + gpio5 = &rpmsg_gpio1; +}; + +rpmsg_gpio0: rpmsg-gpio0 { + compatible = "fsl,imx-rpmsg-gpio"; + port_idx = <0>; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; +}; + +rpmsg_gpio1: rpmsg-gpio1 { + compatible = "fsl,imx-rpmsg-gpio"; + port_idx = <1>; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; +}; diff --git a/arch/arm/boot/dts/imx7ulp-evk.dts b/arch/arm/boot/dts/imx7ulp-evk.dts index 0ff23f42aa44..3ee6e3c34747 100644 --- a/arch/arm/boot/dts/imx7ulp-evk.dts +++ b/arch/arm/boot/dts/imx7ulp-evk.dts @@ -16,6 +16,11 @@ model = "NXP i.MX7ULP EVK"; compatible = "fsl,imx7ulp-evk", "fsl,imx7ulp", "Generic DT based system"; + aliases { + gpio4 = &rpmsg_gpio0; + gpio5 = &rpmsg_gpio1; + }; + chosen { bootargs = "console=ttyLP0,115200 earlycon=lpuart32,0x402D0000,115200"; stdout-path = &lpuart4; @@ -166,6 +171,22 @@ status = "okay"; }; + rpmsg_gpio0: rpmsg-gpio0 { + compatible = "fsl,imx-rpmsg-gpio"; + port_idx = <0>; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; + }; + + rpmsg_gpio1: rpmsg-gpio1 { + compatible = "fsl,imx-rpmsg-gpio"; + port_idx = <1>; + gpio-controller; + #gpio-cells = <2>; + status = "okay"; + }; + rpmsg_keys: rpmsg-keys { compatible = "fsl,rpmsg-keys"; @@ -484,15 +505,11 @@ &rpmsg{ /* * 64K for one rpmsg instance, default using 2 rpmsg instances: - * --0x9FF00000~0x9FF0FFFF: pingpong - * --0x9FF10000~0x9FF1FFFF: pmic - * --0x9FF20000~0x9FF2FFFF: pm - * --0x9FF30000~0x9FF3FFFF: audio - * --0x9FF40000~0x9FF4FFFF: virtual tty - * --0x9FF50000~0x9FF5FFFF: keys + * --0x9FF00000~0x9FF0FFFF: pmic,pm,audio,keys,gpio + * --0x9FF10000~0x9FF1FFFF: pingpong,virtual tty */ - vdev-nums = <6>; - reg = <0x9FF00000 0x60000>; + vdev-nums = <2>; + reg = <0x9FF00000 0x20000>; status = "okay"; }; diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig index 25fbcf67a6c2..5d6d3d0189b5 100644 --- a/arch/arm/configs/imx_v7_defconfig +++ b/arch/arm/configs/imx_v7_defconfig @@ -198,6 +198,7 @@ CONFIG_SPI_IMX=y CONFIG_SPI_FSL_LPSPI=y CONFIG_SPI_SPIDEV=y CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_IMX_RPMSG=y CONFIG_GPIO_MAX732X=y CONFIG_GPIO_PCA953X=y CONFIG_GPIO_74X164=y diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c index e6e55723d9cf..d46f68417bbb 100644 --- a/arch/arm/mach-imx/anatop.c +++ b/arch/arm/mach-imx/anatop.c @@ -1,5 +1,6 @@ /* * Copyright (C) 2013-2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -145,7 +146,7 @@ void imx_anatop_pre_suspend(void) return; } - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) imx_anatop_disable_pu(true); if ((imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2 || @@ -175,7 +176,7 @@ void imx_anatop_post_resume(void) return; } - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) imx_anatop_disable_pu(false); if ((imx_mmdc_get_ddr_type() == IMX_DDR_TYPE_LPDDR2 || @@ -209,6 +210,7 @@ void __init imx_init_revision_from_anatop(void) unsigned int revision; u32 digprog; u16 offset = ANADIG_DIGPROG; + u16 major_part, minor_part; np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-anatop"); anatop_base = of_iomap(np, 0); @@ -220,45 +222,25 @@ void __init imx_init_revision_from_anatop(void) digprog = readl_relaxed(anatop_base + offset); iounmap(anatop_base); - switch (digprog & 0xff) { - case 0: - /* - * For i.MX6QP, most of the code for i.MX6Q can be resued, - * so internally, we identify it as i.MX6Q Rev 2.0 - */ - if (digprog >> 8 & 0x01) - revision = IMX_CHIP_REVISION_2_0; - else - revision = IMX_CHIP_REVISION_1_0; - break; - case 1: - revision = IMX_CHIP_REVISION_1_1; - break; - case 2: - revision = IMX_CHIP_REVISION_1_2; - break; - case 3: - revision = IMX_CHIP_REVISION_1_3; - break; - case 4: - revision = IMX_CHIP_REVISION_1_4; - break; - case 5: - /* - * i.MX6DQ TO1.5 is defined as Rev 1.3 in Data Sheet, marked - * as 'D' in Part Number last character. - */ - revision = IMX_CHIP_REVISION_1_5; - break; - default: + /* + * On i.MX7D digprog value match linux version format, so + * it needn't map again and we can use register value directly. + */ + if (of_device_is_compatible(np, "fsl,imx7d-anatop")) { + revision = digprog & 0xff; + } else { /* - * Fail back to return raw register value instead of 0xff. - * It will be easy to know version information in SOC if it - * can't be recognized by known version. And some chip's (i.MX7D) - * digprog value match linux version format, so it needn't map - * again and we can use register value directly. + * MAJOR: [15:8], the major silicon revison; + * MINOR: [7: 0], the minor silicon revison; + * + * please refer to the i.MX RM for the detailed + * silicon revison bit define. + * format the major part and minor part to match the + * linux kernel soc version format. */ - revision = digprog & 0xff; + major_part = (digprog >> 8) & 0xf; + minor_part = digprog & 0xf; + revision = ((major_part + 1) << 4) | minor_part; } mxc_set_cpu_type(digprog >> 16 & 0xff); diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index d23af05447d0..30ab50744cd3 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -173,9 +173,7 @@ struct device * __init imx_soc_device_init(void) soc_id = "i.MX6SX"; break; case MXC_CPU_IMX6Q: - soc_dev_attr->unique_id = kasprintf(GFP_KERNEL, "%llx", imx_get_soc_uid()); - - if (imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) soc_id = "i.MX6QP"; else soc_id = "i.MX6Q"; diff --git a/arch/arm/mach-imx/gpc.c b/arch/arm/mach-imx/gpc.c index 1629608899a9..551ecd0f2a4f 100644 --- a/arch/arm/mach-imx/gpc.c +++ b/arch/arm/mach-imx/gpc.c @@ -1,6 +1,7 @@ /* * Copyright 2011-2016 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. + * Copyright 2017 NXP. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -231,7 +232,7 @@ void imx_gpc_pre_suspend(bool arm_power_off) void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) _imx6q_pm_pu_power_off(&imx6q_pu_domain.base); /* power down the mega-fast power domain */ @@ -254,7 +255,7 @@ void imx_gpc_post_resume(void) void __iomem *reg_imr1 = gpc_base + GPC_IMR1; int i; - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) _imx6q_pm_pu_power_on(&imx6q_pu_domain.base); /* Keep ARM core powered on for other low-power modes */ @@ -648,7 +649,7 @@ static int imx6q_pm_pu_power_off(struct generic_pm_domain *genpd) struct pu_domain *pu = container_of(genpd, struct pu_domain, base); if (&imx6q_pu_domain == pu && pu_on && cpu_is_imx6q() && - imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) return 0; _imx6q_pm_pu_power_off(genpd); @@ -693,7 +694,7 @@ static int imx6q_pm_pu_power_on(struct generic_pm_domain *genpd) struct pu_domain *pu = container_of(genpd, struct pu_domain, base); int ret; - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0 + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0 && &imx6q_pu_domain == pu) { if (!pu_on) pu_on = true; @@ -856,7 +857,7 @@ static int imx_gpc_genpd_init(struct device *dev, struct regulator *pu_reg) is_off = IS_ENABLED(CONFIG_PM); if (is_off && !(cpu_is_imx6q() && - imx_get_soc_revision() == IMX_CHIP_REVISION_2_0)) { + imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0)) { _imx6q_pm_pu_power_off(&imx6q_pu_domain.base); } else { /* diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 7569722ff7ea..c0e3928d5f73 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -1,6 +1,7 @@ /* * Copyright 2011-2015 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. + * Copyright 2017 NXP. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -342,7 +343,7 @@ static inline void imx6q_enet_init(void) imx6_enet_mac_init("fsl,imx6q-fec", "fsl,imx6q-ocotp"); imx6q_enet_phy_init(); imx6q_1588_init(); - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) imx6q_enet_clk_sel(); } @@ -350,7 +351,7 @@ static void __init imx6q_init_machine(void) { struct device *parent; - if (cpu_is_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) + if (cpu_is_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) imx_print_silicon_rev("i.MX6QP", IMX_CHIP_REVISION_1_0); else imx_print_silicon_rev(cpu_is_imx6dl() ? "i.MX6DL" : "i.MX6Q", diff --git a/drivers/clk/imx/clk-imx6q.c b/drivers/clk/imx/clk-imx6q.c index 35f9fa9c1f59..8b2c94c7529e 100644 --- a/drivers/clk/imx/clk-imx6q.c +++ b/drivers/clk/imx/clk-imx6q.c @@ -1,6 +1,7 @@ /* * Copyright (C) 2011-2016 Freescale Semiconductor, Inc. * Copyright 2011 Linaro Ltd. + * Copyright 2017 NXP. * * The code contained herein is licensed under the GNU General Public * License. You may obtain a copy of the GNU General Public License @@ -664,7 +665,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk[IMX6QDL_CLK_IPU1_SEL] = imx_clk_mux("ipu1_sel", base + 0x3c, 9, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); clk[IMX6QDL_CLK_IPU2_SEL] = imx_clk_mux("ipu2_sel", base + 0x3c, 14, 2, ipu_sels, ARRAY_SIZE(ipu_sels)); - if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) { + if (clk_on_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) { clk[IMX6QDL_CLK_LDB_DI0_SEL] = imx_clk_mux_flags("ldb_di0_sel", base + 0x2c, 9, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); clk[IMX6QDL_CLK_LDB_DI1_SEL] = imx_clk_mux_flags("ldb_di1_sel", base + 0x2c, 12, 3, ldb_di_sels, ARRAY_SIZE(ldb_di_sels), CLK_SET_RATE_PARENT); } else { @@ -992,7 +993,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], clk[IMX6QDL_CLK_PLL2_PFD1_594M]); imx_clk_set_rate(clk[IMX6QDL_CLK_GPU3D_CORE], 528000000); } else if (clk_on_imx6q()) { - if (imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) { + if (imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) { clk_set_parent(clk[IMX6QDL_CLK_GPU3D_SHADER_SEL], clk[IMX6QDL_CLK_PLL3_PFD0_720M]); imx_clk_set_rate(clk[IMX6QDL_CLK_GPU3D_SHADER], 720000000); clk_set_parent(clk[IMX6QDL_CLK_GPU3D_CORE_SEL], clk[IMX6QDL_CLK_PLL2_PFD1_594M]); @@ -1096,7 +1097,7 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node) * for i.MX6QP with speeding grading set to 1.2GHz, * VPU should run at 396MHz. */ - if (clk_on_imx6q() && imx_get_soc_revision() == IMX_CHIP_REVISION_2_0) { + if (clk_on_imx6q() && imx_get_soc_revision() >= IMX_CHIP_REVISION_2_0) { np = of_find_compatible_node(NULL, NULL, "fsl,imx6q-ocotp"); WARN_ON(!np); diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index dafa3f9aaf12..600c4c07e8d8 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -442,6 +442,12 @@ config GPIO_VF610 help Say yes here to support Vybrid vf610 GPIOs. +config GPIO_IMX_RPMSG + bool "NXP i.MX7ULP RPMSG GPIO support" + depends on ARCH_MXC && RPMSG && GPIOLIB + help + This driver support i.MX7ULP RPMSG virtual GPIOs. + config GPIO_VR41XX tristate "NEC VR4100 series General-purpose I/O Uint support" depends on CPU_VR41XX diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index d074c2299393..95f52f4a97ce 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -124,6 +124,7 @@ obj-$(CONFIG_GPIO_TZ1090) += gpio-tz1090.o obj-$(CONFIG_GPIO_TZ1090_PDC) += gpio-tz1090-pdc.o obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o +obj-$(CONFIG_GPIO_IMX_RPMSG) += gpio-imx-rpmsg.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o diff --git a/drivers/gpio/gpio-imx-rpmsg.c b/drivers/gpio/gpio-imx-rpmsg.c new file mode 100644 index 000000000000..9bfd1880fc57 --- /dev/null +++ b/drivers/gpio/gpio-imx-rpmsg.c @@ -0,0 +1,322 @@ +/* + * Copyright 2017 NXP + * + * 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. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/imx_rpmsg.h> +#include <linux/init.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/pm_qos.h> +#include <linux/rpmsg.h> +#include <linux/virtio.h> + +#define IMX_RPMSG_GPIO_PER_PORT 32 +#define RPMSG_TIMEOUT 1000 + +enum gpio_input_trigger_type { + GPIO_RPMSG_TRI_IGNORE, + GPIO_RPMSG_TRI_RISING, + GPIO_RPMSG_TRI_FALLING, + GPIO_RPMSG_TRI_BOTH_EDGE, + GPIO_RPMSG_TRI_LOW_LEVEL, + GPIO_RPMSG_TRI_HIGH_LEVEL, +}; + +enum gpio_rpmsg_header_type { + GPIO_RPMSG_SETUP, + GPIO_RPMSG_REPLY, + GPIO_RPMSG_NOTIFY, +}; + +enum gpio_rpmsg_header_cmd { + GPIO_RPMSG_INPUT_INIT, + GPIO_RPMSG_OUTPUT_INIT, + GPIO_RPMSG_INPUT_GET, +}; + +struct gpio_rpmsg_data { + struct imx_rpmsg_head header; + u8 pin_idx; + u8 port_idx; + union { + u8 event; + u8 retcode; + u8 value; + } out; + union { + u8 wakeup; + u8 value; + } in; +} __packed __aligned(8); + +struct imx_rpmsg_gpio_port { + struct gpio_chip gc; + struct gpio_rpmsg_data msg; + int idx; +}; + +struct imx_gpio_rpmsg_info { + struct rpmsg_device *rpdev; + struct gpio_rpmsg_data *notify_msg; + struct gpio_rpmsg_data *reply_msg; + struct pm_qos_request pm_qos_req; + struct completion cmd_complete; + struct mutex lock; +}; + +static struct imx_gpio_rpmsg_info gpio_rpmsg; + +static int gpio_send_message(struct imx_rpmsg_gpio_port *port, + struct gpio_rpmsg_data *msg, struct imx_gpio_rpmsg_info *info) +{ + int err; + + if (!info->rpdev) { + dev_dbg(&info->rpdev->dev, + "rpmsg channel not ready, m4 image ready?\n"); + return -EINVAL; + } + + mutex_lock(&info->lock); + pm_qos_add_request(&info->pm_qos_req, + PM_QOS_CPU_DMA_LATENCY, 0); + + reinit_completion(&info->cmd_complete); + + err = rpmsg_send(info->rpdev->ept, (void *)msg, + sizeof(struct gpio_rpmsg_data)); + + if (err) { + dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err); + goto err_out; + } + + err = wait_for_completion_timeout(&info->cmd_complete, + msecs_to_jiffies(RPMSG_TIMEOUT)); + if (!err) { + dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n"); + err = -ETIMEDOUT; + goto err_out; + } + + if (info->reply_msg->out.retcode != 0) { + dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n", + info->reply_msg->out.retcode); + err = -EINVAL; + goto err_out; + } + + /* copy the reply message */ + memcpy(&port->msg, info->reply_msg, sizeof(*info->reply_msg)); + + err = 0; + +err_out: + pm_qos_remove_request(&info->pm_qos_req); + mutex_unlock(&info->lock); + + return err; +} + +static int gpio_rpmsg_cb(struct rpmsg_device *rpdev, + void *data, int len, void *priv, u32 src) +{ + struct gpio_rpmsg_data *msg = (struct gpio_rpmsg_data *)data; + + if (msg->header.type == GPIO_RPMSG_REPLY) { + gpio_rpmsg.reply_msg = msg; + complete(&gpio_rpmsg.cmd_complete); + } else if (msg->header.type == GPIO_RPMSG_NOTIFY) { + gpio_rpmsg.notify_msg = msg; + /* TBD for interrupt handler */ + } else + dev_err(&gpio_rpmsg.rpdev->dev, "wrong command type!\n"); + + return 0; +} + +static int imx_rpmsg_gpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc); + struct gpio_rpmsg_data msg; + int ret; + + memset(&msg, 0, sizeof(struct gpio_rpmsg_data)); + msg.header.cate = IMX_RPMSG_GPIO; + msg.header.major = IMX_RMPSG_MAJOR; + msg.header.minor = IMX_RMPSG_MINOR; + msg.header.type = GPIO_RPMSG_SETUP; + msg.header.cmd = GPIO_RPMSG_INPUT_GET; + msg.pin_idx = gpio; + msg.port_idx = port->idx; + + ret = gpio_send_message(port, &msg, &gpio_rpmsg); + if (!ret) + return !!port->msg.in.value; + + return ret; +} + +static int imx_rpmsg_gpio_direction_input(struct gpio_chip *gc, + unsigned int gpio) +{ + struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc); + struct gpio_rpmsg_data msg; + + memset(&msg, 0, sizeof(struct gpio_rpmsg_data)); + msg.header.cate = IMX_RPMSG_GPIO; + msg.header.major = IMX_RMPSG_MAJOR; + msg.header.minor = IMX_RMPSG_MINOR; + msg.header.type = GPIO_RPMSG_SETUP; + msg.header.cmd = GPIO_RPMSG_INPUT_INIT; + msg.pin_idx = gpio; + msg.port_idx = port->idx; + + /* TBD: get event trigger and wakeup from GPIO descriptor */ + msg.out.event = GPIO_RPMSG_TRI_IGNORE; + msg.in.wakeup = 0; + + return gpio_send_message(port, &msg, &gpio_rpmsg); +} + +static inline void imx_rpmsg_gpio_direction_output_init(struct gpio_chip *gc, + unsigned int gpio, int val, struct gpio_rpmsg_data *msg) +{ + struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc); + + msg->header.cate = IMX_RPMSG_GPIO; + msg->header.major = IMX_RMPSG_MAJOR; + msg->header.minor = IMX_RMPSG_MINOR; + msg->header.type = GPIO_RPMSG_SETUP; + msg->header.cmd = GPIO_RPMSG_OUTPUT_INIT; + msg->pin_idx = gpio; + msg->port_idx = port->idx; + msg->out.value = val; +} + +static void imx_rpmsg_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc); + struct gpio_rpmsg_data msg; + + memset(&msg, 0, sizeof(struct gpio_rpmsg_data)); + imx_rpmsg_gpio_direction_output_init(gc, gpio, val, &msg); + gpio_send_message(port, &msg, &gpio_rpmsg); +} + +static int imx_rpmsg_gpio_direction_output(struct gpio_chip *gc, + unsigned int gpio, int val) +{ + struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc); + struct gpio_rpmsg_data msg; + + memset(&msg, 0, sizeof(struct gpio_rpmsg_data)); + imx_rpmsg_gpio_direction_output_init(gc, gpio, val, &msg); + return gpio_send_message(port, &msg, &gpio_rpmsg); +} + +static int gpio_rpmsg_probe(struct rpmsg_device *rpdev) +{ + gpio_rpmsg.rpdev = rpdev; + dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n", + rpdev->src, rpdev->dst); + + init_completion(&gpio_rpmsg.cmd_complete); + mutex_init(&gpio_rpmsg.lock); + + return 0; +} + +static struct rpmsg_device_id gpio_rpmsg_id_table[] = { + { .name = "rpmsg-io-channel" }, + {}, +}; + +static struct rpmsg_driver gpio_rpmsg_driver = { + .drv.name = "gpio_rpmsg", + .drv.owner = THIS_MODULE, + .id_table = gpio_rpmsg_id_table, + .probe = gpio_rpmsg_probe, + .callback = gpio_rpmsg_cb, +}; + +static int imx_rpmsg_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct imx_rpmsg_gpio_port *port; + struct gpio_chip *gc; + int ret; + + port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + ret = of_property_read_u32(np, "port_idx", &port->idx); + if (ret) + return ret; + + gc = &port->gc; + gc->of_node = np; + gc->parent = dev; + gc->label = "imx-rpmsg-gpio"; + gc->ngpio = IMX_RPMSG_GPIO_PER_PORT; + gc->base = of_alias_get_id(np, "gpio") * IMX_RPMSG_GPIO_PER_PORT; + + gc->direction_input = imx_rpmsg_gpio_direction_input; + gc->direction_output = imx_rpmsg_gpio_direction_output; + gc->get = imx_rpmsg_gpio_get; + gc->set = imx_rpmsg_gpio_set; + + platform_set_drvdata(pdev, port); + + ret = devm_gpiochip_add_data(dev, gc, port); + if (ret < 0) + return ret; + + return 0; +} + +static const struct of_device_id imx_rpmsg_gpio_dt_ids[] = { + { .compatible = "fsl,imx-rpmsg-gpio" }, + { /* sentinel */ } +}; + +static struct platform_driver imx_rpmsg_gpio_driver = { + .driver = { + .name = "gpio-imx-rpmsg", + .of_match_table = imx_rpmsg_gpio_dt_ids, + }, + .probe = imx_rpmsg_gpio_probe, +}; + +static int __init gpio_imx_rpmsg_init(void) +{ + int ret; + + ret = register_rpmsg_driver(&gpio_rpmsg_driver); + if (ret) + return ret; + + return platform_driver_register(&imx_rpmsg_gpio_driver); +} +device_initcall(gpio_imx_rpmsg_init); + +MODULE_AUTHOR("NXP Semiconductor"); +MODULE_DESCRIPTION("NXP i.MX7ULP rpmsg gpio driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rpmsg/imx_rpmsg.c b/drivers/rpmsg/imx_rpmsg.c index 09cd3cb2f510..31b2f7f94eac 100644 --- a/drivers/rpmsg/imx_rpmsg.c +++ b/drivers/rpmsg/imx_rpmsg.c @@ -52,7 +52,7 @@ struct imx_rpmsg_vproc { char *rproc_name; struct mutex lock; int vdev_nums; -#define MAX_VDEV_NUMS 6 +#define MAX_VDEV_NUMS 7 struct imx_virdev ivdev[MAX_VDEV_NUMS]; }; diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index ae36fa4ed883..92b94341b1b4 100644 --- a/drivers/usb/phy/phy-mxs-usb.c +++ b/drivers/usb/phy/phy-mxs-usb.c @@ -77,6 +77,9 @@ #define BM_USBPHY_PLL_EN_USB_CLKS BIT(6) /* Anatop Registers */ +#define ANADIG_PLL_USB2 0x20 +#define ANADIG_PLL_USB2_SET 0x24 +#define ANADIG_PLL_USB2_CLR 0x28 #define ANADIG_REG_1P1_SET 0x114 #define ANADIG_REG_1P1_CLR 0x118 @@ -114,6 +117,8 @@ #define BM_ANADIG_REG_1P1_ENABLE_WEAK_LINREG BIT(18) #define BM_ANADIG_REG_1P1_TRACK_VDD_SOC_CAP BIT(19) +#define BM_ANADIG_PLL_USB2_HOLD_RING_OFF BIT(11) + #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy) /* Do disconnection between PHY and controller without vbus */ @@ -523,6 +528,22 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) } else { writel(0xffffffff, x->io_priv + HW_USBPHY_PWD); } + + /* + * USB2 PLL use ring VCO, when the PLL power up, the ring + * VCO’s supply also ramp up. There is a possibility that + * the ring VCO start oscillation at multi nodes in this + * phase, especially for VCO which has many stages, then + * the multiwave will be kept until PLL power down. the bit + * hold_ring_off can force the VCO in one determined state + * to avoid the multiwave issue when VCO supply start ramp + * up. + */ + if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop) + regmap_write(mxs_phy->regmap_anatop, + ANADIG_PLL_USB2_SET, + BM_ANADIG_PLL_USB2_HOLD_RING_OFF); + writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_SET); if (!(mxs_phy->port_id == 1 && @@ -536,6 +557,20 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend) if (ret) return ret; } + + /* + * Per IC design's requirement, hold_ring_off bit can be + * cleared 25us after PLL power up and 25us before any USB + * TX/RX. + */ + if (mxs_phy->port_id == 1 && mxs_phy->regmap_anatop) { + udelay(25); + regmap_write(mxs_phy->regmap_anatop, + ANADIG_PLL_USB2_CLR, + BM_ANADIG_PLL_USB2_HOLD_RING_OFF); + udelay(25); + } + writel(BM_USBPHY_CTRL_CLKGATE, x->io_priv + HW_USBPHY_CTRL_CLR); writel(0, x->io_priv + HW_USBPHY_PWD); diff --git a/include/linux/imx_rpmsg.h b/include/linux/imx_rpmsg.h index 894ebaf50578..0cf531ceb57d 100644 --- a/include/linux/imx_rpmsg.h +++ b/include/linux/imx_rpmsg.h @@ -27,6 +27,7 @@ #define IMX_RPMSG_PMIC 2 #define IMX_RPMSG_AUDIO 3 #define IMX_RPMSG_KEY 4 +#define IMX_RPMSG_GPIO 5 /* rpmsg version */ #define IMX_RMPSG_MAJOR 1 #define IMX_RMPSG_MINOR 0 |