* [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board
@ 2021-10-13 14:03 Santosh Kumar Yadav
2021-10-19 14:50 ` Hans de Goede
2021-10-20 12:36 ` [PATCH v2] " Peter Korsgaard
0 siblings, 2 replies; 9+ messages in thread
From: Santosh Kumar Yadav @ 2021-10-13 14:03 UTC (permalink / raw)
To: santoshyadav30
Cc: santoshkumar.yadav, Peter Korsgaard, Hans de Goede, Mark Gross,
Peter Korsgaard, linux-kernel, platform-driver-x86
Add a driver providing access to the GPIOs for the identify button and led
present on Barco P50 board, based on the pcengines-apuv2.c driver.
There is unfortunately no suitable ACPI entry for the EC communication
interface, so instead bind to boards with "P50" as their DMI product family
and hard code the I/O port number (0x299).
The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the
GPIOs, so they are finally exposed as:
LED:
/sys/class/leds/identify
Button: (/proc/bus/input/devices)
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="identify"
P: Phys=gpio-keys-polled/input0
S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10
U: Uniq=
H: Handlers=event10
B: PROP=0
B: EV=3
B: KEY=1000000 0 0 0 0 0 0
Signed-off-by: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
---
MAINTAINERS | 6 +
drivers/platform/x86/Kconfig | 10 +
drivers/platform/x86/Makefile | 3 +
drivers/platform/x86/barco-p50-gpio.c | 427 ++++++++++++++++++++++++++
4 files changed, 446 insertions(+)
create mode 100644 drivers/platform/x86/barco-p50-gpio.c
diff --git a/MAINTAINERS b/MAINTAINERS
index a4a0c2baaf27..604f544a9e17 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3220,6 +3220,12 @@ F: drivers/video/backlight/
F: include/linux/backlight.h
F: include/linux/pwm_backlight.h
+BARCO P50 GPIO DRIVER
+M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
+M: Peter Korsgaard <peter.korsgaard@barco.com>
+S: Maintained
+F: drivers/platform/x86/barco-p50-gpio.c
+
BATMAN ADVANCED
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <sw@simonwunderlich.de>
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index e21ea3d23e6f..42b4895e4acc 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -713,6 +713,16 @@ config PCENGINES_APU2
To compile this driver as a module, choose M here: the module
will be called pcengines-apuv2.
+config BARCO_P50_GPIO
+ tristate "Barco P50 GPIO driver for identify LED/button"
+ depends on GPIOLIB
+ help
+ This driver provides access to the GPIOs for the identify button
+ and led present on Barco P50 board.
+
+ To compile this driver as a module, choose M here: the module
+ will be called barco-p50-gpio.
+
config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
depends on RFKILL || RFKILL = n
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 69690e26bb6d..e1acfc03301f 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -80,6 +80,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
# PC Engines
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
+# Barco
+obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
+
# Samsung
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
new file mode 100644
index 000000000000..1629cfbb85db
--- /dev/null
+++ b/drivers/platform/x86/barco-p50-gpio.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Support for EC-connected GPIOs for identify
+ * LED/button on Barco P50 board
+ *
+ * Copyright (C) 2021 Barco NV
+ * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_keys.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/input.h>
+
+
+#define DRIVER_NAME "barco-p50-gpio"
+
+/* GPIO lines */
+#define P50_GPIO_LINE_LED 0
+#define P50_GPIO_LINE_BTN 1
+
+/* GPIO IO Ports */
+#define P50_GPIO_IO_PORT_BASE 0x299
+
+#define P50_PORT_DATA 0x00
+#define P50_PORT_CMD 0x01
+
+#define P50_STATUS_OBF 0x01 /* EC output buffer full */
+#define P50_STATUS_IBF 0x02 /* EC input buffer full */
+
+#define P50_CMD_READ 0xa0
+#define P50_CMD_WRITE 0x50
+
+/* EC mailbox registers */
+#define P50_MBOX_REG_CMD 0x00
+#define P50_MBOX_REG_STATUS 0x01
+#define P50_MBOX_REG_PARAM 0x02
+#define P50_MBOX_REG_DATA 0x03
+
+#define P50_MBOX_CMD_READ_GPIO 0x11
+#define P50_MBOX_CMD_WRITE_GPIO 0x12
+#define P50_MBOX_CMD_CLEAR 0xff
+
+#define P50_MBOX_STATUS_SUCCESS 0x01
+
+#define P50_MBOX_PARAM_LED 0x12
+#define P50_MBOX_PARAM_BTN 0x13
+
+
+struct p50_gpio {
+ struct gpio_chip gc;
+ struct mutex lock;
+ unsigned long base;
+ struct platform_device *leds_pdev;
+ struct platform_device *keys_pdev;
+};
+
+static struct platform_device *gpio_pdev;
+
+static int gpio_params[] = {
+ [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
+ [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
+};
+
+static const char * const gpio_names[] = {
+ [P50_GPIO_LINE_LED] = "identify-led",
+ [P50_GPIO_LINE_BTN] = "identify-button",
+};
+
+
+static struct gpiod_lookup_table p50_gpio_led_table = {
+ .dev_id = "leds-gpio",
+ .table = {
+ GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
+ {}
+ }
+};
+
+
+/* low level access routines */
+
+static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
+{
+ int i, val;
+
+ for (i = 0; i < 100; i++) {
+ val = inb(p50->base + P50_PORT_CMD) & mask;
+ if (val == expected)
+ return 0;
+ usleep_range(500, 2000);
+ }
+
+ dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
+ return -ETIMEDOUT;
+}
+
+
+static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
+{
+ int ret;
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* clear output buffer flag, prevent unfinished commands */
+ inb(p50->base + P50_PORT_DATA);
+
+ /* cmd/address */
+ outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
+
+ ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
+ if (ret)
+ return ret;
+
+ return inb(p50->base + P50_PORT_DATA);
+}
+
+static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
+{
+ int ret;
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* cmd/address */
+ outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* data */
+ outb(val, p50->base + P50_PORT_DATA);
+
+ return 0;
+}
+
+
+/* mbox routines */
+
+static int p50_wait_mbox_idle(struct p50_gpio *p50)
+{
+ int i, val;
+
+ for (i = 0; i < 1000; i++) {
+ val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
+ /* cmd is 0 when idle */
+ if (val <= 0)
+ return val;
+
+ usleep_range(500, 2000);
+ }
+
+ dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
+
+ return -ETIMEDOUT;
+}
+
+static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
+{
+ int ret;
+
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
+ if (ret)
+ return ret;
+
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+ ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (ret == P50_MBOX_STATUS_SUCCESS)
+ return 0;
+
+ dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
+ cmd, ret, param, data);
+
+ return -EIO;
+}
+
+
+/* gpio routines */
+
+static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ switch (offset) {
+ case P50_GPIO_LINE_BTN:
+ return GPIO_LINE_DIRECTION_IN;
+
+ case P50_GPIO_LINE_LED:
+ return GPIO_LINE_DIRECTION_OUT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct p50_gpio *p50 = gpiochip_get_data(gc);
+ int ret;
+
+ mutex_lock(&p50->lock);
+
+ ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
+ if (ret == 0)
+ ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
+
+ mutex_unlock(&p50->lock);
+
+ return ret;
+}
+
+static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct p50_gpio *p50 = gpiochip_get_data(gc);
+
+ mutex_lock(&p50->lock);
+
+ p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
+
+ mutex_unlock(&p50->lock);
+}
+
+static int p50_gpio_probe(struct platform_device *pdev)
+{
+ /* GPIO LEDs */
+ struct gpio_led leds[] = {
+ { .name = "identify" }
+ };
+
+ struct gpio_led_platform_data leds_pdata = {
+ .num_leds = ARRAY_SIZE(leds),
+ .leds = leds,
+ };
+
+ /* GPIO keyboard */
+ struct gpio_keys_button buttons[] = {
+ {
+ .code = KEY_RESTART,
+ .gpio = P50_GPIO_LINE_BTN,
+ .active_low = 1,
+ .type = EV_KEY,
+ .value = 1,
+ },
+ };
+
+ struct gpio_keys_platform_data keys_pdata = {
+ .buttons = buttons,
+ .nbuttons = ARRAY_SIZE(buttons),
+ .poll_interval = 100,
+ .rep = 0,
+ .name = "identify",
+ };
+
+ struct p50_gpio *p50;
+ struct resource *res;
+ int ret;
+
+ p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
+ if (!p50)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Cannot get I/O ports\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "Unable to reserve I/O region\n");
+ return -EBUSY;
+ }
+
+ p50->base = res->start;
+
+ ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_CLEAR, 0, 0);
+ if (ret < 0)
+ return -ENODEV;
+
+ p50->gc.owner = THIS_MODULE;
+ p50->gc.parent = &pdev->dev;
+ p50->gc.label = dev_name(&pdev->dev);
+ p50->gc.ngpio = ARRAY_SIZE(gpio_names);
+ p50->gc.names = gpio_names;
+ p50->gc.can_sleep = true;
+ p50->gc.base = -1;
+ p50->gc.get_direction = p50_gpio_get_direction;
+ p50->gc.get = p50_gpio_get;
+ p50->gc.set = p50_gpio_set;
+
+ mutex_init(&p50->lock);
+
+ platform_set_drvdata(pdev, p50);
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_add_lookup_table(&p50_gpio_led_table);
+
+ p50->leds_pdev = platform_device_register_data(&pdev->dev,
+ "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
+
+ if (IS_ERR(p50->leds_pdev)) {
+ ret = PTR_ERR(p50->leds_pdev);
+ dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
+ goto err_leds;
+ }
+
+ /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
+ buttons[0].gpio += p50->gc.base;
+
+ p50->keys_pdev =
+ platform_device_register_data(&pdev->dev, "gpio-keys-polled",
+ PLATFORM_DEVID_NONE,
+ &keys_pdata, sizeof(keys_pdata));
+
+ if (IS_ERR(p50->keys_pdev)) {
+ ret = PTR_ERR(p50->keys_pdev);
+ dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
+ goto err_keys;
+ }
+
+ return 0;
+
+err_keys:
+ platform_device_unregister(p50->leds_pdev);
+err_leds:
+ gpiod_remove_lookup_table(&p50_gpio_led_table);
+
+ return ret;
+}
+
+static int p50_gpio_remove(struct platform_device *pdev)
+{
+ struct p50_gpio *p50 = platform_get_drvdata(pdev);
+
+ platform_device_unregister(p50->keys_pdev);
+ platform_device_unregister(p50->leds_pdev);
+
+ gpiod_remove_lookup_table(&p50_gpio_led_table);
+
+ return 0;
+}
+
+static struct platform_driver p50_gpio_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = p50_gpio_probe,
+ .remove = p50_gpio_remove,
+};
+
+/* Board setup */
+static const struct dmi_system_id dmi_ids[] __initconst = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
+ },
+ },
+ {}
+};
+
+static int __init p50_module_init(void)
+{
+ struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
+
+ if (!dmi_first_match(dmi_ids))
+ return -ENODEV;
+
+ platform_driver_register(&p50_gpio_driver);
+
+ gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
+ if (IS_ERR(gpio_pdev)) {
+ pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
+ platform_driver_unregister(&p50_gpio_driver);
+ return PTR_ERR(gpio_pdev);
+ }
+
+ return 0;
+}
+
+static void __exit p50_module_exit(void)
+{
+ platform_device_unregister(gpio_pdev);
+ platform_driver_unregister(&p50_gpio_driver);
+}
+
+module_init(p50_module_init);
+module_exit(p50_module_exit);
+
+MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
+MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
+MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: platform:leds-gpio platform:gpio-keys-polled");
--
2.19.1
This message is subject to the following terms and conditions: MAIL DISCLAIMER<http://www.barco.com/en/maildisclaimer>
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board
2021-10-13 14:03 [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board Santosh Kumar Yadav
@ 2021-10-19 14:50 ` Hans de Goede
2021-10-19 15:14 ` Peter Korsgaard
2021-10-20 12:36 ` [PATCH v2] " Peter Korsgaard
1 sibling, 1 reply; 9+ messages in thread
From: Hans de Goede @ 2021-10-19 14:50 UTC (permalink / raw)
To: Santosh Kumar Yadav, santoshyadav30
Cc: Peter Korsgaard, Mark Gross, Peter Korsgaard, linux-kernel,
platform-driver-x86
Hi,
On 10/13/21 16:03, Santosh Kumar Yadav wrote:
> Add a driver providing access to the GPIOs for the identify button and led
> present on Barco P50 board, based on the pcengines-apuv2.c driver.
>
> There is unfortunately no suitable ACPI entry for the EC communication
> interface, so instead bind to boards with "P50" as their DMI product family
> and hard code the I/O port number (0x299).
>
> The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the
> GPIOs, so they are finally exposed as:
>
> LED:
> /sys/class/leds/identify
>
> Button: (/proc/bus/input/devices)
> I: Bus=0019 Vendor=0001 Product=0001 Version=0100
> N: Name="identify"
> P: Phys=gpio-keys-polled/input0
> S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10
> U: Uniq=
> H: Handlers=event10
> B: PROP=0
> B: EV=3
> B: KEY=1000000 0 0 0 0 0 0
>
> Signed-off-by: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
Thanks, overall this looks pretty good. I've a couple of comments inline,
please send a v2 addresing this.
> ---
> MAINTAINERS | 6 +
> drivers/platform/x86/Kconfig | 10 +
> drivers/platform/x86/Makefile | 3 +
> drivers/platform/x86/barco-p50-gpio.c | 427 ++++++++++++++++++++++++++
> 4 files changed, 446 insertions(+)
> create mode 100644 drivers/platform/x86/barco-p50-gpio.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index a4a0c2baaf27..604f544a9e17 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3220,6 +3220,12 @@ F: drivers/video/backlight/
> F: include/linux/backlight.h
> F: include/linux/pwm_backlight.h
>
> +BARCO P50 GPIO DRIVER
> +M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> +M: Peter Korsgaard <peter.korsgaard@barco.com>
> +S: Maintained
> +F: drivers/platform/x86/barco-p50-gpio.c
> +
> BATMAN ADVANCED
> M: Marek Lindner <mareklindner@neomailbox.ch>
> M: Simon Wunderlich <sw@simonwunderlich.de>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index e21ea3d23e6f..42b4895e4acc 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -713,6 +713,16 @@ config PCENGINES_APU2
> To compile this driver as a module, choose M here: the module
> will be called pcengines-apuv2.
>
> +config BARCO_P50_GPIO
> + tristate "Barco P50 GPIO driver for identify LED/button"
> + depends on GPIOLIB
> + help
> + This driver provides access to the GPIOs for the identify button
> + and led present on Barco P50 board.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called barco-p50-gpio.
> +
> config SAMSUNG_LAPTOP
> tristate "Samsung Laptop driver"
> depends on RFKILL || RFKILL = n
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 69690e26bb6d..e1acfc03301f 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -80,6 +80,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
> # PC Engines
> obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
>
> +# Barco
> +obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
> +
> # Samsung
> obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
> obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
> diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
> new file mode 100644
> index 000000000000..1629cfbb85db
> --- /dev/null
> +++ b/drivers/platform/x86/barco-p50-gpio.c
> @@ -0,0 +1,427 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/*
> + * Support for EC-connected GPIOs for identify
> + * LED/button on Barco P50 board
> + *
> + * Copyright (C) 2021 Barco NV
> + * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/dmi.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio_keys.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/input.h>
> +
> +
> +#define DRIVER_NAME "barco-p50-gpio"
> +
> +/* GPIO lines */
> +#define P50_GPIO_LINE_LED 0
> +#define P50_GPIO_LINE_BTN 1
> +
> +/* GPIO IO Ports */
> +#define P50_GPIO_IO_PORT_BASE 0x299
> +
> +#define P50_PORT_DATA 0x00
> +#define P50_PORT_CMD 0x01
> +
> +#define P50_STATUS_OBF 0x01 /* EC output buffer full */
> +#define P50_STATUS_IBF 0x02 /* EC input buffer full */
> +
> +#define P50_CMD_READ 0xa0
> +#define P50_CMD_WRITE 0x50
> +
> +/* EC mailbox registers */
> +#define P50_MBOX_REG_CMD 0x00
> +#define P50_MBOX_REG_STATUS 0x01
> +#define P50_MBOX_REG_PARAM 0x02
> +#define P50_MBOX_REG_DATA 0x03
> +
> +#define P50_MBOX_CMD_READ_GPIO 0x11
> +#define P50_MBOX_CMD_WRITE_GPIO 0x12
> +#define P50_MBOX_CMD_CLEAR 0xff
> +
> +#define P50_MBOX_STATUS_SUCCESS 0x01
> +
> +#define P50_MBOX_PARAM_LED 0x12
> +#define P50_MBOX_PARAM_BTN 0x13
> +
> +
> +struct p50_gpio {
> + struct gpio_chip gc;
> + struct mutex lock;
> + unsigned long base;
> + struct platform_device *leds_pdev;
> + struct platform_device *keys_pdev;
> +};
> +
> +static struct platform_device *gpio_pdev;
> +
> +static int gpio_params[] = {
> + [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
> + [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
> +};
> +
> +static const char * const gpio_names[] = {
> + [P50_GPIO_LINE_LED] = "identify-led",
> + [P50_GPIO_LINE_BTN] = "identify-button",
> +};
> +
> +
> +static struct gpiod_lookup_table p50_gpio_led_table = {
> + .dev_id = "leds-gpio",
> + .table = {
> + GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
> + {}
> + }
> +};
> +
> +
> +/* low level access routines */
> +
> +static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
> +{
> + int i, val;
> +
> + for (i = 0; i < 100; i++) {
> + val = inb(p50->base + P50_PORT_CMD) & mask;
> + if (val == expected)
> + return 0;
> + usleep_range(500, 2000);
> + }
> +
> + dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
> + return -ETIMEDOUT;
> +}
> +
> +
> +static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
> +{
> + int ret;
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* clear output buffer flag, prevent unfinished commands */
> + inb(p50->base + P50_PORT_DATA);
> +
> + /* cmd/address */
> + outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
> +
> + ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
> + if (ret)
> + return ret;
> +
> + return inb(p50->base + P50_PORT_DATA);
> +}
> +
> +static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
> +{
> + int ret;
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* cmd/address */
> + outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* data */
> + outb(val, p50->base + P50_PORT_DATA);
> +
> + return 0;
> +}
> +
> +
> +/* mbox routines */
> +
> +static int p50_wait_mbox_idle(struct p50_gpio *p50)
> +{
> + int i, val;
> +
> + for (i = 0; i < 1000; i++) {
> + val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
> + /* cmd is 0 when idle */
> + if (val <= 0)
> + return val;
> +
> + usleep_range(500, 2000);
> + }
> +
> + dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
> +{
> + int ret;
> +
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
> + if (ret)
> + return ret;
> +
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
> + if (ret < 0)
> + return ret;
> +
> + if (ret == P50_MBOX_STATUS_SUCCESS)
> + return 0;
> +
> + dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
> + cmd, ret, param, data);
> +
> + return -EIO;
> +}
> +
> +
> +/* gpio routines */
> +
> +static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> + switch (offset) {
> + case P50_GPIO_LINE_BTN:
> + return GPIO_LINE_DIRECTION_IN;
> +
> + case P50_GPIO_LINE_LED:
> + return GPIO_LINE_DIRECTION_OUT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
> +{
> + struct p50_gpio *p50 = gpiochip_get_data(gc);
> + int ret;
> +
> + mutex_lock(&p50->lock);
> +
> + ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
> + if (ret == 0)
> + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
> +
> + mutex_unlock(&p50->lock);
> +
> + return ret;
> +}
> +
> +static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> + struct p50_gpio *p50 = gpiochip_get_data(gc);
> +
> + mutex_lock(&p50->lock);
> +
> + p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
> +
> + mutex_unlock(&p50->lock);
> +}
> +
> +static int p50_gpio_probe(struct platform_device *pdev)
> +{
> + /* GPIO LEDs */
> + struct gpio_led leds[] = {
> + { .name = "identify" }
> + };
> +
> + struct gpio_led_platform_data leds_pdata = {
> + .num_leds = ARRAY_SIZE(leds),
> + .leds = leds,
> + };
> +
> + /* GPIO keyboard */
> + struct gpio_keys_button buttons[] = {
> + {
> + .code = KEY_RESTART,
> + .gpio = P50_GPIO_LINE_BTN,
> + .active_low = 1,
> + .type = EV_KEY,
> + .value = 1,
> + },
> + };
> +
> + struct gpio_keys_platform_data keys_pdata = {
> + .buttons = buttons,
> + .nbuttons = ARRAY_SIZE(buttons),
> + .poll_interval = 100,
> + .rep = 0,
> + .name = "identify",
> + };
> +
> + struct p50_gpio *p50;
> + struct resource *res;
> + int ret;
> +
> + p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
> + if (!p50)
> + return -ENOMEM;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "Cannot get I/O ports\n");
> + return -ENODEV;
> + }
> +
> + if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
> + dev_err(&pdev->dev, "Unable to reserve I/O region\n");
> + return -EBUSY;
> + }
> +
> + p50->base = res->start;
> +
> + ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_CLEAR, 0, 0);
> + if (ret < 0)
> + return -ENODEV;
> +
> + p50->gc.owner = THIS_MODULE;
> + p50->gc.parent = &pdev->dev;
> + p50->gc.label = dev_name(&pdev->dev);
> + p50->gc.ngpio = ARRAY_SIZE(gpio_names);
> + p50->gc.names = gpio_names;
> + p50->gc.can_sleep = true;
> + p50->gc.base = -1;
> + p50->gc.get_direction = p50_gpio_get_direction;
> + p50->gc.get = p50_gpio_get;
> + p50->gc.set = p50_gpio_set;
> +
> + mutex_init(&p50->lock);
> +
> + platform_set_drvdata(pdev, p50);
> +
> + ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
> + return ret;
> + }
> +
> + gpiod_add_lookup_table(&p50_gpio_led_table);
> +
> + p50->leds_pdev = platform_device_register_data(&pdev->dev,
> + "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
> +
> + if (IS_ERR(p50->leds_pdev)) {
> + ret = PTR_ERR(p50->leds_pdev);
> + dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
> + goto err_leds;
> + }
> +
> + /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
> + buttons[0].gpio += p50->gc.base;
> +
> + p50->keys_pdev =
> + platform_device_register_data(&pdev->dev, "gpio-keys-polled",
> + PLATFORM_DEVID_NONE,
> + &keys_pdata, sizeof(keys_pdata));
> +
> + if (IS_ERR(p50->keys_pdev)) {
> + ret = PTR_ERR(p50->keys_pdev);
> + dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
> + goto err_keys;
> + }
> +
> + return 0;
> +
> +err_keys:
> + platform_device_unregister(p50->leds_pdev);
> +err_leds:
> + gpiod_remove_lookup_table(&p50_gpio_led_table);
> +
> + return ret;
> +}
> +
> +static int p50_gpio_remove(struct platform_device *pdev)
> +{
> + struct p50_gpio *p50 = platform_get_drvdata(pdev);
> +
> + platform_device_unregister(p50->keys_pdev);
> + platform_device_unregister(p50->leds_pdev);
> +
> + gpiod_remove_lookup_table(&p50_gpio_led_table);
> +
> + return 0;
> +}
> +
> +static struct platform_driver p50_gpio_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> + .probe = p50_gpio_probe,
> + .remove = p50_gpio_remove,
> +};
> +
> +/* Board setup */
> +static const struct dmi_system_id dmi_ids[] __initconst = {
> + {
> + .matches = {
> + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
> + },
> + },
But I'm a bit worried about the DMI match, it seems a bit too generic.
E.g. Lenovo also has a P50 laptop series.
For v2 please make the DMI match also on e.g. sys_vendor.
> + {}
> +};
You should put a:
MODULE_DEVICE_TABLE(dmi, dmi_ids);
here, this will add a dmi based modalias to the module, so that it will
be automatically loaded at boot on systems which match the dmi_ids table.
> +
> +static int __init p50_module_init(void)
> +{
> + struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
> +
> + if (!dmi_first_match(dmi_ids))
> + return -ENODEV;
> +
> + platform_driver_register(&p50_gpio_driver);
> +
> + gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
> + if (IS_ERR(gpio_pdev)) {
> + pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
> + platform_driver_unregister(&p50_gpio_driver);
> + return PTR_ERR(gpio_pdev);
> + }
> +
> + return 0;
> +}
> +
> +static void __exit p50_module_exit(void)
> +{
> + platform_device_unregister(gpio_pdev);
> + platform_driver_unregister(&p50_gpio_driver);
> +}
> +
> +module_init(p50_module_init);
> +module_exit(p50_module_exit);
> +
> +MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
> +MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
> +MODULE_LICENSE("GPL");
> +MODULE_SOFTDEP("pre: platform:leds-gpio platform:gpio-keys-polled");
Is this softdep really necessary ? I would expect things to work fine too if
the leds-gpio and gpio-keys-polled drivers are loaded automatically after
the platform_devices for them have been created .
Regards,
Hans
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board
2021-10-19 14:50 ` Hans de Goede
@ 2021-10-19 15:14 ` Peter Korsgaard
0 siblings, 0 replies; 9+ messages in thread
From: Peter Korsgaard @ 2021-10-19 15:14 UTC (permalink / raw)
To: Hans de Goede
Cc: Santosh Kumar Yadav, santoshyadav30, Mark Gross, Peter Korsgaard,
linux-kernel, platform-driver-x86
>>>>> "Hans" == Hans de Goede <hdegoede@redhat.com> writes:
> Hi,
> On 10/13/21 16:03, Santosh Kumar Yadav wrote:
>> Add a driver providing access to the GPIOs for the identify button and led
>> present on Barco P50 board, based on the pcengines-apuv2.c driver.
>>
>> There is unfortunately no suitable ACPI entry for the EC communication
>> interface, so instead bind to boards with "P50" as their DMI product family
>> and hard code the I/O port number (0x299).
>>
>> The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the
>> GPIOs, so they are finally exposed as:
>>
>> LED:
>> /sys/class/leds/identify
>>
>> Button: (/proc/bus/input/devices)
>> I: Bus=0019 Vendor=0001 Product=0001 Version=0100
>> N: Name="identify"
>> P: Phys=gpio-keys-polled/input0
>> S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10
>> U: Uniq=
>> H: Handlers=event10
>> B: PROP=0
>> B: EV=3
>> B: KEY=1000000 0 0 0 0 0 0
>>
>> Signed-off-by: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
>> Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
> Thanks, overall this looks pretty good. I've a couple of comments inline,
> please send a v2 addresing this.
..
>> +/* Board setup */
>> +static const struct dmi_system_id dmi_ids[] __initconst = {
>> + {
>> + .matches = {
>> + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
>> + },
>> + },
> But I'm a bit worried about the DMI match, it seems a bit too generic.
> E.g. Lenovo also has a P50 laptop series.
> For v2 please make the DMI match also on e.g. sys_vendor.
Agreed, will add a match on vendor = Barco.
> You should put a:
> MODULE_DEVICE_TABLE(dmi, dmi_ids);
> here, this will add a dmi based modalias to the module, so that it will
> be automatically loaded at boot on systems which match the dmi_ids table.
Ok.
>> +MODULE_SOFTDEP("pre: platform:leds-gpio platform:gpio-keys-polled");
> Is this softdep really necessary ? I would expect things to work fine too if
> the leds-gpio and gpio-keys-polled drivers are loaded automatically after
> the platform_devices for them have been created .
True. This was copied over from pcengines-apuv2.c, but we'll drop it for
v2.
--
Bye, Peter Korsgaard
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v2] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board
2021-10-13 14:03 [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board Santosh Kumar Yadav
2021-10-19 14:50 ` Hans de Goede
@ 2021-10-20 12:36 ` Peter Korsgaard
2021-10-20 14:39 ` Hans de Goede
1 sibling, 1 reply; 9+ messages in thread
From: Peter Korsgaard @ 2021-10-20 12:36 UTC (permalink / raw)
To: platform-driver-x86, hdegoede
Cc: mgross, Santosh Kumar Yadav, Peter Korsgaard
From: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
Add a driver providing access to the GPIOs for the identify button and led
present on Barco P50 board, based on the pcengines-apuv2.c driver.
There is unfortunately no suitable ACPI entry for the EC communication
interface, so instead bind to boards with "P50" as their DMI product family
and hard code the I/O port number (0x299).
The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the
GPIOs, so they are finally exposed as:
LED:
/sys/class/leds/identify
Button: (/proc/bus/input/devices)
I: Bus=0019 Vendor=0001 Product=0001 Version=0100
N: Name="identify"
P: Phys=gpio-keys-polled/input0
S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10
U: Uniq=
H: Handlers=event10
B: PROP=0
B: EV=3
B: KEY=1000000 0 0 0 0 0 0
Signed-off-by: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
---
v2: Match on DMI vendor as well, add module table, drop softdeps
MAINTAINERS | 6 +
drivers/platform/x86/Kconfig | 10 +
drivers/platform/x86/Makefile | 3 +
drivers/platform/x86/barco-p50-gpio.c | 436 ++++++++++++++++++++++++++
4 files changed, 455 insertions(+)
create mode 100644 drivers/platform/x86/barco-p50-gpio.c
diff --git a/MAINTAINERS b/MAINTAINERS
index 8d118d7957d2..60980f7cc29a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -3220,6 +3220,12 @@ F: drivers/video/backlight/
F: include/linux/backlight.h
F: include/linux/pwm_backlight.h
+BARCO P50 GPIO DRIVER
+M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
+M: Peter Korsgaard <peter.korsgaard@barco.com>
+S: Maintained
+F: drivers/platform/x86/barco-p50-gpio.c
+
BATMAN ADVANCED
M: Marek Lindner <mareklindner@neomailbox.ch>
M: Simon Wunderlich <sw@simonwunderlich.de>
diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
index e21ea3d23e6f..42b4895e4acc 100644
--- a/drivers/platform/x86/Kconfig
+++ b/drivers/platform/x86/Kconfig
@@ -713,6 +713,16 @@ config PCENGINES_APU2
To compile this driver as a module, choose M here: the module
will be called pcengines-apuv2.
+config BARCO_P50_GPIO
+ tristate "Barco P50 GPIO driver for identify LED/button"
+ depends on GPIOLIB
+ help
+ This driver provides access to the GPIOs for the identify button
+ and led present on Barco P50 board.
+
+ To compile this driver as a module, choose M here: the module
+ will be called barco-p50-gpio.
+
config SAMSUNG_LAPTOP
tristate "Samsung Laptop driver"
depends on RFKILL || RFKILL = n
diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
index 69690e26bb6d..931dc55f6f3e 100644
--- a/drivers/platform/x86/Makefile
+++ b/drivers/platform/x86/Makefile
@@ -80,6 +80,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
# PC Engines
obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
+# Barco
+obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
+
# Samsung
obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
new file mode 100644
index 000000000000..ca0b2564c407
--- /dev/null
+++ b/drivers/platform/x86/barco-p50-gpio.c
@@ -0,0 +1,436 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+/*
+ * Support for EC-connected GPIOs for identify
+ * LED/button on Barco P50 board
+ *
+ * Copyright (C) 2021 Barco NV
+ * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/dmi.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/gpio_keys.h>
+#include <linux/gpio/driver.h>
+#include <linux/gpio/machine.h>
+#include <linux/input.h>
+
+
+#define DRIVER_NAME "barco-p50-gpio"
+
+/* GPIO lines */
+#define P50_GPIO_LINE_LED 0
+#define P50_GPIO_LINE_BTN 1
+
+/* GPIO IO Ports */
+#define P50_GPIO_IO_PORT_BASE 0x299
+
+#define P50_PORT_DATA 0x00
+#define P50_PORT_CMD 0x01
+
+#define P50_STATUS_OBF 0x01 /* EC output buffer full */
+#define P50_STATUS_IBF 0x02 /* EC input buffer full */
+
+#define P50_CMD_READ 0xa0
+#define P50_CMD_WRITE 0x50
+
+/* EC mailbox registers */
+#define P50_MBOX_REG_CMD 0x00
+#define P50_MBOX_REG_STATUS 0x01
+#define P50_MBOX_REG_PARAM 0x02
+#define P50_MBOX_REG_DATA 0x03
+
+#define P50_MBOX_CMD_READ_GPIO 0x11
+#define P50_MBOX_CMD_WRITE_GPIO 0x12
+#define P50_MBOX_CMD_CLEAR 0xff
+
+#define P50_MBOX_STATUS_SUCCESS 0x01
+
+#define P50_MBOX_PARAM_LED 0x12
+#define P50_MBOX_PARAM_BTN 0x13
+
+
+struct p50_gpio {
+ struct gpio_chip gc;
+ struct mutex lock;
+ unsigned long base;
+ struct platform_device *leds_pdev;
+ struct platform_device *keys_pdev;
+};
+
+static struct platform_device *gpio_pdev;
+
+static int gpio_params[] = {
+ [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
+ [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
+};
+
+static const char * const gpio_names[] = {
+ [P50_GPIO_LINE_LED] = "identify-led",
+ [P50_GPIO_LINE_BTN] = "identify-button",
+};
+
+
+static struct gpiod_lookup_table p50_gpio_led_table = {
+ .dev_id = "leds-gpio",
+ .table = {
+ GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
+ {}
+ }
+};
+
+/* GPIO LEDs */
+static struct gpio_led leds[] = {
+ { .name = "identify" }
+};
+
+static struct gpio_led_platform_data leds_pdata = {
+ .num_leds = ARRAY_SIZE(leds),
+ .leds = leds,
+};
+
+/* GPIO keyboard */
+static struct gpio_keys_button buttons[] = {
+ {
+ .code = KEY_RESTART,
+ .gpio = P50_GPIO_LINE_BTN,
+ .active_low = 1,
+ .type = EV_KEY,
+ .value = 1,
+ },
+};
+
+static struct gpio_keys_platform_data keys_pdata = {
+ .buttons = buttons,
+ .nbuttons = ARRAY_SIZE(buttons),
+ .poll_interval = 100,
+ .rep = 0,
+ .name = "identify",
+};
+
+
+/* low level access routines */
+
+static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
+{
+ int i, val;
+
+ for (i = 0; i < 100; i++) {
+ val = inb(p50->base + P50_PORT_CMD) & mask;
+ if (val == expected)
+ return 0;
+ usleep_range(500, 2000);
+ }
+
+ dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
+ return -ETIMEDOUT;
+}
+
+
+static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
+{
+ int ret;
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* clear output buffer flag, prevent unfinished commands */
+ inb(p50->base + P50_PORT_DATA);
+
+ /* cmd/address */
+ outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
+
+ ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
+ if (ret)
+ return ret;
+
+ return inb(p50->base + P50_PORT_DATA);
+}
+
+static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
+{
+ int ret;
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* cmd/address */
+ outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
+
+ ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
+ if (ret)
+ return ret;
+
+ /* data */
+ outb(val, p50->base + P50_PORT_DATA);
+
+ return 0;
+}
+
+
+/* mbox routines */
+
+static int p50_wait_mbox_idle(struct p50_gpio *p50)
+{
+ int i, val;
+
+ for (i = 0; i < 1000; i++) {
+ val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
+ /* cmd is 0 when idle */
+ if (val <= 0)
+ return val;
+
+ usleep_range(500, 2000);
+ }
+
+ dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
+
+ return -ETIMEDOUT;
+}
+
+static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
+{
+ int ret;
+
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
+ if (ret)
+ return ret;
+
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+ ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (ret == P50_MBOX_STATUS_SUCCESS)
+ return 0;
+
+ dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
+ cmd, ret, param, data);
+
+ return -EIO;
+}
+
+
+/* gpio routines */
+
+static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
+{
+ switch (offset) {
+ case P50_GPIO_LINE_BTN:
+ return GPIO_LINE_DIRECTION_IN;
+
+ case P50_GPIO_LINE_LED:
+ return GPIO_LINE_DIRECTION_OUT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
+{
+ struct p50_gpio *p50 = gpiochip_get_data(gc);
+ int ret;
+
+ mutex_lock(&p50->lock);
+
+ ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
+ if (ret == 0)
+ ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
+
+ mutex_unlock(&p50->lock);
+
+ return ret;
+}
+
+static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
+{
+ struct p50_gpio *p50 = gpiochip_get_data(gc);
+
+ mutex_lock(&p50->lock);
+
+ p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
+
+ mutex_unlock(&p50->lock);
+}
+
+static int p50_gpio_probe(struct platform_device *pdev)
+{
+ struct p50_gpio *p50;
+ struct resource *res;
+ int ret;
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (!res) {
+ dev_err(&pdev->dev, "Cannot get I/O ports\n");
+ return -ENODEV;
+ }
+
+ if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
+ dev_err(&pdev->dev, "Unable to reserve I/O region\n");
+ return -EBUSY;
+ }
+
+ p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
+ if (!p50)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, p50);
+ mutex_init(&p50->lock);
+ p50->base = res->start;
+ p50->gc.owner = THIS_MODULE;
+ p50->gc.parent = &pdev->dev;
+ p50->gc.label = dev_name(&pdev->dev);
+ p50->gc.ngpio = ARRAY_SIZE(gpio_names);
+ p50->gc.names = gpio_names;
+ p50->gc.can_sleep = true;
+ p50->gc.base = -1;
+ p50->gc.get_direction = p50_gpio_get_direction;
+ p50->gc.get = p50_gpio_get;
+ p50->gc.set = p50_gpio_set;
+
+
+ /* reset mbox */
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+ ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR);
+ if (ret)
+ return ret;
+
+ ret = p50_wait_mbox_idle(p50);
+ if (ret)
+ return ret;
+
+
+ ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
+ return ret;
+ }
+
+ gpiod_add_lookup_table(&p50_gpio_led_table);
+
+ p50->leds_pdev = platform_device_register_data(&pdev->dev,
+ "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
+
+ if (IS_ERR(p50->leds_pdev)) {
+ ret = PTR_ERR(p50->leds_pdev);
+ dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
+ goto err_leds;
+ }
+
+ /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
+ buttons[0].gpio += p50->gc.base;
+
+ p50->keys_pdev =
+ platform_device_register_data(&pdev->dev, "gpio-keys-polled",
+ PLATFORM_DEVID_NONE,
+ &keys_pdata, sizeof(keys_pdata));
+
+ if (IS_ERR(p50->keys_pdev)) {
+ ret = PTR_ERR(p50->keys_pdev);
+ dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
+ goto err_keys;
+ }
+
+ return 0;
+
+err_keys:
+ platform_device_unregister(p50->leds_pdev);
+err_leds:
+ gpiod_remove_lookup_table(&p50_gpio_led_table);
+
+ return ret;
+}
+
+static int p50_gpio_remove(struct platform_device *pdev)
+{
+ struct p50_gpio *p50 = platform_get_drvdata(pdev);
+
+ platform_device_unregister(p50->keys_pdev);
+ platform_device_unregister(p50->leds_pdev);
+
+ gpiod_remove_lookup_table(&p50_gpio_led_table);
+
+ return 0;
+}
+
+static struct platform_driver p50_gpio_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = p50_gpio_probe,
+ .remove = p50_gpio_remove,
+};
+
+/* Board setup */
+static const struct dmi_system_id dmi_ids[] __initconst = {
+ {
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
+ },
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(dmi, dmi_ids);
+
+static int __init p50_module_init(void)
+{
+ struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
+
+ if (!dmi_first_match(dmi_ids))
+ return -ENODEV;
+
+ platform_driver_register(&p50_gpio_driver);
+
+ gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
+ if (IS_ERR(gpio_pdev)) {
+ pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
+ platform_driver_unregister(&p50_gpio_driver);
+ return PTR_ERR(gpio_pdev);
+ }
+
+ return 0;
+}
+
+static void __exit p50_module_exit(void)
+{
+ platform_device_unregister(gpio_pdev);
+ platform_driver_unregister(&p50_gpio_driver);
+}
+
+module_init(p50_module_init);
+module_exit(p50_module_exit);
+
+MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
+MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
+MODULE_LICENSE("GPL");
--
2.20.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH v2] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board
2021-10-20 12:36 ` [PATCH v2] " Peter Korsgaard
@ 2021-10-20 14:39 ` Hans de Goede
2021-10-22 12:46 ` [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART Peter Korsgaard
0 siblings, 1 reply; 9+ messages in thread
From: Hans de Goede @ 2021-10-20 14:39 UTC (permalink / raw)
To: Peter Korsgaard, platform-driver-x86; +Cc: mgross, Santosh Kumar Yadav
Hi,
On 10/20/21 14:36, Peter Korsgaard wrote:
> From: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
>
> Add a driver providing access to the GPIOs for the identify button and led
> present on Barco P50 board, based on the pcengines-apuv2.c driver.
>
> There is unfortunately no suitable ACPI entry for the EC communication
> interface, so instead bind to boards with "P50" as their DMI product family
> and hard code the I/O port number (0x299).
>
> The driver also hooks up the leds-gpio and gpio-keys-polled drivers to the
> GPIOs, so they are finally exposed as:
>
> LED:
> /sys/class/leds/identify
>
> Button: (/proc/bus/input/devices)
> I: Bus=0019 Vendor=0001 Product=0001 Version=0100
> N: Name="identify"
> P: Phys=gpio-keys-polled/input0
> S: Sysfs=/devices/platform/barco-p50-gpio/gpio-keys-polled/input/input10
> U: Uniq=
> H: Handlers=event10
> B: PROP=0
> B: EV=3
> B: KEY=1000000 0 0 0 0 0 0
>
> Signed-off-by: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
> ---
> v2: Match on DMI vendor as well, add module table, drop softdeps
Thank you for your patch, I've applied this patch to my review-hans
branch:
https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/log/?h=review-hans
Note it will show up in my review-hans branch once I've pushed my
local branch there, which might take a while.
Once I've run some tests on this branch the patches there will be
added to the platform-drivers-x86/for-next branch and eventually
will be included in the pdx86 pull-request to Linus for the next
merge-window.
Regards,
Hans
>
> MAINTAINERS | 6 +
> drivers/platform/x86/Kconfig | 10 +
> drivers/platform/x86/Makefile | 3 +
> drivers/platform/x86/barco-p50-gpio.c | 436 ++++++++++++++++++++++++++
> 4 files changed, 455 insertions(+)
> create mode 100644 drivers/platform/x86/barco-p50-gpio.c
>
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 8d118d7957d2..60980f7cc29a 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -3220,6 +3220,12 @@ F: drivers/video/backlight/
> F: include/linux/backlight.h
> F: include/linux/pwm_backlight.h
>
> +BARCO P50 GPIO DRIVER
> +M: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> +M: Peter Korsgaard <peter.korsgaard@barco.com>
> +S: Maintained
> +F: drivers/platform/x86/barco-p50-gpio.c
> +
> BATMAN ADVANCED
> M: Marek Lindner <mareklindner@neomailbox.ch>
> M: Simon Wunderlich <sw@simonwunderlich.de>
> diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig
> index e21ea3d23e6f..42b4895e4acc 100644
> --- a/drivers/platform/x86/Kconfig
> +++ b/drivers/platform/x86/Kconfig
> @@ -713,6 +713,16 @@ config PCENGINES_APU2
> To compile this driver as a module, choose M here: the module
> will be called pcengines-apuv2.
>
> +config BARCO_P50_GPIO
> + tristate "Barco P50 GPIO driver for identify LED/button"
> + depends on GPIOLIB
> + help
> + This driver provides access to the GPIOs for the identify button
> + and led present on Barco P50 board.
> +
> + To compile this driver as a module, choose M here: the module
> + will be called barco-p50-gpio.
> +
> config SAMSUNG_LAPTOP
> tristate "Samsung Laptop driver"
> depends on RFKILL || RFKILL = n
> diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile
> index 69690e26bb6d..931dc55f6f3e 100644
> --- a/drivers/platform/x86/Makefile
> +++ b/drivers/platform/x86/Makefile
> @@ -80,6 +80,9 @@ obj-$(CONFIG_XO1_RFKILL) += xo1-rfkill.o
> # PC Engines
> obj-$(CONFIG_PCENGINES_APU2) += pcengines-apuv2.o
>
> +# Barco
> +obj-$(CONFIG_BARCO_P50_GPIO) += barco-p50-gpio.o
> +
> # Samsung
> obj-$(CONFIG_SAMSUNG_LAPTOP) += samsung-laptop.o
> obj-$(CONFIG_SAMSUNG_Q10) += samsung-q10.o
> diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
> new file mode 100644
> index 000000000000..ca0b2564c407
> --- /dev/null
> +++ b/drivers/platform/x86/barco-p50-gpio.c
> @@ -0,0 +1,436 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +
> +/*
> + * Support for EC-connected GPIOs for identify
> + * LED/button on Barco P50 board
> + *
> + * Copyright (C) 2021 Barco NV
> + * Author: Santosh Kumar Yadav <santoshkumar.yadav@barco.com>
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/io.h>
> +#include <linux/delay.h>
> +#include <linux/dmi.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/leds.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/gpio_keys.h>
> +#include <linux/gpio/driver.h>
> +#include <linux/gpio/machine.h>
> +#include <linux/input.h>
> +
> +
> +#define DRIVER_NAME "barco-p50-gpio"
> +
> +/* GPIO lines */
> +#define P50_GPIO_LINE_LED 0
> +#define P50_GPIO_LINE_BTN 1
> +
> +/* GPIO IO Ports */
> +#define P50_GPIO_IO_PORT_BASE 0x299
> +
> +#define P50_PORT_DATA 0x00
> +#define P50_PORT_CMD 0x01
> +
> +#define P50_STATUS_OBF 0x01 /* EC output buffer full */
> +#define P50_STATUS_IBF 0x02 /* EC input buffer full */
> +
> +#define P50_CMD_READ 0xa0
> +#define P50_CMD_WRITE 0x50
> +
> +/* EC mailbox registers */
> +#define P50_MBOX_REG_CMD 0x00
> +#define P50_MBOX_REG_STATUS 0x01
> +#define P50_MBOX_REG_PARAM 0x02
> +#define P50_MBOX_REG_DATA 0x03
> +
> +#define P50_MBOX_CMD_READ_GPIO 0x11
> +#define P50_MBOX_CMD_WRITE_GPIO 0x12
> +#define P50_MBOX_CMD_CLEAR 0xff
> +
> +#define P50_MBOX_STATUS_SUCCESS 0x01
> +
> +#define P50_MBOX_PARAM_LED 0x12
> +#define P50_MBOX_PARAM_BTN 0x13
> +
> +
> +struct p50_gpio {
> + struct gpio_chip gc;
> + struct mutex lock;
> + unsigned long base;
> + struct platform_device *leds_pdev;
> + struct platform_device *keys_pdev;
> +};
> +
> +static struct platform_device *gpio_pdev;
> +
> +static int gpio_params[] = {
> + [P50_GPIO_LINE_LED] = P50_MBOX_PARAM_LED,
> + [P50_GPIO_LINE_BTN] = P50_MBOX_PARAM_BTN,
> +};
> +
> +static const char * const gpio_names[] = {
> + [P50_GPIO_LINE_LED] = "identify-led",
> + [P50_GPIO_LINE_BTN] = "identify-button",
> +};
> +
> +
> +static struct gpiod_lookup_table p50_gpio_led_table = {
> + .dev_id = "leds-gpio",
> + .table = {
> + GPIO_LOOKUP_IDX(DRIVER_NAME, P50_GPIO_LINE_LED, NULL, 0, GPIO_ACTIVE_HIGH),
> + {}
> + }
> +};
> +
> +/* GPIO LEDs */
> +static struct gpio_led leds[] = {
> + { .name = "identify" }
> +};
> +
> +static struct gpio_led_platform_data leds_pdata = {
> + .num_leds = ARRAY_SIZE(leds),
> + .leds = leds,
> +};
> +
> +/* GPIO keyboard */
> +static struct gpio_keys_button buttons[] = {
> + {
> + .code = KEY_RESTART,
> + .gpio = P50_GPIO_LINE_BTN,
> + .active_low = 1,
> + .type = EV_KEY,
> + .value = 1,
> + },
> +};
> +
> +static struct gpio_keys_platform_data keys_pdata = {
> + .buttons = buttons,
> + .nbuttons = ARRAY_SIZE(buttons),
> + .poll_interval = 100,
> + .rep = 0,
> + .name = "identify",
> +};
> +
> +
> +/* low level access routines */
> +
> +static int p50_wait_ec(struct p50_gpio *p50, int mask, int expected)
> +{
> + int i, val;
> +
> + for (i = 0; i < 100; i++) {
> + val = inb(p50->base + P50_PORT_CMD) & mask;
> + if (val == expected)
> + return 0;
> + usleep_range(500, 2000);
> + }
> +
> + dev_err(p50->gc.parent, "Timed out waiting for EC (0x%x)\n", val);
> + return -ETIMEDOUT;
> +}
> +
> +
> +static int p50_read_mbox_reg(struct p50_gpio *p50, int reg)
> +{
> + int ret;
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* clear output buffer flag, prevent unfinished commands */
> + inb(p50->base + P50_PORT_DATA);
> +
> + /* cmd/address */
> + outb(P50_CMD_READ | reg, p50->base + P50_PORT_CMD);
> +
> + ret = p50_wait_ec(p50, P50_STATUS_OBF, P50_STATUS_OBF);
> + if (ret)
> + return ret;
> +
> + return inb(p50->base + P50_PORT_DATA);
> +}
> +
> +static int p50_write_mbox_reg(struct p50_gpio *p50, int reg, int val)
> +{
> + int ret;
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* cmd/address */
> + outb(P50_CMD_WRITE | reg, p50->base + P50_PORT_CMD);
> +
> + ret = p50_wait_ec(p50, P50_STATUS_IBF, 0);
> + if (ret)
> + return ret;
> +
> + /* data */
> + outb(val, p50->base + P50_PORT_DATA);
> +
> + return 0;
> +}
> +
> +
> +/* mbox routines */
> +
> +static int p50_wait_mbox_idle(struct p50_gpio *p50)
> +{
> + int i, val;
> +
> + for (i = 0; i < 1000; i++) {
> + val = p50_read_mbox_reg(p50, P50_MBOX_REG_CMD);
> + /* cmd is 0 when idle */
> + if (val <= 0)
> + return val;
> +
> + usleep_range(500, 2000);
> + }
> +
> + dev_err(p50->gc.parent, "Timed out waiting for EC mbox idle (CMD: 0x%x)\n", val);
> +
> + return -ETIMEDOUT;
> +}
> +
> +static int p50_send_mbox_cmd(struct p50_gpio *p50, int cmd, int param, int data)
> +{
> + int ret;
> +
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_DATA, data);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_PARAM, param);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, cmd);
> + if (ret)
> + return ret;
> +
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_STATUS);
> + if (ret < 0)
> + return ret;
> +
> + if (ret == P50_MBOX_STATUS_SUCCESS)
> + return 0;
> +
> + dev_err(p50->gc.parent, "Mbox command failed (CMD=0x%x STAT=0x%x PARAM=0x%x DATA=0x%x)\n",
> + cmd, ret, param, data);
> +
> + return -EIO;
> +}
> +
> +
> +/* gpio routines */
> +
> +static int p50_gpio_get_direction(struct gpio_chip *gc, unsigned int offset)
> +{
> + switch (offset) {
> + case P50_GPIO_LINE_BTN:
> + return GPIO_LINE_DIRECTION_IN;
> +
> + case P50_GPIO_LINE_LED:
> + return GPIO_LINE_DIRECTION_OUT;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static int p50_gpio_get(struct gpio_chip *gc, unsigned int offset)
> +{
> + struct p50_gpio *p50 = gpiochip_get_data(gc);
> + int ret;
> +
> + mutex_lock(&p50->lock);
> +
> + ret = p50_send_mbox_cmd(p50, P50_MBOX_CMD_READ_GPIO, gpio_params[offset], 0);
> + if (ret == 0)
> + ret = p50_read_mbox_reg(p50, P50_MBOX_REG_DATA);
> +
> + mutex_unlock(&p50->lock);
> +
> + return ret;
> +}
> +
> +static void p50_gpio_set(struct gpio_chip *gc, unsigned int offset, int value)
> +{
> + struct p50_gpio *p50 = gpiochip_get_data(gc);
> +
> + mutex_lock(&p50->lock);
> +
> + p50_send_mbox_cmd(p50, P50_MBOX_CMD_WRITE_GPIO, gpio_params[offset], value);
> +
> + mutex_unlock(&p50->lock);
> +}
> +
> +static int p50_gpio_probe(struct platform_device *pdev)
> +{
> + struct p50_gpio *p50;
> + struct resource *res;
> + int ret;
> +
> + res = platform_get_resource(pdev, IORESOURCE_IO, 0);
> + if (!res) {
> + dev_err(&pdev->dev, "Cannot get I/O ports\n");
> + return -ENODEV;
> + }
> +
> + if (!devm_request_region(&pdev->dev, res->start, resource_size(res), pdev->name)) {
> + dev_err(&pdev->dev, "Unable to reserve I/O region\n");
> + return -EBUSY;
> + }
> +
> + p50 = devm_kzalloc(&pdev->dev, sizeof(*p50), GFP_KERNEL);
> + if (!p50)
> + return -ENOMEM;
> +
> + platform_set_drvdata(pdev, p50);
> + mutex_init(&p50->lock);
> + p50->base = res->start;
> + p50->gc.owner = THIS_MODULE;
> + p50->gc.parent = &pdev->dev;
> + p50->gc.label = dev_name(&pdev->dev);
> + p50->gc.ngpio = ARRAY_SIZE(gpio_names);
> + p50->gc.names = gpio_names;
> + p50->gc.can_sleep = true;
> + p50->gc.base = -1;
> + p50->gc.get_direction = p50_gpio_get_direction;
> + p50->gc.get = p50_gpio_get;
> + p50->gc.set = p50_gpio_set;
> +
> +
> + /* reset mbox */
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> + ret = p50_write_mbox_reg(p50, P50_MBOX_REG_CMD, P50_MBOX_CMD_CLEAR);
> + if (ret)
> + return ret;
> +
> + ret = p50_wait_mbox_idle(p50);
> + if (ret)
> + return ret;
> +
> +
> + ret = devm_gpiochip_add_data(&pdev->dev, &p50->gc, p50);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "Could not register gpiochip: %d\n", ret);
> + return ret;
> + }
> +
> + gpiod_add_lookup_table(&p50_gpio_led_table);
> +
> + p50->leds_pdev = platform_device_register_data(&pdev->dev,
> + "leds-gpio", PLATFORM_DEVID_NONE, &leds_pdata, sizeof(leds_pdata));
> +
> + if (IS_ERR(p50->leds_pdev)) {
> + ret = PTR_ERR(p50->leds_pdev);
> + dev_err(&pdev->dev, "Could not register leds-gpio: %d\n", ret);
> + goto err_leds;
> + }
> +
> + /* gpio-keys-polled uses old-style gpio interface, pass the right identifier */
> + buttons[0].gpio += p50->gc.base;
> +
> + p50->keys_pdev =
> + platform_device_register_data(&pdev->dev, "gpio-keys-polled",
> + PLATFORM_DEVID_NONE,
> + &keys_pdata, sizeof(keys_pdata));
> +
> + if (IS_ERR(p50->keys_pdev)) {
> + ret = PTR_ERR(p50->keys_pdev);
> + dev_err(&pdev->dev, "Could not register gpio-keys-polled: %d\n", ret);
> + goto err_keys;
> + }
> +
> + return 0;
> +
> +err_keys:
> + platform_device_unregister(p50->leds_pdev);
> +err_leds:
> + gpiod_remove_lookup_table(&p50_gpio_led_table);
> +
> + return ret;
> +}
> +
> +static int p50_gpio_remove(struct platform_device *pdev)
> +{
> + struct p50_gpio *p50 = platform_get_drvdata(pdev);
> +
> + platform_device_unregister(p50->keys_pdev);
> + platform_device_unregister(p50->leds_pdev);
> +
> + gpiod_remove_lookup_table(&p50_gpio_led_table);
> +
> + return 0;
> +}
> +
> +static struct platform_driver p50_gpio_driver = {
> + .driver = {
> + .name = DRIVER_NAME,
> + },
> + .probe = p50_gpio_probe,
> + .remove = p50_gpio_remove,
> +};
> +
> +/* Board setup */
> +static const struct dmi_system_id dmi_ids[] __initconst = {
> + {
> + .matches = {
> + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Barco"),
> + DMI_EXACT_MATCH(DMI_PRODUCT_FAMILY, "P50")
> + },
> + },
> + {}
> +};
> +MODULE_DEVICE_TABLE(dmi, dmi_ids);
> +
> +static int __init p50_module_init(void)
> +{
> + struct resource res = DEFINE_RES_IO(P50_GPIO_IO_PORT_BASE, P50_PORT_CMD + 1);
> +
> + if (!dmi_first_match(dmi_ids))
> + return -ENODEV;
> +
> + platform_driver_register(&p50_gpio_driver);
> +
> + gpio_pdev = platform_device_register_simple(DRIVER_NAME, PLATFORM_DEVID_NONE, &res, 1);
> + if (IS_ERR(gpio_pdev)) {
> + pr_err("failed registering %s: %ld\n", DRIVER_NAME, PTR_ERR(gpio_pdev));
> + platform_driver_unregister(&p50_gpio_driver);
> + return PTR_ERR(gpio_pdev);
> + }
> +
> + return 0;
> +}
> +
> +static void __exit p50_module_exit(void)
> +{
> + platform_device_unregister(gpio_pdev);
> + platform_driver_unregister(&p50_gpio_driver);
> +}
> +
> +module_init(p50_module_init);
> +module_exit(p50_module_exit);
> +
> +MODULE_AUTHOR("Santosh Kumar Yadav, Barco NV <santoshkumar.yadav@barco.com>");
> +MODULE_DESCRIPTION("Barco P50 identify GPIOs driver");
> +MODULE_LICENSE("GPL");
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART
2021-10-20 14:39 ` Hans de Goede
@ 2021-10-22 12:46 ` Peter Korsgaard
2021-10-22 15:40 ` Peter Korsgaard
2021-10-24 8:19 ` Hans de Goede
0 siblings, 2 replies; 9+ messages in thread
From: Peter Korsgaard @ 2021-10-22 12:46 UTC (permalink / raw)
To: platform-driver-x86, hdegoede, santoshkumar.yadav; +Cc: Peter Korsgaard
From: Peter Korsgaard <peter.korsgaard@barco.com>
It turns out that systemd-logind by default listens for KEY_RESTART input
events and reboots the machine, which isn't great - So use KEY_VENDOR for
the vendor specific identify button instead to not conflict.
Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
---
drivers/platform/x86/barco-p50-gpio.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
index ca0b2564c407..f5c72e33f9ae 100644
--- a/drivers/platform/x86/barco-p50-gpio.c
+++ b/drivers/platform/x86/barco-p50-gpio.c
@@ -101,7 +101,7 @@ static struct gpio_led_platform_data leds_pdata = {
/* GPIO keyboard */
static struct gpio_keys_button buttons[] = {
{
- .code = KEY_RESTART,
+ .code = KEY_VENDOR,
.gpio = P50_GPIO_LINE_BTN,
.active_low = 1,
.type = EV_KEY,
--
2.20.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART
2021-10-22 12:46 ` [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART Peter Korsgaard
@ 2021-10-22 15:40 ` Peter Korsgaard
2021-10-22 16:00 ` Hans de Goede
2021-10-24 8:19 ` Hans de Goede
1 sibling, 1 reply; 9+ messages in thread
From: Peter Korsgaard @ 2021-10-22 15:40 UTC (permalink / raw)
To: platform-driver-x86; +Cc: hdegoede, santoshkumar.yadav, Peter Korsgaard
>>>>> "Peter" == Peter Korsgaard <peter@korsgaard.com> writes:
Ups, s/insted/instead/ in the subject. Do you want me to send a v2 for that?
> From: Peter Korsgaard <peter.korsgaard@barco.com>
> It turns out that systemd-logind by default listens for KEY_RESTART input
> events and reboots the machine, which isn't great - So use KEY_VENDOR for
> the vendor specific identify button instead to not conflict.
> Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
> ---
> drivers/platform/x86/barco-p50-gpio.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
> diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
> index ca0b2564c407..f5c72e33f9ae 100644
> --- a/drivers/platform/x86/barco-p50-gpio.c
> +++ b/drivers/platform/x86/barco-p50-gpio.c
> @@ -101,7 +101,7 @@ static struct gpio_led_platform_data leds_pdata = {
> /* GPIO keyboard */
> static struct gpio_keys_button buttons[] = {
> {
> - .code = KEY_RESTART,
> + .code = KEY_VENDOR,
> .gpio = P50_GPIO_LINE_BTN,
> .active_low = 1,
> .type = EV_KEY,
> --
> 2.20.1
--
Bye, Peter Korsgaard
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART
2021-10-22 15:40 ` Peter Korsgaard
@ 2021-10-22 16:00 ` Hans de Goede
0 siblings, 0 replies; 9+ messages in thread
From: Hans de Goede @ 2021-10-22 16:00 UTC (permalink / raw)
To: Peter Korsgaard, platform-driver-x86; +Cc: santoshkumar.yadav, Peter Korsgaard
Hi,
On 10/22/21 17:40, Peter Korsgaard wrote:
>>>>>> "Peter" == Peter Korsgaard <peter@korsgaard.com> writes:
>
> Ups, s/insted/instead/ in the subject. Do you want me to send a v2 for that?
No need for a v2, I'll fix this up when applying.
Regards,
Hans
>
> > From: Peter Korsgaard <peter.korsgaard@barco.com>
> > It turns out that systemd-logind by default listens for KEY_RESTART input
> > events and reboots the machine, which isn't great - So use KEY_VENDOR for
> > the vendor specific identify button instead to not conflict.
>
> > Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
> > ---
> > drivers/platform/x86/barco-p50-gpio.c | 2 +-
> > 1 file changed, 1 insertion(+), 1 deletion(-)
>
> > diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
> > index ca0b2564c407..f5c72e33f9ae 100644
> > --- a/drivers/platform/x86/barco-p50-gpio.c
> > +++ b/drivers/platform/x86/barco-p50-gpio.c
> > @@ -101,7 +101,7 @@ static struct gpio_led_platform_data leds_pdata = {
> > /* GPIO keyboard */
> > static struct gpio_keys_button buttons[] = {
> > {
> > - .code = KEY_RESTART,
> > + .code = KEY_VENDOR,
> > .gpio = P50_GPIO_LINE_BTN,
> > .active_low = 1,
> > .type = EV_KEY,
> > --
> > 2.20.1
>
>
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART
2021-10-22 12:46 ` [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART Peter Korsgaard
2021-10-22 15:40 ` Peter Korsgaard
@ 2021-10-24 8:19 ` Hans de Goede
1 sibling, 0 replies; 9+ messages in thread
From: Hans de Goede @ 2021-10-24 8:19 UTC (permalink / raw)
To: Peter Korsgaard, platform-driver-x86, santoshkumar.yadav; +Cc: Peter Korsgaard
Hi,
On 10/22/21 14:46, Peter Korsgaard wrote:
> From: Peter Korsgaard <peter.korsgaard@barco.com>
>
> It turns out that systemd-logind by default listens for KEY_RESTART input
> events and reboots the machine, which isn't great - So use KEY_VENDOR for
> the vendor specific identify button instead to not conflict.
>
> Signed-off-by: Peter Korsgaard <peter.korsgaard@barco.com>
Thank you for your patch, I've applied this patch to my review-hans
branch:
https://git.kernel.org/pub/scm/linux/kernel/git/pdx86/platform-drivers-x86.git/log/?h=review-hans
Note it will show up in my review-hans branch once I've pushed my
local branch there, which might take a while.
Once I've run some tests on this branch the patches there will be
added to the platform-drivers-x86/for-next branch and eventually
will be included in the pdx86 pull-request to Linus for the next
merge-window.
Regards,
Hans
> ---
> drivers/platform/x86/barco-p50-gpio.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/platform/x86/barco-p50-gpio.c b/drivers/platform/x86/barco-p50-gpio.c
> index ca0b2564c407..f5c72e33f9ae 100644
> --- a/drivers/platform/x86/barco-p50-gpio.c
> +++ b/drivers/platform/x86/barco-p50-gpio.c
> @@ -101,7 +101,7 @@ static struct gpio_led_platform_data leds_pdata = {
> /* GPIO keyboard */
> static struct gpio_keys_button buttons[] = {
> {
> - .code = KEY_RESTART,
> + .code = KEY_VENDOR,
> .gpio = P50_GPIO_LINE_BTN,
> .active_low = 1,
> .type = EV_KEY,
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2021-10-24 8:19 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-10-13 14:03 [PATCH] platform/x86: Support for EC-connected GPIOs for identify LED/button on Barco P50 board Santosh Kumar Yadav
2021-10-19 14:50 ` Hans de Goede
2021-10-19 15:14 ` Peter Korsgaard
2021-10-20 12:36 ` [PATCH v2] " Peter Korsgaard
2021-10-20 14:39 ` Hans de Goede
2021-10-22 12:46 ` [PATCH] platform/x86: barco-p50-gpio: use KEY_VENDOR for button insted of KEY_RESTART Peter Korsgaard
2021-10-22 15:40 ` Peter Korsgaard
2021-10-22 16:00 ` Hans de Goede
2021-10-24 8:19 ` Hans de Goede
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.