From 23b0361c2fbbdce59b2cb914ea7e067a5b10d295 Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Tue, 26 Sep 2017 14:28:16 +0800 Subject: [PATCH 1/6] MLK-16564-01 gpio: imx-rpmsg: add rpmsg virtual gpio driver Add rpmsg virtual gpio driver support. i.MX7ULP GPIO PTA and PTB resource are managed by M4 core, setup one simple protocol with M4 core based on RPMSG virtual IO to let A core access such GPIOs that is what the driver do. Reviewed-by: Richard Zhu Reviewed-by: Robin Gong Signed-off-by: Fugang Duan --- .../bindings/gpio/gpio-imx-rpmsg.txt | 38 +++ drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-imx-rpmsg.c | 322 ++++++++++++++++++ drivers/rpmsg/imx_rpmsg.c | 2 +- include/linux/imx_rpmsg.h | 1 + 6 files changed, 369 insertions(+), 1 deletion(-) create mode 100644 Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt create mode 100644 drivers/gpio/gpio-imx-rpmsg.c 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 00000000000000..49c7d321e2bfbb --- /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/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index dafa3f9aaf129d..600c4c07e8d863 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 d074c2299393dc..95f52f4a97ce48 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 00000000000000..9bfd1880fc57b3 --- /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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 d63e353b5c60fe..44ec65aa12758e 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/include/linux/imx_rpmsg.h b/include/linux/imx_rpmsg.h index 894ebaf50578c6..0cf531ceb57d76 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 From 572a89ccbf4b87e31fc46aae20c94f800e5e0d6e Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 11 Oct 2017 14:22:10 +0800 Subject: [PATCH 2/6] MLK-16564-02 dts: imx7ulp-evk: add rpmsg gpio PTA and PTB support Add rpmsg gpio PTA and PTB support. Since currently M4 image support dynamical channel allocation, and reserve below memory for kernel service and app channel: * --0x9FF00000~0x9FF0FFFF: pmic,pm,audio,keys,gpio * --0x9FF10000~0x9FF1FFFF: pingpong,virtual tty Change the rpmsg instances of A core part to sync with M core. Test M4 image built from Wayne Feng, M4 image commit ID: b1321d4aca82 Reviewed-by: Richard Zhu Reviewed-by: Robin Gong Signed-off-by: Fugang Duan --- arch/arm/boot/dts/imx7ulp-evk.dts | 33 +++++++++++++++++++++++-------- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/arch/arm/boot/dts/imx7ulp-evk.dts b/arch/arm/boot/dts/imx7ulp-evk.dts index 0ff23f42aa44fb..3ee6e3c34747d4 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"; }; From be46ed59b545045a9cdc25c39d72f27e99f18cbe Mon Sep 17 00:00:00 2001 From: Fugang Duan Date: Wed, 11 Oct 2017 14:32:30 +0800 Subject: [PATCH 3/6] MLK-16564-03 ARM: imx_v7_defconfig: enable rpmsg gpio driver Enable CONFIG_GPIO_IMX_RPMSG to support rpmsg gpio driver in default. Reviewed-by: Richard Zhu Reviewed-by: Robin Gong Signed-off-by: Fugang Duan --- arch/arm/configs/imx_v7_defconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/arch/arm/configs/imx_v7_defconfig b/arch/arm/configs/imx_v7_defconfig index 25fbcf67a6c206..5d6d3d0189b53c 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 From 81802e860f68bb3e45ce0d68d16ed213771f8fe8 Mon Sep 17 00:00:00 2001 From: Li Jun Date: Mon, 16 Oct 2017 23:13:19 +0800 Subject: [PATCH 4/6] MLK-16576 usb: phy: mxs: set hold_ring_off for USB2 PLL power up MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 kept until PLL power down. Hold_ring_off(bit11) can force the VCO in one determined state when VCO supply start ramp up, to avoid this multiwave issue. Per IC design's suggestion it's better this bit can be off from 25us after pll power up to 25us before USB TX/RX. Acked-by: Peter Chen Signed-off-by: Li Jun (cherry picked from commit a094377f04c9ed2c8e702ee7bfab843caa03eb96) --- drivers/usb/phy/phy-mxs-usb.c | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c index ae36fa4ed8835e..92b94341b1b416 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); From d6464bc843047549e47734c3cca6e72f97179774 Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Fri, 25 Aug 2017 13:07:27 +0800 Subject: [PATCH 5/6] MLK-16266-01 ARM: imx: improve the soc revision calculation flow On our i.MX6 SOC, the DIGPROG register is used for represent the SOC ID and silicon revision. The revision has two part: MAJOR and MINOR. each is represented in 8 bits in the register. bits [15:8]: reflect the MAJOR part of the revision; bits [7:0]: reflect the MINOR part of the revision; In our linux kernel, the soc revision is represented in 8 bits. MAJOR part and MINOR each occupy 4 bits. previous method does NOT take care about the MAJOR part in DIGPROG register. So reformat the revision read from the HW to compatible the revision format used in kernel. Signed-off-by: Bai Ping (cherry picked from commit cf6fa1477149d4bddd096c71b46fa6c4cf9cf750) --- arch/arm/mach-imx/anatop.c | 56 +++++++++++++------------------------- 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c index e6e55723d9cfa1..2470e1a4feade3 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 @@ -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); From 427eb8371fd5e9bceec18ca8838ac6eb1e51ad3a Mon Sep 17 00:00:00 2001 From: Bai Ping Date: Fri, 25 Aug 2017 13:22:32 +0800 Subject: [PATCH 6/6] MLK-16266-02 ARM: imx: Enhance the code to support new TO for imx6qp Previous code don't take care about the i.MX6QP revision update of new TO. So improve the code to include future TO support for i.MX6QP. Signed-off-by: Bai Ping (cherry picked from commit 513e1c9f70cfd74cb758a2c56efccd2f031b4f1b) --- arch/arm/mach-imx/anatop.c | 4 ++-- arch/arm/mach-imx/cpu.c | 2 +- arch/arm/mach-imx/gpc.c | 11 ++++++----- arch/arm/mach-imx/mach-imx6q.c | 5 +++-- drivers/clk/imx/clk-imx6q.c | 7 ++++--- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/arch/arm/mach-imx/anatop.c b/arch/arm/mach-imx/anatop.c index 2470e1a4feade3..d46f68417bbb66 100644 --- a/arch/arm/mach-imx/anatop.c +++ b/arch/arm/mach-imx/anatop.c @@ -146,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 || @@ -176,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 || diff --git a/arch/arm/mach-imx/cpu.c b/arch/arm/mach-imx/cpu.c index 3a28b16936f4f1..a02b47a0be36bd 100644 --- a/arch/arm/mach-imx/cpu.c +++ b/arch/arm/mach-imx/cpu.c @@ -126,7 +126,7 @@ struct device * __init imx_soc_device_init(void) soc_id = "i.MX6SX"; break; case MXC_CPU_IMX6Q: - 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 1629608899a9de..551ecd0f2a4f84 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 b884209edaa6c2..7b2462f7f59895 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 @@ -310,7 +311,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(); } @@ -318,7 +319,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 6f5d683c1074fa..8cde9cc327987e 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 @@ -662,7 +663,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 { @@ -990,7 +991,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]); @@ -1087,7 +1088,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);