summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDong Aisheng <aisheng.dong@nxp.com>2021-11-30 15:00:21 +0800
committerDong Aisheng <aisheng.dong@nxp.com>2021-11-30 15:00:21 +0800
commit853a3c8e3bbae9d7011e4591996d667a931eb68d (patch)
tree42c796c87e3b2da7108b692c226d0c6ab5c605ad
parent8e33e9a47296b6a3afd7da3c950b77eaacfc42b3 (diff)
parent450fb3f8adf69d0a7e1a9b4d61d2dd726ca94e12 (diff)
Merge branch 'gpio/next' into next
* gpio/next: (25 commits) LF-4558-3 gpio: gpio-imx-rpmsg: do not alloc msg dynamically MLK-25649-2 gpio: mxc: Add support for identifying SCU wakeup source from sysfs MLK-25166-1 gpio: gpio-scu: gpio driver over scu fw misc api LF-4273-2 gpio: vf610: clarify the content of GPIO labels LF-4273-1 gpio: imx-rpmsg: clarify the content of GPIO labels ...
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt57
-rw-r--r--Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml4
-rw-r--r--drivers/gpio/Kconfig21
-rw-r--r--drivers/gpio/Makefile2
-rw-r--r--drivers/gpio/gpio-74x164.c3
-rw-r--r--drivers/gpio/gpio-imx-rpmsg.c535
-rw-r--r--drivers/gpio/gpio-max732x.c22
-rw-r--r--drivers/gpio/gpio-mpc8xxx.c11
-rw-r--r--drivers/gpio/gpio-mxc.c296
-rw-r--r--drivers/gpio/gpio-pca953x.c46
-rw-r--r--drivers/gpio/gpio-scu.c137
-rw-r--r--drivers/gpio/gpio-vf610.c2
12 files changed, 1115 insertions, 21 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..25001da92da5
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt
@@ -0,0 +1,57 @@
+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
+- interrupt-controller: Marks the device node as an interrupt controller.
+- #interrupt-cells : Should be 2. The first cell is the GPIO number.
+ The second cell bits[3:0] is used to specify trigger type and level flags:
+ 1 = low-to-high edge triggered.
+ 2 = high-to-low edge triggered.
+ 4 = active high level-sensitive.
+ 8 = active low level-sensitive.
+
+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>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ interrupt-parent = <&rpmsg_gpio0>;
+ status = "okay";
+};
+
+rpmsg_gpio1: rpmsg-gpio1 {
+ compatible = "fsl,imx-rpmsg-gpio";
+ port_idx = <1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ #interrupt-cells = <2>;
+ interrupt-controller;
+ interrupt-parent = <&rpmsg_gpio1>;
+ status = "okay";
+};
+
+&skeleton_node {
+ interrupt-parent = <&rpmsg_gpio1>;
+ interrupts = <7 2>;
+ wakeup-gpios = <&rpmsg_gpio1 7 GPIO_ACTIVE_LOW>;
+};
diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
index b6a6e742b66d..ba30fb1d6d6f 100644
--- a/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
+++ b/Documentation/devicetree/bindings/gpio/gpio-pca95xx.yaml
@@ -94,6 +94,10 @@ properties:
wakeup-source:
$ref: /schemas/types.yaml#/definitions/flag
+ out-default:
+ description:
+ set the output IO default voltage. Exp: out-default = /bits/ 16 <mask val>;
+
patternProperties:
"^(hog-[0-9]+|.+-hog(-[0-9]+)?)$":
type: object
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index fae5141251e5..aad7feaf5255 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -439,6 +439,12 @@ config GPIO_MXC
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
+config GPIO_SCU
+ def_bool y
+ depends on IMX_SCU
+ help
+ Say Y here to enable the imx8 gpio over SCFW MISC API
+
config GPIO_MXS
bool "Freescale MXS GPIO support" if COMPILE_TEST
depends on ARCH_MXS || COMPILE_TEST
@@ -446,6 +452,13 @@ config GPIO_MXS
select GPIO_GENERIC
select GENERIC_IRQ_CHIP
+config GPIO_MXC_PAD_WAKEUP
+ def_bool y
+ depends on IMX_SCU
+ select GPIO_MXC
+ help
+ Say Y here to enable the imx8 gpio pad wakeup
+
config GPIO_OCTEON
tristate "Cavium OCTEON GPIO"
depends on CAVIUM_OCTEON_SOC
@@ -663,7 +676,7 @@ config GPIO_UNIPHIER
config GPIO_VF610
def_bool y
- depends on ARCH_MXC && SOC_VF610
+ depends on ARCH_MXC
select GPIOLIB_IRQCHIP
help
Say yes here to support Vybrid vf610 GPIOs.
@@ -678,6 +691,12 @@ config GPIO_VISCONTI
help
Say yes here to support GPIO on Tohisba Visconti.
+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 fbcda637d5e1..5740cd358832 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -132,6 +132,7 @@ obj-$(CONFIG_ARCH_SA1100) += gpio-sa1100.o
obj-$(CONFIG_GPIO_SAMA5D2_PIOBU) += gpio-sama5d2-piobu.o
obj-$(CONFIG_GPIO_SCH311X) += gpio-sch311x.o
obj-$(CONFIG_GPIO_SCH) += gpio-sch.o
+obj-$(CONFIG_GPIO_SCU) += gpio-scu.o
obj-$(CONFIG_GPIO_SIFIVE) += gpio-sifive.o
obj-$(CONFIG_GPIO_SIOX) += gpio-siox.o
obj-$(CONFIG_GPIO_SL28CPLD) += gpio-sl28cpld.o
@@ -164,6 +165,7 @@ obj-$(CONFIG_GPIO_TWL6040) += gpio-twl6040.o
obj-$(CONFIG_GPIO_UCB1400) += gpio-ucb1400.o
obj-$(CONFIG_GPIO_UNIPHIER) += gpio-uniphier.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_VIRTIO) += gpio-virtio.o
obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o
diff --git a/drivers/gpio/gpio-74x164.c b/drivers/gpio/gpio-74x164.c
index 4a55cdf089d6..bf66a8d93912 100644
--- a/drivers/gpio/gpio-74x164.c
+++ b/drivers/gpio/gpio-74x164.c
@@ -141,6 +141,9 @@ static int gen_74x164_probe(struct spi_device *spi)
chip->registers = nregs;
chip->gpio_chip.ngpio = GEN_74X164_NUMBER_GPIOS * chip->registers;
+ of_property_read_u8_array(spi->dev.of_node, "registers-default",
+ chip->buffer, chip->registers);
+
chip->gpio_chip.can_sleep = true;
chip->gpio_chip.parent = &spi->dev;
chip->gpio_chip.owner = THIS_MODULE;
diff --git a/drivers/gpio/gpio-imx-rpmsg.c b/drivers/gpio/gpio-imx-rpmsg.c
new file mode 100644
index 000000000000..f97ce459dc41
--- /dev/null
+++ b/drivers/gpio/gpio-imx-rpmsg.c
@@ -0,0 +1,535 @@
+/*
+ * 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/irqdomain.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>
+#include <linux/workqueue.h>
+
+#define IMX_RPMSG_GPIO_PER_PORT 32
+#define RPMSG_TIMEOUT 1000
+#define IMX_RPMSG_GPIO_PORT_PER_SOC_MAX 10
+
+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_pin {
+ u32 irq_type;
+ struct gpio_rpmsg_data msg;
+};
+
+struct imx_rpmsg_gpio_port {
+ struct gpio_chip gc;
+ struct irq_chip chip;
+ struct irq_domain *domain;
+ struct imx_rpmsg_gpio_pin gpio_pins[IMX_RPMSG_GPIO_PER_PORT];
+ 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 imx_rpmsg_gpio_port *port_store[IMX_RPMSG_GPIO_PORT_PER_SOC_MAX];
+ struct mutex lock;
+};
+
+struct imx_rpmsg_gpio_work {
+ struct gpio_rpmsg_data *msg;
+ struct imx_rpmsg_gpio_port *port;
+ struct work_struct rpmsg_send_wq;
+};
+
+static struct imx_rpmsg_gpio_work imx_rpmsg_gpio_send_work;
+static struct workqueue_struct *imx_rpmsg_gpio_workqueue;
+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,
+ bool sync)
+{
+ int err;
+
+ if (!info->rpdev) {
+ dev_dbg(&info->rpdev->dev,
+ "rpmsg channel not ready, m4 image ready?\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&info->lock);
+ cpu_latency_qos_add_request(&info->pm_qos_req,
+ 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;
+ }
+
+ if (sync) {
+ 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->gpio_pins[info->reply_msg->pin_idx].msg,
+ info->reply_msg, sizeof(*info->reply_msg));
+
+ err = 0;
+ }
+
+err_out:
+ cpu_latency_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;
+ unsigned long flags;
+
+ if (msg->header.type == GPIO_RPMSG_REPLY) {
+ /* TBD: Add irq request_id check for A core msg */
+ gpio_rpmsg.reply_msg = msg;
+ complete(&gpio_rpmsg.cmd_complete);
+ } else if (msg->header.type == GPIO_RPMSG_NOTIFY) {
+ gpio_rpmsg.notify_msg = msg;
+ local_irq_save(flags);
+ generic_handle_irq(irq_find_mapping(gpio_rpmsg.port_store[msg->port_idx]->domain, msg->pin_idx));
+ local_irq_restore(flags);
+ } else
+ dev_err(&gpio_rpmsg.rpdev->dev, "wrong command type!\n");
+
+ return 0;
+}
+
+static struct gpio_rpmsg_data *gpio_get_pin_msg(struct imx_rpmsg_gpio_port *port, unsigned int offset)
+{
+ struct gpio_rpmsg_data *msg = &port->gpio_pins[offset].msg;
+
+ memset(msg, 0, sizeof(struct gpio_rpmsg_data));
+
+ return msg;
+};
+
+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 = NULL;
+ int ret;
+
+ msg = gpio_get_pin_msg(port, gpio);
+ 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, true);
+ if (!ret)
+ return !!port->gpio_pins[gpio].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 = NULL;
+
+ msg = gpio_get_pin_msg(port, gpio);
+ 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;
+
+ msg->out.event = GPIO_RPMSG_TRI_IGNORE;
+ msg->in.wakeup = 0;
+
+ return gpio_send_message(port, msg, &gpio_rpmsg, true);
+}
+
+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 = NULL;
+
+ msg = gpio_get_pin_msg(port, gpio);
+ imx_rpmsg_gpio_direction_output_init(gc, gpio, val, msg);
+ gpio_send_message(port, msg, &gpio_rpmsg, true);
+}
+
+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 = NULL;
+
+ msg = gpio_get_pin_msg(port, gpio);
+ imx_rpmsg_gpio_direction_output_init(gc, gpio, val, msg);
+ return gpio_send_message(port, msg, &gpio_rpmsg, true);
+}
+
+static int imx_rpmsg_irq_set_type(struct irq_data *d, u32 type)
+{
+ struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
+ u32 gpio_idx = d->hwirq;
+ int edge = 0;
+ int ret = 0;
+
+ switch (type) {
+ case IRQ_TYPE_EDGE_RISING:
+ edge = GPIO_RPMSG_TRI_RISING;
+ break;
+ case IRQ_TYPE_EDGE_FALLING:
+ edge = GPIO_RPMSG_TRI_FALLING;
+ break;
+ case IRQ_TYPE_EDGE_BOTH:
+ edge = GPIO_RPMSG_TRI_BOTH_EDGE;
+ break;
+ case IRQ_TYPE_LEVEL_LOW:
+ edge = GPIO_RPMSG_TRI_LOW_LEVEL;
+ break;
+ case IRQ_TYPE_LEVEL_HIGH:
+ edge = GPIO_RPMSG_TRI_HIGH_LEVEL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ port->gpio_pins[gpio_idx].irq_type = edge;
+ return ret;
+}
+
+static int imx_rpmsg_irq_set_wake(struct irq_data *d, u32 enable)
+{
+ struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct gpio_rpmsg_data *msg = NULL;
+ u32 gpio_idx = d->hwirq;
+
+ msg = gpio_get_pin_msg(port, gpio_idx);
+ 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_idx;
+ msg->port_idx = port->idx;
+
+ /* set wakeup trigger source,
+ * if not set irq type, then use high level as trigger type
+ */
+ msg->out.event = port->gpio_pins[gpio_idx].irq_type;
+ if (!msg->out.event)
+ msg->out.event = GPIO_RPMSG_TRI_LOW_LEVEL;
+
+ msg->in.wakeup = enable;
+
+ /* here should be atomic context */
+ gpio_send_message(port, msg, &gpio_rpmsg, false);
+
+ return 0;
+}
+
+void imx_rpmsg_gpio_do_send(struct work_struct *w)
+{
+ struct imx_rpmsg_gpio_work *gpio_send_work =
+ container_of(w, struct imx_rpmsg_gpio_work, rpmsg_send_wq);
+
+ gpio_send_message(gpio_send_work->port,
+ gpio_send_work->msg, &gpio_rpmsg, false);
+}
+
+/*
+ * This function will be called at:
+ * - one interrupt setup.
+ * - the end of one interrupt happened
+ * The gpio over rpmsg driver will not write the real register, so save
+ * all infos before this function and then send all infos to M core in this
+ * step.
+ */
+static void imx_rpmsg_unmask_irq(struct irq_data *d)
+{
+ struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct gpio_rpmsg_data *msg = NULL;
+ u32 gpio_idx = d->hwirq;
+
+ msg = gpio_get_pin_msg(port, gpio_idx);
+ 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_idx;
+ msg->port_idx = port->idx;
+
+ /*
+ * set wakeup trigger source,
+ * if not set irq type, then use high level as trigger type
+ */
+ msg->out.event = port->gpio_pins[gpio_idx].irq_type;
+ if (!msg->out.event)
+ msg->out.event = GPIO_RPMSG_TRI_LOW_LEVEL;
+
+ msg->in.wakeup = 0;
+
+ imx_rpmsg_gpio_send_work.msg = msg;
+ imx_rpmsg_gpio_send_work.port = port;
+
+ queue_work(imx_rpmsg_gpio_workqueue, &(imx_rpmsg_gpio_send_work.rpmsg_send_wq));
+}
+
+static void imx_rpmsg_mask_irq(struct irq_data *d)
+{
+ /*
+ * No need to implement the callback at A core side.
+ * M core will mask interrupt after a interrupt occurred, and then
+ * sends a notify to A core.
+ * After A core dealt with the notify, A core will send a rpmsg to
+ * M core to enable this interrupt again.
+ */
+}
+
+static void imx_rpmsg_irq_shutdown(struct irq_data *d)
+{
+ struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
+ struct gpio_rpmsg_data *msg = NULL;
+ u32 gpio_idx = d->hwirq;
+
+ msg = gpio_get_pin_msg(port, gpio_idx);
+ 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_idx;
+ msg->port_idx = port->idx;
+
+ /* Disable interrupt here */
+ msg->out.event = GPIO_RPMSG_TRI_IGNORE;
+ msg->in.wakeup = 0;
+
+ imx_rpmsg_gpio_send_work.msg = msg;
+ imx_rpmsg_gpio_send_work.port = port;
+
+ queue_work(imx_rpmsg_gpio_workqueue, &(imx_rpmsg_gpio_send_work.rpmsg_send_wq));
+}
+
+static struct irq_chip imx_rpmsg_irq_chip = {
+ .irq_mask = imx_rpmsg_mask_irq,
+ .irq_unmask = imx_rpmsg_unmask_irq,
+ .irq_set_wake = imx_rpmsg_irq_set_wake,
+ .irq_set_type = imx_rpmsg_irq_set_type,
+ .irq_shutdown = imx_rpmsg_irq_shutdown,
+ /* TBD: Add .irq_disable support */
+};
+
+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 i, irq_base;
+ 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;
+
+ gpio_rpmsg.port_store[port->idx] = port;
+
+ gc = &port->gc;
+ gc->of_node = np;
+ gc->parent = dev;
+ gc->label = kasprintf(GFP_KERNEL, "imx-rpmsg-gpio-%d", port->idx);
+ 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;
+
+ /* generate one new irq domain */
+ port->chip = imx_rpmsg_irq_chip;
+ port->chip.name = kasprintf(GFP_KERNEL, "rpmsg-irq-port-%d", port->idx);
+ port->chip.parent_device = NULL;
+
+ irq_base = irq_alloc_descs(-1, 0, IMX_RPMSG_GPIO_PER_PORT,
+ numa_node_id());
+ WARN_ON(irq_base < 0);
+
+ port->domain = irq_domain_add_legacy(np, IMX_RPMSG_GPIO_PER_PORT,
+ irq_base, 0,
+ &irq_domain_simple_ops, port);
+ WARN_ON(!port->domain);
+ for (i = irq_base; i < irq_base + IMX_RPMSG_GPIO_PER_PORT; i++) {
+ irq_set_chip_and_handler(i, &port->chip, handle_level_irq);
+ irq_set_chip_data(i, port);
+ irq_clear_status_flags(i, IRQ_NOREQUEST);
+ irq_set_probe(i);
+ }
+
+ imx_rpmsg_gpio_workqueue = create_workqueue("imx_rpmsg_gpio_workqueue");
+ if (!imx_rpmsg_gpio_workqueue)
+ dev_err(&pdev->dev, "Failed to create imx_rpmsg_gpio_workqueue\n");
+ INIT_WORK(&(imx_rpmsg_gpio_send_work.rpmsg_send_wq), imx_rpmsg_gpio_do_send);
+
+ 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 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 platform_driver_register(&imx_rpmsg_gpio_driver);
+}
+
+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 __init gpio_imx_rpmsg_init(void)
+{
+ return register_rpmsg_driver(&gpio_rpmsg_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/gpio/gpio-max732x.c b/drivers/gpio/gpio-max732x.c
index 238cbe926b9f..4af7ea075fb5 100644
--- a/drivers/gpio/gpio-max732x.c
+++ b/drivers/gpio/gpio-max732x.c
@@ -19,6 +19,7 @@
#include <linux/i2c.h>
#include <linux/platform_data/max732x.h>
#include <linux/of.h>
+#include <linux/reset.h>
/*
@@ -77,6 +78,12 @@
#define INT_CAPS(x) (((uint64_t)(x)) << 32)
enum {
+ OUTPUT_MASK,
+ OUTPUT_VAL,
+ OUTPUT_NUM,
+};
+
+enum {
MAX7319,
MAX7320,
MAX7321,
@@ -622,6 +629,8 @@ static int max732x_probe(struct i2c_client *client,
struct i2c_client *c;
uint16_t addr_a, addr_b;
int ret, nr_port;
+ u16 out_set[OUTPUT_NUM];
+ unsigned long mask, val;
pdata = dev_get_platdata(&client->dev);
node = client->dev.of_node;
@@ -639,6 +648,10 @@ static int max732x_probe(struct i2c_client *client,
return -ENOMEM;
chip->client = client;
+ ret = device_reset(&client->dev);
+ if (ret == -EPROBE_DEFER)
+ return ret;
+
nr_port = max732x_setup_gpio(chip, id, pdata->gpio_base);
chip->gpio_chip.parent = &client->dev;
@@ -711,6 +724,15 @@ static int max732x_probe(struct i2c_client *client,
}
i2c_set_clientdata(client, chip);
+
+ /* set the output IO default voltage */
+ if (!of_property_read_u16_array(node, "out-default", out_set,
+ ARRAY_SIZE(out_set))) {
+ mask = out_set[OUTPUT_MASK] & chip->dir_output;
+ val = out_set[OUTPUT_VAL];
+ max732x_gpio_set_multiple(&chip->gpio_chip, &mask, &val);
+ }
+
return 0;
}
diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c
index 70d6ae20b1da..d8532c448976 100644
--- a/drivers/gpio/gpio-mpc8xxx.c
+++ b/drivers/gpio/gpio-mpc8xxx.c
@@ -432,6 +432,16 @@ static int mpc8xxx_remove(struct platform_device *pdev)
return 0;
}
+static void mpc8xxx_shutdown(struct platform_device *pdev)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = platform_get_drvdata(pdev);
+
+ if (mpc8xxx_gc->irq) {
+ irq_set_chained_handler_and_data(mpc8xxx_gc->irqn, NULL, NULL);
+ irq_domain_remove(mpc8xxx_gc->irq);
+ }
+}
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id gpio_acpi_ids[] = {
{"NXP0031",},
@@ -443,6 +453,7 @@ MODULE_DEVICE_TABLE(acpi, gpio_acpi_ids);
static struct platform_driver mpc8xxx_plat_driver = {
.probe = mpc8xxx_probe,
.remove = mpc8xxx_remove,
+ .shutdown = mpc8xxx_shutdown,
.driver = {
.name = "gpio-mpc8xxx",
.of_match_table = mpc8xxx_gpio_ids,
diff --git a/drivers/gpio/gpio-mxc.c b/drivers/gpio/gpio-mxc.c
index c871602fc5ba..b0c0047c03c9 100644
--- a/drivers/gpio/gpio-mxc.c
+++ b/drivers/gpio/gpio-mxc.c
@@ -17,12 +17,47 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/syscore_ops.h>
#include <linux/gpio/driver.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/bug.h>
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+#include <linux/firmware/imx/sci.h>
+
+#define IMX_SC_PAD_FUNC_GET_WAKEUP 9
+#define IMX_SC_PAD_FUNC_SET_WAKEUP 4
+#define IMX_SC_PAD_WAKEUP_OFF 0
+#endif
+
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+struct mxc_gpio_pad_wakeup {
+ u32 pin_id;
+ u32 type;
+ u32 line;
+};
+
+struct imx_sc_msg_gpio_get_pad_wakeup {
+ struct imx_sc_rpc_msg hdr;
+ union {
+ struct req_pad {
+ u16 pad;
+ } __packed req;
+ struct resp_wakeup {
+ u8 wakeup;
+ } resp;
+ } data;
+} __packed __aligned(4);
+
+struct imx_sc_msg_gpio_set_pad_wakeup {
+ struct imx_sc_rpc_msg hdr;
+ u16 pad;
+ u8 wakeup;
+} __packed __aligned(4);
+
+#endif
/* device type dependent stuff */
struct mxc_gpio_hwdata {
@@ -62,8 +97,17 @@ struct mxc_gpio_port {
struct mxc_gpio_reg_saved gpio_saved_reg;
bool power_off;
const struct mxc_gpio_hwdata *hwdata;
+ bool gpio_ranges;
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+ u32 pad_wakeup_num;
+ struct mxc_gpio_pad_wakeup pad_wakeup[32];
+#endif
};
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+static struct imx_sc_ipc *gpio_ipc_handle;
+#endif
+
static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
.dr_reg = 0x1c,
.gdir_reg = 0x00,
@@ -285,6 +329,85 @@ static void mx2_gpio_irq_handler(struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+static int mxc_gpio_get_pad_wakeup(struct mxc_gpio_port *port)
+{
+ struct imx_sc_msg_gpio_get_pad_wakeup msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ u8 wakeup_type;
+ int ret;
+ int i;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_PAD;
+ hdr->func = IMX_SC_PAD_FUNC_GET_WAKEUP;
+ hdr->size = 2;
+
+ for (i = 0; i < port->pad_wakeup_num; i++) {
+ /* get original pad type */
+ wakeup_type = port->pad_wakeup[i].type;
+ msg.data.req.pad = port->pad_wakeup[i].pin_id;
+ ret = imx_scu_call_rpc(gpio_ipc_handle, &msg, true);
+ if (ret) {
+ dev_err(port->gc.parent, "get pad wakeup failed, ret %d\n", ret);
+ return ret;
+ }
+ wakeup_type = msg.data.resp.wakeup;
+ /* return wakeup gpio pin's line */
+ if (wakeup_type != port->pad_wakeup[i].type)
+ return port->pad_wakeup[i].line;
+ }
+
+ return -EINVAL;
+}
+
+static void mxc_gpio_set_pad_wakeup(struct mxc_gpio_port *port, bool enable)
+{
+ struct imx_sc_msg_gpio_set_pad_wakeup msg;
+ struct imx_sc_rpc_msg *hdr = &msg.hdr;
+ int ret;
+ int i;
+
+ hdr->ver = IMX_SC_RPC_VERSION;
+ hdr->svc = IMX_SC_RPC_SVC_PAD;
+ hdr->func = IMX_SC_PAD_FUNC_SET_WAKEUP;
+ hdr->size = 2;
+
+ for (i = 0; i < port->pad_wakeup_num; i++) {
+ msg.pad = port->pad_wakeup[i].pin_id;
+ msg.wakeup = enable ? port->pad_wakeup[i].type : IMX_SC_PAD_WAKEUP_OFF;
+ ret = imx_scu_call_rpc(gpio_ipc_handle, &msg, true);
+ if (ret) {
+ dev_err(port->gc.parent, "set pad wakeup failed, ret %d\n", ret);
+ return;
+ }
+ }
+}
+
+static void mxc_gpio_handle_pad_wakeup(struct mxc_gpio_port *port, int line)
+{
+ struct irq_desc *desc = irq_to_desc(port->irq);
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ u32 irq_stat;
+
+ /* skip invalid line */
+ if (line > 31) {
+ dev_err(port->gc.parent, "invalid wakeup line %d\n", line);
+ return;
+ }
+
+ dev_info(port->gc.parent, "wakeup by pad, line %d\n", line);
+
+ chained_irq_enter(chip, desc);
+
+ irq_stat = (1 << line);
+
+ mxc_gpio_irq_handler(port, irq_stat);
+
+ chained_irq_exit(chip, desc);
+}
+#endif
+
/*
* Set interrupt number "irq" in the GPIO as a wake-up source.
* While system is running, all registered GPIO interrupts need to have
@@ -316,7 +439,32 @@ static int gpio_set_wake_irq(struct irq_data *d, u32 enable)
return ret;
}
-static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
+static int mxc_gpio_irq_reqres(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+
+ if (gpiochip_lock_as_irq(&port->gc, d->hwirq)) {
+ dev_err(port->gc.parent,
+ "unable to lock HW IRQ %lu for IRQ\n",
+ d->hwirq);
+ return -EINVAL;
+ }
+
+ return irq_chip_pm_get(d);
+}
+
+static void mxc_gpio_irq_relres(struct irq_data *d)
+{
+ struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
+ struct mxc_gpio_port *port = gc->private;
+
+ gpiochip_unlock_as_irq(&port->gc, d->hwirq);
+ irq_chip_pm_put(d);
+}
+
+static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base,
+ struct device *dev)
{
struct irq_chip_generic *gc;
struct irq_chip_type *ct;
@@ -329,12 +477,15 @@ static int mxc_gpio_init_gc(struct mxc_gpio_port *port, int irq_base)
gc->private = port;
ct = gc->chip_types;
+ ct->chip.parent_device = dev;
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_clr_bit;
ct->chip.irq_unmask = irq_gc_mask_set_bit;
ct->chip.irq_set_type = gpio_set_irq_type;
ct->chip.irq_set_wake = gpio_set_wake_irq;
ct->chip.flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND;
+ ct->chip.irq_request_resources = mxc_gpio_irq_reqres;
+ ct->chip.irq_release_resources = mxc_gpio_irq_relres,
ct->regs.ack = GPIO_ISR;
ct->regs.mask = GPIO_IMR;
@@ -352,6 +503,30 @@ static int mxc_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
return irq_find_mapping(port->domain, offset);
}
+static int mxc_gpio_request(struct gpio_chip *chip, unsigned offset)
+{
+ struct mxc_gpio_port *port = gpiochip_get_data(chip);
+ int ret;
+
+ if (port->gpio_ranges) {
+ ret = gpiochip_generic_request(chip, offset);
+ if (ret)
+ return ret;
+ }
+
+ ret = pm_runtime_get_sync(chip->parent);
+ return ret < 0 ? ret : 0;
+}
+
+static void mxc_gpio_free(struct gpio_chip *chip, unsigned offset)
+{
+ struct mxc_gpio_port *port = gpiochip_get_data(chip);
+
+ if (port->gpio_ranges)
+ gpiochip_generic_free(chip, offset);
+ pm_runtime_put(chip->parent);
+}
+
static int mxc_gpio_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -359,6 +534,9 @@ static int mxc_gpio_probe(struct platform_device *pdev)
int irq_count;
int irq_base;
int err;
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+ int i;
+#endif
port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
if (!port)
@@ -377,7 +555,7 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return irq_count;
if (irq_count > 1) {
- port->irq_high = platform_get_irq(pdev, 1);
+ port->irq_high = platform_get_irq_optional(pdev, 1);
if (port->irq_high < 0)
port->irq_high = 0;
}
@@ -397,9 +575,44 @@ static int mxc_gpio_probe(struct platform_device *pdev)
return err;
}
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+ /*
+ * parse pad wakeup info from dtb, each pad has to provide
+ * <pin_id, type, line>, these info should be put in each
+ * gpio node and with a "pad-wakeup-num" to indicate the
+ * total lines are with pad wakeup enabled.
+ */
+ if (!of_property_read_u32(np, "pad-wakeup-num", &port->pad_wakeup_num)) {
+ if (port->pad_wakeup_num != 0) {
+ if (!gpio_ipc_handle) {
+ err = imx_scu_get_handle(&gpio_ipc_handle);
+ if (err)
+ return err;
+ }
+ for (i = 0; i < port->pad_wakeup_num; i++) {
+ of_property_read_u32_index(np, "pad-wakeup",
+ i * 3 + 0, &port->pad_wakeup[i].pin_id);
+ of_property_read_u32_index(np, "pad-wakeup",
+ i * 3 + 1, &port->pad_wakeup[i].type);
+ of_property_read_u32_index(np, "pad-wakeup",
+ i * 3 + 2, &port->pad_wakeup[i].line);
+ }
+ err = imx_scu_irq_group_enable(IMX_SC_IRQ_GROUP_WAKE, IMX_SC_IRQ_PAD, true);
+ if (err)
+ dev_warn(&pdev->dev, "Enable irq failed, GPIO pad wakeup NOT supported\n");
+ }
+ }
+#endif
+
if (of_device_is_compatible(np, "fsl,imx7d-gpio"))
port->power_off = true;
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+ err = pm_runtime_get_sync(&pdev->dev);
+ if (err < 0)
+ goto out_pm_dis;
+
/* disable the interrupt and clear the status */
writel(0, port->base + GPIO_IMR);
writel(~0, port->base + GPIO_ISR);
@@ -430,8 +643,14 @@ static int mxc_gpio_probe(struct platform_device *pdev)
if (err)
goto out_bgio;
- port->gc.request = gpiochip_generic_request;
- port->gc.free = gpiochip_generic_free;
+ if (of_property_read_bool(np, "gpio_ranges"))
+ port->gpio_ranges = true;
+ else
+ port->gpio_ranges = false;
+
+ port->gc.request = mxc_gpio_request;
+ port->gc.free = mxc_gpio_free;
+ port->gc.parent = &pdev->dev;
port->gc.to_irq = mxc_gpio_to_irq;
port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
pdev->id * 32;
@@ -454,16 +673,20 @@ static int mxc_gpio_probe(struct platform_device *pdev)
}
/* gpio-mxc can be a generic irq chip */
- err = mxc_gpio_init_gc(port, irq_base);
+ err = mxc_gpio_init_gc(port, irq_base, &pdev->dev);
if (err < 0)
goto out_irqdomain_remove;
list_add_tail(&port->node, &mxc_gpio_ports);
platform_set_drvdata(pdev, port);
+ pm_runtime_put(&pdev->dev);
return 0;
+out_pm_dis:
+ pm_runtime_disable(&pdev->dev);
+ clk_disable_unprepare(port->clk);
out_irqdomain_remove:
irq_domain_remove(port->domain);
out_bgio:
@@ -498,12 +721,73 @@ static void mxc_gpio_restore_regs(struct mxc_gpio_port *port)
writel(port->gpio_saved_reg.dr, port->base + GPIO_DR);
}
+static int __maybe_unused mxc_gpio_runtime_suspend(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+ mxc_gpio_save_regs(port);
+ clk_disable_unprepare(port->clk);
+
+ return 0;
+}
+
+static int __maybe_unused mxc_gpio_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+ int ret;
+
+ ret = clk_prepare_enable(port->clk);
+ if (ret)
+ return ret;
+
+ mxc_gpio_restore_regs(port);
+
+ return 0;
+}
+
+static int __maybe_unused mxc_gpio_noirq_suspend(struct device *dev)
+{
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+
+ mxc_gpio_set_pad_wakeup(port, true);
+#endif
+ return 0;
+}
+
+static int __maybe_unused mxc_gpio_noirq_resume(struct device *dev)
+{
+#ifdef CONFIG_GPIO_MXC_PAD_WAKEUP
+ struct platform_device *pdev = to_platform_device(dev);
+ struct mxc_gpio_port *port = platform_get_drvdata(pdev);
+ int wakeup_line = mxc_gpio_get_pad_wakeup(port);
+
+ mxc_gpio_set_pad_wakeup(port, false);
+
+ if (wakeup_line >= 0)
+ mxc_gpio_handle_pad_wakeup(port, wakeup_line);
+#endif
+ return 0;
+}
+
+static const struct dev_pm_ops mxc_gpio_dev_pm_ops = {
+ SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mxc_gpio_noirq_suspend, mxc_gpio_noirq_resume)
+ SET_RUNTIME_PM_OPS(mxc_gpio_runtime_suspend, mxc_gpio_runtime_resume, NULL)
+};
+
static int mxc_gpio_syscore_suspend(void)
{
struct mxc_gpio_port *port;
+ int ret;
/* walk through all ports */
list_for_each_entry(port, &mxc_gpio_ports, node) {
+ ret = clk_prepare_enable(port->clk);
+ if (ret)
+ return ret;
mxc_gpio_save_regs(port);
clk_disable_unprepare(port->clk);
}
@@ -524,6 +808,7 @@ static void mxc_gpio_syscore_resume(void)
return;
}
mxc_gpio_restore_regs(port);
+ clk_disable_unprepare(port->clk);
}
}
@@ -537,6 +822,7 @@ static struct platform_driver mxc_gpio_driver = {
.name = "gpio-mxc",
.of_match_table = mxc_gpio_dt_ids,
.suppress_bind_attrs = true,
+ .pm = &mxc_gpio_dev_pm_ops,
},
.probe = mxc_gpio_probe,
};
diff --git a/drivers/gpio/gpio-pca953x.c b/drivers/gpio/gpio-pca953x.c
index d2fe76f3f34f..bf9288d307e5 100644
--- a/drivers/gpio/gpio-pca953x.c
+++ b/drivers/gpio/gpio-pca953x.c
@@ -20,6 +20,7 @@
#include <linux/platform_data/pca953x.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
#include <linux/slab.h>
#include <asm/unaligned.h>
@@ -982,14 +983,19 @@ static int pca953x_probe(struct i2c_client *client,
chip->client = client;
- reg = devm_regulator_get(&client->dev, "vcc");
- if (IS_ERR(reg))
- return dev_err_probe(&client->dev, PTR_ERR(reg), "reg get err\n");
+ reg = devm_regulator_get_optional(&client->dev, "vcc");
+ if (IS_ERR(reg)) {
+ if (PTR_ERR(reg) != -ENODEV)
+ return dev_err_probe(&client->dev, PTR_ERR(reg), "reg get err\n");
+ reg = NULL;
+ }
- ret = regulator_enable(reg);
- if (ret) {
- dev_err(&client->dev, "reg en err: %d\n", ret);
- return ret;
+ if (reg) {
+ ret = regulator_enable(reg);
+ if (ret) {
+ dev_err(&client->dev, "reg en err: %d\n", ret);
+ return ret;
+ }
}
chip->regulator = reg;
@@ -1047,6 +1053,10 @@ static int pca953x_probe(struct i2c_client *client,
lockdep_set_subclass(&chip->i2c_lock,
i2c_adapter_depth(client->adapter));
+ ret = device_reset(&client->dev);
+ if (ret == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
/* initialize cached registers from their original values.
* we can't share this chip with another i2c master.
*/
@@ -1079,7 +1089,8 @@ static int pca953x_probe(struct i2c_client *client,
return 0;
err_exit:
- regulator_disable(chip->regulator);
+ if (chip->regulator)
+ regulator_disable(chip->regulator);
return ret;
}
@@ -1098,7 +1109,8 @@ static int pca953x_remove(struct i2c_client *client)
ret = 0;
}
- regulator_disable(chip->regulator);
+ if (chip->regulator)
+ regulator_disable(chip->regulator);
return ret;
}
@@ -1159,7 +1171,8 @@ static int pca953x_suspend(struct device *dev)
if (atomic_read(&chip->wakeup_path))
device_set_wakeup_path(dev);
else
- regulator_disable(chip->regulator);
+ if (chip->regulator)
+ regulator_disable(chip->regulator);
return 0;
}
@@ -1169,15 +1182,20 @@ static int pca953x_resume(struct device *dev)
struct pca953x_chip *chip = dev_get_drvdata(dev);
int ret;
+ regcache_cache_only(chip->regmap, false);
+
if (!atomic_read(&chip->wakeup_path)) {
- ret = regulator_enable(chip->regulator);
- if (ret) {
- dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ if (chip->regulator) {
+ ret = regulator_enable(chip->regulator);
+ if (ret) {
+ dev_err(dev, "Failed to enable regulator: %d\n", ret);
+ return 0;
+ }
+ } else {
return 0;
}
}
- regcache_cache_only(chip->regmap, false);
regcache_mark_dirty(chip->regmap);
ret = pca953x_regcache_sync(dev);
if (ret)
diff --git a/drivers/gpio/gpio-scu.c b/drivers/gpio/gpio-scu.c
new file mode 100644
index 000000000000..5fb2d8b72653
--- /dev/null
+++ b/drivers/gpio/gpio-scu.c
@@ -0,0 +1,137 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright 2021 NXP
+ *
+ * The driver exports a standard gpiochip interface
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/module.h>
+#include <linux/gpio/driver.h>
+#include <linux/platform_device.h>
+#include <linux/firmware/imx/svc/rm.h>
+#include <dt-bindings/firmware/imx/rsrc.h>
+
+#define PIN_NUMBER 8
+
+struct imxscfw {
+ struct mutex lock;
+ struct imx_sc_ipc *handle;
+ struct gpio_chip chip;
+ struct device *dev;
+};
+
+static unsigned int sc_arr[] = {
+ IMX_SC_R_BOARD_R0,
+ IMX_SC_R_BOARD_R1,
+ IMX_SC_R_BOARD_R2,
+ IMX_SC_R_BOARD_R3,
+ IMX_SC_R_BOARD_R4,
+ IMX_SC_R_BOARD_R5,
+ IMX_SC_R_BOARD_R6, //R6 is MII select
+ IMX_SC_R_BOARD_R7,
+};
+
+static int imxscfw_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct imxscfw *scu = gpiochip_get_data(chip);
+ int err = -EINVAL, level = 0;
+
+ if (offset >= sizeof(sc_arr)/sizeof(unsigned int))
+ return err;
+
+ mutex_lock(&scu->lock);
+
+ /* to read PIN state via scu api */
+ err = imx_sc_misc_get_control(scu->handle, sc_arr[offset],
+ 0, &level);
+ mutex_unlock(&scu->lock);
+
+ if (err) {
+ pr_err("%s: failed %d\n", __func__, err);
+ return -EINVAL;
+ }
+
+ return level;
+}
+
+static void imxscfw_set(struct gpio_chip *chip, unsigned int offset, int value)
+{
+ struct imxscfw *scu = gpiochip_get_data(chip);
+ int err;
+
+ if (offset >= sizeof(sc_arr)/sizeof(unsigned int))
+ return;
+
+ mutex_lock(&scu->lock);
+
+ /* to set PIN output level via scu api */
+ err = imx_sc_misc_set_control(scu->handle, sc_arr[offset], 0, value);
+
+ mutex_unlock(&scu->lock);
+
+ if (err)
+ pr_err("%s: failed %d\n", __func__, err);
+
+
+}
+
+static int imx_scu_gpio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct imxscfw *port;
+ struct gpio_chip *gc;
+ int ret;
+
+ port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
+ if (!port)
+ return -ENOMEM;
+
+ ret = imx_scu_get_handle(&port->handle);
+ if (ret)
+ return ret;
+
+ mutex_init(&port->lock);
+ gc = &port->chip;
+ gc->of_node = np;
+ gc->parent = dev;
+ gc->label = "imx-scu-gpio";
+ gc->ngpio = PIN_NUMBER;
+ gc->base = of_alias_get_id(np, "gpio") * 32;
+
+ gc->get = imxscfw_get;
+ gc->set = imxscfw_set;
+
+ platform_set_drvdata(pdev, port);
+
+ ret = devm_gpiochip_add_data(dev, gc, port);
+
+ return ret;
+}
+
+static const struct of_device_id imx_scu_gpio_dt_ids[] = {
+ { .compatible = "fsl,imx-scu-gpio" },
+ { /* sentinel */ }
+};
+
+static struct platform_driver imx_scu_gpio_driver = {
+ .driver = {
+ .name = "gpio-imx-scu",
+ .of_match_table = imx_scu_gpio_dt_ids,
+ },
+ .probe = imx_scu_gpio_probe,
+};
+
+static int __init _imx_scu_gpio_init(void)
+{
+ return platform_driver_register(&imx_scu_gpio_driver);
+}
+
+subsys_initcall_sync(_imx_scu_gpio_init);
+
+MODULE_AUTHOR("Shenwei Wang");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("NXP GPIO over SCU-MISC API, i.MX8");
diff --git a/drivers/gpio/gpio-vf610.c b/drivers/gpio/gpio-vf610.c
index e0f2b67558e7..4c5ec52c723f 100644
--- a/drivers/gpio/gpio-vf610.c
+++ b/drivers/gpio/gpio-vf610.c
@@ -300,7 +300,7 @@ static int vf610_gpio_probe(struct platform_device *pdev)
gc = &port->gc;
gc->of_node = np;
gc->parent = dev;
- gc->label = "vf610-gpio";
+ gc->label = dev_name(dev);
gc->ngpio = VF610_GPIO_PER_PORT;
gc->base = of_alias_get_id(np, "gpio") * VF610_GPIO_PER_PORT;