* Re: [PATCH] HID: logitech-hidpp: document sysfs interface
From: Jiri Kosina @ 2018-03-06 14:18 UTC (permalink / raw)
To: Aishwarya Pant
Cc: Benjamin Tissoires, linux-input, linux-kernel, Jonathan Corbet,
Greg KH, Julia Lawall, linux-doc
In-Reply-To: <20180302131653.GA24934@mordor.localdomain>
On Fri, 2 Mar 2018, Aishwarya Pant wrote:
> Descriptions have been collected from git commit logs.
Applied, thanks.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH 1/2] HID: elecom: refer to trackballs by model name instead of series
From: Jiri Kosina @ 2018-03-06 14:17 UTC (permalink / raw)
To: Tomasz Kramkowski; +Cc: Benjamin Tissoires, linux-input, linux-kernel
In-Reply-To: <20180301172224.1276-1-tk@the-tk.com>
I have applied both patches to for-4.17/elecom.
Thanks,
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH v2 2/2] dt-bindings: input: Add binding for RAVE SP input driver
From: Fabio Estevam @ 2018-03-06 14:17 UTC (permalink / raw)
To: Andrey Smirnov
Cc: Dmitry Torokhov, linux-input, linux-kernel, Rob Herring,
Mark Rutland,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
Guenter Roeck, Chris Healy, Lucas Stach
In-Reply-To: <20180301165527.22274-2-andrew.smirnov@gmail.com>
Hi Andrey,
On Thu, Mar 1, 2018 at 1:55 PM, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
l
> +++ b/Documentation/devicetree/bindings/input/zii,rave-sp-pwrbutton.txt
> @@ -0,0 +1,24 @@
> +Zodiac Inflight Innovations RAVE Supervisory Processor Power Button Bindings
> +
> +RAVE SP input device is a "MFD cell" device corresponding to power
> +button functionality of RAVE Supervisory Processor. It is expected
> +that its Device Tree node is specified as a child of the node
> +corresponding to the parent RAVE SP device (as documented in
> +Documentation/devicetree/bindings/mfd/zii,rave-sp.txt)
> +
> +Required properties:
> +
> +- compatible: Should be "zii,rave-sp-pwrbutton"
> +
> +Example:
> +
> + rave-sp {
> + compatible = "zii,rave-sp-rdu1";
> + current-speed = <38400>;
> +
> + pwrbutton {
> + compatible = "zii,rave-sp-pwrbutton";
> + status = "okay";
In the dts patch review Shawn said:
"The okay status is to flip the state of devices that are initially
disabled. I think it's unnecessary for the case here."
so please remove the status line.
^ permalink raw reply
* Re: [PATCH v2 2/2] dt-bindings: input: Add binding for RAVE SP input driver
From: Lucas Stach @ 2018-03-06 14:13 UTC (permalink / raw)
To: Andrey Smirnov, Dmitry Torokhov
Cc: linux-input, linux-kernel, Rob Herring, Mark Rutland, devicetree,
Guenter Roeck, Chris Healy
In-Reply-To: <20180301165527.22274-2-andrew.smirnov@gmail.com>
Am Donnerstag, den 01.03.2018, 08:55 -0800 schrieb Andrey Smirnov:
> Add Device Tree bindings for RAVE SP input drvier - an MFD cell of
> parent RAVE SP driver (documented in
> Documentation/devicetree/bindings/mfd/zii,rave-sp.txt).
>
> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: linux-input@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: devicetree@vger.kernel.org
> > Cc: Guenter Roeck <linux@roeck-us.net>
> > Cc: Chris Healy <cphealy@gmail.com>
> > Cc: Lucas Stach <l.stach@pengutronix.de>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Acked-by: Lucas Stach <l.stach@pengutronix.de>
> ---
> .../bindings/input/zii,rave-sp-pwrbutton.txt | 24 ++++++++++++++++++++++
> 1 file changed, 24 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/input/zii,rave-sp-pwrbutton.txt
>
> diff --git a/Documentation/devicetree/bindings/input/zii,rave-sp-pwrbutton.txt b/Documentation/devicetree/bindings/input/zii,rave-sp-pwrbutton.txt
> new file mode 100644
> index 000000000000..e14e12d27772
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/input/zii,rave-sp-pwrbutton.txt
> @@ -0,0 +1,24 @@
> +Zodiac Inflight Innovations RAVE Supervisory Processor Power Button Bindings
> +
> +RAVE SP input device is a "MFD cell" device corresponding to power
> +button functionality of RAVE Supervisory Processor. It is expected
> +that its Device Tree node is specified as a child of the node
> +corresponding to the parent RAVE SP device (as documented in
> +Documentation/devicetree/bindings/mfd/zii,rave-sp.txt)
> +
> +Required properties:
> +
> +- compatible: Should be "zii,rave-sp-pwrbutton"
> +
> +Example:
> +
> > + rave-sp {
> > + compatible = "zii,rave-sp-rdu1";
> > + current-speed = <38400>;
> +
> > + pwrbutton {
> > + compatible = "zii,rave-sp-pwrbutton";
> > + status = "okay";
> > + };
> > + }
> +
^ permalink raw reply
* Re: [PATCH v2 1/2] input: Add RAVE SP Powerbutton driver
From: Lucas Stach @ 2018-03-06 14:12 UTC (permalink / raw)
To: Andrey Smirnov, Dmitry Torokhov
Cc: linux-input, linux-kernel, Rob Herring, Mark Rutland, devicetree,
Guenter Roeck, Chris Healy
In-Reply-To: <20180301165527.22274-1-andrew.smirnov@gmail.com>
Am Donnerstag, den 01.03.2018, 08:55 -0800 schrieb Andrey Smirnov:
> Add driver that properly handles input event emitted by RAVE SP
> devices.
>
> > Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Cc: linux-input@vger.kernel.org
> Cc: linux-kernel@vger.kernel.org
> > Cc: Rob Herring <robh+dt@kernel.org>
> > Cc: Mark Rutland <mark.rutland@arm.com>
> Cc: devicetree@vger.kernel.org
> > Cc: Guenter Roeck <linux@roeck-us.net>
> > Cc: Chris Healy <cphealy@gmail.com>
> > Cc: Lucas Stach <l.stach@pengutronix.de>
> Signed-off-by: Andrey Smirnov <andrew.smirnov@gmail.com>
Reviewed-by: Lucas Stach <l.stach@pengutronix.de>
> ---
>
> Changes since [v1]:
>
> - Removed redundant dev.parent assignment
>
> - Various cosmetic changes
>
> [v1] lkml.kernel.org/r/20180226154130.25774-1-andrew.smirnov@gmail.com
>
> drivers/input/misc/Kconfig | 9 ++++
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/rave-sp-pwrbutton.c | 94 ++++++++++++++++++++++++++++++++++
> 3 files changed, 104 insertions(+)
> create mode 100644 drivers/input/misc/rave-sp-pwrbutton.c
>
> diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
> index 62a1312a7387..6a3c753b093b 100644
> --- a/drivers/input/misc/Kconfig
> +++ b/drivers/input/misc/Kconfig
> @@ -841,4 +841,13 @@ config INPUT_HISI_POWERKEY
> > To compile this driver as a module, choose M here: the
> > module will be called hisi_powerkey.
>
> +config INPUT_RAVE_SP_PWRBUTTON
> > + tristate "RAVE SP Power button Driver"
> > + depends on RAVE_SP_CORE
> > + help
> > + Say Y here if you want to enable power key reporting from RAVE SP
> +
> > + To compile this driver as a module, choose M here: the
> > + module will be called rave-sp-pwrbutton.
> +
> endif
> diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
> index a8f61af865aa..8cc58f362bb8 100644
> --- a/drivers/input/misc/Makefile
> +++ b/drivers/input/misc/Makefile
> > @@ -60,6 +60,7 @@ obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o
> > obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
> > obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
> > obj-$(CONFIG_INPUT_PWM_VIBRA) += pwm-vibra.o
> > +obj-$(CONFIG_INPUT_RAVE_SP_PWRBUTTON) += rave-sp-pwrbutton.o
> > obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
> > obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
> > obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
> diff --git a/drivers/input/misc/rave-sp-pwrbutton.c b/drivers/input/misc/rave-sp-pwrbutton.c
> new file mode 100644
> index 000000000000..bcab3cdb7ebd
> --- /dev/null
> +++ b/drivers/input/misc/rave-sp-pwrbutton.c
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +//
> +// Power Button driver for RAVE SP
> +//
> +// Copyright (C) 2017 Zodiac Inflight Innovations
> +//
> +//
> +
> +#include <linux/input.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/mfd/rave-sp.h>
> +#include <linux/platform_device.h>
> +
> > +#define RAVE_SP_EVNT_BUTTON_PRESS (RAVE_SP_EVNT_BASE + 0x00)
> +
> +struct rave_sp_power_button {
> > + struct input_dev *idev;
> > + struct notifier_block nb;
> +};
> +
> +static int rave_sp_power_button_event(struct notifier_block *nb,
> > + unsigned long action, void *data)
> +{
> > + struct rave_sp_power_button *pb =
> > + container_of(nb, struct rave_sp_power_button, nb);
> > + const u8 event = rave_sp_action_unpack_event(action);
> > + const u8 value = rave_sp_action_unpack_value(action);
> > + struct input_dev *idev = pb->idev;
> +
> > + if (event == RAVE_SP_EVNT_BUTTON_PRESS) {
> > + input_report_key(idev, KEY_POWER, value);
> > + input_sync(idev);
> +
> > + return NOTIFY_STOP;
> > + }
> +
> > + return NOTIFY_DONE;
> +}
> +
> +static int rave_sp_pwrbutton_probe(struct platform_device *pdev)
> +{
> > + struct device *dev = &pdev->dev;
> > + struct rave_sp_power_button *pb;
> > + struct input_dev *idev;
> > + int error;
> +
> > + pb = devm_kzalloc(dev, sizeof(*pb), GFP_KERNEL);
> > + if (!pb)
> > + return -ENOMEM;
> +
> > + idev = devm_input_allocate_device(dev);
> > + if (!idev)
> > + return -ENOMEM;
> +
> > + idev->name = pdev->name;
> +
> > + input_set_capability(idev, EV_KEY, KEY_POWER);
> +
> > + error = input_register_device(idev);
> > + if (error)
> > + return error;
> +
> > + pb->idev = idev;
> > + pb->nb.notifier_call = rave_sp_power_button_event;
> > + pb->nb.priority = 128;
> +
> > + error = devm_rave_sp_register_event_notifier(dev, &pb->nb);
> > + if (error)
> > + return error;
> +
> > + return 0;
> +}
> +
> +static const struct of_device_id rave_sp_pwrbutton_of_match[] = {
> > + { .compatible = "zii,rave-sp-pwrbutton" },
> > + {}
> +};
> +
> +static struct platform_driver rave_sp_pwrbutton_driver = {
> > + .probe = rave_sp_pwrbutton_probe,
> > > + .driver = {
> > + .name = KBUILD_MODNAME,
> > + .of_match_table = rave_sp_pwrbutton_of_match,
> > + },
> +};
> +module_platform_driver(rave_sp_pwrbutton_driver);
> +
> +MODULE_DEVICE_TABLE(of, rave_sp_pwrbutton_of_match);
> +MODULE_LICENSE("GPL");
> > +MODULE_AUTHOR("Andrey Vostrikov <andrey.vostrikov@cogentembedded.com>");
> > +MODULE_AUTHOR("Nikita Yushchenko <nikita.yoush@cogentembedded.com>");
> > +MODULE_AUTHOR("Andrey Smirnov <andrew.smirnov@gmail.com>");
> +MODULE_DESCRIPTION("RAVE SP Power Button driver");
^ permalink raw reply
* [PATCH resend] usb: quirks: add control message delay for 1b1c:1b20
From: Danilo Krummrich @ 2018-03-06 8:38 UTC (permalink / raw)
To: gregkh, felipe.balbi
Cc: linux-usb, linux-input, linux-kernel, kai.heng.feng,
Danilo Krummrich, stable
Corsair Strafe RGB keyboard does not respond to usb control messages
sometimes and hence generates timeouts.
Commit de3af5bf259d ("usb: quirks: add delay init quirk for Corsair
Strafe RGB keyboard") tried to fix those timeouts by adding
USB_QUIRK_DELAY_INIT.
Unfortunately, even with this quirk timeouts of usb_control_msg()
can still be seen, but with a lower frequency (approx. 1 out of 15):
[ 29.103520] usb 1-8: string descriptor 0 read error: -110
[ 34.363097] usb 1-8: can't set config #1, error -110
Adding further delays to different locations where usb control
messages are issued just moves the timeouts to other locations,
e.g.:
[ 35.400533] usbhid 1-8:1.0: can't add hid device: -110
[ 35.401014] usbhid: probe of 1-8:1.0 failed with error -110
The only way to reliably avoid those issues is having a pause after
each usb control message. In approx. 200 boot cycles no more timeouts
were seen.
Addionaly, keep USB_QUIRK_DELAY_INIT as it turned out to be necessary
to have the delay in hub_port_connect() after hub_port_init().
The overall boot time seems not to be influenced by these additional
delays, even on fast machines and lightweight distributions.
Fixes: de3af5bf259d ("usb: quirks: add delay init quirk for Corsair Strafe RGB keyboard")
Cc: stable@vger.kernel.org
Signed-off-by: Danilo Krummrich <danilokrummrich@dk-develop.de>
---
drivers/usb/core/message.c | 4 ++++
drivers/usb/core/quirks.c | 3 ++-
include/linux/usb/quirks.h | 3 +++
3 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index c64cf6c4a83d..0c11d40a12bc 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -151,6 +151,10 @@ int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request,
ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout);
+ /* Linger a bit, prior to the next control message. */
+ if (dev->quirks & USB_QUIRK_DELAY_CTRL_MSG)
+ msleep(200);
+
kfree(dr);
return ret;
diff --git a/drivers/usb/core/quirks.c b/drivers/usb/core/quirks.c
index f4a548471f0f..54b019e267c5 100644
--- a/drivers/usb/core/quirks.c
+++ b/drivers/usb/core/quirks.c
@@ -230,7 +230,8 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x1b1c, 0x1b13), .driver_info = USB_QUIRK_DELAY_INIT },
/* Corsair Strafe RGB */
- { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT },
+ { USB_DEVICE(0x1b1c, 0x1b20), .driver_info = USB_QUIRK_DELAY_INIT |
+ USB_QUIRK_DELAY_CTRL_MSG },
/* Corsair K70 LUX */
{ USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
diff --git a/include/linux/usb/quirks.h b/include/linux/usb/quirks.h
index f1fcec2fd5f8..b7a99ce56bc9 100644
--- a/include/linux/usb/quirks.h
+++ b/include/linux/usb/quirks.h
@@ -63,4 +63,7 @@
*/
#define USB_QUIRK_DISCONNECT_SUSPEND BIT(12)
+/* Device needs a pause after every control message. */
+#define USB_QUIRK_DELAY_CTRL_MSG BIT(13)
+
#endif /* __LINUX_USB_QUIRKS_H */
--
2.16.2
^ permalink raw reply related
* [PATCH v4 2/3] Input: gpio-keys - allow setting wakeup event action in DT
From: Jeffy Chen @ 2018-03-06 7:51 UTC (permalink / raw)
To: linux-kernel
Cc: briannorris, heiko, dtor, dianders, Jeffy Chen, devicetree,
Rob Herring, Dmitry Torokhov, linux-input, Mark Rutland
In-Reply-To: <20180306074418.8879-2-jeffy.chen@rock-chips.com>
Allow specifying event actions to trigger wakeup when using the
gpio-keys input device as a wakeup source.
Reviewed-by: Rob Herring <robh@kernel.org>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v4: None
Changes in v3: None
Changes in v2:
Specify wakeup event action instead of irq trigger type as Brian
suggested.
Documentation/devicetree/bindings/input/gpio-keys.txt | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/input/gpio-keys.txt b/Documentation/devicetree/bindings/input/gpio-keys.txt
index a94940481e55..996ce84352cb 100644
--- a/Documentation/devicetree/bindings/input/gpio-keys.txt
+++ b/Documentation/devicetree/bindings/input/gpio-keys.txt
@@ -26,6 +26,14 @@ Optional subnode-properties:
If not specified defaults to 5.
- wakeup-source: Boolean, button can wake-up the system.
(Legacy property supported: "gpio-key,wakeup")
+ - wakeup-event-action: Specifies whether the key should wake the
+ system when asserted, when deasserted, or both. This property is
+ only valid for keys that wake up the system (e.g., when the
+ "wakeup-source" property is also provided).
+ Supported values are defined in linux-event-codes.h:
+ EV_ACT_ASSERTED - asserted
+ EV_ACT_DEASSERTED - deasserted
+ EV_ACT_ANY - both asserted and deasserted
- linux,can-disable: Boolean, indicates that button is connected
to dedicated (not shared) interrupt which can be disabled to
suppress events from the button.
--
2.11.0
^ permalink raw reply related
* [PATCH v4 1/3] Input: gpio-keys - add support for wakeup event action
From: Jeffy Chen @ 2018-03-06 7:44 UTC (permalink / raw)
To: linux-kernel
Cc: briannorris, heiko, dtor, dianders, Jeffy Chen, devicetree,
Guenter Roeck, Thomas Gleixner, Joseph Lo, stephen lu,
Rob Herring, Dmitry Torokhov, Kate Stewart, linux-input,
Greg Kroah-Hartman, Mark Rutland, Philippe Ombredanne,
Arvind Yadav
In-Reply-To: <20180306074418.8879-1-jeffy.chen@rock-chips.com>
Add support for specifying event actions to trigger wakeup when using
the gpio-keys input device as a wakeup source.
This would allow the device to configure when to wakeup the system. For
example a gpio-keys input device for pen insert, may only want to wakeup
the system when ejecting the pen.
Suggested-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
---
Changes in v4:
Add dt-binding gpio-keys.h, stop saving irq trigger type, add enable/disable wakeup helpers as Dmitry suggested.
Changes in v3:
Adding more comments as Brian suggested.
Changes in v2:
Specify wakeup event action instead of irq trigger type as Brian
suggested.
drivers/input/keyboard/gpio_keys.c | 67 +++++++++++++++++++++++++++++++++--
include/dt-bindings/input/gpio-keys.h | 13 +++++++
include/linux/gpio_keys.h | 2 ++
3 files changed, 79 insertions(+), 3 deletions(-)
create mode 100644 include/dt-bindings/input/gpio-keys.h
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
index 87e613dc33b8..4bc23648b6a7 100644
--- a/drivers/input/keyboard/gpio_keys.c
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -30,6 +30,7 @@
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/spinlock.h>
+#include <dt-bindings/input/gpio-keys.h>
struct gpio_button_data {
const struct gpio_keys_button *button;
@@ -45,10 +46,12 @@ struct gpio_button_data {
unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
unsigned int irq;
+ unsigned int wakeup_trigger_type;
spinlock_t lock;
bool disabled;
bool key_pressed;
bool suspended;
+ bool wakeup_enabled;
};
struct gpio_keys_drvdata {
@@ -540,6 +543,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
}
if (bdata->gpiod) {
+ int active_low = gpiod_is_active_low(bdata->gpiod);
+
if (button->debounce_interval) {
error = gpiod_set_debounce(bdata->gpiod,
button->debounce_interval * 1000);
@@ -568,6 +573,24 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_gpio_isr;
irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
+ switch (button->wakeup_event_action) {
+ case EV_ACT_ASSERTED:
+ bdata->wakeup_trigger_type = active_low ?
+ IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
+ break;
+ case EV_ACT_DEASSERTED:
+ bdata->wakeup_trigger_type = active_low ?
+ IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
+ break;
+ case EV_ACT_ANY:
+ /* fall through */
+ default:
+ /*
+ * For other cases, we are OK letting suspend/resume
+ * not reconfigure the trigger type.
+ */
+ break;
+ }
} else {
if (!button->irq) {
dev_err(dev, "Found button without gpio or irq\n");
@@ -586,6 +609,11 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
isr = gpio_keys_irq_isr;
irqflags = 0;
+
+ /*
+ * For IRQ buttons, there is no interrupt for release.
+ * So we don't need to reconfigure the trigger type for wakeup.
+ */
}
bdata->code = &ddata->keymap[idx];
@@ -718,6 +746,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
/* legacy name */
fwnode_property_read_bool(child, "gpio-key,wakeup");
+ fwnode_property_read_u32(child, "wakeup-event-action",
+ &button->wakeup_event_action);
+
button->can_disable =
fwnode_property_read_bool(child, "linux,can-disable");
@@ -845,6 +876,31 @@ static int gpio_keys_probe(struct platform_device *pdev)
return 0;
}
+static int gpio_keys_enable_wakeup(struct gpio_button_data *bdata)
+{
+ int ret;
+
+ ret = enable_irq_wake(bdata->irq);
+ if (ret)
+ return ret;
+
+ if (bdata->wakeup_trigger_type)
+ irq_set_irq_type(bdata->irq, bdata->wakeup_trigger_type);
+
+ return 0;
+}
+
+static void gpio_keys_disable_wakeup(struct gpio_button_data *bdata)
+{
+ /**
+ * The trigger type is always both edges for gpio-based keys and we do
+ * not support changing wakeup trigger for interrupt-based keys.
+ */
+ if (bdata->wakeup_trigger_type)
+ irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH);
+ disable_irq_wake(bdata->irq);
+}
+
static int __maybe_unused gpio_keys_suspend(struct device *dev)
{
struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
@@ -854,8 +910,10 @@ static int __maybe_unused gpio_keys_suspend(struct device *dev)
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
+
if (bdata->button->wakeup)
- enable_irq_wake(bdata->irq);
+ bdata->wakeup_enabled =
+ !gpio_keys_enable_wakeup(bdata);
bdata->suspended = true;
}
} else {
@@ -878,9 +936,12 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
if (device_may_wakeup(dev)) {
for (i = 0; i < ddata->pdata->nbuttons; i++) {
struct gpio_button_data *bdata = &ddata->data[i];
- if (bdata->button->wakeup)
- disable_irq_wake(bdata->irq);
+
bdata->suspended = false;
+ if (bdata->wakeup_enabled) {
+ gpio_keys_disable_wakeup(bdata);
+ bdata->wakeup_enabled = false;
+ }
}
} else {
mutex_lock(&input->mutex);
diff --git a/include/dt-bindings/input/gpio-keys.h b/include/dt-bindings/input/gpio-keys.h
new file mode 100644
index 000000000000..8962df79e753
--- /dev/null
+++ b/include/dt-bindings/input/gpio-keys.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * This header provides constants for gpio keys bindings.
+ */
+
+#ifndef _DT_BINDINGS_GPIO_KEYS_H
+#define _DT_BINDINGS_GPIO_KEYS_H
+
+#define EV_ACT_ANY 0x00 /* asserted or deasserted */
+#define EV_ACT_ASSERTED 0x01 /* asserted */
+#define EV_ACT_DEASSERTED 0x02 /* deasserted */
+
+#endif /* _DT_BINDINGS_GPIO_KEYS_H */
diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
index d06bf77400f1..7160df54a6fe 100644
--- a/include/linux/gpio_keys.h
+++ b/include/linux/gpio_keys.h
@@ -13,6 +13,7 @@ struct device;
* @desc: label that will be attached to button's gpio
* @type: input event type (%EV_KEY, %EV_SW, %EV_ABS)
* @wakeup: configure the button as a wake-up source
+ * @wakeup_event_action: event action to trigger wakeup
* @debounce_interval: debounce ticks interval in msecs
* @can_disable: %true indicates that userspace is allowed to
* disable button via sysfs
@@ -26,6 +27,7 @@ struct gpio_keys_button {
const char *desc;
unsigned int type;
int wakeup;
+ int wakeup_event_action;
int debounce_interval;
bool can_disable;
int value;
--
2.11.0
^ permalink raw reply related
* Re: [PATCH v3 1/3] Input: gpio-keys - add support for wakeup event action
From: JeffyChen @ 2018-03-06 7:44 UTC (permalink / raw)
To: Dmitry Torokhov
Cc: linux-kernel, briannorris, heiko, dtor, dianders, Guenter Roeck,
Enric Balletbo i Serra, Thomas Gleixner, Joseph Lo, stephen lu,
Kate Stewart, linux-input, Greg Kroah-Hartman,
Philippe Ombredanne, Arvind Yadav
In-Reply-To: <20180306003009.GA2205@dtor-ws>
Hi Dmitry,
Thanks for your review.
On 03/06/2018 08:30 AM, Dmitry Torokhov wrote:
>> >+ switch (button->wakeup_event_action) {
>> >+ case EV_ACT_ASSERTED:
>> >+ bdata->wakeup_trigger_type = active_low ?
>> >+ IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
> IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
>
ok, will fix in next version
>> >+ break;
>> >+ case EV_ACT_DEASSERTED:
>> >+ bdata->wakeup_trigger_type = active_low ?
>> >+ IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
>> >+ break;
> case EV_ACT_ANY:
ok, will fix in next version
>> >+ default:
>> >+ /*
>> >+ * For other cases, we are OK letting suspend/resume
>> >+ * not reconfigure the trigger type.
>> >+ */
>> >+ break;
>> >+ }
>> > } else {
>> > if (!button->irq) {
>> > dev_err(dev, "Found button without gpio or irq\n");
>> >@@ -586,6 +606,12 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
>> >
>> > isr = gpio_keys_irq_isr;
>> > irqflags = 0;
>> >+
>> >+ /*
>> >+ * For IRQ buttons, the irq trigger type for press and release
>> >+ * are the same. So we don't need to reconfigure the trigger
>> >+ * type for wakeup.
> That is not entirely accurate. Interrupt triggers button press, which is
> followed by either immediate or delayed release. There is no interrupt
> for release.
ok, will fix the comment
>
>> >+ */
>> > }
>> >
>> > bdata->code = &ddata->keymap[idx];
>> >@@ -618,6 +644,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
>> > return error;
>> > }
>> >
>> >+ bdata->irq_trigger_type = irq_get_trigger_type(bdata->irq);
> Why do we need to store the trigger type? It is always both edges for
> gpio-based keys and we do not support changing wakeup trigger for
> interrupt-based keys.
right, this is not needed.
>
>> >+
>> > return 0;
>> > }
>> >
>> >@@ -718,6 +746,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
>> > /* legacy name */
>> > fwnode_property_read_bool(child, "gpio-key,wakeup");
>> >
>> >+ fwnode_property_read_u32(child, "wakeup-event-action",
>> >+ &button->wakeup_event_action);
>> >+
>> > button->can_disable =
>> > fwnode_property_read_bool(child, "linux,can-disable");
>> >
>> >@@ -854,6 +885,10 @@ static int __maybe_unused gpio_keys_suspend(struct device *dev)
>> > if (device_may_wakeup(dev)) {
>> > for (i = 0; i < ddata->pdata->nbuttons; i++) {
>> > struct gpio_button_data *bdata = &ddata->data[i];
>> >+
>> >+ if (bdata->button->wakeup && bdata->wakeup_trigger_type)
>> >+ irq_set_irq_type(bdata->irq,
>> >+ bdata->wakeup_trigger_type);
> if (bdata->button->wakeup) {
> if (bdata->wakeup_trigger_type) {
> error = ...;
> }
>
> enable_irq_wake(bdata->irq);
> }
>
> Might need to be split into a helper; if you add error handling to
> enable_irq_wake() that woudl be great too.
ok, will do that.
>
>> > if (bdata->button->wakeup)
>> > enable_irq_wake(bdata->irq);
>> > bdata->suspended = true;
>> >@@ -878,6 +913,10 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
>> > if (device_may_wakeup(dev)) {
>> > for (i = 0; i < ddata->pdata->nbuttons; i++) {
>> > struct gpio_button_data *bdata = &ddata->data[i];
>> >+
>> >+ if (bdata->button->wakeup && bdata->wakeup_trigger_type)
>> >+ irq_set_irq_type(bdata->irq,
>> >+ bdata->irq_trigger_type);
> Just use IRQ_TYPE_EDGE_BOTH.
>
>> > if (bdata->button->wakeup)
>> > disable_irq_wake(bdata->irq);
>> > bdata->suspended = false;
>> >diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
>> >index d06bf77400f1..7160df54a6fe 100644
>> >--- a/include/linux/gpio_keys.h
>> >+++ b/include/linux/gpio_keys.h
>> >@@ -13,6 +13,7 @@ struct device;
>> > * @desc: label that will be attached to button's gpio
>> > * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS)
>> > * @wakeup: configure the button as a wake-up source
>> >+ * @wakeup_event_action: event action to trigger wakeup
>> > * @debounce_interval: debounce ticks interval in msecs
>> > * @can_disable: %true indicates that userspace is allowed to
>> > * disable button via sysfs
>> >@@ -26,6 +27,7 @@ struct gpio_keys_button {
>> > const char *desc;
>> > unsigned int type;
>> > int wakeup;
>> >+ int wakeup_event_action;
>> > int debounce_interval;
>> > bool can_disable;
>> > int value;
>> >diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
>> >index 53fbae27b280..d7917b0bd438 100644
>> >--- a/include/uapi/linux/input-event-codes.h
>> >+++ b/include/uapi/linux/input-event-codes.h
>> >@@ -32,6 +32,15 @@
>> > #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
>> >
>> > /*
>> >+ * Event action types
>> >+ */
>> >+#define EV_ACT_ANY 0x00 /* asserted or deasserted */
>> >+#define EV_ACT_ASSERTED 0x01 /* asserted */
>> >+#define EV_ACT_DEASSERTED 0x02 /* deasserted */
> These do not belong here: they are of no interest to userspace but
> simply a driver specific quirk. If you want to share symbolic names add
> include/dt-bindings/input/gpio-keys.h
ok
>
>
>> >+#define EV_ACT_MAX 0x02
>> >+#define EV_ACT_CNT (EV_ACT_MAX+1)
> These are not needed at all: you are not defining input bitmasks.
>
^ permalink raw reply
* [PATCH v4 0/3] gpio-keys: Add support for specifying wakeup event action
From: Jeffy Chen @ 2018-03-06 7:44 UTC (permalink / raw)
To: linux-kernel
Cc: briannorris, heiko, dtor, dianders, Jeffy Chen, Guenter Roeck,
Arnd Bergmann, Joseph Lo, Rob Herring, Catalin Marinas,
Emil Renner Berthing, Brian Norris, Thomas Gleixner,
Philippe Ombredanne, linux-rockchip, Kate Stewart, linux-input,
Will Deacon, Matthias Kaehlcke, devicetree, stephen lu,
Greg Kroah-Hartman, Arvind Yadav <arv>
On chromebook kevin, we are using gpio-keys for pen insert event. But
we only want it to wakeup the system when ejecting the pen.
So we may need to change the interrupt trigger type during suspending.
Changes in v4:
Add dt-binding gpio-keys.h, stop saving irq trigger type, add enable/disable wakeup helpers as Dmitry suggested.
Include dt-binding gpio-keys.h
Changes in v3:
Adding more comments as Brian suggested.
Changes in v2:
Specify wakeup event action instead of irq trigger type as Brian
suggested.
Specify wakeup event action instead of irq trigger type as Brian
suggested.
Specify wakeup event action instead of irq trigger type as Brian
suggested.
Jeffy Chen (3):
Input: gpio-keys - add support for wakeup event action
Input: gpio-keys - allow setting wakeup event action in DT
arm64: dts: rockchip: kevin: Avoid wakeup when inserting the pen
.../devicetree/bindings/input/gpio-keys.txt | 8 +++
arch/arm64/boot/dts/rockchip/rk3399-gru-kevin.dts | 3 +
drivers/input/keyboard/gpio_keys.c | 67 +++++++++++++++++++++-
include/dt-bindings/input/gpio-keys.h | 13 +++++
include/linux/gpio_keys.h | 2 +
5 files changed, 90 insertions(+), 3 deletions(-)
create mode 100644 include/dt-bindings/input/gpio-keys.h
--
2.11.0
^ permalink raw reply
* Re: [PATCH v3 1/3] Input: gpio-keys - add support for wakeup event action
From: Dmitry Torokhov @ 2018-03-06 0:30 UTC (permalink / raw)
To: Jeffy Chen
Cc: linux-kernel, briannorris, heiko, dtor, dianders, Guenter Roeck,
Enric Balletbo i Serra, Thomas Gleixner, Joseph Lo, stephen lu,
Kate Stewart, linux-input, Greg Kroah-Hartman,
Philippe Ombredanne, Arvind Yadav
In-Reply-To: <20180302035102.10084-2-jeffy.chen@rock-chips.com>
Hi Jeffy,
On Fri, Mar 02, 2018 at 11:51:00AM +0800, Jeffy Chen wrote:
> Add support for specifying event actions to trigger wakeup when using
> the gpio-keys input device as a wakeup source.
>
> This would allow the device to configure when to wakeup the system. For
> example a gpio-keys input device for pen insert, may only want to wakeup
> the system when ejecting the pen.
>
> Suggested-by: Brian Norris <briannorris@chromium.org>
> Signed-off-by: Jeffy Chen <jeffy.chen@rock-chips.com>
> ---
>
> Changes in v3:
> Adding more comments as Brian suggested.
>
> Changes in v2:
> Specify wakeup event action instead of irq trigger type as Brian
> suggested.
>
> drivers/input/keyboard/gpio_keys.c | 39 ++++++++++++++++++++++++++++++++++
> include/linux/gpio_keys.h | 2 ++
> include/uapi/linux/input-event-codes.h | 9 ++++++++
> 3 files changed, 50 insertions(+)
>
> diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
> index 87e613dc33b8..607f3960c886 100644
> --- a/drivers/input/keyboard/gpio_keys.c
> +++ b/drivers/input/keyboard/gpio_keys.c
> @@ -45,6 +45,8 @@ struct gpio_button_data {
> unsigned int software_debounce; /* in msecs, for GPIO-driven buttons */
>
> unsigned int irq;
> + unsigned int irq_trigger_type;
> + unsigned int wakeup_trigger_type;
> spinlock_t lock;
> bool disabled;
> bool key_pressed;
> @@ -540,6 +542,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
> }
>
> if (bdata->gpiod) {
> + int active_low = gpiod_is_active_low(bdata->gpiod);
> +
> if (button->debounce_interval) {
> error = gpiod_set_debounce(bdata->gpiod,
> button->debounce_interval * 1000);
> @@ -568,6 +572,22 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
> isr = gpio_keys_gpio_isr;
> irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
>
> + switch (button->wakeup_event_action) {
> + case EV_ACT_ASSERTED:
> + bdata->wakeup_trigger_type = active_low ?
> + IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING;
IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
> + break;
> + case EV_ACT_DEASSERTED:
> + bdata->wakeup_trigger_type = active_low ?
> + IRQF_TRIGGER_RISING : IRQF_TRIGGER_FALLING;
> + break;
case EV_ACT_ANY:
> + default:
> + /*
> + * For other cases, we are OK letting suspend/resume
> + * not reconfigure the trigger type.
> + */
> + break;
> + }
> } else {
> if (!button->irq) {
> dev_err(dev, "Found button without gpio or irq\n");
> @@ -586,6 +606,12 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
>
> isr = gpio_keys_irq_isr;
> irqflags = 0;
> +
> + /*
> + * For IRQ buttons, the irq trigger type for press and release
> + * are the same. So we don't need to reconfigure the trigger
> + * type for wakeup.
That is not entirely accurate. Interrupt triggers button press, which is
followed by either immediate or delayed release. There is no interrupt
for release.
> + */
> }
>
> bdata->code = &ddata->keymap[idx];
> @@ -618,6 +644,8 @@ static int gpio_keys_setup_key(struct platform_device *pdev,
> return error;
> }
>
> + bdata->irq_trigger_type = irq_get_trigger_type(bdata->irq);
Why do we need to store the trigger type? It is always both edges for
gpio-based keys and we do not support changing wakeup trigger for
interrupt-based keys.
> +
> return 0;
> }
>
> @@ -718,6 +746,9 @@ gpio_keys_get_devtree_pdata(struct device *dev)
> /* legacy name */
> fwnode_property_read_bool(child, "gpio-key,wakeup");
>
> + fwnode_property_read_u32(child, "wakeup-event-action",
> + &button->wakeup_event_action);
> +
> button->can_disable =
> fwnode_property_read_bool(child, "linux,can-disable");
>
> @@ -854,6 +885,10 @@ static int __maybe_unused gpio_keys_suspend(struct device *dev)
> if (device_may_wakeup(dev)) {
> for (i = 0; i < ddata->pdata->nbuttons; i++) {
> struct gpio_button_data *bdata = &ddata->data[i];
> +
> + if (bdata->button->wakeup && bdata->wakeup_trigger_type)
> + irq_set_irq_type(bdata->irq,
> + bdata->wakeup_trigger_type);
if (bdata->button->wakeup) {
if (bdata->wakeup_trigger_type) {
error = ...;
}
enable_irq_wake(bdata->irq);
}
Might need to be split into a helper; if you add error handling to
enable_irq_wake() that woudl be great too.
> if (bdata->button->wakeup)
> enable_irq_wake(bdata->irq);
> bdata->suspended = true;
> @@ -878,6 +913,10 @@ static int __maybe_unused gpio_keys_resume(struct device *dev)
> if (device_may_wakeup(dev)) {
> for (i = 0; i < ddata->pdata->nbuttons; i++) {
> struct gpio_button_data *bdata = &ddata->data[i];
> +
> + if (bdata->button->wakeup && bdata->wakeup_trigger_type)
> + irq_set_irq_type(bdata->irq,
> + bdata->irq_trigger_type);
Just use IRQ_TYPE_EDGE_BOTH.
> if (bdata->button->wakeup)
> disable_irq_wake(bdata->irq);
> bdata->suspended = false;
> diff --git a/include/linux/gpio_keys.h b/include/linux/gpio_keys.h
> index d06bf77400f1..7160df54a6fe 100644
> --- a/include/linux/gpio_keys.h
> +++ b/include/linux/gpio_keys.h
> @@ -13,6 +13,7 @@ struct device;
> * @desc: label that will be attached to button's gpio
> * @type: input event type (%EV_KEY, %EV_SW, %EV_ABS)
> * @wakeup: configure the button as a wake-up source
> + * @wakeup_event_action: event action to trigger wakeup
> * @debounce_interval: debounce ticks interval in msecs
> * @can_disable: %true indicates that userspace is allowed to
> * disable button via sysfs
> @@ -26,6 +27,7 @@ struct gpio_keys_button {
> const char *desc;
> unsigned int type;
> int wakeup;
> + int wakeup_event_action;
> int debounce_interval;
> bool can_disable;
> int value;
> diff --git a/include/uapi/linux/input-event-codes.h b/include/uapi/linux/input-event-codes.h
> index 53fbae27b280..d7917b0bd438 100644
> --- a/include/uapi/linux/input-event-codes.h
> +++ b/include/uapi/linux/input-event-codes.h
> @@ -32,6 +32,15 @@
> #define INPUT_PROP_CNT (INPUT_PROP_MAX + 1)
>
> /*
> + * Event action types
> + */
> +#define EV_ACT_ANY 0x00 /* asserted or deasserted */
> +#define EV_ACT_ASSERTED 0x01 /* asserted */
> +#define EV_ACT_DEASSERTED 0x02 /* deasserted */
These do not belong here: they are of no interest to userspace but
simply a driver specific quirk. If you want to share symbolic names add
include/dt-bindings/input/gpio-keys.h
> +#define EV_ACT_MAX 0x02
> +#define EV_ACT_CNT (EV_ACT_MAX+1)
These are not needed at all: you are not defining input bitmasks.
> +
> +/*
> * Event types
> */
>
> --
> 2.11.0
>
>
Thanks.
--
Dmitry
^ permalink raw reply
* [PATCH] HID: hid-multitouch: Use true and false for boolean values
From: Gustavo A. R. Silva @ 2018-03-06 0:10 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Gustavo A. R. Silva
Assign true or false to boolean variables instead of an integer value.
This issue was detected with the help of Coccinelle.
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
drivers/hid/hid-multitouch.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c
index 2e1736b..a144d30 100644
--- a/drivers/hid/hid-multitouch.c
+++ b/drivers/hid/hid-multitouch.c
@@ -723,7 +723,7 @@ static void mt_complete_slot(struct mt_device *td, struct input_dev *input)
}
if (!(td->mtclass.quirks & MT_QUIRK_CONFIDENCE))
- s->confidence_state = 1;
+ s->confidence_state = true;
active = (s->touch_state || s->inrange_state) &&
s->confidence_state;
--
2.7.4
^ permalink raw reply related
* [PATCH] HID: hid-ntrig: use true and false for boolean values
From: Gustavo A. R. Silva @ 2018-03-06 0:06 UTC (permalink / raw)
To: Jiri Kosina, Benjamin Tissoires
Cc: linux-input, linux-kernel, Gustavo A. R. Silva
Assign true or false to boolean variables instead of an integer value.
This issue was detected with the help of Coccinelle.
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
drivers/hid/hid-ntrig.c | 12 ++++++------
1 file changed, 6 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/hid-ntrig.c b/drivers/hid/hid-ntrig.c
index 3d121d8..43b1c72 100644
--- a/drivers/hid/hid-ntrig.c
+++ b/drivers/hid/hid-ntrig.c
@@ -591,8 +591,8 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
switch (usage->hid) {
case 0xff000001:
/* Tag indicating the start of a multitouch group */
- nd->reading_mt = 1;
- nd->first_contact_touch = 0;
+ nd->reading_mt = true;
+ nd->first_contact_touch = false;
break;
case HID_DG_TIPSWITCH:
nd->tipswitch = value;
@@ -663,7 +663,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
* even if deactivation slack is turned off.
*/
nd->act_state = deactivate_slack - 1;
- nd->confidence = 0;
+ nd->confidence = false;
break;
}
@@ -679,7 +679,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
*/
if (nd->w < nd->min_width ||
nd->h < nd->min_height)
- nd->confidence = 0;
+ nd->confidence = false;
} else
break;
@@ -758,7 +758,7 @@ static int ntrig_event (struct hid_device *hid, struct hid_field *field,
if (!nd->reading_mt) /* Just to be sure */
break;
- nd->reading_mt = 0;
+ nd->reading_mt = false;
/*
@@ -910,7 +910,7 @@ static int ntrig_probe(struct hid_device *hdev, const struct hid_device_id *id)
return -ENOMEM;
}
- nd->reading_mt = 0;
+ nd->reading_mt = false;
nd->min_width = 0;
nd->min_height = 0;
nd->activate_slack = activate_slack;
--
2.7.4
^ permalink raw reply related
* [PATCH] Input: appletouch - use true and false for boolean values
From: Gustavo A. R. Silva @ 2018-03-06 0:00 UTC (permalink / raw)
To: Dmitry Torokhov; +Cc: linux-input, linux-kernel, Gustavo A. R. Silva
Assign true or false to boolean variables instead of an integer value.
This issue was detected with the help of Coccinelle.
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
drivers/input/mouse/appletouch.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/drivers/input/mouse/appletouch.c b/drivers/input/mouse/appletouch.c
index 81a695d..032d279 100644
--- a/drivers/input/mouse/appletouch.c
+++ b/drivers/input/mouse/appletouch.c
@@ -587,7 +587,7 @@ static void atp_complete_geyser_1_2(struct urb *urb)
/* Perform size detection, if not done already */
if (unlikely(!dev->size_detect_done)) {
atp_detect_size(dev);
- dev->size_detect_done = 1;
+ dev->size_detect_done = true;
goto exit;
}
}
@@ -813,7 +813,7 @@ static int atp_open(struct input_dev *input)
if (usb_submit_urb(dev->urb, GFP_ATOMIC))
return -EIO;
- dev->open = 1;
+ dev->open = true;
return 0;
}
@@ -823,7 +823,7 @@ static void atp_close(struct input_dev *input)
usb_kill_urb(dev->urb);
cancel_work_sync(&dev->work);
- dev->open = 0;
+ dev->open = false;
}
static int atp_handle_geyser(struct atp *dev)
--
2.7.4
^ permalink raw reply related
* Re: [PATCH v2 1/1] HID: Logitech K290: Add driver for the Logitech K290 USB keyboard
From: Florent Flament @ 2018-03-05 23:31 UTC (permalink / raw)
To: Benjamin Tissoires, Nestor Lopez Casado
Cc: andy.shevchenko, Jiri Kosina, lkml, open list:HID CORE LAYER
In-Reply-To: <CAO-hwJK=LVZXU5M1CxRvm43TzL7qVqNNepwT2qrriMnCS3akeA@mail.gmail.com>
On Mon, 2018-03-05 at 18:26 +0100, Benjamin Tissoires wrote:
> Hi Florent,
Hi Benjamin,
>
> On Mon, Mar 5, 2018 at 10:31 AM, Nestor Lopez Casado
> <nlopezcasad@logitech.com> wrote:
> > Hello Florent,
> >
> > In my view, this driver may not be a good idea. The default
> > behaviour
> > of K290 is 'send multimedia keycodes' with the user given the
> > choice
> > to change that behaviour via vendor commands. Putting a driver that
> > will unconditionally change that behaviour without the user's
> > consent
> > might bother other users that prefer the multimedia keycodes by
> > default.
> >
> > Besides, I'd argue that instead of a kernel module this would be
> > best
> > achieved from a user space application. Something in the lines of
> > Solaar (github pwr/solaar) or libratbag (there's an issue open to
> > support keyboards) or even a specific application built for the
> > purpose. Anyways, please collect the input from Benjamin and Jiri
> > as
> > they as they best placed to advise than myself.
>
> On top of what Nestor said, this type of functionality, if we want to
> have them in the kernel should probably be integrated in
> hid-logitech-hidpp, in order not having some magic reports to send.
>
> Things like reconnect of the device would be handled far more easily
> in hid-logitech-hidpp while you would be reinventing the wheel here.
>
> One other thing I do not like in this submission of the driver is the
> direct use of USB while we have a full transport agnostic layer
> called
> HID.
Fair enough, I didn't have a look at how hid-logitech-hidpp is working
yet. I'll dig into that to see if this driver can me implemented more
elegantly.
Regards,
Florent
> Cheers,
> Benjamin
>
> >
> > Cheers,
> > -nestor
> >
> > On Sun, Mar 4, 2018 at 11:14 PM, Florent Flament
> > <contact@florentflament.com> wrote:
> > > With the generic HID driver, K290 keyboards' F1 to F12 keys send
> > > multimedia events by default, and standard keycodes when the
> > > function
> > > key is pressed. This driver allows to configure K290 keyboards,
> > > so
> > > that F1 to F12 have a standard behavior and send multimedia
> > > events
> > > when the function key is pressed. The keyboard mode is set
> > > through the
> > > fn_mode module parameter: when set to 1 (default setting) the
> > > keyboard
> > > behaves as with the generic HID driver, when set to 0 the
> > > keyboard is
> > > configured to work as standard keyboards.
> > >
> > > Signed-off-by: Florent Flament <contact@florentflament.com>
> > > ---
> > > drivers/hid/Kconfig | 18 ++++++++
> > > drivers/hid/Makefile | 1 +
> > > drivers/hid/hid-ids.h | 1 +
> > > drivers/hid/hid-logitech-k290.c | 100
> > > ++++++++++++++++++++++++++++++++++++++++
> > > 4 files changed, 120 insertions(+)
> > > create mode 100644 drivers/hid/hid-logitech-k290.c
> > >
> > > diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> > > index 19c499f5623d..6686da8daac6 100644
> > > --- a/drivers/hid/Kconfig
> > > +++ b/drivers/hid/Kconfig
> > > @@ -488,6 +488,24 @@ config HID_LOGITECH_HIDPP
> > > T651, TK820), some mice (Zone Touch mouse), or even
> > > keyboards (Solar
> > > Keyboard).
> > >
> > > +config HID_LOGITECH_K290
> > > + tristate "Logitech K290 Keyboard support"
> > > + depends on USB_HID
> > > + ---help---
> > > + This enhances support of Logitech K290 keyboards.
> > > +
> > > + With the generic HID driver, K290 keyboards' F1 to F12
> > > keys
> > > + send multimedia events by default, and standard keycodes
> > > when
> > > + the function key is pressed. This driver allows to
> > > configure
> > > + K290 keyboards, so that F1 to F12 have a standard
> > > behavior and
> > > + send multimedia events when the function key is pressed.
> > > The
> > > + keyboard mode is set through the fn_mode module
> > > parameter:
> > > + when set to 1 (default setting) the keyboard behaves as
> > > with
> > > + the generic HID driver, when set to 0 the keyboard is
> > > + configured to work as standard keyboards.
> > > +
> > > + Say Y if you have a Logitech K290 keyboard.
> > > +
> > > config LOGITECH_FF
> > > bool "Logitech force feedback support"
> > > depends on HID_LOGITECH
> > > diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
> > > index eb13b9e92d85..78079d3a5d58 100644
> > > --- a/drivers/hid/Makefile
> > > +++ b/drivers/hid/Makefile
> > > @@ -61,6 +61,7 @@ obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
> > > obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
> > > obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
> > > obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
> > > +obj-$(CONFIG_HID_LOGITECH_K290) += hid-logitech-k290.o
> > > obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
> > > obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
> > > obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
> > > diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
> > > index 9454ac134ce2..68caba7e666c 100644
> > > --- a/drivers/hid/hid-ids.h
> > > +++ b/drivers/hid/hid-ids.h
> > > @@ -693,6 +693,7 @@
> > > #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
> > > #define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
> > > #define USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS 0xc24d
> > > +#define USB_DEVICE_ID_LOGITECH_KEYBOARD_K290 0xc31f
> > > #define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
> > > #define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
> > > #define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
> > > diff --git a/drivers/hid/hid-logitech-k290.c b/drivers/hid/hid-
> > > logitech-k290.c
> > > new file mode 100644
> > > index 000000000000..36fdb5838842
> > > --- /dev/null
> > > +++ b/drivers/hid/hid-logitech-k290.c
> > > @@ -0,0 +1,100 @@
> > > +// SPDX-License-Identifier: GPL-2.0
> > > +/*
> > > + * HID driver for Logitech K290 keyboard
> > > + *
> > > + * Copyright (c) 2018 Florent Flament
> > > + *
> > > + * This drivers allows to configure the K290 keyboard's function
> > > key
> > > + * behaviour (whether function mode is activated or not by
> > > default).
> > > + *
> > > + * Logitech custom commands taken from Marcus Ilgner k290-
> > > fnkeyctl
> > > + * (https://github.com/milgner/k290-fnkeyctl):
> > > + * K290_SET_FUNCTION_CMD
> > > + * K290_SET_FUNCTION_VAL
> > > + * K290_SET_FUNCTION_OFF
> > > + * K290_SET_FUNCTION_ON
> > > + *
> > > + * Based on hid-accutouch.c and hid-elo.c
> > > + */
> > > +
> > > +#include <linux/device.h>
> > > +#include <linux/hid.h>
> > > +#include <linux/module.h>
> > > +#include <linux/stat.h>
> > > +#include <linux/types.h>
> > > +#include <linux/usb.h>
> > > +
> > > +#include "hid-ids.h"
> > > +#include "usbhid/usbhid.h"
> > > +
> > > +// Logitech K290 custom USB command and value to setup function
> > > key
> > > +#define K290_SET_FUNCTION_CMD 0x02
> > > +#define K290_SET_FUNCTION_VAL 0x001a
> > > +
> > > +// Have function mode turned off (as with standard keyboards)
> > > +#define K290_SET_FUNCTION_OFF 0x0001
> > > +// Have function mode turned on (default k290 behavior)
> > > +#define K290_SET_FUNCTION_ON 0x0000
> > > +
> > > +// Function key default mode is set at module load time for
> > > every K290
> > > +// keyboards plugged on the machine. By default fn_mode = 1, i.e
> > > +// sending K290_SET_FUNCTION_ON (default K290 behavior).
> > > +static bool fn_mode = 1;
> > > +module_param(fn_mode, bool, 0444);
> > > +MODULE_PARM_DESC(fn_mode, "Logitech K290 function key mode
> > > (default = 1)");
> > > +
> > > +static void k290_set_function(struct usb_device *dev, uint16_t
> > > function_mode)
> > > +{
> > > + int ret;
> > > +
> > > + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
> > > + K290_SET_FUNCTION_CMD,
> > > + USB_DIR_OUT | USB_TYPE_VENDOR |
> > > USB_RECIP_DEVICE,
> > > + K290_SET_FUNCTION_VAL,
> > > + function_mode, 0, 0,
> > > USB_CTRL_SET_TIMEOUT);
> > > +
> > > + if (ret < 0)
> > > + dev_err(&dev->dev,
> > > + "Failed to setup K290 function key, error
> > > %d\n", ret);
> > > +}
> > > +
> > > +static int k290_set_function_hid_device(struct hid_device *hid)
> > > +{
> > > + struct usb_device *usb_dev = hid_to_usb_dev(hid);
> > > +
> > > + k290_set_function(usb_dev,
> > > + fn_mode ? K290_SET_FUNCTION_ON :
> > > K290_SET_FUNCTION_OFF);
> > > + return 0;
> > > +}
> > > +
> > > +static int k290_input_configured(struct hid_device *hid,
> > > + struct hid_input *hidinput)
> > > +{
> > > + return k290_set_function_hid_device(hid);
> > > +}
> > > +
> > > +static int k290_resume(struct hid_device *hid)
> > > +{
> > > + return k290_set_function_hid_device(hid);
> > > +}
> > > +
> > > +static const struct hid_device_id k290_devices[] = {
> > > + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
> > > + USB_DEVICE_ID_LOGITECH_KEYBOARD_K290) },
> > > + { }
> > > +};
> > > +MODULE_DEVICE_TABLE(hid, k290_devices);
> > > +
> > > +static struct hid_driver k290_driver = {
> > > + .name = "hid-logitech-k290",
> > > + .id_table = k290_devices,
> > > + .input_configured = k290_input_configured,
> > > + .resume = k290_resume,
> > > + .reset_resume = k290_resume,
> > > +};
> > > +
> > > +module_hid_driver(k290_driver);
> > > +
> > > +MODULE_AUTHOR("Florent Flament <contact@florentflament.com>");
> > > +MODULE_DESCRIPTION("Logitech K290 keyboard driver");
> > > +MODULE_LICENSE("GPL v2");
> > > --
> > > 2.14.3
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-
> > > input" in
> > > the body of a message to majordomo@vger.kernel.org
> > > More majordomo info at http://vger.kernel.org/majordomo-info.htm
> > > l
^ permalink raw reply
* Re: [PATCH v2 1/1] HID: Logitech K290: Add driver for the Logitech K290 USB keyboard
From: Florent Flament @ 2018-03-05 23:25 UTC (permalink / raw)
To: Nestor Lopez Casado
Cc: andy.shevchenko, jikos, Benjamin Tissoires, linux-kernel,
open list:HID CORE LAYER
In-Reply-To: <CAE7qMrpF4dGCUrtnXbxb4MLFPZ0Wf13mrQEPypwPbEHaB9e44A@mail.gmail.com>
On Mon, 2018-03-05 at 10:31 +0100, Nestor Lopez Casado wrote:
> Hello Florent,
Hi Nestor,
> In my view, this driver may not be a good idea. The default behaviour
> of K290 is 'send multimedia keycodes' with the user given the choice
> to change that behaviour via vendor commands. Putting a driver that
> will unconditionally change that behaviour without the user's consent
> might bother other users that prefer the multimedia keycodes by
> default.
Actually, the default behavior of the proposed driver is currently to
let the K290 send multimedia keycodes by default (as if using the
generic HID driver). And this behavior can be changed by using the
fn_mode parameter. We may also add a third behavior consisting in not
doing anything, and letting a user space application managing the
keyboard, which could possibly be the default behavior.
> Besides, I'd argue that instead of a kernel module this would be best
> achieved from a user space application. Something in the lines of
> Solaar (github pwr/solaar) or libratbag (there's an issue open to
> support keyboards) or even a specific application built for the
> purpose. Anyways, please collect the input from Benjamin and Jiri as
> they as they best placed to advise than myself.
Indeed, this driver is based on a working user space application
available there https://github.com/milgner/k290-fnkeyctl . However, I
feel a bit awkward to have to install a dedicated package, or compile &
install an application to have proper support of a keyboard. I feel
like it would be more beautiful to have it supported directly by a
module, like most devices.
Regards,
Florent
^ permalink raw reply
* Re: [PATCH v2 3/4] hwmon: add Gateworks System Controller support
From: Guenter Roeck @ 2018-03-05 23:12 UTC (permalink / raw)
To: Tim Harvey
Cc: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov,
linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input
In-Reply-To: <1520287361-12569-4-git-send-email-tharvey@gateworks.com>
On Mon, Mar 05, 2018 at 02:02:40PM -0800, Tim Harvey wrote:
> The Gateworks System Controller has a hwmon sub-component that exposes
> up to 16 ADC's, some of which are temperature sensors, others which are
> voltage inputs. The ADC configuration (register mapping and name) is
> configured via device-tree and varies board to board.
>
> Cc: Guenter Roeck <linux@roeck-us.net>
> Signed-off-by: Tim Harvey <tharvey@gateworks.com>
> ---
> v2:
> - change license comment style
> - remove DEBUG
> - simplify regmap_bulk_read err check
> - remove break after returns in switch statement
> - fix fan setpoint buffer address
> - remove unnecessary parens
> - consistently use struct device *dev pointer
> - change license/comment block
> - add validation for hwmon child node props
> - move parsing of of to own function
> - use strlcpy to ensure null termination
> - fix static array sizes and removed unnecessary initializers
> - dynamically allocate channels
> - fix fan input label
> - support platform data
> - fixed whitespace issues
>
> drivers/hwmon/Kconfig | 9 +
> drivers/hwmon/Makefile | 1 +
> drivers/hwmon/gsc-hwmon.c | 330 ++++++++++++++++++++++++++++++++
> include/linux/platform_data/gsc_hwmon.h | 34 ++++
> 4 files changed, 374 insertions(+)
> create mode 100644 drivers/hwmon/gsc-hwmon.c
> create mode 100644 include/linux/platform_data/gsc_hwmon.h
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index 7ad0176..e052798b 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -475,6 +475,15 @@ config SENSORS_F75375S
> This driver can also be built as a module. If so, the module
> will be called f75375s.
>
> +config SENSORS_GSC
> + tristate "Gateworks System Controller ADC"
> + depends on MFD_GSC
> + help
> + Support for the Gateworks System Controller A/D converters.
> +
> + To compile this driver as a module, choose M here:
> + the module will be called gsc-hwmon.
> +
> config SENSORS_MC13783_ADC
> tristate "Freescale MC13783/MC13892 ADC"
> depends on MFD_MC13XXX
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 0fe489f..835a536 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -69,6 +69,7 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o
> obj-$(CONFIG_SENSORS_G762) += g762.o
> obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
> obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
> +obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
> obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
> obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
> obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
> diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c
> new file mode 100644
> index 0000000..cf55457
> --- /dev/null
> +++ b/drivers/hwmon/gsc-hwmon.c
> @@ -0,0 +1,330 @@
> +/* SPDX-License-Identifier: GPL-2.0
> + *
> + * Copyright (C) 2018 Gateworks Corporation
> + *
> + * This driver registers Linux HWMON attributes for GSC ADC's
> + */
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/mfd/gsc.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/regmap.h>
> +#include <linux/slab.h>
> +
> +#include <linux/platform_data/gsc_hwmon.h>
> +
> +#define GSC_HWMON_MAX_TEMP_CH 16
> +#define GSC_HWMON_MAX_IN_CH 16
> +#define GSC_HWMON_MAX_FAN_CH 6
> +
> +struct gsc_hwmon_data {
> + struct gsc_dev *gsc;
> + struct device *dev;
> + struct gsc_hwmon_platform_data *pdata;
> + const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH];
> + const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH];
> + const struct gsc_hwmon_channel *fan_ch[GSC_HWMON_MAX_FAN_CH];
> + u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1];
> + u32 in_config[GSC_HWMON_MAX_IN_CH + 1];
> + u32 fan_config[GSC_HWMON_MAX_FAN_CH + 1];
> + struct hwmon_channel_info temp_info;
> + struct hwmon_channel_info in_info;
> + struct hwmon_channel_info fan_info;
> + const struct hwmon_channel_info *info[4];
> + struct hwmon_chip_info chip;
> +};
> +
> +static int
> +gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long *val)
> +{
> + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
> + int sz, ret;
> + u8 reg;
> + u8 buf[3];
> +
> + dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
> + channel);
> + switch (type) {
> + case hwmon_in:
> + if (channel > GSC_HWMON_MAX_IN_CH)
> + return -EOPNOTSUPP;
> + reg = hwmon->in_ch[channel]->reg;
> + sz = 3;
> + break;
> + case hwmon_temp:
> + if (channel > GSC_HWMON_MAX_TEMP_CH)
> + return -EOPNOTSUPP;
> + reg = hwmon->temp_ch[channel]->reg;
> + sz = 2;
> + break;
> + case hwmon_fan:
> + if (channel > GSC_HWMON_MAX_FAN_CH)
> + return -EOPNOTSUPP;
> + reg = hwmon->fan_ch[channel]->reg;
> + sz = 2;
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> +
> + ret = regmap_bulk_read(hwmon->gsc->regmap_hwmon, reg, &buf, sz);
> + if (ret)
> + return ret;
> +
> + *val = 0;
> + while (sz-- > 0)
> + *val |= (buf[sz] << (8*sz));
> + if ((type == hwmon_temp) && *val > 0x8000)
> + *val -= 0xffff;
> +
> + return 0;
> +}
> +
> +static int
> +gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
> + u32 attr, int channel, const char **buf)
> +{
> + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
> +
> + dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
> + channel);
> + switch (type) {
> + case hwmon_in:
> + if (channel > GSC_HWMON_MAX_IN_CH)
> + return -EOPNOTSUPP;
First of all, this is wrong. in_ch only has GSC_HWMON_MAX_IN_CH
elements, meaning hwmon->in_ch[GSC_HWMON_MAX_IN_CH] will access
data after the end of the array. Second, valitations should not
be done here but in the is_visible function.
> + *buf = hwmon->in_ch[channel]->name;
> + break;
> + case hwmon_temp:
> + if (channel > GSC_HWMON_MAX_TEMP_CH)
> + return -EOPNOTSUPP;
Same here and everywhere else.
> + *buf = hwmon->temp_ch[channel]->name;
> + break;
> + case hwmon_fan:
> + if (channel > GSC_HWMON_MAX_FAN_CH)
> + return -EOPNOTSUPP;
> + *buf = hwmon->fan_ch[channel]->name;
> + break;
> + default:
> + return -ENOTSUPP;
> + }
> +
> + return 0;
> +}
> +
> +static int
> +gsc_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
> + int channel, long val)
> +{
> + struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
> + u8 buf[3];
> +
> + dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
> + channel);
> + switch (type) {
> + case hwmon_fan:
> + if (channel == GSC_HWMON_MAX_FAN_CH)
> + return -EOPNOTSUPP;
> + buf[0] = val & 0xff;
> + buf[1] = (val >> 8) & 0xff;
> + return regmap_bulk_write(hwmon->gsc->regmap_hwmon,
> + hwmon->fan_ch[channel]->reg, buf, 2);
> + default:
> + break;
> + }
> +
> + return -EOPNOTSUPP;
> +}
> +
> +static umode_t
> +gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
> + int ch)
> +{
> + const struct gsc_hwmon_data *hwmon = _data;
> + struct device *dev = hwmon->gsc->dev;
> + umode_t mode = 0;
> +
> + switch (type) {
> + case hwmon_fan:
> + mode = S_IRUGO;
> + if (attr == hwmon_fan_input)
> + mode |= S_IWUSR;
> + break;
> + case hwmon_temp:
> + case hwmon_in:
> + mode = S_IRUGO;
> + break;
> + default:
> + break;
> + }
> + dev_dbg(dev, "%s type=%d attr=%d ch=%d mode=0x%x\n", __func__, type,
> + attr, ch, mode);
> +
> + return mode;
> +}
> +
> +static const struct hwmon_ops gsc_hwmon_ops = {
> + .is_visible = gsc_hwmon_is_visible,
> + .read = gsc_hwmon_read,
> + .read_string = gsc_hwmon_read_string,
> + .write = gsc_hwmon_write,
> +};
> +
> +static struct gsc_hwmon_platform_data *
> +gsc_hwmon_get_devtree_pdata(struct device *dev)
> +{
> + struct gsc_hwmon_platform_data *pdata;
> + struct gsc_hwmon_channel *ch;
> + struct fwnode_handle *child;
> + int nchannels;
> +
> + nchannels = device_get_child_node_count(dev);
> + dev_dbg(dev, "channels=%d\n", nchannels);
> + if (nchannels == 0)
> + return ERR_PTR(-ENODEV);
> +
> + pdata = devm_kzalloc(dev,
> + sizeof(*pdata) + nchannels * sizeof(*ch),
> + GFP_KERNEL);
> + if (!pdata)
> + return ERR_PTR(-ENOMEM);
> + ch = (struct gsc_hwmon_channel *)(pdata + 1);
> + pdata->channels = ch;
> + pdata->nchannels = nchannels;
> +
> + /* allocate structures for channels and count instances of each type */
> + device_for_each_child_node(dev, child) {
> + if (fwnode_property_read_string(child, "label", &ch->name)) {
> + dev_err(dev, "channel without label\n");
> + fwnode_handle_put(child);
> + return ERR_PTR(-EINVAL);
> + }
> + if (fwnode_property_read_u32(child, "reg", &ch->reg)) {
> + dev_err(dev, "channel without reg\n");
> + fwnode_handle_put(child);
> + return ERR_PTR(-EINVAL);
> + }
> + if (fwnode_property_read_u32(child, "type", &ch->type)) {
> + dev_err(dev, "channel without type\n");
> + fwnode_handle_put(child);
> + return ERR_PTR(-EINVAL);
> + }
> + dev_dbg(dev, "of: reg=0x%02x type=%d %s\n", ch->reg, ch->type,
> + ch->name);
> + ch++;
> + }
> +
> + return pdata;
> +}
> +
> +static int gsc_hwmon_probe(struct platform_device *pdev)
> +{
> + struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent);
> + struct device *dev = &pdev->dev;
> + struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev);
> + struct gsc_hwmon_data *hwmon;
> + int i, i_in, i_temp, i_fan;
> +
> + if (!pdata) {
> + pdata = gsc_hwmon_get_devtree_pdata(dev);
> + if (IS_ERR(pdata))
> + return PTR_ERR(pdata);
> + }
> +
> + hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
> + if (!hwmon)
> + return -ENOMEM;
> + hwmon->gsc = gsc;
> + hwmon->pdata = pdata;
> +
> + for (i = 0, i_in = 0, i_temp = 0, i_fan = 0;
> + i < hwmon->pdata->nchannels; i++) {
> + const struct gsc_hwmon_channel *ch = &pdata->channels[i];
> +
> + if (ch->reg > GSC_HWMON_MAX_REG) {
> + dev_err(dev, "invalid reg: 0x%02x\n", ch->reg);
> + return -EINVAL;
> + }
> + switch (ch->type) {
> + case type_temperature:
> + if (i_temp == GSC_HWMON_MAX_TEMP_CH) {
> + dev_err(dev, "too many temp channels\n");
> + return -EINVAL;
> + }
> + hwmon->temp_ch[i_temp] = ch;
> + hwmon->temp_config[i_temp] = HWMON_T_INPUT |
> + HWMON_T_LABEL;
> + i_temp++;
> + break;
> + case type_voltage:
> + if (i_in == GSC_HWMON_MAX_IN_CH) {
> + dev_err(dev, "too many voltage channels\n");
> + return -EINVAL;
> + }
> + hwmon->in_ch[i_in] = ch;
> + hwmon->in_config[i_in] =
> + HWMON_I_INPUT | HWMON_I_LABEL;
> + i_in++;
> + break;
> + case type_fan:
> + if (i_fan == GSC_HWMON_MAX_FAN_CH) {
> + dev_err(dev, "too many voltage channels\n");
> + return -EINVAL;
> + }
> + hwmon->fan_ch[i_fan] = ch;
> + hwmon->fan_config[i_fan] =
> + HWMON_F_INPUT | HWMON_F_LABEL;
> + i_fan++;
> + break;
> + default:
> + dev_err(dev, "invalid type: %d\n", ch->type);
> + return -EINVAL;
> + }
> + dev_dbg(dev, "pdata: reg=0x%02x type=%d %s\n", ch->reg,
> + ch->type, ch->name);
> + }
> +
> + /* terminate channel config lists */
> + hwmon->temp_config[i_temp] = 0;
> + hwmon->in_config[i_in] = 0;
> + hwmon->fan_config[i_fan] = 0;
> +
> + /* setup config structures */
> + hwmon->chip.ops = &gsc_hwmon_ops;
> + hwmon->chip.info = hwmon->info;
> + hwmon->info[0] = &hwmon->temp_info;
> + hwmon->info[1] = &hwmon->in_info;
> + hwmon->info[2] = &hwmon->fan_info;
> + hwmon->temp_info.type = hwmon_temp;
> + hwmon->temp_info.config = hwmon->temp_config;
> + hwmon->in_info.type = hwmon_in;
> + hwmon->in_info.config = hwmon->in_config;
> + hwmon->fan_info.type = hwmon_fan;
> + hwmon->fan_info.config = hwmon->fan_config;
> +
> + hwmon->dev = devm_hwmon_device_register_with_info(dev,
> + KBUILD_MODNAME, hwmon,
> + &hwmon->chip, NULL);
> + return PTR_ERR_OR_ZERO(hwmon->dev);
> +}
> +
> +static const struct of_device_id gsc_hwmon_of_match[] = {
> + { .compatible = "gw,gsc-hwmon", },
> + {}
> +};
> +
> +static struct platform_driver gsc_hwmon_driver = {
> + .driver = {
> + .name = KBUILD_MODNAME,
> + .of_match_table = gsc_hwmon_of_match,
> + },
> + .probe = gsc_hwmon_probe,
> +};
> +
> +module_platform_driver(gsc_hwmon_driver);
> +
> +MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
> +MODULE_DESCRIPTION("GSC hardware monitor driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/platform_data/gsc_hwmon.h b/include/linux/platform_data/gsc_hwmon.h
> new file mode 100644
> index 0000000..ba4f811
> --- /dev/null
> +++ b/include/linux/platform_data/gsc_hwmon.h
> @@ -0,0 +1,34 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _GSC_HWMON_H
> +#define _GSC_HWMON_H
> +
> +enum gsc_hwmon_type {
> + type_temperature,
> + type_voltage,
> + type_fan,
> +};
> +
> +/**
> + * struct gsc_hwmon_channel - configuration parameters
> + * @reg: I2C register offset
> + * @type: channel type
> + * @name: channel name
> + */
> +struct gsc_hwmon_channel {
> + unsigned int reg;
> + unsigned int type;
> + const char *name;
> +};
> +
> +/**
> + * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver
> + * @channels: pointer to array of gsc_hwmon_channel structures
> + * describing channels
> + * @nchannels: number of elements in @channels array
> + */
> +struct gsc_hwmon_platform_data {
> + const struct gsc_hwmon_channel *channels;
> + int nchannels;
> +};
> +
> +#endif
> --
> 2.7.4
>
^ permalink raw reply
* [PATCH v4 00/38] DRM Rockchip rk3399 (Kevin)
From: Enric Balletbo i Serra @ 2018-03-05 22:12 UTC (permalink / raw)
To: architt, inki.dae, thierry.reding, hjc, seanpaul, airlied, tfiga,
heiko
Cc: hshi, wzz, hl, linux-kernel, dri-devel, dianders, linux-rockchip,
ykk, orjan.eide, mark.yao, m.szyprowski, Andrzej Hajda,
Yakir Yang, Jingoo Han, Laurent Pinchart, linux-samsung-soc,
Seung-Woo Kim, Kyungmin Park, linux-input, Jeffy Chen, Kukjin Kim,
Tomeu Vizoso, Henrik Rydberg, Joonyoung Shim
Hi,
This patchset includes cleanups, improvements, and bug fixes for
Rockchip DRM driver and PSR support.
This new version is the same as before but rebased on top of
drm-misc-next plus the Marek patches that fixes Chromebook2 Peach-Pit [1]
and the latest series of Rockchip rk3399 eDP support [2].
[1] https://www.spinics.net/lists/dri-devel/msg167804.html
[2] https://www.spinics.net/lists/dri-devel/msg167770.html
Regards,
Enric
Changes in v4:
- Rebased all on top of drm-misc-next
- Removed the following patches as are already applied.
[PATCH v3 01/43] drm/rockchip: Get rid of unnecessary struct fields
[PATCH v3 02/43] drm/rockchip: support prime import sg table
[PATCH v3 03/43] drm/rockchip: Respect page offset for PRIME mmap
calls
- Removed the following patches as now are part of another patchset
[PATCH v3 05/43] drm/bridge: analogix_dp: Don't power bridge in
analogix_dp_bind
[PATCH v3 33/43] drm/panel: simple: Change mode for Sharp lq123p1jx31
Changes in v3:
- Addressed some of the comments from Sean on the v2
Changes in v2:
- A few patches have been replaced by newer and cleaner versions from
the ChromeOS kernel gerrit, especially about disallowing PSR for the
whole atomic commit.
Douglas Anderson (4):
drm/bridge: analogix_dp: Reorder plat_data->power_off to happen sooner
drm/bridge: analogix_dp: Properly log AUX CH errors
drm/bridge: analogix_dp: Properly disable aux chan retries on rockchip
drm/bridge: analogix_dp: Split the platform-specific poweron in two
parts
Kristian H. Kristensen (1):
drm/rockchip: Disable PSR on input events
Lin Huang (6):
drm/bridge: analogix_dp: Move enable video into config_video()
drm/bridge: analogix_dp: Check AUX_EN status when doing AUX transfer
drm/bridge: analogix_dp: Ensure edp is disabled when shutting down the
panel
drm/bridge: analogix_dp: Extend hpd check time to 100ms
drm/bridge: analogix_dp: Check dpcd write/read status
drm/bridge: analogix_dp: Reset aux channel if an error occurred
Mark Yao (1):
drm/rockchip: pre dither down when output bpc is 8bit
Sean Paul (2):
drm/rockchip: Don't use atomic constructs for psr
drm/rockchip: Remove analogix psr worker
Tomasz Figa (7):
drm/rockchip: analogix_dp: Do not call Analogix code before bind
drm/rockchip: Cancel PSR enable work before changing the state
drm/rockchip: psr: Avoid redundant calls to .set() callback
drm/rockchip: psr: Sanitize semantics of allow/disallow API
drm/rockchip: Disable PSR from reboot notifier
drm/rockchip: Disallow PSR for the whole atomic commit
drm/rockchip: psr: Remove flush by CRTC
Yakir Yang (1):
drm/bridge: analogix_dp: detect Sink PSR state after configuring the
PSR
zain wang (16):
drm/bridge: analogix_dp: set psr activate/deactivate when
enable/disable bridge
drm/bridge: analogix_dp: Don't change psr while bridge is disabled
drm/rockchip: add mutex vop lock
drm/bridge: analogix_dp: add fast link train for eDP
drm/rockchip: Only wait for panel ACK on PSR entry
drm/bridge: analogix_dp: Don't use fast link training when panel just
powered up
drm/bridge: analogix_dp: Retry bridge enable when it failed
drm/bridge: analogix_dp: Wait for HPD signal before configuring link
drm/bridge: analogix_dp: Set PD_INC_BG first when powering up edp phy
drm/bridge: analogix_dp: Fix incorrect usage of enhanced mode
drm/bridge: analogix_dp: Fix AUX_PD bit for Rockchip
drm/rockchip: Restore psr->state when enable/disable psr failed
drm/bridge: analogix_dp: Don't use ANALOGIX_DP_PLL_CTL to control pll
drm/bridge: analogix_dp: Fix timeout of video streamclk config
drm/bridge: analogix_dp: Fix incorrect operations with register
ANALOGIX_DP_FUNC_EN_1
drm/bridge: analogix_dp: Move fast link training detect to set_bridge
drivers/gpu/drm/bridge/analogix/analogix_dp_core.c | 452 ++++++++++++++++-----
drivers/gpu/drm/bridge/analogix/analogix_dp_core.h | 14 +-
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.c | 274 ++++++++-----
drivers/gpu/drm/bridge/analogix/analogix_dp_reg.h | 7 +
drivers/gpu/drm/exynos/exynos_dp.c | 2 +-
drivers/gpu/drm/rockchip/analogix_dp-rockchip.c | 73 ++--
drivers/gpu/drm/rockchip/rockchip_drm_drv.c | 2 +-
drivers/gpu/drm/rockchip/rockchip_drm_drv.h | 3 +-
drivers/gpu/drm/rockchip/rockchip_drm_fb.c | 61 ++-
drivers/gpu/drm/rockchip/rockchip_drm_psr.c | 361 ++++++++++------
drivers/gpu/drm/rockchip/rockchip_drm_psr.h | 7 +-
drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 47 ++-
drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 1 +
drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 1 +
include/drm/bridge/analogix_dp.h | 5 +-
15 files changed, 915 insertions(+), 395 deletions(-)
--
2.16.1
^ permalink raw reply
* [PATCH v2 4/4] input: misc: Add Gateworks System Controller support
From: Tim Harvey @ 2018-03-05 22:02 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov
Cc: linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input
In-Reply-To: <1520287361-12569-1-git-send-email-tharvey@gateworks.com>
Add support for dispatching Linux Input events for the various interrupts
the Gateworks System Controller provides.
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
---
v2:
- reword Kconfig
- revise license comment block
- remove unnecessary read of status register
- remove unnecessary setting of input->dev.parent
- use dt bindings for irq to keycode mapping
- add support for platform-data
- remove work-queue
drivers/input/misc/Kconfig | 9 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/gsc-input.c | 180 ++++++++++++++++++++++++++++++++
include/linux/platform_data/gsc_input.h | 30 ++++++
4 files changed, 220 insertions(+)
create mode 100644 drivers/input/misc/gsc-input.c
create mode 100644 include/linux/platform_data/gsc_input.h
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 9f082a3..e05f4fe 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -117,6 +117,15 @@ config INPUT_E3X0_BUTTON
To compile this driver as a module, choose M here: the
module will be called e3x0_button.
+config INPUT_GSC
+ tristate "Gateworks System Controller input support"
+ depends on MFD_GSC
+ help
+ Support GSC events as Linux input events.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gsc_input.
+
config INPUT_PCSPKR
tristate "PC Speaker support"
depends on PCSPKR_PLATFORM
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 4b6118d..969debe 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_INPUT_GP2A) += gp2ap002a00f.o
obj-$(CONFIG_INPUT_GPIO_BEEPER) += gpio-beeper.o
obj-$(CONFIG_INPUT_GPIO_TILT_POLLED) += gpio_tilt_polled.o
obj-$(CONFIG_INPUT_GPIO_DECODER) += gpio_decoder.o
+obj-$(CONFIG_INPUT_GSC) += gsc-input.o
obj-$(CONFIG_INPUT_HISI_POWERKEY) += hisi_powerkey.o
obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
obj-$(CONFIG_INPUT_IMS_PCU) += ims-pcu.o
diff --git a/drivers/input/misc/gsc-input.c b/drivers/input/misc/gsc-input.c
new file mode 100644
index 0000000..27b5e93
--- /dev/null
+++ b/drivers/input/misc/gsc-input.c
@@ -0,0 +1,180 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Gateworks Corporation
+ *
+ * This driver dispatches Linux input events for GSC interrupt events
+ */
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#include <linux/mfd/gsc.h>
+#include <linux/platform_data/gsc_input.h>
+
+struct gsc_input_button_priv {
+ struct input_dev *input;
+ const struct gsc_input_button *button;
+};
+
+struct gsc_input_data {
+ struct gsc_dev *gsc;
+ struct gsc_input_platform_data *pdata;
+ struct input_dev *input;
+ struct gsc_input_button_priv *buttons;
+ int nbuttons;
+ unsigned int irqs[];
+#if 0
+ int irq;
+ struct work_struct irq_work;
+ struct mutex mutex;
+#endif
+};
+
+static irqreturn_t gsc_input_irq(int irq, void *data)
+{
+ const struct gsc_input_button_priv *button_priv = data;
+ struct input_dev *input = button_priv->input;
+
+ dev_dbg(&input->dev, "irq%d code=%d\n", irq, button_priv->button->code);
+ input_report_key(input, button_priv->button->code, 1);
+ input_sync(input);
+ input_report_key(input, button_priv->button->code, 0);
+ input_sync(input);
+
+ return IRQ_HANDLED;
+}
+
+static struct gsc_input_platform_data *
+gsc_input_get_devtree_pdata(struct device *dev)
+{
+ struct gsc_input_platform_data *pdata;
+ struct fwnode_handle *child;
+ struct gsc_input_button *button;
+ int nbuttons;
+
+ nbuttons = device_get_child_node_count(dev);
+ dev_dbg(dev, "buttons=%d\n", nbuttons);
+ if (nbuttons == 0)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(dev,
+ sizeof(*pdata) + nbuttons * sizeof(*button),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ button = (struct gsc_input_button *)(pdata + 1);
+ pdata->buttons = button;
+ pdata->nbuttons = nbuttons;
+
+ device_property_read_string(dev, "label", &pdata->name);
+
+ device_for_each_child_node(dev, child) {
+ if (is_of_node(child))
+ button->irq = irq_of_parse_and_map(to_of_node(child),
+ 0);
+ if (fwnode_property_read_u32(child, "linux,code",
+ &button->code)) {
+ dev_err(dev, "Button without keycode\n");
+ fwnode_handle_put(child);
+ return ERR_PTR(-EINVAL);
+ }
+ fwnode_property_read_string(child, "label", &button->desc);
+ button++;
+ }
+
+ return pdata;
+}
+
+static int gsc_input_probe(struct platform_device *pdev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct gsc_input_platform_data *pdata = dev_get_platdata(dev);
+ struct gsc_input_data *input;
+ int i, ret;
+
+ if (!pdata) {
+ pdata = gsc_input_get_devtree_pdata(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ input = devm_kzalloc(dev, sizeof(*input), GFP_KERNEL);
+ if (!input)
+ return -ENOMEM;
+ input->gsc = gsc;
+ input->pdata = pdata;
+
+ input->buttons = devm_kzalloc(dev, pdata->nbuttons *
+ sizeof(struct gsc_input_button_priv),
+ GFP_KERNEL);
+ if (!input->buttons)
+ return -ENOMEM;
+
+ /* Register input device */
+ input->input = devm_input_allocate_device(dev);
+ if (!input->input) {
+ dev_err(dev, "Can't allocate input device\n");
+ return -ENOMEM;
+ }
+ input->input->name = pdata->name ? : pdev->name;
+
+ platform_set_drvdata(pdev, gsc);
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ const struct gsc_input_button *button = &pdata->buttons[i];
+ struct gsc_input_button_priv *button_priv = &input->buttons[i];
+
+ button_priv = &input->buttons[i];
+ button_priv->button = &pdata->buttons[i];
+ button_priv->input = input->input;
+ input_set_capability(input->input, EV_KEY, button->code);
+
+ ret = devm_request_threaded_irq(dev, button->irq, NULL,
+ gsc_input_irq, 0,
+ button->desc,
+ button_priv);
+ if (ret) {
+ dev_err(dev, "irq%d request failed: %d\n)", button->irq,
+ ret);
+ return ret;
+ }
+ dev_dbg(dev, "button: irq%d/%d code=%d %s\n",
+ button->irq - pdata->buttons[0].irq,
+ button->irq,
+ button->code, button->desc);
+ }
+
+ ret = input_register_device(input->input);
+ if (ret < 0) {
+ dev_err(dev, "Can't register input device: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id gsc_input_dt_ids[] = {
+ { .compatible = "gw,gsc-input", },
+ {}
+};
+
+static struct platform_driver gsc_input_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = gsc_input_dt_ids,
+ },
+ .probe = gsc_input_probe,
+};
+
+module_platform_driver(gsc_input_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("GSC input driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/gsc_input.h b/include/linux/platform_data/gsc_input.h
new file mode 100644
index 0000000..1a34284
--- /dev/null
+++ b/include/linux/platform_data/gsc_input.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _GSC_INPUT_H
+#define _GSC_INPUT_H
+
+/**
+ * struct gsc_input_button - configuration parameters
+ * @code: input event code (KEY_*, SW_*)
+ * @irq: irq index
+ * @desc: name of interrupt
+ */
+struct gsc_input_button {
+ unsigned int irq;
+ unsigned int code;
+ const char *desc;
+};
+
+/**
+ * struct gsc_input_platform_data - platform data for gsc_input driver
+ * @buttons: pointer to array of gsc_input_button structures
+ * describing buttons mapped to interrupt events
+ * @nbuttons: number of elements in @buttons array
+ * @name: input device name
+ */
+struct gsc_input_platform_data {
+ const struct gsc_input_button *buttons;
+ int nbuttons;
+ const char *name;
+};
+
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v2 3/4] hwmon: add Gateworks System Controller support
From: Tim Harvey @ 2018-03-05 22:02 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov
Cc: linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input, Guenter Roeck
In-Reply-To: <1520287361-12569-1-git-send-email-tharvey@gateworks.com>
The Gateworks System Controller has a hwmon sub-component that exposes
up to 16 ADC's, some of which are temperature sensors, others which are
voltage inputs. The ADC configuration (register mapping and name) is
configured via device-tree and varies board to board.
Cc: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
---
v2:
- change license comment style
- remove DEBUG
- simplify regmap_bulk_read err check
- remove break after returns in switch statement
- fix fan setpoint buffer address
- remove unnecessary parens
- consistently use struct device *dev pointer
- change license/comment block
- add validation for hwmon child node props
- move parsing of of to own function
- use strlcpy to ensure null termination
- fix static array sizes and removed unnecessary initializers
- dynamically allocate channels
- fix fan input label
- support platform data
- fixed whitespace issues
drivers/hwmon/Kconfig | 9 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gsc-hwmon.c | 330 ++++++++++++++++++++++++++++++++
include/linux/platform_data/gsc_hwmon.h | 34 ++++
4 files changed, 374 insertions(+)
create mode 100644 drivers/hwmon/gsc-hwmon.c
create mode 100644 include/linux/platform_data/gsc_hwmon.h
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 7ad0176..e052798b 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -475,6 +475,15 @@ config SENSORS_F75375S
This driver can also be built as a module. If so, the module
will be called f75375s.
+config SENSORS_GSC
+ tristate "Gateworks System Controller ADC"
+ depends on MFD_GSC
+ help
+ Support for the Gateworks System Controller A/D converters.
+
+ To compile this driver as a module, choose M here:
+ the module will be called gsc-hwmon.
+
config SENSORS_MC13783_ADC
tristate "Freescale MC13783/MC13892 ADC"
depends on MFD_MC13XXX
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 0fe489f..835a536 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -69,6 +69,7 @@ obj-$(CONFIG_SENSORS_G760A) += g760a.o
obj-$(CONFIG_SENSORS_G762) += g762.o
obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
+obj-$(CONFIG_SENSORS_GSC) += gsc-hwmon.o
obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o
obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o
obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o
diff --git a/drivers/hwmon/gsc-hwmon.c b/drivers/hwmon/gsc-hwmon.c
new file mode 100644
index 0000000..cf55457
--- /dev/null
+++ b/drivers/hwmon/gsc-hwmon.c
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Gateworks Corporation
+ *
+ * This driver registers Linux HWMON attributes for GSC ADC's
+ */
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/mfd/gsc.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <linux/platform_data/gsc_hwmon.h>
+
+#define GSC_HWMON_MAX_TEMP_CH 16
+#define GSC_HWMON_MAX_IN_CH 16
+#define GSC_HWMON_MAX_FAN_CH 6
+
+struct gsc_hwmon_data {
+ struct gsc_dev *gsc;
+ struct device *dev;
+ struct gsc_hwmon_platform_data *pdata;
+ const struct gsc_hwmon_channel *temp_ch[GSC_HWMON_MAX_TEMP_CH];
+ const struct gsc_hwmon_channel *in_ch[GSC_HWMON_MAX_IN_CH];
+ const struct gsc_hwmon_channel *fan_ch[GSC_HWMON_MAX_FAN_CH];
+ u32 temp_config[GSC_HWMON_MAX_TEMP_CH + 1];
+ u32 in_config[GSC_HWMON_MAX_IN_CH + 1];
+ u32 fan_config[GSC_HWMON_MAX_FAN_CH + 1];
+ struct hwmon_channel_info temp_info;
+ struct hwmon_channel_info in_info;
+ struct hwmon_channel_info fan_info;
+ const struct hwmon_channel_info *info[4];
+ struct hwmon_chip_info chip;
+};
+
+static int
+gsc_hwmon_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long *val)
+{
+ struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+ int sz, ret;
+ u8 reg;
+ u8 buf[3];
+
+ dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
+ channel);
+ switch (type) {
+ case hwmon_in:
+ if (channel > GSC_HWMON_MAX_IN_CH)
+ return -EOPNOTSUPP;
+ reg = hwmon->in_ch[channel]->reg;
+ sz = 3;
+ break;
+ case hwmon_temp:
+ if (channel > GSC_HWMON_MAX_TEMP_CH)
+ return -EOPNOTSUPP;
+ reg = hwmon->temp_ch[channel]->reg;
+ sz = 2;
+ break;
+ case hwmon_fan:
+ if (channel > GSC_HWMON_MAX_FAN_CH)
+ return -EOPNOTSUPP;
+ reg = hwmon->fan_ch[channel]->reg;
+ sz = 2;
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ ret = regmap_bulk_read(hwmon->gsc->regmap_hwmon, reg, &buf, sz);
+ if (ret)
+ return ret;
+
+ *val = 0;
+ while (sz-- > 0)
+ *val |= (buf[sz] << (8*sz));
+ if ((type == hwmon_temp) && *val > 0x8000)
+ *val -= 0xffff;
+
+ return 0;
+}
+
+static int
+gsc_hwmon_read_string(struct device *dev, enum hwmon_sensor_types type,
+ u32 attr, int channel, const char **buf)
+{
+ struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
+ channel);
+ switch (type) {
+ case hwmon_in:
+ if (channel > GSC_HWMON_MAX_IN_CH)
+ return -EOPNOTSUPP;
+ *buf = hwmon->in_ch[channel]->name;
+ break;
+ case hwmon_temp:
+ if (channel > GSC_HWMON_MAX_TEMP_CH)
+ return -EOPNOTSUPP;
+ *buf = hwmon->temp_ch[channel]->name;
+ break;
+ case hwmon_fan:
+ if (channel > GSC_HWMON_MAX_FAN_CH)
+ return -EOPNOTSUPP;
+ *buf = hwmon->fan_ch[channel]->name;
+ break;
+ default:
+ return -ENOTSUPP;
+ }
+
+ return 0;
+}
+
+static int
+gsc_hwmon_write(struct device *dev, enum hwmon_sensor_types type, u32 attr,
+ int channel, long val)
+{
+ struct gsc_hwmon_data *hwmon = dev_get_drvdata(dev);
+ u8 buf[3];
+
+ dev_dbg(dev, "%s type=%d attr=%d channel=%d\n", __func__, type, attr,
+ channel);
+ switch (type) {
+ case hwmon_fan:
+ if (channel == GSC_HWMON_MAX_FAN_CH)
+ return -EOPNOTSUPP;
+ buf[0] = val & 0xff;
+ buf[1] = (val >> 8) & 0xff;
+ return regmap_bulk_write(hwmon->gsc->regmap_hwmon,
+ hwmon->fan_ch[channel]->reg, buf, 2);
+ default:
+ break;
+ }
+
+ return -EOPNOTSUPP;
+}
+
+static umode_t
+gsc_hwmon_is_visible(const void *_data, enum hwmon_sensor_types type, u32 attr,
+ int ch)
+{
+ const struct gsc_hwmon_data *hwmon = _data;
+ struct device *dev = hwmon->gsc->dev;
+ umode_t mode = 0;
+
+ switch (type) {
+ case hwmon_fan:
+ mode = S_IRUGO;
+ if (attr == hwmon_fan_input)
+ mode |= S_IWUSR;
+ break;
+ case hwmon_temp:
+ case hwmon_in:
+ mode = S_IRUGO;
+ break;
+ default:
+ break;
+ }
+ dev_dbg(dev, "%s type=%d attr=%d ch=%d mode=0x%x\n", __func__, type,
+ attr, ch, mode);
+
+ return mode;
+}
+
+static const struct hwmon_ops gsc_hwmon_ops = {
+ .is_visible = gsc_hwmon_is_visible,
+ .read = gsc_hwmon_read,
+ .read_string = gsc_hwmon_read_string,
+ .write = gsc_hwmon_write,
+};
+
+static struct gsc_hwmon_platform_data *
+gsc_hwmon_get_devtree_pdata(struct device *dev)
+{
+ struct gsc_hwmon_platform_data *pdata;
+ struct gsc_hwmon_channel *ch;
+ struct fwnode_handle *child;
+ int nchannels;
+
+ nchannels = device_get_child_node_count(dev);
+ dev_dbg(dev, "channels=%d\n", nchannels);
+ if (nchannels == 0)
+ return ERR_PTR(-ENODEV);
+
+ pdata = devm_kzalloc(dev,
+ sizeof(*pdata) + nchannels * sizeof(*ch),
+ GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+ ch = (struct gsc_hwmon_channel *)(pdata + 1);
+ pdata->channels = ch;
+ pdata->nchannels = nchannels;
+
+ /* allocate structures for channels and count instances of each type */
+ device_for_each_child_node(dev, child) {
+ if (fwnode_property_read_string(child, "label", &ch->name)) {
+ dev_err(dev, "channel without label\n");
+ fwnode_handle_put(child);
+ return ERR_PTR(-EINVAL);
+ }
+ if (fwnode_property_read_u32(child, "reg", &ch->reg)) {
+ dev_err(dev, "channel without reg\n");
+ fwnode_handle_put(child);
+ return ERR_PTR(-EINVAL);
+ }
+ if (fwnode_property_read_u32(child, "type", &ch->type)) {
+ dev_err(dev, "channel without type\n");
+ fwnode_handle_put(child);
+ return ERR_PTR(-EINVAL);
+ }
+ dev_dbg(dev, "of: reg=0x%02x type=%d %s\n", ch->reg, ch->type,
+ ch->name);
+ ch++;
+ }
+
+ return pdata;
+}
+
+static int gsc_hwmon_probe(struct platform_device *pdev)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(pdev->dev.parent);
+ struct device *dev = &pdev->dev;
+ struct gsc_hwmon_platform_data *pdata = dev_get_platdata(dev);
+ struct gsc_hwmon_data *hwmon;
+ int i, i_in, i_temp, i_fan;
+
+ if (!pdata) {
+ pdata = gsc_hwmon_get_devtree_pdata(dev);
+ if (IS_ERR(pdata))
+ return PTR_ERR(pdata);
+ }
+
+ hwmon = devm_kzalloc(dev, sizeof(*hwmon), GFP_KERNEL);
+ if (!hwmon)
+ return -ENOMEM;
+ hwmon->gsc = gsc;
+ hwmon->pdata = pdata;
+
+ for (i = 0, i_in = 0, i_temp = 0, i_fan = 0;
+ i < hwmon->pdata->nchannels; i++) {
+ const struct gsc_hwmon_channel *ch = &pdata->channels[i];
+
+ if (ch->reg > GSC_HWMON_MAX_REG) {
+ dev_err(dev, "invalid reg: 0x%02x\n", ch->reg);
+ return -EINVAL;
+ }
+ switch (ch->type) {
+ case type_temperature:
+ if (i_temp == GSC_HWMON_MAX_TEMP_CH) {
+ dev_err(dev, "too many temp channels\n");
+ return -EINVAL;
+ }
+ hwmon->temp_ch[i_temp] = ch;
+ hwmon->temp_config[i_temp] = HWMON_T_INPUT |
+ HWMON_T_LABEL;
+ i_temp++;
+ break;
+ case type_voltage:
+ if (i_in == GSC_HWMON_MAX_IN_CH) {
+ dev_err(dev, "too many voltage channels\n");
+ return -EINVAL;
+ }
+ hwmon->in_ch[i_in] = ch;
+ hwmon->in_config[i_in] =
+ HWMON_I_INPUT | HWMON_I_LABEL;
+ i_in++;
+ break;
+ case type_fan:
+ if (i_fan == GSC_HWMON_MAX_FAN_CH) {
+ dev_err(dev, "too many voltage channels\n");
+ return -EINVAL;
+ }
+ hwmon->fan_ch[i_fan] = ch;
+ hwmon->fan_config[i_fan] =
+ HWMON_F_INPUT | HWMON_F_LABEL;
+ i_fan++;
+ break;
+ default:
+ dev_err(dev, "invalid type: %d\n", ch->type);
+ return -EINVAL;
+ }
+ dev_dbg(dev, "pdata: reg=0x%02x type=%d %s\n", ch->reg,
+ ch->type, ch->name);
+ }
+
+ /* terminate channel config lists */
+ hwmon->temp_config[i_temp] = 0;
+ hwmon->in_config[i_in] = 0;
+ hwmon->fan_config[i_fan] = 0;
+
+ /* setup config structures */
+ hwmon->chip.ops = &gsc_hwmon_ops;
+ hwmon->chip.info = hwmon->info;
+ hwmon->info[0] = &hwmon->temp_info;
+ hwmon->info[1] = &hwmon->in_info;
+ hwmon->info[2] = &hwmon->fan_info;
+ hwmon->temp_info.type = hwmon_temp;
+ hwmon->temp_info.config = hwmon->temp_config;
+ hwmon->in_info.type = hwmon_in;
+ hwmon->in_info.config = hwmon->in_config;
+ hwmon->fan_info.type = hwmon_fan;
+ hwmon->fan_info.config = hwmon->fan_config;
+
+ hwmon->dev = devm_hwmon_device_register_with_info(dev,
+ KBUILD_MODNAME, hwmon,
+ &hwmon->chip, NULL);
+ return PTR_ERR_OR_ZERO(hwmon->dev);
+}
+
+static const struct of_device_id gsc_hwmon_of_match[] = {
+ { .compatible = "gw,gsc-hwmon", },
+ {}
+};
+
+static struct platform_driver gsc_hwmon_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = gsc_hwmon_of_match,
+ },
+ .probe = gsc_hwmon_probe,
+};
+
+module_platform_driver(gsc_hwmon_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("GSC hardware monitor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/platform_data/gsc_hwmon.h b/include/linux/platform_data/gsc_hwmon.h
new file mode 100644
index 0000000..ba4f811
--- /dev/null
+++ b/include/linux/platform_data/gsc_hwmon.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _GSC_HWMON_H
+#define _GSC_HWMON_H
+
+enum gsc_hwmon_type {
+ type_temperature,
+ type_voltage,
+ type_fan,
+};
+
+/**
+ * struct gsc_hwmon_channel - configuration parameters
+ * @reg: I2C register offset
+ * @type: channel type
+ * @name: channel name
+ */
+struct gsc_hwmon_channel {
+ unsigned int reg;
+ unsigned int type;
+ const char *name;
+};
+
+/**
+ * struct gsc_hwmon_platform_data - platform data for gsc_hwmon driver
+ * @channels: pointer to array of gsc_hwmon_channel structures
+ * describing channels
+ * @nchannels: number of elements in @channels array
+ */
+struct gsc_hwmon_platform_data {
+ const struct gsc_hwmon_channel *channels;
+ int nchannels;
+};
+
+#endif
--
2.7.4
^ permalink raw reply related
* [PATCH v2 2/4] mfd: add Gateworks System Controller core driver
From: Tim Harvey @ 2018-03-05 22:02 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov
Cc: linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input, Randy Dunlap
In-Reply-To: <1520287361-12569-1-git-send-email-tharvey@gateworks.com>
The Gateworks System Controller (GSC) is an I2C slave controller
implemented with an MSP430 micro-controller whose firmware embeds the
following features:
- I/O expander (16 GPIO's) using PCA955x protocol
- Real Time Clock using DS1672 protocol
- User EEPROM using AT24 protocol
- HWMON using custom protocol
- Interrupt controller with tamper detect, user pushbotton
- Watchdog controller capable of full board power-cycle
- Power Control capable of full board power-cycle
see http://trac.gateworks.com/wiki/gsc for more details
Cc: Randy Dunlap <rdunlap@infradead.org>
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
---
v2:
- change license comment block style
- remove COMPILE_TEST (Randy)
- fixed whitespace issues
- replaced a printk with dev_err
drivers/mfd/Kconfig | 13 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/gsc.c | 330 ++++++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/gsc.h | 82 ++++++++++++
4 files changed, 426 insertions(+)
create mode 100644 drivers/mfd/gsc.c
create mode 100644 include/linux/mfd/gsc.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 1d20a80..5694013 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -341,6 +341,19 @@ config MFD_EXYNOS_LPASS
Select this option to enable support for Samsung Exynos Low Power
Audio Subsystem.
+config MFD_GSC
+ tristate "Gateworks System Controller"
+ depends on (I2C && OF)
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ help
+ Enable support for the Gateworks System Controller found
+ on Gateworks Single Board Computers.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gsc.
+
config MFD_MC13XXX
tristate
depends on (SPI_MASTER || I2C)
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index d9474ad..aede0bc 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_MFD_CROS_EC) += cros_ec_core.o
obj-$(CONFIG_MFD_CROS_EC_I2C) += cros_ec_i2c.o
obj-$(CONFIG_MFD_CROS_EC_SPI) += cros_ec_spi.o
obj-$(CONFIG_MFD_EXYNOS_LPASS) += exynos-lpass.o
+obj-$(CONFIG_MFD_GSC) += gsc.o
rtsx_pci-objs := rtsx_pcr.o rts5209.o rts5229.o rtl8411.o rts5227.o rts5249.o
obj-$(CONFIG_MFD_RTSX_PCI) += rtsx_pci.o
diff --git a/drivers/mfd/gsc.c b/drivers/mfd/gsc.c
new file mode 100644
index 0000000..4ac400a
--- /dev/null
+++ b/drivers/mfd/gsc.c
@@ -0,0 +1,330 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Gateworks Corporation
+ *
+ * The Gateworks System Controller (GSC) is a family of a multi-function
+ * "Power Management and System Companion Device" chips originally designed for
+ * use in Gateworks Single Board Computers. The control interface is I2C,
+ * at 100kbps, with an interrupt.
+ *
+ */
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/mfd/core.h>
+#include <linux/mfd/gsc.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/*
+ * The GSC suffers from an errata where occasionally during
+ * ADC cycles the chip can NAK i2c transactions. To ensure we have reliable
+ * register access we place retries around register access.
+ */
+#define I2C_RETRIES 3
+
+static int gsc_regmap_regwrite(void *context, unsigned int reg,
+ unsigned int val)
+{
+ struct i2c_client *client = context;
+ int retry, ret;
+
+ for (retry = 0; retry < I2C_RETRIES; retry++) {
+ ret = i2c_smbus_write_byte_data(client, reg, val);
+ /*
+ * -EAGAIN returned when the i2c host controller is busy
+ * -EIO returned when i2c device is busy
+ */
+ if (ret != -EAGAIN && ret != -EIO)
+ break;
+ }
+ if (ret < 0) {
+ dev_err(&client->dev, ">> 0x%02x %d\n", reg, ret);
+ return ret;
+ }
+ dev_dbg(&client->dev, ">> 0x%02x=0x%02x (%d)\n", reg, val, retry);
+
+ return 0;
+}
+
+static int gsc_regmap_regread(void *context, unsigned int reg,
+ unsigned int *val)
+{
+ struct i2c_client *client = context;
+ int retry, ret;
+
+ for (retry = 0; retry < I2C_RETRIES; retry++) {
+ ret = i2c_smbus_read_byte_data(client, reg);
+ /*
+ * -EAGAIN returned when the i2c host controller is busy
+ * -EIO returned when i2c device is busy
+ */
+ if (ret != -EAGAIN && ret != -EIO)
+ break;
+ }
+ if (ret < 0) {
+ dev_err(&client->dev, "<< 0x%02x %d\n", reg, ret);
+ return ret;
+ }
+
+ *val = ret & 0xff;
+ dev_dbg(&client->dev, "<< 0x%02x=0x%02x (%d)\n", reg, *val, retry);
+
+ return 0;
+}
+
+static struct regmap_bus regmap_gsc = {
+ .reg_write = gsc_regmap_regwrite,
+ .reg_read = gsc_regmap_regread,
+};
+
+/*
+ * gsc_powerdown - API to use GSC to power down board for a specific time
+ *
+ * secs - number of seconds to remain powered off
+ */
+static int gsc_powerdown(struct gsc_dev *gsc, unsigned long secs)
+{
+ int ret;
+ unsigned char regs[4];
+
+ dev_info(&gsc->i2c->dev, "GSC powerdown for %ld seconds\n",
+ secs);
+ regs[0] = secs & 0xff;
+ regs[1] = (secs >> 8) & 0xff;
+ regs[2] = (secs >> 16) & 0xff;
+ regs[3] = (secs >> 24) & 0xff;
+ ret = regmap_bulk_write(gsc->regmap, GSC_TIME_ADD, regs, 4);
+
+ return ret;
+}
+
+/*
+ * sysfs hooks
+ */
+static ssize_t gsc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+ const char *name = attr->attr.name;
+ int rz = 0;
+
+ if (strcasecmp(name, "fw_version") == 0)
+ rz = sprintf(buf, "%d\n", gsc->fwver);
+ else if (strcasecmp(name, "fw_crc") == 0)
+ rz = sprintf(buf, "0x%04x\n", gsc->fwcrc);
+
+ return rz;
+}
+
+static ssize_t gsc_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct gsc_dev *gsc = dev_get_drvdata(dev);
+ const char *name = attr->attr.name;
+ int ret;
+
+ if (strcasecmp(name, "powerdown") == 0) {
+ long value;
+
+ ret = kstrtol(buf, 0, &value);
+ if (ret == 0)
+ gsc_powerdown(gsc, value);
+ } else
+ dev_err(dev, "invalid name '%s\n", name);
+
+ return count;
+}
+
+
+/*
+ * Create a group of attributes so that we can create and destroy them all
+ * at once.
+ */
+static struct device_attribute attr_fwver =
+ __ATTR(fw_version, 0440, gsc_show, NULL);
+static struct device_attribute attr_fwcrc =
+ __ATTR(fw_crc, 0440, gsc_show, NULL);
+static struct device_attribute attr_pwrdown =
+ __ATTR(powerdown, 0220, NULL, gsc_store);
+
+static struct attribute *gsc_attrs[] = {
+ &attr_fwver.attr,
+ &attr_fwcrc.attr,
+ &attr_pwrdown.attr,
+ NULL,
+};
+
+static struct attribute_group attr_group = {
+ .attrs = gsc_attrs,
+};
+
+static const struct i2c_device_id gsc_i2c_ids[] = {
+ { "gsc_v1", gsc_v1 },
+ { "gsc_v2", gsc_v2 },
+ { "gsc_v3", gsc_v3 },
+ { },
+};
+
+static const struct of_device_id gsc_of_match[] = {
+ { .compatible = "gw,gsc_v1", .data = (void *)gsc_v1 },
+ { .compatible = "gw,gsc_v2", .data = (void *)gsc_v2 },
+ { .compatible = "gw,gsc_v3", .data = (void *)gsc_v3 },
+ { }
+};
+
+static const struct regmap_config gsc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .max_register = 0xf,
+};
+
+static const struct regmap_config gsc_regmap_hwmon_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_NONE,
+ .max_register = 0x37,
+};
+
+static const struct regmap_irq gsc_irqs[] = {
+ REGMAP_IRQ_REG(GSC_IRQ_PB, 0, BIT(GSC_IRQ_PB)),
+ REGMAP_IRQ_REG(GSC_IRQ_KEY_ERASED, 0, BIT(GSC_IRQ_KEY_ERASED)),
+ REGMAP_IRQ_REG(GSC_IRQ_EEPROM_WP, 0, BIT(GSC_IRQ_EEPROM_WP)),
+ REGMAP_IRQ_REG(GSC_IRQ_RESV, 0, BIT(GSC_IRQ_RESV)),
+ REGMAP_IRQ_REG(GSC_IRQ_GPIO, 0, BIT(GSC_IRQ_GPIO)),
+ REGMAP_IRQ_REG(GSC_IRQ_TAMPER, 0, BIT(GSC_IRQ_TAMPER)),
+ REGMAP_IRQ_REG(GSC_IRQ_WDT_TIMEOUT, 0, BIT(GSC_IRQ_WDT_TIMEOUT)),
+ REGMAP_IRQ_REG(GSC_IRQ_SWITCH_HOLD, 0, BIT(GSC_IRQ_SWITCH_HOLD)),
+};
+
+static const struct regmap_irq_chip gsc_irq_chip = {
+ .name = KBUILD_MODNAME,
+ .irqs = gsc_irqs,
+ .num_irqs = ARRAY_SIZE(gsc_irqs),
+ .num_regs = 1,
+ .status_base = GSC_IRQ_STATUS,
+ .mask_base = GSC_IRQ_ENABLE,
+ .mask_invert = true,
+ .ack_base = GSC_IRQ_STATUS,
+ .ack_invert = true,
+};
+
+static int gsc_of_probe(struct device_node *np, struct gsc_dev *gsc)
+{
+ const struct of_device_id *of_id;
+
+ if (!np)
+ return -ENODEV;
+
+ of_id = of_match_device(gsc_of_match, gsc->dev);
+ if (of_id)
+ gsc->type = (enum gsc_type)of_id->data;
+
+ return 0;
+}
+
+static int
+gsc_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct gsc_dev *gsc;
+ int ret;
+ unsigned int reg;
+
+ gsc = devm_kzalloc(dev, sizeof(*gsc), GFP_KERNEL);
+ if (!gsc)
+ return -ENOMEM;
+
+ gsc->dev = &client->dev;
+ gsc->i2c = client;
+ gsc->irq = client->irq;
+ i2c_set_clientdata(client, gsc);
+
+ gsc->regmap = devm_regmap_init(dev, ®map_gsc, client,
+ &gsc_regmap_config);
+ if (IS_ERR(gsc->regmap))
+ return PTR_ERR(gsc->regmap);
+
+ ret = gsc_of_probe(dev->of_node, gsc);
+ if (reg < 0)
+ return ret;
+
+ if (regmap_read(gsc->regmap, GSC_FW_VER, ®))
+ return -EIO;
+ gsc->fwver = reg;
+ regmap_read(gsc->regmap, GSC_FW_CRC, ®);
+ gsc->fwcrc = reg;
+ regmap_read(gsc->regmap, GSC_FW_CRC + 1, ®);
+ gsc->fwcrc |= reg << 8;
+
+ gsc->i2c_hwmon = i2c_new_dummy(client->adapter, GSC_HWMON);
+ if (!gsc->i2c_hwmon) {
+ dev_err(dev, "Failed to allocate I2C device for HWMON\n");
+ return -ENODEV;
+ }
+ i2c_set_clientdata(gsc->i2c_hwmon, gsc);
+
+ gsc->regmap_hwmon = devm_regmap_init(dev, ®map_gsc, gsc->i2c_hwmon,
+ &gsc_regmap_hwmon_config);
+ if (IS_ERR(gsc->regmap_hwmon)) {
+ ret = PTR_ERR(gsc->regmap_hwmon);
+ dev_err(dev, "failed to allocate register map: %d\n", ret);
+ goto err_regmap;
+ }
+
+ ret = devm_regmap_add_irq_chip(dev, gsc->regmap, gsc->irq,
+ IRQF_ONESHOT | IRQF_SHARED |
+ IRQF_TRIGGER_FALLING, 0,
+ &gsc_irq_chip, &gsc->irq_chip_data);
+ if (ret)
+ goto err_regmap;
+
+ dev_info(dev, "Gateworks System Controller v%d: fw v%02d 0x%04x\n",
+ gsc->type, gsc->fwver, gsc->fwcrc);
+
+ /* sysfs hooks */
+ ret = sysfs_create_group(&dev->kobj, &attr_group);
+ if (ret)
+ dev_err(dev, "failed to create sysfs attrs\n");
+
+ ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
+ if (ret)
+ goto err_sysfs;
+
+ return 0;
+
+err_sysfs:
+ sysfs_remove_group(&dev->kobj, &attr_group);
+err_regmap:
+ i2c_unregister_device(gsc->i2c_hwmon);
+
+ return ret;
+}
+
+static int gsc_remove(struct i2c_client *client)
+{
+ sysfs_remove_group(&client->dev.kobj, &attr_group);
+
+ return 0;
+}
+
+static struct i2c_driver gsc_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = of_match_ptr(gsc_of_match),
+ },
+ .probe = gsc_probe,
+ .remove = gsc_remove,
+ .id_table = gsc_i2c_ids,
+};
+
+module_i2c_driver(gsc_driver);
+
+MODULE_AUTHOR("Tim Harvey <tharvey@gateworks.com>");
+MODULE_DESCRIPTION("I2C Core interface for GSC");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/gsc.h b/include/linux/mfd/gsc.h
new file mode 100644
index 0000000..e0d8c8f
--- /dev/null
+++ b/include/linux/mfd/gsc.h
@@ -0,0 +1,82 @@
+/* SPDX-License-Identifier: GPL-2.0
+ *
+ * Copyright (C) 2018 Gateworks Corporation
+ */
+#ifndef __LINUX_MFD_GSC_H_
+#define __LINUX_MFD_GSC_H_
+
+/* Device Addresses */
+#define GSC_MISC 0x20
+#define GSC_UPDATE 0x21
+#define GSC_GPIO 0x23
+#define GSC_HWMON 0x29
+#define GSC_EEPROM0 0x50
+#define GSC_EEPROM1 0x51
+#define GSC_EEPROM2 0x52
+#define GSC_EEPROM3 0x53
+#define GSC_RTC 0x68
+
+/* Register offsets */
+#define GSC_CTRL_0 0x00
+#define GSC_CTRL_1 0x01
+#define GSC_TIME 0x02
+#define GSC_TIME_ADD 0x06
+#define GSC_IRQ_STATUS 0x0A
+#define GSC_IRQ_ENABLE 0x0B
+#define GSC_FW_CRC 0x0C
+#define GSC_FW_VER 0x0E
+#define GSC_WP 0x0F
+
+/* Bit definitions */
+#define GSC_CTRL_0_PB_HARD_RESET 0
+#define GSC_CTRL_0_PB_CLEAR_SECURE_KEY 1
+#define GSC_CTRL_0_PB_SOFT_POWER_DOWN 2
+#define GSC_CTRL_0_PB_BOOT_ALTERNATE 3
+#define GSC_CTRL_0_PERFORM_CRC 4
+#define GSC_CTRL_0_TAMPER_DETECT 5
+#define GSC_CTRL_0_SWITCH_HOLD 6
+
+#define GSC_CTRL_1_SLEEP_ENABLE 0
+#define GSC_CTRL_1_ACTIVATE_SLEEP 1
+#define GSC_CTRL_1_LATCH_SLEEP_ADD 2
+#define GSC_CTRL_1_SLEEP_NOWAKEPB 3
+#define GSC_CTRL_1_WDT_TIME 4
+#define GSC_CTRL_1_WDT_ENABLE 5
+#define GSC_CTRL_1_SWITCH_BOOT_ENABLE 6
+#define GSC_CTRL_1_SWITCH_BOOT_CLEAR 7
+
+#define GSC_IRQ_PB 0
+#define GSC_IRQ_KEY_ERASED 1
+#define GSC_IRQ_EEPROM_WP 2
+#define GSC_IRQ_RESV 3
+#define GSC_IRQ_GPIO 4
+#define GSC_IRQ_TAMPER 5
+#define GSC_IRQ_WDT_TIMEOUT 6
+#define GSC_IRQ_SWITCH_HOLD 7
+
+/* Max registers */
+#define GSC_HWMON_MAX_REG 56
+
+enum gsc_type {
+ gsc_v1 = 1,
+ gsc_v2 = 2,
+ gsc_v3 = 3,
+};
+
+struct gsc_dev {
+ struct device *dev;
+
+ struct i2c_client *i2c; /* 0x20: interrupt controller, WDT */
+ struct i2c_client *i2c_hwmon; /* 0x29: hwmon, fan controller */
+
+ struct regmap *regmap;
+ struct regmap *regmap_hwmon;
+ struct regmap_irq_chip_data *irq_chip_data;
+
+ int irq;
+ enum gsc_type type;
+ unsigned int fwver;
+ unsigned short fwcrc;
+};
+
+#endif /* __LINUX_MFD_GSC_H_ */
--
2.7.4
^ permalink raw reply related
* [PATCH v2 1/4] dt-bindings: mfd: Add Gateworks System Controller bindings
From: Tim Harvey @ 2018-03-05 22:02 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov
Cc: linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input
In-Reply-To: <1520287361-12569-1-git-send-email-tharvey@gateworks.com>
This patch adds documentation of device-tree bindings for the
Gateworks System Controller (GSC).
Signed-off-by: Tim Harvey <tharvey@gateworks.com>
---
Documentation/devicetree/bindings/mfd/gsc.txt | 159 ++++++++++++++++++++++++++
1 file changed, 159 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/gsc.txt
diff --git a/Documentation/devicetree/bindings/mfd/gsc.txt b/Documentation/devicetree/bindings/mfd/gsc.txt
new file mode 100644
index 0000000..fe5d114
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/gsc.txt
@@ -0,0 +1,159 @@
+Gateworks System Controller multi-function device
+
+The GSC is a Multifunction I2C slave device with the following submodules:
+- WDT
+- GPIO
+- Pushbutton controller
+- HWMON
+
+Required properties:
+- compatible : Must be "gw,gsc_v1", "gw,gsc_v2", "gw,gsc_v3"
+- reg: I2C address of the device
+- interrupts: interrupt triggered by GSC_IRQ# signal
+- interrupt-parent: Interrupt controller GSC is connected to
+- #interrupt-cells: should be <1>, index of the interrupt within the
+ controller, in accordance with the "one cell" variant of
+ <devicetree/bindings/interrupt-controller/interrupt.txt>
+
+Optional nodes:
+* watchdog:
+The GSC provides a Watchdog monitor which can power cycle the board's
+primary power supply on most board models when tripped.
+
+Required properties:
+- compatible: must be "gw,gsc-watchdog"
+
+* input:
+The GSC provides an input device capable of dispatching Linux Input events
+for user pushbutton events, tamper switch events, etc.
+
+Required properties:
+- compatible: must be "gw,gsc-input"
+
+* hwmon:
+The GSC provides a set of Analog to Digitcal Converter (ADC) pins used for
+temperature and/or voltage monitoring.
+
+Required properties:
+- compatible: must be "gw,gsc-hwmon"
+
+Example:
+
+ gsc: gsc@20 {
+ compatible = "gw,gsc_v2";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <4 GPIO_ACTIVE_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ gsc_input {
+ compatible = "gw,gsc-input";
+ };
+
+ gsc_watchdog {
+ compatible = "gw,gsc-watchdog";
+ };
+
+ gsc_hwmon {
+ compatible = "gw,gsc-hwmon";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hwmon@0 { /* A0: Board Temperature */
+ type = <0>;
+ reg = <0x00>;
+ label = "temp";
+ };
+
+ hwmon@1 { /* A1: Input Voltage */
+ type = <1>;
+ reg = <0x02>;
+ label = "Vin";
+ };
+
+ hwmon@2 { /* A2: 5P0 */
+ type = <1>;
+ reg = <0x0b>;
+ label = "5P0";
+ };
+
+ hwmon@4 { /* A4: 0-5V input */
+ type = <1>;
+ reg = <0x14>;
+ label = "ANL0";
+ };
+
+ hwmon@5 { /* A5: 2P5 PCIe/GigE */
+ type = <1>;
+ reg = <0x23>;
+ label = "2P5";
+ };
+
+ hwmon@6 { /* A6: 1P8 Aud/Vid */
+ type = <1>;
+ reg = <0x1d>;
+ label = "1P8";
+ };
+
+ hwmon@7 { /* A7: GPS */
+ type = <1>;
+ reg = <0x26>;
+ label = "GPS";
+ };
+
+ hwmon@12 { /* A12: VDD_CORE */
+ type = <1>;
+ reg = <0x3>;
+ label = "VDD_CORE";
+ };
+
+ hwmon@13 { /* A13: VDD_SOC */
+ type = <1>;
+ reg = <0x11>;
+ label = "VDD_SOC";
+ };
+
+ hwmon@14 { /* A14: 1P0 PCIe SW */
+ type = <1>;
+ reg = <0x20>;
+ label = "1P0";
+ };
+
+ hwmon@15 { /* fan0 */
+ type = <2>;
+ reg = <0x2c>;
+ label = "fan_50p";
+ };
+
+ hwmon@16 { /* fan1 */
+ type = <2>;
+ reg = <0x2e>;
+ label = "fan_60p";
+ };
+
+ hwmon@17 { /* fan2 */
+ type = <2>;
+ reg = <0x30>;
+ label = "fan_70p";
+ };
+
+ hwmon@18 { /* fan3 */
+ type = <2>;
+ reg = <0x32>;
+ label = "fan_80p";
+ };
+
+ hwmon@19 { /* fan4 */
+ type = <2>;
+ reg = <0x34>;
+ label = "fan_90p";
+ };
+
+ hwmon@20 { /* fan5 */
+ type = <2>;
+ reg = <0x36>;
+ label = "fan_100p";
+ };
+ };
+ };
--
2.7.4
^ permalink raw reply related
* [PATCH v2 0/4] Add support for the Gateworks System Controller
From: Tim Harvey @ 2018-03-05 22:02 UTC (permalink / raw)
To: Lee Jones, Rob Herring, Mark Rutland, Mark Brown, Dmitry Torokhov
Cc: linux-kernel, devicetree, linux-arm-kernel, linux-hwmon,
linux-input
This series adds support for the Gateworks System Controller used on Gateworks
Laguna, Ventana, and Newport product families.
The GSC is an MSP430 I2C slave controller whose firmware embeds the following
features:
- I/O expander (16 GPIO's emulating a PCA955x)
- EEPROM (enumating AT24)
- RTC (enumating DS1672)
- HWMON
- Interrupt controller with tamper detect, user pushbotton
- Watchdog controller capable of full board power-cycle
- Power Control capable of full board power-cycle
see http://trac.gateworks.com/wiki/gsc for more details
Tim Harvey (4):
dt-bindings: mfd: Add Gateworks System Controller bindings
mfd: add Gateworks System Controller core driver
hwmon: add Gateworks System Controller support
input: misc: Add Gateworks System Controller support
Documentation/devicetree/bindings/mfd/gsc.txt | 159 +++++++++++++
drivers/hwmon/Kconfig | 9 +
drivers/hwmon/Makefile | 1 +
drivers/hwmon/gsc-hwmon.c | 330 ++++++++++++++++++++++++++
drivers/input/misc/Kconfig | 9 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/gsc-input.c | 180 ++++++++++++++
drivers/mfd/Kconfig | 13 +
drivers/mfd/Makefile | 1 +
drivers/mfd/gsc.c | 330 ++++++++++++++++++++++++++
include/linux/mfd/gsc.h | 82 +++++++
include/linux/platform_data/gsc_hwmon.h | 34 +++
include/linux/platform_data/gsc_input.h | 30 +++
13 files changed, 1179 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/gsc.txt
create mode 100644 drivers/hwmon/gsc-hwmon.c
create mode 100644 drivers/input/misc/gsc-input.c
create mode 100644 drivers/mfd/gsc.c
create mode 100644 include/linux/mfd/gsc.h
create mode 100644 include/linux/platform_data/gsc_hwmon.h
create mode 100644 include/linux/platform_data/gsc_input.h
--
2.7.4
^ permalink raw reply
* Re: [PATCH v2 1/1] HID: Logitech K290: Add driver for the Logitech K290 USB keyboard
From: kbuild test robot @ 2018-03-05 21:11 UTC (permalink / raw)
Cc: kbuild-all, andy.shevchenko, jikos, benjamin.tissoires,
linux-kernel, linux-input, Florent Flament
In-Reply-To: <20180304221425.28611-2-contact@florentflament.com>
[-- Attachment #1: Type: text/plain, Size: 2277 bytes --]
Hi Florent,
Thank you for the patch! Yet something to improve:
[auto build test ERROR on hid/for-next]
[also build test ERROR on v4.16-rc4 next-20180305]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
url: https://github.com/0day-ci/linux/commits/Florent-Flament/Logitech-K290-Add-driver-for-the-Logitech-K290-USB-keyboard/20180305-153311
base: https://git.kernel.org/pub/scm/linux/kernel/git/jikos/hid.git for-next
config: tile-allyesconfig (attached as .config)
compiler: tilegx-linux-gcc (GCC) 7.2.0
reproduce:
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=tile
All errors (new ones prefixed by >>):
>> drivers/hid/hid-logitech-k290.c:92:3: error: 'struct hid_driver' has no member named 'resume'; did you mean 'remove'?
.resume = k290_resume,
^~~~~~
remove
>> drivers/hid/hid-logitech-k290.c:92:12: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.resume = k290_resume,
^~~~~~~~~~~
drivers/hid/hid-logitech-k290.c:92:12: note: (near initialization for 'k290_driver.feature_mapping')
>> drivers/hid/hid-logitech-k290.c:93:3: error: 'struct hid_driver' has no member named 'reset_resume'
.reset_resume = k290_resume,
^~~~~~~~~~~~
drivers/hid/hid-logitech-k290.c:93:18: error: initialization from incompatible pointer type [-Werror=incompatible-pointer-types]
.reset_resume = k290_resume,
^~~~~~~~~~~
drivers/hid/hid-logitech-k290.c:93:18: note: (near initialization for 'k290_driver.bus_add_driver')
cc1: some warnings being treated as errors
vim +92 drivers/hid/hid-logitech-k290.c
87
88 static struct hid_driver k290_driver = {
89 .name = "hid-logitech-k290",
90 .id_table = k290_devices,
91 .input_configured = k290_input_configured,
> 92 .resume = k290_resume,
> 93 .reset_resume = k290_resume,
94 };
95
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 51495 bytes --]
^ permalink raw reply
* Re: [PATCH v2 1/1] HID: Logitech K290: Add driver for the Logitech K290 USB keyboard
From: Benjamin Tissoires @ 2018-03-05 17:26 UTC (permalink / raw)
To: Nestor Lopez Casado
Cc: Florent Flament, andy.shevchenko, Jiri Kosina, lkml,
open list:HID CORE LAYER
In-Reply-To: <CAE7qMrpF4dGCUrtnXbxb4MLFPZ0Wf13mrQEPypwPbEHaB9e44A@mail.gmail.com>
Hi Florent,
On Mon, Mar 5, 2018 at 10:31 AM, Nestor Lopez Casado
<nlopezcasad@logitech.com> wrote:
> Hello Florent,
>
> In my view, this driver may not be a good idea. The default behaviour
> of K290 is 'send multimedia keycodes' with the user given the choice
> to change that behaviour via vendor commands. Putting a driver that
> will unconditionally change that behaviour without the user's consent
> might bother other users that prefer the multimedia keycodes by
> default.
>
> Besides, I'd argue that instead of a kernel module this would be best
> achieved from a user space application. Something in the lines of
> Solaar (github pwr/solaar) or libratbag (there's an issue open to
> support keyboards) or even a specific application built for the
> purpose. Anyways, please collect the input from Benjamin and Jiri as
> they as they best placed to advise than myself.
On top of what Nestor said, this type of functionality, if we want to
have them in the kernel should probably be integrated in
hid-logitech-hidpp, in order not having some magic reports to send.
Things like reconnect of the device would be handled far more easily
in hid-logitech-hidpp while you would be reinventing the wheel here.
One other thing I do not like in this submission of the driver is the
direct use of USB while we have a full transport agnostic layer called
HID.
Cheers,
Benjamin
>
> Cheers,
> -nestor
>
> On Sun, Mar 4, 2018 at 11:14 PM, Florent Flament
> <contact@florentflament.com> wrote:
>> With the generic HID driver, K290 keyboards' F1 to F12 keys send
>> multimedia events by default, and standard keycodes when the function
>> key is pressed. This driver allows to configure K290 keyboards, so
>> that F1 to F12 have a standard behavior and send multimedia events
>> when the function key is pressed. The keyboard mode is set through the
>> fn_mode module parameter: when set to 1 (default setting) the keyboard
>> behaves as with the generic HID driver, when set to 0 the keyboard is
>> configured to work as standard keyboards.
>>
>> Signed-off-by: Florent Flament <contact@florentflament.com>
>> ---
>> drivers/hid/Kconfig | 18 ++++++++
>> drivers/hid/Makefile | 1 +
>> drivers/hid/hid-ids.h | 1 +
>> drivers/hid/hid-logitech-k290.c | 100 ++++++++++++++++++++++++++++++++++++++++
>> 4 files changed, 120 insertions(+)
>> create mode 100644 drivers/hid/hid-logitech-k290.c
>>
>> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
>> index 19c499f5623d..6686da8daac6 100644
>> --- a/drivers/hid/Kconfig
>> +++ b/drivers/hid/Kconfig
>> @@ -488,6 +488,24 @@ config HID_LOGITECH_HIDPP
>> T651, TK820), some mice (Zone Touch mouse), or even keyboards (Solar
>> Keyboard).
>>
>> +config HID_LOGITECH_K290
>> + tristate "Logitech K290 Keyboard support"
>> + depends on USB_HID
>> + ---help---
>> + This enhances support of Logitech K290 keyboards.
>> +
>> + With the generic HID driver, K290 keyboards' F1 to F12 keys
>> + send multimedia events by default, and standard keycodes when
>> + the function key is pressed. This driver allows to configure
>> + K290 keyboards, so that F1 to F12 have a standard behavior and
>> + send multimedia events when the function key is pressed. The
>> + keyboard mode is set through the fn_mode module parameter:
>> + when set to 1 (default setting) the keyboard behaves as with
>> + the generic HID driver, when set to 0 the keyboard is
>> + configured to work as standard keyboards.
>> +
>> + Say Y if you have a Logitech K290 keyboard.
>> +
>> config LOGITECH_FF
>> bool "Logitech force feedback support"
>> depends on HID_LOGITECH
>> diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile
>> index eb13b9e92d85..78079d3a5d58 100644
>> --- a/drivers/hid/Makefile
>> +++ b/drivers/hid/Makefile
>> @@ -61,6 +61,7 @@ obj-$(CONFIG_HID_LENOVO) += hid-lenovo.o
>> obj-$(CONFIG_HID_LOGITECH) += hid-logitech.o
>> obj-$(CONFIG_HID_LOGITECH_DJ) += hid-logitech-dj.o
>> obj-$(CONFIG_HID_LOGITECH_HIDPP) += hid-logitech-hidpp.o
>> +obj-$(CONFIG_HID_LOGITECH_K290) += hid-logitech-k290.o
>> obj-$(CONFIG_HID_MAGICMOUSE) += hid-magicmouse.o
>> obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o
>> obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o
>> diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
>> index 9454ac134ce2..68caba7e666c 100644
>> --- a/drivers/hid/hid-ids.h
>> +++ b/drivers/hid/hid-ids.h
>> @@ -693,6 +693,7 @@
>> #define USB_DEVICE_ID_LOGITECH_HARMONY_LAST 0xc14f
>> #define USB_DEVICE_ID_LOGITECH_HARMONY_PS3 0x0306
>> #define USB_DEVICE_ID_LOGITECH_KEYBOARD_G710_PLUS 0xc24d
>> +#define USB_DEVICE_ID_LOGITECH_KEYBOARD_K290 0xc31f
>> #define USB_DEVICE_ID_LOGITECH_MOUSE_C01A 0xc01a
>> #define USB_DEVICE_ID_LOGITECH_MOUSE_C05A 0xc05a
>> #define USB_DEVICE_ID_LOGITECH_MOUSE_C06A 0xc06a
>> diff --git a/drivers/hid/hid-logitech-k290.c b/drivers/hid/hid-logitech-k290.c
>> new file mode 100644
>> index 000000000000..36fdb5838842
>> --- /dev/null
>> +++ b/drivers/hid/hid-logitech-k290.c
>> @@ -0,0 +1,100 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * HID driver for Logitech K290 keyboard
>> + *
>> + * Copyright (c) 2018 Florent Flament
>> + *
>> + * This drivers allows to configure the K290 keyboard's function key
>> + * behaviour (whether function mode is activated or not by default).
>> + *
>> + * Logitech custom commands taken from Marcus Ilgner k290-fnkeyctl
>> + * (https://github.com/milgner/k290-fnkeyctl):
>> + * K290_SET_FUNCTION_CMD
>> + * K290_SET_FUNCTION_VAL
>> + * K290_SET_FUNCTION_OFF
>> + * K290_SET_FUNCTION_ON
>> + *
>> + * Based on hid-accutouch.c and hid-elo.c
>> + */
>> +
>> +#include <linux/device.h>
>> +#include <linux/hid.h>
>> +#include <linux/module.h>
>> +#include <linux/stat.h>
>> +#include <linux/types.h>
>> +#include <linux/usb.h>
>> +
>> +#include "hid-ids.h"
>> +#include "usbhid/usbhid.h"
>> +
>> +// Logitech K290 custom USB command and value to setup function key
>> +#define K290_SET_FUNCTION_CMD 0x02
>> +#define K290_SET_FUNCTION_VAL 0x001a
>> +
>> +// Have function mode turned off (as with standard keyboards)
>> +#define K290_SET_FUNCTION_OFF 0x0001
>> +// Have function mode turned on (default k290 behavior)
>> +#define K290_SET_FUNCTION_ON 0x0000
>> +
>> +// Function key default mode is set at module load time for every K290
>> +// keyboards plugged on the machine. By default fn_mode = 1, i.e
>> +// sending K290_SET_FUNCTION_ON (default K290 behavior).
>> +static bool fn_mode = 1;
>> +module_param(fn_mode, bool, 0444);
>> +MODULE_PARM_DESC(fn_mode, "Logitech K290 function key mode (default = 1)");
>> +
>> +static void k290_set_function(struct usb_device *dev, uint16_t function_mode)
>> +{
>> + int ret;
>> +
>> + ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
>> + K290_SET_FUNCTION_CMD,
>> + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
>> + K290_SET_FUNCTION_VAL,
>> + function_mode, 0, 0, USB_CTRL_SET_TIMEOUT);
>> +
>> + if (ret < 0)
>> + dev_err(&dev->dev,
>> + "Failed to setup K290 function key, error %d\n", ret);
>> +}
>> +
>> +static int k290_set_function_hid_device(struct hid_device *hid)
>> +{
>> + struct usb_device *usb_dev = hid_to_usb_dev(hid);
>> +
>> + k290_set_function(usb_dev,
>> + fn_mode ? K290_SET_FUNCTION_ON : K290_SET_FUNCTION_OFF);
>> + return 0;
>> +}
>> +
>> +static int k290_input_configured(struct hid_device *hid,
>> + struct hid_input *hidinput)
>> +{
>> + return k290_set_function_hid_device(hid);
>> +}
>> +
>> +static int k290_resume(struct hid_device *hid)
>> +{
>> + return k290_set_function_hid_device(hid);
>> +}
>> +
>> +static const struct hid_device_id k290_devices[] = {
>> + { HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH,
>> + USB_DEVICE_ID_LOGITECH_KEYBOARD_K290) },
>> + { }
>> +};
>> +MODULE_DEVICE_TABLE(hid, k290_devices);
>> +
>> +static struct hid_driver k290_driver = {
>> + .name = "hid-logitech-k290",
>> + .id_table = k290_devices,
>> + .input_configured = k290_input_configured,
>> + .resume = k290_resume,
>> + .reset_resume = k290_resume,
>> +};
>> +
>> +module_hid_driver(k290_driver);
>> +
>> +MODULE_AUTHOR("Florent Flament <contact@florentflament.com>");
>> +MODULE_DESCRIPTION("Logitech K290 keyboard driver");
>> +MODULE_LICENSE("GPL v2");
>> --
>> 2.14.3
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox