diff --git a/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt b/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt index 49c7d321e2bf..25001da92da5 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-imx-rpmsg.txt @@ -10,6 +10,13 @@ Required properties: 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. @@ -26,6 +33,9 @@ rpmsg_gpio0: rpmsg-gpio0 { port_idx = <0>; gpio-controller; #gpio-cells = <2>; + #interrupt-cells = <2>; + interrupt-controller; + interrupt-parent = <&rpmsg_gpio0>; status = "okay"; }; @@ -34,5 +44,14 @@ rpmsg_gpio1: rpmsg-gpio1 { 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/drivers/gpio/gpio-imx-rpmsg.c b/drivers/gpio/gpio-imx-rpmsg.c index 9bfd1880fc57..092c290942ba 100644 --- a/drivers/gpio/gpio-imx-rpmsg.c +++ b/drivers/gpio/gpio-imx-rpmsg.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -66,7 +67,10 @@ struct gpio_rpmsg_data { struct imx_rpmsg_gpio_port { struct gpio_chip gc; + struct irq_chip chip; + struct irq_domain *domain; struct gpio_rpmsg_data msg; + u32 irq_type[IMX_RPMSG_GPIO_PER_PORT]; int idx; }; @@ -82,7 +86,9 @@ struct imx_gpio_rpmsg_info { 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) + struct gpio_rpmsg_data *msg, + struct imx_gpio_rpmsg_info *info, + bool sync) { int err; @@ -106,26 +112,28 @@ static int gpio_send_message(struct imx_rpmsg_gpio_port *port, 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 (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->msg, info->reply_msg, sizeof(*info->reply_msg)); + + err = 0; } - 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); @@ -165,7 +173,7 @@ static int imx_rpmsg_gpio_get(struct gpio_chip *gc, unsigned int gpio) msg.pin_idx = gpio; msg.port_idx = port->idx; - ret = gpio_send_message(port, &msg, &gpio_rpmsg); + ret = gpio_send_message(port, &msg, &gpio_rpmsg, true); if (!ret) return !!port->msg.in.value; @@ -191,7 +199,7 @@ static int imx_rpmsg_gpio_direction_input(struct gpio_chip *gc, msg.out.event = GPIO_RPMSG_TRI_IGNORE; msg.in.wakeup = 0; - return gpio_send_message(port, &msg, &gpio_rpmsg); + return gpio_send_message(port, &msg, &gpio_rpmsg, true); } static inline void imx_rpmsg_gpio_direction_output_init(struct gpio_chip *gc, @@ -216,7 +224,7 @@ static void imx_rpmsg_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val) 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); + gpio_send_message(port, &msg, &gpio_rpmsg, true); } static int imx_rpmsg_gpio_direction_output(struct gpio_chip *gc, @@ -227,7 +235,7 @@ static int imx_rpmsg_gpio_direction_output(struct gpio_chip *gc, 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); + return gpio_send_message(port, &msg, &gpio_rpmsg, true); } static int gpio_rpmsg_probe(struct rpmsg_device *rpdev) @@ -255,12 +263,92 @@ static struct rpmsg_driver gpio_rpmsg_driver = { .callback = gpio_rpmsg_cb, }; +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->irq_type[gpio_idx] = 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; + u32 gpio_idx = d->hwirq; + + 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_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->irq_type[gpio_idx]; + if (!msg.out.event) + msg.out.event = GPIO_RPMSG_TRI_HIGH_LEVEL; + + msg.in.wakeup = enable; + + /* here should be atomic context */ + gpio_send_message(port, &msg, &gpio_rpmsg, false); + + return 0; +} + +static void imx_rpmsg_unmask_irq(struct irq_data *d) +{ + /* No need to implement the callback */ +} + +static void imx_rpmsg_mask_irq(struct irq_data *d) +{ + /* No need to implement the callback */ +} + +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, +}; + 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); @@ -289,6 +377,26 @@ static int imx_rpmsg_gpio_probe(struct platform_device *pdev) 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); + } + return 0; }