* [PATCH v3 00/26] compat_ioctl: cleanups
From: Arnd Bergmann @ 2019-04-16 20:19 UTC (permalink / raw)
To: Alexander Viro
Cc: linux-fsdevel, y2038, linux-kernel, Arnd Bergmann,
David S. Miller, Greg Kroah-Hartman, Karsten Keil,
James E.J. Bottomley, Martin K. Petersen, Marcel Holtmann, netdev,
devel, linux-integrity, qat-linux, linux-crypto, linux-media,
dri-devel, linux1394-devel, amd-gfx, linux-input, linux-usb,
linux-arm-kernel, linux-ide, linux-iio
Hi Al,
It took me way longer than I had hoped to revisit this series, see
https://lore.kernel.org/lkml/20180912150142.157913-1-arnd@arndb.de/
for the previously posted version.
I've come to the point where all conversion handlers and most
COMPATIBLE_IOCTL() entries are gone from this file, but for
now, this series only has the parts that have either been reviewed
previously, or that are simple enough to include.
The main missing piece is the SG_IO/SG_GET_REQUEST_TABLE conversion.
I'll post the patches I made for that later, as they need more
testing and review from the scsi maintainers.
I hope you can still take these for the coming merge window, unless
new problems come up.
Arnd
Arnd Bergmann (26):
compat_ioctl: pppoe: fix PPPOEIOCSFWD handling
compat_ioctl: move simple ppp command handling into driver
compat_ioctl: avoid unused function warning for do_ioctl
compat_ioctl: move PPPIOCSCOMPRESS32 to ppp-generic.c
compat_ioctl: move PPPIOCSPASS32/PPPIOCSACTIVE32 to ppp_generic.c
compat_ioctl: handle PPPIOCGIDLE for 64-bit time_t
compat_ioctl: move rtc handling into rtc-dev.c
compat_ioctl: add compat_ptr_ioctl()
compat_ioctl: move drivers to compat_ptr_ioctl
compat_ioctl: use correct compat_ptr() translation in drivers
ceph: fix compat_ioctl for ceph_dir_operations
compat_ioctl: move more drivers to compat_ptr_ioctl
compat_ioctl: move tape handling into drivers
compat_ioctl: move ATYFB_CLK handling to atyfb driver
compat_ioctl: move isdn/capi ioctl translation into driver
compat_ioctl: move rfcomm handlers into driver
compat_ioctl: move hci_sock handlers into driver
compat_ioctl: remove HCIUART handling
compat_ioctl: remove HIDIO translation
compat_ioctl: remove translation for sound ioctls
compat_ioctl: remove IGNORE_IOCTL()
compat_ioctl: remove /dev/random commands
compat_ioctl: remove joystick ioctl translation
compat_ioctl: remove PCI ioctl translation
compat_ioctl: remove /dev/raw ioctl translation
compat_ioctl: remove last RAID handling code
Documentation/networking/ppp_generic.txt | 2 +
arch/um/drivers/hostaudio_kern.c | 1 +
drivers/android/binder.c | 2 +-
drivers/char/ppdev.c | 12 +-
drivers/char/random.c | 1 +
drivers/char/tpm/tpm_vtpm_proxy.c | 12 +-
drivers/crypto/qat/qat_common/adf_ctl_drv.c | 2 +-
drivers/dma-buf/dma-buf.c | 4 +-
drivers/dma-buf/sw_sync.c | 2 +-
drivers/dma-buf/sync_file.c | 2 +-
drivers/firewire/core-cdev.c | 12 +-
drivers/gpu/drm/amd/amdkfd/kfd_chardev.c | 2 +-
drivers/hid/hidraw.c | 4 +-
drivers/hid/usbhid/hiddev.c | 11 +-
drivers/hwtracing/stm/core.c | 12 +-
drivers/ide/ide-tape.c | 31 +-
drivers/iio/industrialio-core.c | 2 +-
drivers/infiniband/core/uverbs_main.c | 4 +-
drivers/isdn/capi/capi.c | 31 +
drivers/isdn/i4l/isdn_ppp.c | 14 +-
drivers/media/rc/lirc_dev.c | 4 +-
drivers/mfd/cros_ec_dev.c | 4 +-
drivers/misc/cxl/flash.c | 8 +-
drivers/misc/genwqe/card_dev.c | 23 +-
drivers/misc/mei/main.c | 22 +-
drivers/misc/vmw_vmci/vmci_host.c | 2 +-
drivers/mtd/ubi/cdev.c | 36 +-
drivers/net/ppp/ppp_generic.c | 99 +++-
drivers/net/ppp/pppoe.c | 7 +
drivers/net/ppp/pptp.c | 3 +
drivers/net/tap.c | 12 +-
drivers/nvdimm/bus.c | 4 +-
drivers/nvme/host/core.c | 2 +-
drivers/pci/switch/switchtec.c | 2 +-
drivers/platform/x86/wmi.c | 2 +-
drivers/rpmsg/rpmsg_char.c | 4 +-
drivers/rtc/dev.c | 13 +-
drivers/rtc/rtc-vr41xx.c | 10 +
drivers/s390/char/tape_char.c | 41 +-
drivers/sbus/char/display7seg.c | 2 +-
drivers/sbus/char/envctrl.c | 4 +-
drivers/scsi/3w-xxxx.c | 4 +-
drivers/scsi/cxlflash/main.c | 2 +-
drivers/scsi/esas2r/esas2r_main.c | 2 +-
drivers/scsi/megaraid/megaraid_mm.c | 28 +-
drivers/scsi/osst.c | 34 +-
drivers/scsi/pmcraid.c | 4 +-
drivers/scsi/st.c | 35 +-
drivers/staging/android/ion/ion.c | 4 +-
drivers/staging/pi433/pi433_if.c | 12 +-
drivers/staging/vme/devices/vme_user.c | 2 +-
drivers/tee/tee_core.c | 2 +-
drivers/usb/class/cdc-wdm.c | 2 +-
drivers/usb/class/usbtmc.c | 4 +-
drivers/usb/core/devio.c | 16 +-
drivers/usb/gadget/function/f_fs.c | 12 +-
drivers/vfio/vfio.c | 39 +-
drivers/vhost/net.c | 12 +-
drivers/vhost/scsi.c | 12 +-
drivers/vhost/test.c | 12 +-
drivers/vhost/vsock.c | 12 +-
drivers/video/fbdev/aty/atyfb_base.c | 12 +-
drivers/virt/fsl_hypervisor.c | 2 +-
fs/btrfs/super.c | 2 +-
fs/ceph/dir.c | 1 +
fs/ceph/file.c | 2 +-
fs/compat_ioctl.c | 602 +-------------------
fs/fat/file.c | 13 +-
fs/fuse/dev.c | 2 +-
fs/notify/fanotify/fanotify_user.c | 2 +-
fs/userfaultfd.c | 2 +-
include/linux/fs.h | 7 +
include/linux/if_pppox.h | 2 +
include/linux/mtio.h | 58 ++
include/uapi/linux/ppp-ioctl.h | 2 +
include/uapi/linux/ppp_defs.h | 14 +
net/bluetooth/hci_sock.c | 21 +-
net/bluetooth/rfcomm/sock.c | 14 +-
net/l2tp/l2tp_ppp.c | 3 +
net/rfkill/core.c | 2 +-
sound/core/oss/pcm_oss.c | 4 +
sound/oss/dmasound/dmasound_core.c | 2 +
82 files changed, 452 insertions(+), 1034 deletions(-)
create mode 100644 include/linux/mtio.h
--
2.20.0
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Karsten Keil <isdn@linux-pingi.de>
Cc: "James E.J. Bottomley" <jejb@linux.ibm.com>
Cc: "Martin K. Petersen" <martin.petersen@oracle.com>
Cc: Marcel Holtmann <marcel@holtmann.org>
Cc: netdev@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: devel@driverdev.osuosl.org
Cc: linux-integrity@vger.kernel.org
Cc: qat-linux@intel.com
Cc: linux-crypto@vger.kernel.org
Cc: linux-media@vger.kernel.org
Cc: dri-devel@lists.freedesktop.org
Cc: linux1394-devel@lists.sourceforge.net
Cc: amd-gfx@lists.freedesktop.org
Cc: linux-input@vger.kernel.org
Cc: linux-usb@vger.kernel.org
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-ide@vger.kernel.org
Cc: linux-iio@vger.kernel.org
Cc: linux-rdma@vger.kernel.org
Cc: linuxppc-dev@lists.ozlabs.org
Cc: linux-mtd@lists.infradead.org
Cc: linux-ppp@vger.kernel.org
Cc: linux-nvme@lists.infradead.org
Cc: platform-driver-x86@vger.kernel.org
Cc: linux-remoteproc@vger.kernel.org
Cc: linux-rtc@vger.kernel.org
Cc: linux-s390@vger.kernel.org
Cc: sparclinux@vger.kernel.org
Cc: linux-scsi@vger.kernel.org
Cc: linux-fbdev@vger.kernel.org
Cc: linux-btrfs@vger.kernel.org
Cc: ceph-devel@vger.kernel.org
Cc: linux-fsdevel@vger.kernel.org
Cc: linux-bluetooth@vger.kernel.org
Cc: linux-wireless@vger.kernel.org
Cc: alsa-devel@alsa-project.org
Cc: y2038@lists.linaro.org
^ permalink raw reply
* [PATCH 4/4] input: keyboard: gpio-keys-polled: skip oftree code when CONFIG_OF disabled
From: Enrico Weigelt, metux IT consult @ 2019-04-16 19:57 UTC (permalink / raw)
To: linux-kernel; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <1555444645-15156-1-git-send-email-info@metux.net>
we don't need to build in oftree probing stuff when oftree isn't
enabled at all.
Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
---
drivers/input/keyboard/gpio_keys_polled.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index 3f773b2..fbccb89 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -147,6 +147,7 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev)
static struct gpio_keys_platform_data *
gpio_keys_polled_get_devtree_pdata(struct device *dev)
{
+#ifdef CONFIG_OF
struct gpio_keys_platform_data *pdata;
struct gpio_keys_button *button;
struct fwnode_handle *child;
@@ -200,6 +201,9 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev)
}
return pdata;
+#else /* CONFIG_OF */
+ return ERR_PTR(-ENOENT);
+#endif /* CONFIG_OF */
}
static void gpio_keys_polled_set_abs_params(struct input_dev *input,
@@ -226,7 +230,7 @@ static void gpio_keys_polled_set_abs_params(struct input_dev *input,
{ .compatible = "gpio-keys-polled", },
{ },
};
-MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
+MODULE_DEVICE_TABLE_OF(gpio_keys_polled_of_match);
static struct gpio_desc *gpio_keys_polled_get_gpiod_fwnode(
struct device *dev,
@@ -452,7 +456,9 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
.probe = gpio_keys_polled_probe,
.driver = {
.name = DRV_NAME,
+#ifdef CONFIG_OF
.of_match_table = gpio_keys_polled_of_match,
+#endif /* CONFIG_OF */
},
};
module_platform_driver(gpio_keys_polled_driver);
--
1.9.1
^ permalink raw reply related
* [PATCH 3/4] input: keyboard: gpio_keys_polled: use gpio lookup table
From: Enrico Weigelt, metux IT consult @ 2019-04-16 19:57 UTC (permalink / raw)
To: linux-kernel; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <1555444645-15156-1-git-send-email-info@metux.net>
Support the recently introduced gpio lookup tables for
attaching to gpio lines. So, harcoded gpio numbers aren't
needed anymore.
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org
Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
---
drivers/input/keyboard/gpio_keys_polled.c | 167 +++++++++++++++++++++---------
1 file changed, 119 insertions(+), 48 deletions(-)
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index 3312186..3f773b2 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -24,6 +24,7 @@
#include <linux/platform_device.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
+#include <linux/gpio/machine.h>
#include <linux/gpio_keys.h>
#include <linux/property.h>
@@ -227,6 +228,119 @@ static void gpio_keys_polled_set_abs_params(struct input_dev *input,
};
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
+static struct gpio_desc *gpio_keys_polled_get_gpiod_fwnode(
+ struct device *dev,
+ int idx,
+ const char *desc)
+{
+ struct gpio_desc *gpiod;
+ struct fwnode_handle *child;
+ int x;
+
+ /* get the idx'th child node */
+ child = device_get_next_child_node(dev, NULL);
+ while (child && x) {
+ child = device_get_next_child_node(dev, child);
+ x--;
+ }
+
+ if (!child) {
+ dev_err(dev, "missing oftree child node #%d\n", idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ gpiod = devm_fwnode_get_gpiod_from_child(dev,
+ NULL,
+ child,
+ GPIOD_IN,
+ desc);
+ if (IS_ERR(gpiod)) {
+ if (PTR_ERR(gpiod) != -EPROBE_DEFER)
+ dev_err(dev,
+ "failed to get gpio: %ld\n",
+ PTR_ERR(gpiod));
+ fwnode_handle_put(child);
+ return gpiod;
+ }
+
+ return gpiod;
+}
+
+static struct gpio_desc *gpio_keys_polled_get_gpiod_legacy(
+ struct device *dev,
+ int idx,
+ const struct gpio_keys_button *button)
+{
+ /*
+ * Legacy GPIO number so request the GPIO here and
+ * convert it to descriptor.
+ */
+ unsigned int flags = GPIOF_IN;
+ struct gpio_desc *gpiod;
+ int error;
+
+ dev_info(dev, "hardcoded gpio IDs are deprecated.\n");
+
+ if (button->active_low)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ error = devm_gpio_request_one(dev, button->gpio,
+ flags, button->desc ? : DRV_NAME);
+ if (error) {
+ dev_err(dev,
+ "unable to claim gpio %u, err=%d\n",
+ button->gpio, error);
+ return ERR_PTR(error);
+ }
+
+ gpiod = gpio_to_desc(button->gpio);
+ if (!gpiod) {
+ dev_err(dev,
+ "unable to convert gpio %u to descriptor\n",
+ button->gpio);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return gpiod;
+}
+
+static struct gpio_desc *gpio_keys_polled_get_gpiod(
+ struct device *dev,
+ int idx,
+ const struct gpio_keys_button *button)
+{
+ struct gpio_desc *gpiod = NULL;
+ int error;
+
+ /* No legacy static platform data - use oftree */
+ if (!dev_get_platdata(dev)) {
+ return gpio_keys_polled_get_gpiod_fwnode(
+ dev, idx, button->desc);
+ }
+
+ gpiod = devm_gpiod_get_index(dev, NULL, idx, GPIOF_IN);
+
+ if (!IS_ERR(gpiod)) {
+ dev_info(dev, "picked gpiod idx %d from gpio table\n", idx);
+ gpiod_set_consumer_name(gpiod, button->desc ? : DRV_NAME);
+ return gpiod;
+ }
+
+ if (PTR_ERR(gpiod) != -ENOENT) {
+ dev_err(dev, "failed fetching gpiod #%d: %d\n",
+ idx, PTR_ERR(gpiod));
+ return gpiod;
+ }
+
+ /* Use legacy gpio id, if defined */
+ if (gpio_is_valid(button->gpio)) {
+ return gpio_keys_polled_get_gpiod_legacy(
+ dev, idx, button);
+ }
+
+ return ERR_PTR(-ENOENT);
+}
+
static int gpio_keys_polled_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -291,57 +405,14 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
if (button->wakeup) {
dev_err(dev, DRV_NAME " does not support wakeup\n");
- fwnode_handle_put(child);
return -EINVAL;
}
- if (!dev_get_platdata(dev)) {
- /* No legacy static platform data */
- child = device_get_next_child_node(dev, child);
- if (!child) {
- dev_err(dev, "missing child device node\n");
- return -EINVAL;
- }
-
- bdata->gpiod = devm_fwnode_get_gpiod_from_child(dev,
- NULL, child,
- GPIOD_IN,
- button->desc);
- if (IS_ERR(bdata->gpiod)) {
- error = PTR_ERR(bdata->gpiod);
- if (error != -EPROBE_DEFER)
- dev_err(dev,
- "failed to get gpio: %d\n",
- error);
- fwnode_handle_put(child);
- return error;
- }
- } else if (gpio_is_valid(button->gpio)) {
- /*
- * Legacy GPIO number so request the GPIO here and
- * convert it to descriptor.
- */
- unsigned flags = GPIOF_IN;
-
- if (button->active_low)
- flags |= GPIOF_ACTIVE_LOW;
-
- error = devm_gpio_request_one(dev, button->gpio,
- flags, button->desc ? : DRV_NAME);
- if (error) {
- dev_err(dev,
- "unable to claim gpio %u, err=%d\n",
- button->gpio, error);
- return error;
- }
-
- bdata->gpiod = gpio_to_desc(button->gpio);
- if (!bdata->gpiod) {
- dev_err(dev,
- "unable to convert gpio %u to descriptor\n",
- button->gpio);
- return -EINVAL;
- }
+ bdata->gpiod = gpio_keys_polled_get_gpiod(dev, i, button);
+
+ if (IS_ERR(bdata->gpiod)) {
+ dev_err(dev, "failed to fetch gpiod #%d\n", i);
+ return PTR_ERR(bdata->gpiod);
}
bdata->last_state = -1;
--
1.9.1
^ permalink raw reply related
* [PATCH 2/4] input: keyboard: gpio-keys-polled: use input name from pdata if available
From: Enrico Weigelt, metux IT consult @ 2019-04-16 19:57 UTC (permalink / raw)
To: linux-kernel; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <1555444645-15156-1-git-send-email-info@metux.net>
Instead of hardcoding the input name to the driver name
('gpio-keys-polled'), allow the passing a name via platform data
('name' field was already present), but default to old behaviour
in case of NULL.
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: linux-input@vger.kernel.org
Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
---
drivers/input/keyboard/gpio_keys_polled.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c
index edc7262..3312186 100644
--- a/drivers/input/keyboard/gpio_keys_polled.c
+++ b/drivers/input/keyboard/gpio_keys_polled.c
@@ -272,7 +272,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
input = poll_dev->input;
- input->name = pdev->name;
+ input->name = (pdata->name ? pdata->name : pdev->name);
input->phys = DRV_NAME"/input0";
input->id.bustype = BUS_HOST;
--
1.9.1
^ permalink raw reply related
* [PATCH 1/4] mod_devicetable: helper macro for declaring oftree module device table
From: Enrico Weigelt, metux IT consult @ 2019-04-16 19:57 UTC (permalink / raw)
To: linux-kernel; +Cc: dmitry.torokhov, linux-input
In-Reply-To: <1555444645-15156-1-git-send-email-info@metux.net>
Little helper macro that declares an oftree module device table,
if CONFIG_OF is enabled. Otherwise it's just noop.
This is also helpful if some drivers can be built w/ or w/o
oftree support.
Signed-off-by: Enrico Weigelt, metux IT consult <info@metux.net>
---
include/linux/mod_devicetable.h | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index 448621c..853e301 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -245,6 +245,15 @@ struct of_device_id {
const void *data;
};
+/*
+ * macro for adding the of module device table only if CONFIG_OF enabled
+ */
+#ifdef CONFIG_OF
+#define MODULE_DEVICE_TABLE_OF(name) MODULE_DEVICE_TABLE(of,name)
+#else
+#define MODULE_DEVICE_TABLE_OF(name)
+#endif /* CONFIG_OF */
+
/* VIO */
struct vio_device_id {
char type[32];
--
1.9.1
^ permalink raw reply related
* gpio-keys-polled improvements
From: Enrico Weigelt, metux IT consult @ 2019-04-16 19:57 UTC (permalink / raw)
To: linux-kernel; +Cc: dmitry.torokhov, linux-input
Hello folks,
here're some improvements for the gpio-keys-polled driver.
The first patch isn't in the driver itself, but adds a little module helper
for conditionally declaring oftree module table if oftree is enabled
(only needed for the last patch)
have fun.
--mtx
^ permalink raw reply
* Re: [RFC v3] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: H. Nikolaus Schaller @ 2019-04-16 19:33 UTC (permalink / raw)
To: Bastien Nocera
Cc: Jonathan Cameron, Dmitry Torokhov, Eric Piel, linux-input,
letux-kernel, kernel, Hartmut Knaack, Lars-Peter Clausen,
Peter Meerwald-Stadler, linux-kernel, linux-iio
In-Reply-To: <0189da8d91652dd3ecf729b03029ab9db5a24f99.camel@hadess.net>
Hi Bastien,
> Am 16.04.2019 um 18:04 schrieb Bastien Nocera <hadess@hadess.net>:
>
> Having written a "bridge" myself (I called it a "proxy"[1]), I have a
> few comments.
>
> [1]: https://github.com/hadess/iio-sensor-proxy
Nice work!
>
> Let's start with the easy ones ;) there's a typo in the subject line.
>
> The subject line also says "optionally" but there doesn't seem to be
> any ways to disable the feature if it's shipped by the kernel used.
Well, the "optionally" refers to that this can be completely disabled
by a Kconfig option. Maybe it is the wrong wording for this and should
be changed.
>
> On Mon, 2019-04-15 at 23:05 +0200, H. Nikolaus Schaller wrote:
>> Some user spaces (e.g. some Android devices) use /dev/input/event*
>> for handling
>> the 3D position of the device with respect to the center of gravity
>> (earth).
>> This can be used for gaming input, auto-rotation of screens etc.
>>
>> This interface should be the standard for such use cases because it
>> is an abstraction
>> of how orientation data is acquired from sensor chips. Sensor chips
>> may be connected
>> through different interfaces and in different positions. They may
>> also have different
>> parameters. And, if a chip is replaced by a different one, the values
>> reported by
>> the device position interface should remain the same, provided the
>> device tree reflects
>> the changed chip.
>
> I don't understand this section of the commit message. The IIO drivers
> are already that abstraction interface, no?
IIO is also some abstraction but a different one than input accelerometers.
IIO reports physical measurement data in some standardized way reporting value,
scale, units, type of measurement. But this has no inherent purpose.
Accelerator input events are something different. They report the orientation
of a handheld device in space relative to center of earth. They may be implemented
through iio drivers but do not need to.
You will find several non-iio accelerometer drivers in drivers/input/misc and
elsewhere.
>
>> This did initially lead to input accelerometer drivers like
>> drivers/input/misc/bma150.c
>> or drivers/misc/lis3lv02d/
>>
>> But nowadays, new accelerometer chips mostly get iio drivers and
>> rarely input drivers.
>>
>> Therefore we need something like a protocol stack which bridges raw
>> data and input devices.
>> It can be seen as a similar layering like TCP/IP vs. bare Ethernet.
>> Or keyboard
>> input events vs. raw gpio or raw USB access.
>
> This can be done in user-space, reading the data from the IIO driver,
> and using uinput to feed it back. Why is doing this at the kernel level
> better?
Well, I'd estimate that >80% of the current kernel could be done in user-space
(but not at the same speed/quality).
E.g. TCP could most likely be done by directly accessing the Ethernet layer and
providing other processes access through named pipes instead of sockets.
But usually a user-space daemon feeding things back into the kernel is slower
(because it is scheduled differently) and needs more resources for running the
process and IPC and is less protected against hickups and deadlocks.
Two more aspects come to my mind from reading your project page:
a) "It requires libgudev and systemd"
b) "Note that a number of kernel bugs will prevent it from working correctly"
a) this makes quite significant assumptions about the user-space while a kernel
driver can be kept independent of this
b) if it is in-kernel it will be kept in sync with kernel changes and such bugs
are less likely
>
>> This patch bridges the gap between raw iio data and the input device
>> abstraction
>> so that accelerometer measurements can additionally be presented as
>> X/Y/Z accelerometer
>> channels (INPUT_PROP_ACCELEROMETER) through /dev/input/event*.
>>
>> There are no special requirements or changes needed for an iio
>> driver.
>
> The user-space daemon I wrote supports both IIO drivers and input
> drivers for accelerometers. How do I know from user-space whether a
> device is proxied or not?
Since my proposal does not stop direct iio access, I assume that your
daemon will simply continue to work as is.
>
>> There is no need to define a mapping (e.g. in device tree).
>>
>> This driver simply collects the first 3 accelerometer channels as X,
>> Y and Z.
>> If only 1 or 2 channels are available, they are used for X and Y
>> only. Additional
>> channels are ignored.
>
> In what cases are 2 dimensional accelerometers used?
I don't know. This is just a description that there will be a graceful
behavior, if some accelerometer has less or more than 3 channels. So
the driver will segfault or panic the kernel...
>
>> Scaling is done automatically so that 1g is represented by value 256
>> and
>> range is assumed to be -511 .. +511 which gives a reasonable
>> precision as an
>> input device.
>>
>> If a mount-matrix is provided by the iio driver, it is also taken
>> into account
>> so that the input event automatically gets the correct orientation
>> with respect
>> to the device.
>>
>> If this extension is not configured into the kernel it takes no
>> resources (except
>> source code).
>>
>> If it is configured, but there is no accelerometer, there is only a
>> tiny penalty
>> for scanning for accelerometer channels once during probe of each iio
>> device.
>>
>> If it runs, the driver polls the device(s) once every 100 ms. A mode
>> where the
>> iio device defines the update rate is not implemented and for further
>> study.
>>
>> If there is no user-space client, polling is not running.
>
> Is the bridge going to modify the IIO device's settings behind other
> possible consumer's backs, such as threshold values, and triggers?
No. The bridge only transforms values.
For other parameters it takes what the iio device has been initialized to
Or if they are changed dynamically through the iio API, it uses new parameters.
>
>> The driver is capable to handle multiple iio accelerometers and they
>> are
>> presented by unique /dev/input/event* files. The iio chip name is
>> used to define
>> the input device name so that it can be identified (e.g. by udev
>> rules or evtest).
>
> As you can probably guess, I'm not overly enthusiastic about this piece
> of code. If it had existed 5 years ago, I probably wouldn't have
> written iio-sensor-proxy, but then somebody else would have had to for
> the rest of the IIO sensors that can be consumed.
I have looked into your work and get the impression that my proposal is
not contradicting your work.
You still need mechanisms to rotate e.g. the display if the device is
rotated. This is not done by this kernel code.
This is a similar situation that a keyboard driver reports key event
codes but does not draw glyphs.
The bridge provides a different (and well established) higher-level API
for accelerometers than iio.
So you may check if it simplifies your code by using this higher-layer interface
(input event). From my rough check it appears to me that you can for example
remove the mount-matrix handling from your code because it would be done by
this bridge.
> To me, this bridge has all the drawbacks of a simple user-space
> implementation using uinput, without much of the benefits of being an
> exclusive user of the IIO accelerometers, such as being able to change
> the update rate, or using triggers depending on the usage.
>
> What am I missing? Why shouldn't this live in user-space?
That's a pretty biased question...
It's a matter of philosophy whether you want a microkernel + user-space daemons
or a kernel that prebakes many things to make user-space daemons easier or
even superfluous.
In the other extreme you could even get rid of iio and directly access the
sensors through /dev/i2c from user-space daemon.
So I can't give you a technical answer for that and since I am not a
maintainer I can't answer it at all.
BR and thanks,
Nikolaus
^ permalink raw reply
* Re: [RFC v3] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: Bastien Nocera @ 2019-04-16 16:04 UTC (permalink / raw)
To: H. Nikolaus Schaller, Jonathan Cameron, Dmitry Torokhov
Cc: Eric Piel, linux-input, letux-kernel, kernel, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, linux-kernel,
linux-iio
In-Reply-To: <d52cf9ee5944c90c69f6e74a46d844cef51e487e.1555362312.git.hns@goldelico.com>
Having written a "bridge" myself (I called it a "proxy"[1]), I have a
few comments.
[1]: https://github.com/hadess/iio-sensor-proxy
Let's start with the easy ones ;) there's a typo in the subject line.
The subject line also says "optionally" but there doesn't seem to be
any ways to disable the feature if it's shipped by the kernel used.
On Mon, 2019-04-15 at 23:05 +0200, H. Nikolaus Schaller wrote:
> Some user spaces (e.g. some Android devices) use /dev/input/event*
> for handling
> the 3D position of the device with respect to the center of gravity
> (earth).
> This can be used for gaming input, auto-rotation of screens etc.
>
> This interface should be the standard for such use cases because it
> is an abstraction
> of how orientation data is acquired from sensor chips. Sensor chips
> may be connected
> through different interfaces and in different positions. They may
> also have different
> parameters. And, if a chip is replaced by a different one, the values
> reported by
> the device position interface should remain the same, provided the
> device tree reflects
> the changed chip.
I don't understand this section of the commit message. The IIO drivers
are already that abstraction interface, no?
> This did initially lead to input accelerometer drivers like
> drivers/input/misc/bma150.c
> or drivers/misc/lis3lv02d/
>
> But nowadays, new accelerometer chips mostly get iio drivers and
> rarely input drivers.
>
> Therefore we need something like a protocol stack which bridges raw
> data and input devices.
> It can be seen as a similar layering like TCP/IP vs. bare Ethernet.
> Or keyboard
> input events vs. raw gpio or raw USB access.
This can be done in user-space, reading the data from the IIO driver,
and using uinput to feed it back. Why is doing this at the kernel level
better?
> This patch bridges the gap between raw iio data and the input device
> abstraction
> so that accelerometer measurements can additionally be presented as
> X/Y/Z accelerometer
> channels (INPUT_PROP_ACCELEROMETER) through /dev/input/event*.
>
> There are no special requirements or changes needed for an iio
> driver.
The user-space daemon I wrote supports both IIO drivers and input
drivers for accelerometers. How do I know from user-space whether a
device is proxied or not?
> There is no need to define a mapping (e.g. in device tree).
>
> This driver simply collects the first 3 accelerometer channels as X,
> Y and Z.
> If only 1 or 2 channels are available, they are used for X and Y
> only. Additional
> channels are ignored.
In what cases are 2 dimensional accelerometers used?
> Scaling is done automatically so that 1g is represented by value 256
> and
> range is assumed to be -511 .. +511 which gives a reasonable
> precision as an
> input device.
>
> If a mount-matrix is provided by the iio driver, it is also taken
> into account
> so that the input event automatically gets the correct orientation
> with respect
> to the device.
>
> If this extension is not configured into the kernel it takes no
> resources (except
> source code).
>
> If it is configured, but there is no accelerometer, there is only a
> tiny penalty
> for scanning for accelerometer channels once during probe of each iio
> device.
>
> If it runs, the driver polls the device(s) once every 100 ms. A mode
> where the
> iio device defines the update rate is not implemented and for further
> study.
>
> If there is no user-space client, polling is not running.
Is the bridge going to modify the IIO device's settings behind other
possible consumer's backs, such as threshold values, and triggers?
> The driver is capable to handle multiple iio accelerometers and they
> are
> presented by unique /dev/input/event* files. The iio chip name is
> used to define
> the input device name so that it can be identified (e.g. by udev
> rules or evtest).
As you can probably guess, I'm not overly enthusiastic about this piece
of code. If it had existed 5 years ago, I probably wouldn't have
written iio-sensor-proxy, but then somebody else would have had to for
the rest of the IIO sensors that can be consumed.
To me, this bridge has all the drawbacks of a simple user-space
implementation using uinput, without much of the benefits of being an
exclusive user of the IIO accelerometers, such as being able to change
the update rate, or using triggers depending on the usage.
What am I missing? Why shouldn't this live in user-space?
Cheers
^ permalink raw reply
* Re: [PATCH v3 2/3] Input: add a driver for GPIO controllable vibrators
From: Luca Weiss @ 2019-04-16 16:02 UTC (permalink / raw)
To: Stephen Boyd
Cc: Dmitry Torokhov, Rob Herring, Mark Rutland, Mauro Carvalho Chehab,
Pascal PAILLET-LME, Coly Li, Lee Jones, Xiaotong Lu, Brian Masney,
Rob Herring, Baolin Wang, David Brown,
open list:ARM/QUALCOMM SUPPORT,
open list:INPUT KEYBOARD, MOUSE, JOYSTICK , TOUCHSCREEN...,
open list:OPEN FIRMWARE AND FLATTENED DEVICE TREE BINDINGS,
open list
In-Reply-To: <155509103299.20095.15957232326393337000@swboyd.mtv.corp.google.com>
On Freitag, 12. April 2019 19:43:52 CEST Stephen Boyd wrote:
> Quoting Luca Weiss (2019-04-12 08:06:24)
>
> > diff --git a/drivers/input/misc/gpio-vibra.c
> > b/drivers/input/misc/gpio-vibra.c new file mode 100644
> > index 000000000000..3fd2dfd4f670
> > --- /dev/null
> > +++ b/drivers/input/misc/gpio-vibra.c
> > @@ -0,0 +1,209 @@
> > +
> > +static int gpio_vibrator_probe(struct platform_device *pdev)
> > +{
>
> [...]
>
> > + vibrator->input->id.bustype = BUS_HOST;
> > + vibrator->input->close = gpio_vibrator_close;
> > +
> > + input_set_drvdata(vibrator->input, vibrator);
> > + input_set_capability(vibrator->input, EV_FF, FF_RUMBLE);
> > +
> > + err = input_ff_create_memless(vibrator->input, NULL,
> > + gpio_vibrator_play_effect);
> > + if (err) {
> > + dev_err(&pdev->dev, "Couldn't create FF dev: %d", err);
> > + return err;
> > + }
> > +
> > + err = input_register_device(vibrator->input);
> > + if (err) {
> > + dev_err(&pdev->dev, "Couldn't register input dev: %d",
> > err);
> All the printks in this file need a newline.
Fixed.
> > + return err;
> > + }
> > +
> > + platform_set_drvdata(pdev, vibrator);
> > +
> > + return 0;
> > +}
> > +
> > +
> > +#ifdef CONFIG_OF
> > +static const struct of_device_id gpio_vibra_dt_match_table[] = {
> > + { .compatible = "gpio-vibrator" },
> > + {},
>
> Nitpick: Drop the comma on the sentinel so nothing can go after it
> without causing a compilation error.
Changed as well.
> > +};
> > +MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
> > +#endif
> > +
Thanks for the review! Will send a v4 shortly.
Luca
^ permalink raw reply
* Re: [PATCH v2 2/4] mfd: ioc3: Add driver for SGI IOC3 chip
From: Greg Kroah-Hartman @ 2019-04-16 13:16 UTC (permalink / raw)
To: Thomas Bogendoerfer
Cc: Ralf Baechle, Paul Burton, James Hogan, Dmitry Torokhov,
Lee Jones, David S. Miller, Alessandro Zummo, Alexandre Belloni,
Jiri Slaby, linux-mips, linux-kernel, linux-input, netdev,
linux-rtc, linux-serial
In-Reply-To: <20190409154610.6735-3-tbogendoerfer@suse.de>
On Tue, Apr 09, 2019 at 05:46:06PM +0200, Thomas Bogendoerfer wrote:
> SGI IOC3 chip has integrated ethernet, keyboard and mouse interface.
> It also supports connecting a SuperIO chip for serial and parallel
> interfaces. IOC3 is used inside various SGI systemboards and add-on
> cards with different equipped external interfaces.
>
> Support for ethernet and serial interfaces were implemented inside
> the network driver. This patchset moves out the not network related
> parts to a new MFD driver, which takes care of card detection,
> setup of platform devices and interrupt distribution for the subdevices.
>
> Signed-off-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
> ---
> arch/mips/include/asm/sn/ioc3.h | 345 +++---
> arch/mips/sgi-ip27/ip27-timer.c | 20 -
> drivers/mfd/Kconfig | 13 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/ioc3.c | 802 ++++++++++++++
> drivers/net/ethernet/sgi/Kconfig | 4 +-
> drivers/net/ethernet/sgi/ioc3-eth.c | 1867 ++++++++++++---------------------
> drivers/tty/serial/8250/8250_ioc3.c | 98 ++
> drivers/tty/serial/8250/Kconfig | 11 +
> drivers/tty/serial/8250/Makefile | 1 +
> include/linux/platform_data/ioc3eth.h | 15 +
> 11 files changed, 1762 insertions(+), 1415 deletions(-)
> create mode 100644 drivers/mfd/ioc3.c
> create mode 100644 drivers/tty/serial/8250/8250_ioc3.c
> create mode 100644 include/linux/platform_data/ioc3eth.h
Serial portion:
Acked-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
^ permalink raw reply
* Re: [PATCH 3/3] iio: Add PAT9125 optical tracker sensor
From: Alexandre @ 2019-04-16 12:54 UTC (permalink / raw)
Cc: linux-kernel, linux-iio, linux-input
In-Reply-To: <20190407112024.6297cbfa@archlinux>
Hello Jonathan,
On 4/7/19 12:20, Jonathan Cameron wrote:
> Hi Alexandre,
>
> So I have no problem with this as an IIO driver, but for devices that
> are somewhat 'on the edge' I always like to get a clear answer to the
> question: Why not input?
>
> I would also argue that, to actually be 'useful' we would typically need
> some representation of the 'mechanicals' that are providing the motion
> being measured. Looking at the datasheet this includes, rotating shafts
> (side or end on), disk edges and flat surface tracking (mouse like).
>
> That's easy enough to do with the iio in kernel consumer interface. These
> are similar to when we handle analog electronic front ends.
>
> I you can, please describe what it is being used for in your application
> as that may give us somewhere to start!
>
> + CC Dmitry and linux-input.
I developed this driver to detect the board movement which can't be
detected by accelerometer (very slow motion). I admit this use case can
be handled by an input, and I'm agree with you, PAT9125 driver could be
an input. But, like you said, this chip is able to track different kind
of motion, and additionally have an interrupt GPIO, so using it like
input limit the driver potential. This chip is designed to work in
industrial measurement or embedded systems, and the IIO API match with
these environments, so it's the best way to exploit the entire potential
of this chip.
As I understand (from
https://www.kernel.org/doc/html/v4.12/input/event-codes.html#mice ),
mouse driver must report values when the device move. This feature
souldn't be mandatory for an optical tracker driver, specially for cases
where user prefers to use buffer or poll only when he need data.
> If 1 or 2, I would suggest that you provide absolute position to
> Linux. So add the value to a software counter and provide that.
> 32 bits should be plenty of resolution for that.
I can't provide absolute position, only relative. Do you mean using
input driver to do that ? If not, how is built the position data?
> Silly question for you. What happens if you set the delta values to 0?
> Do we get an interrupt which is effectively data ready?
> If we do, you might want to think about a scheme where that is an option.
> As things currently stand we have a confusing interface where changing this
> threshold effects the buffered data output. That should only be the
> case if this interface is for a trigger, not an event.
I'm not sure to understand your question. Is it possible to read delta_x
and delta_y = 0 in special/corner case because internal value continue
to be updated after toggled motion_detect pin (used for IRQ) until
values registers are read and then motion_detect pin is released:
* Chip move (i.e. +2 on X axis and 0 on Y axis)
* Motion_detect IRQ trigger and internal reg value is updated (i.e.
delta_x = 2 and delta_y = 0)
* GPIO IRQ handled but read_value isn't executed yet (timing reason)
* Chip move back to it origin point (i.e. -2 on X axis and 0 on Y axis)
* Motion_detect IRQ still low because it hasn't been reset by read
value and internal reg value is updated (i.e. delta_x = 0 and
delta_y = 0)
* Read_value is executed, we get delta values = 0.
> If it is actually not possible to report the two channels separately
> then don't report them at all except via the buffered interface and
> set the available scan masks so that both are on.
I found a way to keep the consistency between delta x and delta y
(without losing data). The first part is to reset a value only when user
read it (also when it's buffered). The second part is to add the new
value to the old value. With these two mechanism, X and Y will always be
consistent:
* as possible during a move.
* perfectly when move is finished.
Regards,
Alexandre
^ permalink raw reply
* Re: [PATCH v5 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Andy Shevchenko @ 2019-04-16 12:52 UTC (permalink / raw)
To: Life is hard, and then you die
Cc: Dmitry Torokhov, Henrik Rydberg, Andrzej Hajda, Inki Dae,
Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi,
Laurent Pinchart, linux-input, dri-devel, linux-kernel
In-Reply-To: <20190415230955.GA13033@innovation.ch>
On Mon, Apr 15, 2019 at 04:09:55PM -0700, Life is hard, and then you die wrote:
> On Mon, Apr 15, 2019 at 12:03:46PM +0300, Andy Shevchenko wrote:
> > On Mon, Apr 15, 2019 at 01:13:00AM -0700, Ronald Tschalär wrote:
> > > +static void
> > > +applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol)
> > > +{
> > > + unsigned char tmp;
> >
> > > + u8 bit = BIT(fnremap - 1);
> >
> > The above is UB and I'm sorry I didn't find this earlier.
> >
> > So, something like this would work
> >
> > u8 bit = BIT((fnremap - 1) & 0x07);
>
> fnremap is already constrained by the following:
"already" is a wrong word here. Compiler checks in the order of appearing, so,
for it it is UB and we need to limit bits to allowed range, up to 31 for
unsigned int.
>
> > > +
> > > + if (!fnremap || fnremap > ARRAY_SIZE(applespi_controlcodes) ||
> > > + !applespi_controlcodes[fnremap - 1])
> > > + return;
>
> and the array-size of applespi_controlcodes is constrained to the
> number of bits in u8 according to this assertion
>
> > > + compiletime_assert(ARRAY_SIZE(applespi_controlcodes) ==
> > > + sizeof_field(struct keyboard_protocol, modifiers) * 8,
> > > + "applespi_controlcodes has wrong number of entries");
>
> So I don't see that the masking buys anything new.
It buys us the follow to the standard. But gcc is clever enough to strip number
to allowed one.
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* [PATCH v6 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Ronald Tschalär @ 2019-04-16 10:26 UTC (permalink / raw)
To: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Andrzej Hajda,
Inki Dae, Greg Kroah-Hartman
Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
dri-devel, linux-kernel
In-Reply-To: <20190416102647.5602-1-ronald@innovation.ch>
The keyboard and trackpad on recent MacBook's (since 8,1) and
MacBookPro's (13,* and 14,*) are attached to an SPI controller instead
of USB, as previously. The higher level protocol is not publicly
documented and hence has been reverse engineered. As a consequence there
are still a number of unknown fields and commands. However, the known
parts have been working well and received extensive testing and use.
In order for this driver to work, the proper SPI drivers need to be
loaded too; for MB8,1 these are spi_pxa2xx_platform and spi_pxa2xx_pci;
for all others they are spi_pxa2xx_platform and intel_lpss_pci. For this
reason enabling this driver in the config implies enabling the above
drivers.
CC: Federico Lorenzi <federico@travelground.com>
CC: Lukas Wunner <lukas@wunner.de>
CC: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Link: https://bugzilla.kernel.org/show_bug.cgi?id=99891
Link: https://bugzilla.kernel.org/show_bug.cgi?id=108331
Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
---
drivers/input/keyboard/Kconfig | 15 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/applespi.c | 1975 +++++++++++++++++++++++
drivers/input/keyboard/applespi.h | 29 +
drivers/input/keyboard/applespi_trace.h | 94 ++
5 files changed, 2114 insertions(+)
create mode 100644 drivers/input/keyboard/applespi.c
create mode 100644 drivers/input/keyboard/applespi.h
create mode 100644 drivers/input/keyboard/applespi_trace.h
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a878351f1643..d0a9e7fa2508 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -70,6 +70,21 @@ config KEYBOARD_AMIGA
config ATARI_KBD_CORE
bool
+config KEYBOARD_APPLESPI
+ tristate "Apple SPI keyboard and trackpad"
+ depends on ACPI && EFI
+ depends on SPI
+ depends on X86 || COMPILE_TEST
+ imply SPI_PXA2XX
+ imply SPI_PXA2XX_PCI
+ imply MFD_INTEL_LPSS_PCI
+ help
+ Say Y here if you are running Linux on any Apple MacBook8,1 or later,
+ or any MacBookPro13,* or MacBookPro14,*.
+
+ To compile this driver as a module, choose M here: the
+ module will be called applespi.
+
config KEYBOARD_ATARI
tristate "Atari keyboard"
depends on ATARI
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 182e92985dbf..9283fee2505a 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -10,6 +10,7 @@ obj-$(CONFIG_KEYBOARD_ADP5520) += adp5520-keys.o
obj-$(CONFIG_KEYBOARD_ADP5588) += adp5588-keys.o
obj-$(CONFIG_KEYBOARD_ADP5589) += adp5589-keys.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+obj-$(CONFIG_KEYBOARD_APPLESPI) += applespi.o
obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
obj-$(CONFIG_KEYBOARD_BCM) += bcm-keypad.o
diff --git a/drivers/input/keyboard/applespi.c b/drivers/input/keyboard/applespi.c
new file mode 100644
index 000000000000..74e235e2e543
--- /dev/null
+++ b/drivers/input/keyboard/applespi.c
@@ -0,0 +1,1975 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2018 Federico Lorenzi
+ * Copyright (c) 2017-2018 Ronald Tschalär
+ */
+
+/*
+ * The keyboard and touchpad controller on the MacBookAir6, MacBookPro12,
+ * MacBook8 and newer can be driven either by USB or SPI. However the USB
+ * pins are only connected on the MacBookAir6 and 7 and the MacBookPro12.
+ * All others need this driver. The interface is selected using ACPI methods:
+ *
+ * * UIEN ("USB Interface Enable"): If invoked with argument 1, disables SPI
+ * and enables USB. If invoked with argument 0, disables USB.
+ * * UIST ("USB Interface Status"): Returns 1 if USB is enabled, 0 otherwise.
+ * * SIEN ("SPI Interface Enable"): If invoked with argument 1, disables USB
+ * and enables SPI. If invoked with argument 0, disables SPI.
+ * * SIST ("SPI Interface Status"): Returns 1 if SPI is enabled, 0 otherwise.
+ * * ISOL: Resets the four GPIO pins used for SPI. Intended to be invoked with
+ * argument 1, then once more with argument 0.
+ *
+ * UIEN and UIST are only provided on models where the USB pins are connected.
+ *
+ * SPI-based Protocol
+ * ------------------
+ *
+ * The device and driver exchange messages (struct message); each message is
+ * encapsulated in one or more packets (struct spi_packet). There are two types
+ * of exchanges: reads, and writes. A read is signaled by a GPE, upon which one
+ * message can be read from the device. A write exchange consists of writing a
+ * command message, immediately reading a short status packet, and then, upon
+ * receiving a GPE, reading the response message. Write exchanges cannot be
+ * interleaved, i.e. a new write exchange must not be started till the previous
+ * write exchange is complete. Whether a received message is part of a read or
+ * write exchange is indicated in the encapsulating packet's flags field.
+ *
+ * A single message may be too large to fit in a single packet (which has a
+ * fixed, 256-byte size). In that case it will be split over multiple,
+ * consecutive packets.
+ */
+
+#include <linux/acpi.h>
+#include <linux/crc16.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/efi.h>
+#include <linux/input.h>
+#include <linux/input/mt.h>
+#include <linux/leds.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/spi/spi.h>
+#include <linux/wait.h>
+#include <linux/workqueue.h>
+
+#include <asm/barrier.h>
+#include <asm/unaligned.h>
+
+#define CREATE_TRACE_POINTS
+#include "applespi.h"
+#include "applespi_trace.h"
+
+#define APPLESPI_PACKET_SIZE 256
+#define APPLESPI_STATUS_SIZE 4
+
+#define PACKET_TYPE_READ 0x20
+#define PACKET_TYPE_WRITE 0x40
+#define PACKET_DEV_KEYB 0x01
+#define PACKET_DEV_TPAD 0x02
+#define PACKET_DEV_INFO 0xd0
+
+#define MAX_ROLLOVER 6
+
+#define MAX_FINGERS 11
+#define MAX_FINGER_ORIENTATION 16384
+#define MAX_PKTS_PER_MSG 2
+
+#define KBD_BL_LEVEL_MIN 32U
+#define KBD_BL_LEVEL_MAX 255U
+#define KBD_BL_LEVEL_SCALE 1000000U
+#define KBD_BL_LEVEL_ADJ \
+ ((KBD_BL_LEVEL_MAX - KBD_BL_LEVEL_MIN) * KBD_BL_LEVEL_SCALE / 255U)
+
+#define EFI_BL_LEVEL_NAME L"KeyboardBacklightLevel"
+#define EFI_BL_LEVEL_GUID EFI_GUID(0xa076d2af, 0x9678, 0x4386, 0x8b, 0x58, 0x1f, 0xc8, 0xef, 0x04, 0x16, 0x19)
+
+#define APPLE_FLAG_FKEY 0x01
+
+#define SPI_RW_CHG_DELAY_US 100 /* from experimentation, in µs */
+
+#define SYNAPTICS_VENDOR_ID 0x06cb
+
+static unsigned int fnmode = 1;
+module_param(fnmode, uint, 0644);
+MODULE_PARM_DESC(fnmode, "Mode of Fn key on Apple keyboards (0 = disabled, [1] = fkeyslast, 2 = fkeysfirst)");
+
+static unsigned int fnremap;
+module_param(fnremap, uint, 0644);
+MODULE_PARM_DESC(fnremap, "Remap Fn key ([0] = no-remap; 1 = left-ctrl, 2 = left-shift, 3 = left-alt, 4 = left-meta, 6 = right-shift, 7 = right-alt, 8 = right-meta)");
+
+static bool iso_layout;
+module_param(iso_layout, bool, 0644);
+MODULE_PARM_DESC(iso_layout, "Enable/Disable hardcoded ISO-layout of the keyboard. ([0] = disabled, 1 = enabled)");
+
+static char touchpad_dimensions[40];
+module_param_string(touchpad_dimensions, touchpad_dimensions,
+ sizeof(touchpad_dimensions), 0444);
+MODULE_PARM_DESC(touchpad_dimensions, "The pixel dimensions of the touchpad, as XxY+W+H .");
+
+/**
+ * struct keyboard_protocol - keyboard message.
+ * message.type = 0x0110, message.length = 0x000a
+ *
+ * @unknown1: unknown
+ * @modifiers: bit-set of modifier/control keys pressed
+ * @unknown2: unknown
+ * @keys_pressed: the (non-modifier) keys currently pressed
+ * @fn_pressed: whether the fn key is currently pressed
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct keyboard_protocol {
+ __u8 unknown1;
+ __u8 modifiers;
+ __u8 unknown2;
+ __u8 keys_pressed[MAX_ROLLOVER];
+ __u8 fn_pressed;
+ __le16 crc16;
+};
+
+/**
+ * struct tp_finger - single trackpad finger structure, le16-aligned
+ *
+ * @origin: zero when switching track finger
+ * @abs_x: absolute x coodinate
+ * @abs_y: absolute y coodinate
+ * @rel_x: relative x coodinate
+ * @rel_y: relative y coodinate
+ * @tool_major: tool area, major axis
+ * @tool_minor: tool area, minor axis
+ * @orientation: 16384 when point, else 15 bit angle
+ * @touch_major: touch area, major axis
+ * @touch_minor: touch area, minor axis
+ * @unused: zeros
+ * @pressure: pressure on forcetouch touchpad
+ * @multi: one finger: varies, more fingers: constant
+ * @crc16: on last finger: crc over the whole message struct
+ * (i.e. message header + this struct) minus the last
+ * @crc16 field; unknown on all other fingers.
+ */
+struct tp_finger {
+ __le16 origin;
+ __le16 abs_x;
+ __le16 abs_y;
+ __le16 rel_x;
+ __le16 rel_y;
+ __le16 tool_major;
+ __le16 tool_minor;
+ __le16 orientation;
+ __le16 touch_major;
+ __le16 touch_minor;
+ __le16 unused[2];
+ __le16 pressure;
+ __le16 multi;
+ __le16 crc16;
+};
+
+/**
+ * struct touchpad_protocol - touchpad message.
+ * message.type = 0x0210
+ *
+ * @unknown1: unknown
+ * @clicked: 1 if a button-click was detected, 0 otherwise
+ * @unknown2: unknown
+ * @number_of_fingers: the number of fingers being reported in @fingers
+ * @clicked2: same as @clicked
+ * @unknown3: unknown
+ * @fingers: the data for each finger
+ */
+struct touchpad_protocol {
+ __u8 unknown1[1];
+ __u8 clicked;
+ __u8 unknown2[28];
+ __u8 number_of_fingers;
+ __u8 clicked2;
+ __u8 unknown3[16];
+ struct tp_finger fingers[0];
+};
+
+/**
+ * struct command_protocol_tp_info - get touchpad info.
+ * message.type = 0x1020, message.length = 0x0000
+ *
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct command_protocol_tp_info {
+ __le16 crc16;
+};
+
+/**
+ * struct touchpad_info - touchpad info response.
+ * message.type = 0x1020, message.length = 0x006e
+ *
+ * @unknown1: unknown
+ * @model_flags: flags (vary by model number, but significance otherwise
+ * unknown)
+ * @model_no: the touchpad model number
+ * @unknown2: unknown
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct touchpad_info_protocol {
+ __u8 unknown1[105];
+ __u8 model_flags;
+ __u8 model_no;
+ __u8 unknown2[3];
+ __le16 crc16;
+};
+
+/**
+ * struct command_protocol_mt_init - initialize multitouch.
+ * message.type = 0x0252, message.length = 0x0002
+ *
+ * @cmd: value: 0x0102
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct command_protocol_mt_init {
+ __le16 cmd;
+ __le16 crc16;
+};
+
+/**
+ * struct command_protocol_capsl - toggle caps-lock led
+ * message.type = 0x0151, message.length = 0x0002
+ *
+ * @unknown: value: 0x01 (length?)
+ * @led: 0 off, 2 on
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct command_protocol_capsl {
+ __u8 unknown;
+ __u8 led;
+ __le16 crc16;
+};
+
+/**
+ * struct command_protocol_bl - set keyboard backlight brightness
+ * message.type = 0xB051, message.length = 0x0006
+ *
+ * @const1: value: 0x01B0
+ * @level: the brightness level to set
+ * @const2: value: 0x0001 (backlight off), 0x01F4 (backlight on)
+ * @crc16: crc over the whole message struct (message header +
+ * this struct) minus this @crc16 field
+ */
+struct command_protocol_bl {
+ __le16 const1;
+ __le16 level;
+ __le16 const2;
+ __le16 crc16;
+};
+
+/**
+ * struct message - a complete spi message.
+ *
+ * Each message begins with fixed header, followed by a message-type specific
+ * payload, and ends with a 16-bit crc. Because of the varying lengths of the
+ * payload, the crc is defined at the end of each payload struct, rather than
+ * in this struct.
+ *
+ * @type: the message type
+ * @zero: always 0
+ * @counter: incremented on each message, rolls over after 255; there is a
+ * separate counter for each message type.
+ * @rsp_buf_len:response buffer length (the exact nature of this field is quite
+ * speculative). On a request/write this is often the same as
+ * @length, though in some cases it has been seen to be much larger
+ * (e.g. 0x400); on a response/read this the same as on the
+ * request; for reads that are not responses it is 0.
+ * @length: length of the remainder of the data in the whole message
+ * structure (after re-assembly in case of being split over
+ * multiple spi-packets), minus the trailing crc. The total size
+ * of the message struct is therefore @length + 10.
+ */
+struct message {
+ __le16 type;
+ __u8 zero;
+ __u8 counter;
+ __le16 rsp_buf_len;
+ __le16 length;
+ union {
+ struct keyboard_protocol keyboard;
+ struct touchpad_protocol touchpad;
+ struct touchpad_info_protocol tp_info;
+ struct command_protocol_tp_info tp_info_command;
+ struct command_protocol_mt_init init_mt_command;
+ struct command_protocol_capsl capsl_command;
+ struct command_protocol_bl bl_command;
+ __u8 data[0];
+ };
+};
+
+/* type + zero + counter + rsp_buf_len + length */
+#define MSG_HEADER_SIZE 8
+
+/**
+ * struct spi_packet - a complete spi packet; always 256 bytes. This carries
+ * the (parts of the) message in the data. But note that this does not
+ * necessarily contain a complete message, as in some cases (e.g. many
+ * fingers pressed) the message is split over multiple packets (see the
+ * @offset, @remaining, and @length fields). In general the data parts in
+ * spi_packet's are concatenated until @remaining is 0, and the result is an
+ * message.
+ *
+ * @flags: 0x40 = write (to device), 0x20 = read (from device); note that
+ * the response to a write still has 0x40.
+ * @device: 1 = keyboard, 2 = touchpad
+ * @offset: specifies the offset of this packet's data in the complete
+ * message; i.e. > 0 indicates this is a continuation packet (in
+ * the second packet for a message split over multiple packets
+ * this would then be the same as the @length in the first packet)
+ * @remaining: number of message bytes remaining in subsequents packets (in
+ * the first packet of a message split over two packets this would
+ * then be the same as the @length in the second packet)
+ * @length: length of the valid data in the @data in this packet
+ * @data: all or part of a message
+ * @crc16: crc over this whole structure minus this @crc16 field. This
+ * covers just this packet, even on multi-packet messages (in
+ * contrast to the crc in the message).
+ */
+struct spi_packet {
+ __u8 flags;
+ __u8 device;
+ __le16 offset;
+ __le16 remaining;
+ __le16 length;
+ __u8 data[246];
+ __le16 crc16;
+};
+
+struct spi_settings {
+ u64 spi_cs_delay; /* cs-to-clk delay in us */
+ u64 reset_a2r_usec; /* active-to-receive delay? */
+ u64 reset_rec_usec; /* ? (cur val: 10) */
+};
+
+/* this mimics struct drm_rect */
+struct applespi_tp_info {
+ int x_min;
+ int y_min;
+ int x_max;
+ int y_max;
+};
+
+struct applespi_data {
+ struct spi_device *spi;
+ struct spi_settings spi_settings;
+ struct input_dev *keyboard_input_dev;
+ struct input_dev *touchpad_input_dev;
+
+ u8 *tx_buffer;
+ u8 *tx_status;
+ u8 *rx_buffer;
+
+ u8 *msg_buf;
+ unsigned int saved_msg_len;
+
+ struct applespi_tp_info tp_info;
+
+ u8 last_keys_pressed[MAX_ROLLOVER];
+ u8 last_keys_fn_pressed[MAX_ROLLOVER];
+ u8 last_fn_pressed;
+ struct input_mt_pos pos[MAX_FINGERS];
+ int slots[MAX_FINGERS];
+ int gpe;
+ acpi_handle sien;
+ acpi_handle sist;
+
+ struct spi_transfer dl_t;
+ struct spi_transfer rd_t;
+ struct spi_message rd_m;
+
+ struct spi_transfer ww_t;
+ struct spi_transfer wd_t;
+ struct spi_transfer wr_t;
+ struct spi_transfer st_t;
+ struct spi_message wr_m;
+
+ bool want_tp_info_cmd;
+ bool want_mt_init_cmd;
+ bool want_cl_led_on;
+ bool have_cl_led_on;
+ unsigned int want_bl_level;
+ unsigned int have_bl_level;
+ unsigned int cmd_msg_cntr;
+ /* lock to protect the above parameters and flags below */
+ spinlock_t cmd_msg_lock;
+ bool cmd_msg_queued;
+ enum applespi_evt_type cmd_evt_type;
+
+ struct led_classdev backlight_info;
+
+ bool suspended;
+ bool drain;
+ wait_queue_head_t drain_complete;
+ bool read_active;
+ bool write_active;
+
+ struct work_struct work;
+ struct touchpad_info_protocol rcvd_tp_info;
+
+ struct dentry *debugfs_root;
+ bool debug_tp_dim;
+ char tp_dim_val[40];
+ int tp_dim_min_x;
+ int tp_dim_max_x;
+ int tp_dim_min_y;
+ int tp_dim_max_y;
+};
+
+static const unsigned char applespi_scancodes[] = {
+ 0, 0, 0, 0,
+ KEY_A, KEY_B, KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J,
+ KEY_K, KEY_L, KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T,
+ KEY_U, KEY_V, KEY_W, KEY_X, KEY_Y, KEY_Z,
+ KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, KEY_0,
+ KEY_ENTER, KEY_ESC, KEY_BACKSPACE, KEY_TAB, KEY_SPACE, KEY_MINUS,
+ KEY_EQUAL, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0,
+ KEY_SEMICOLON, KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH,
+ KEY_CAPSLOCK,
+ KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9,
+ KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_RIGHT, KEY_LEFT, KEY_DOWN, KEY_UP,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_102ND,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RO, 0, KEY_YEN, 0, 0, 0, 0, 0,
+ 0, KEY_KATAKANAHIRAGANA, KEY_MUHENKAN
+};
+
+/*
+ * This must have exactly as many entries as there are bits in
+ * struct keyboard_protocol.modifiers .
+ */
+static const unsigned char applespi_controlcodes[] = {
+ KEY_LEFTCTRL,
+ KEY_LEFTSHIFT,
+ KEY_LEFTALT,
+ KEY_LEFTMETA,
+ 0,
+ KEY_RIGHTSHIFT,
+ KEY_RIGHTALT,
+ KEY_RIGHTMETA
+};
+
+struct applespi_key_translation {
+ u16 from;
+ u16 to;
+ u8 flags;
+};
+
+static const struct applespi_key_translation applespi_fn_codes[] = {
+ { KEY_BACKSPACE, KEY_DELETE },
+ { KEY_ENTER, KEY_INSERT },
+ { KEY_F1, KEY_BRIGHTNESSDOWN, APPLE_FLAG_FKEY },
+ { KEY_F2, KEY_BRIGHTNESSUP, APPLE_FLAG_FKEY },
+ { KEY_F3, KEY_SCALE, APPLE_FLAG_FKEY },
+ { KEY_F4, KEY_DASHBOARD, APPLE_FLAG_FKEY },
+ { KEY_F5, KEY_KBDILLUMDOWN, APPLE_FLAG_FKEY },
+ { KEY_F6, KEY_KBDILLUMUP, APPLE_FLAG_FKEY },
+ { KEY_F7, KEY_PREVIOUSSONG, APPLE_FLAG_FKEY },
+ { KEY_F8, KEY_PLAYPAUSE, APPLE_FLAG_FKEY },
+ { KEY_F9, KEY_NEXTSONG, APPLE_FLAG_FKEY },
+ { KEY_F10, KEY_MUTE, APPLE_FLAG_FKEY },
+ { KEY_F11, KEY_VOLUMEDOWN, APPLE_FLAG_FKEY },
+ { KEY_F12, KEY_VOLUMEUP, APPLE_FLAG_FKEY },
+ { KEY_RIGHT, KEY_END },
+ { KEY_LEFT, KEY_HOME },
+ { KEY_DOWN, KEY_PAGEDOWN },
+ { KEY_UP, KEY_PAGEUP },
+ { }
+};
+
+static const struct applespi_key_translation apple_iso_keyboard[] = {
+ { KEY_GRAVE, KEY_102ND },
+ { KEY_102ND, KEY_GRAVE },
+ { }
+};
+
+struct applespi_tp_model_info {
+ u16 model;
+ struct applespi_tp_info tp_info;
+};
+
+static const struct applespi_tp_model_info applespi_tp_models[] = {
+ {
+ .model = 0x04, /* MB8 MB9 MB10 */
+ .tp_info = { -5087, -182, 5579, 6089 },
+ },
+ {
+ .model = 0x05, /* MBP13,1 MBP13,2 MBP14,1 MBP14,2 */
+ .tp_info = { -6243, -170, 6749, 7685 },
+ },
+ {
+ .model = 0x06, /* MBP13,3 MBP14,3 */
+ .tp_info = { -7456, -163, 7976, 9283 },
+ },
+ {}
+};
+
+typedef void (*applespi_trace_fun)(enum applespi_evt_type,
+ enum applespi_pkt_type, u8 *, size_t);
+
+static applespi_trace_fun applespi_get_trace_fun(enum applespi_evt_type type)
+{
+ switch (type) {
+ case ET_CMD_TP_INI:
+ return trace_applespi_tp_ini_cmd;
+ case ET_CMD_BL:
+ return trace_applespi_backlight_cmd;
+ case ET_CMD_CL:
+ return trace_applespi_caps_lock_cmd;
+ case ET_RD_KEYB:
+ return trace_applespi_keyboard_data;
+ case ET_RD_TPAD:
+ return trace_applespi_touchpad_data;
+ case ET_RD_UNKN:
+ return trace_applespi_unknown_data;
+ default:
+ WARN_ONCE(1, "Unknown msg type %d", type);
+ return trace_applespi_unknown_data;
+ }
+}
+
+static void applespi_setup_read_txfrs(struct applespi_data *applespi)
+{
+ struct spi_message *msg = &applespi->rd_m;
+ struct spi_transfer *dl_t = &applespi->dl_t;
+ struct spi_transfer *rd_t = &applespi->rd_t;
+
+ memset(dl_t, 0, sizeof(*dl_t));
+ memset(rd_t, 0, sizeof(*rd_t));
+
+ dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay;
+
+ rd_t->rx_buf = applespi->rx_buffer;
+ rd_t->len = APPLESPI_PACKET_SIZE;
+
+ spi_message_init(msg);
+ spi_message_add_tail(dl_t, msg);
+ spi_message_add_tail(rd_t, msg);
+}
+
+static void applespi_setup_write_txfrs(struct applespi_data *applespi)
+{
+ struct spi_message *msg = &applespi->wr_m;
+ struct spi_transfer *wt_t = &applespi->ww_t;
+ struct spi_transfer *dl_t = &applespi->wd_t;
+ struct spi_transfer *wr_t = &applespi->wr_t;
+ struct spi_transfer *st_t = &applespi->st_t;
+
+ memset(wt_t, 0, sizeof(*wt_t));
+ memset(dl_t, 0, sizeof(*dl_t));
+ memset(wr_t, 0, sizeof(*wr_t));
+ memset(st_t, 0, sizeof(*st_t));
+
+ /*
+ * All we need here is a delay at the beginning of the message before
+ * asserting cs. But the current spi API doesn't support this, so we
+ * end up with an extra unnecessary (but harmless) cs assertion and
+ * deassertion.
+ */
+ wt_t->delay_usecs = SPI_RW_CHG_DELAY_US;
+ wt_t->cs_change = 1;
+
+ dl_t->delay_usecs = applespi->spi_settings.spi_cs_delay;
+
+ wr_t->tx_buf = applespi->tx_buffer;
+ wr_t->len = APPLESPI_PACKET_SIZE;
+ wr_t->delay_usecs = SPI_RW_CHG_DELAY_US;
+
+ st_t->rx_buf = applespi->tx_status;
+ st_t->len = APPLESPI_STATUS_SIZE;
+
+ spi_message_init(msg);
+ spi_message_add_tail(wt_t, msg);
+ spi_message_add_tail(dl_t, msg);
+ spi_message_add_tail(wr_t, msg);
+ spi_message_add_tail(st_t, msg);
+}
+
+static int applespi_async(struct applespi_data *applespi,
+ struct spi_message *message, void (*complete)(void *))
+{
+ message->complete = complete;
+ message->context = applespi;
+
+ return spi_async(applespi->spi, message);
+}
+
+static inline bool applespi_check_write_status(struct applespi_data *applespi,
+ int sts)
+{
+ static u8 status_ok[] = { 0xac, 0x27, 0x68, 0xd5 };
+
+ if (sts < 0) {
+ dev_warn(&applespi->spi->dev, "Error writing to device: %d\n",
+ sts);
+ return false;
+ }
+
+ if (memcmp(applespi->tx_status, status_ok, APPLESPI_STATUS_SIZE)) {
+ dev_warn(&applespi->spi->dev, "Error writing to device: %*ph\n",
+ APPLESPI_STATUS_SIZE, applespi->tx_status);
+ return false;
+ }
+
+ return true;
+}
+
+static int applespi_get_spi_settings(struct applespi_data *applespi)
+{
+ struct acpi_device *adev = ACPI_COMPANION(&applespi->spi->dev);
+ const union acpi_object *o;
+ struct spi_settings *settings = &applespi->spi_settings;
+
+ if (!acpi_dev_get_property(adev, "spiCSDelay", ACPI_TYPE_BUFFER, &o))
+ settings->spi_cs_delay = *(u64 *)o->buffer.pointer;
+ else
+ dev_warn(&applespi->spi->dev,
+ "Property spiCSDelay not found\n");
+
+ if (!acpi_dev_get_property(adev, "resetA2RUsec", ACPI_TYPE_BUFFER, &o))
+ settings->reset_a2r_usec = *(u64 *)o->buffer.pointer;
+ else
+ dev_warn(&applespi->spi->dev,
+ "Property resetA2RUsec not found\n");
+
+ if (!acpi_dev_get_property(adev, "resetRecUsec", ACPI_TYPE_BUFFER, &o))
+ settings->reset_rec_usec = *(u64 *)o->buffer.pointer;
+ else
+ dev_warn(&applespi->spi->dev,
+ "Property resetRecUsec not found\n");
+
+ dev_dbg(&applespi->spi->dev,
+ "SPI settings: spi_cs_delay=%llu reset_a2r_usec=%llu reset_rec_usec=%llu\n",
+ settings->spi_cs_delay, settings->reset_a2r_usec,
+ settings->reset_rec_usec);
+
+ return 0;
+}
+
+static int applespi_setup_spi(struct applespi_data *applespi)
+{
+ int sts;
+
+ sts = applespi_get_spi_settings(applespi);
+ if (sts)
+ return sts;
+
+ spin_lock_init(&applespi->cmd_msg_lock);
+ init_waitqueue_head(&applespi->drain_complete);
+
+ return 0;
+}
+
+static int applespi_enable_spi(struct applespi_data *applespi)
+{
+ acpi_status acpi_sts;
+ unsigned long long spi_status;
+
+ /* check if SPI is already enabled, so we can skip the delay below */
+ acpi_sts = acpi_evaluate_integer(applespi->sist, NULL, NULL,
+ &spi_status);
+ if (ACPI_SUCCESS(acpi_sts) && spi_status)
+ return 0;
+
+ /* SIEN(1) will enable SPI communication */
+ acpi_sts = acpi_execute_simple_method(applespi->sien, NULL, 1);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev, "SIEN failed: %s\n",
+ acpi_format_exception(acpi_sts));
+ return -ENODEV;
+ }
+
+ /*
+ * Allow the SPI interface to come up before returning. Without this
+ * delay, the SPI commands to enable multitouch mode may not reach
+ * the trackpad controller, causing pointer movement to break upon
+ * resume from sleep.
+ */
+ msleep(50);
+
+ return 0;
+}
+
+static int applespi_send_cmd_msg(struct applespi_data *applespi);
+
+static void applespi_msg_complete(struct applespi_data *applespi,
+ bool is_write_msg, bool is_read_compl)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ if (is_read_compl)
+ applespi->read_active = false;
+ if (is_write_msg)
+ applespi->write_active = false;
+
+ if (applespi->drain && !applespi->write_active)
+ wake_up_all(&applespi->drain_complete);
+
+ if (is_write_msg) {
+ applespi->cmd_msg_queued = false;
+ applespi_send_cmd_msg(applespi);
+ }
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static void applespi_async_write_complete(void *context)
+{
+ struct applespi_data *applespi = context;
+ enum applespi_evt_type evt_type = applespi->cmd_evt_type;
+
+ applespi_get_trace_fun(evt_type)(evt_type, PT_WRITE,
+ applespi->tx_buffer,
+ APPLESPI_PACKET_SIZE);
+ applespi_get_trace_fun(evt_type)(evt_type, PT_STATUS,
+ applespi->tx_status,
+ APPLESPI_STATUS_SIZE);
+
+ if (!applespi_check_write_status(applespi, applespi->wr_m.status)) {
+ /*
+ * If we got an error, we presumably won't get the expected
+ * response message either.
+ */
+ applespi_msg_complete(applespi, true, false);
+ }
+}
+
+static int applespi_send_cmd_msg(struct applespi_data *applespi)
+{
+ u16 crc;
+ int sts;
+ struct spi_packet *packet = (struct spi_packet *)applespi->tx_buffer;
+ struct message *message = (struct message *)packet->data;
+ u16 msg_len;
+ u8 device;
+
+ /* check if draining */
+ if (applespi->drain)
+ return 0;
+
+ /* check whether send is in progress */
+ if (applespi->cmd_msg_queued)
+ return 0;
+
+ /* set up packet */
+ memset(packet, 0, APPLESPI_PACKET_SIZE);
+
+ /* are we processing init commands? */
+ if (applespi->want_tp_info_cmd) {
+ applespi->want_tp_info_cmd = false;
+ applespi->want_mt_init_cmd = true;
+ applespi->cmd_evt_type = ET_CMD_TP_INI;
+
+ /* build init command */
+ device = PACKET_DEV_INFO;
+
+ message->type = cpu_to_le16(0x1020);
+ msg_len = sizeof(message->tp_info_command);
+
+ message->zero = 0x02;
+ message->rsp_buf_len = cpu_to_le16(0x0200);
+
+ } else if (applespi->want_mt_init_cmd) {
+ applespi->want_mt_init_cmd = false;
+ applespi->cmd_evt_type = ET_CMD_TP_INI;
+
+ /* build init command */
+ device = PACKET_DEV_TPAD;
+
+ message->type = cpu_to_le16(0x0252);
+ msg_len = sizeof(message->init_mt_command);
+
+ message->init_mt_command.cmd = cpu_to_le16(0x0102);
+
+ /* do we need caps-lock command? */
+ } else if (applespi->want_cl_led_on != applespi->have_cl_led_on) {
+ applespi->have_cl_led_on = applespi->want_cl_led_on;
+ applespi->cmd_evt_type = ET_CMD_CL;
+
+ /* build led command */
+ device = PACKET_DEV_KEYB;
+
+ message->type = cpu_to_le16(0x0151);
+ msg_len = sizeof(message->capsl_command);
+
+ message->capsl_command.unknown = 0x01;
+ message->capsl_command.led = applespi->have_cl_led_on ? 2 : 0;
+
+ /* do we need backlight command? */
+ } else if (applespi->want_bl_level != applespi->have_bl_level) {
+ applespi->have_bl_level = applespi->want_bl_level;
+ applespi->cmd_evt_type = ET_CMD_BL;
+
+ /* build command buffer */
+ device = PACKET_DEV_KEYB;
+
+ message->type = cpu_to_le16(0xB051);
+ msg_len = sizeof(message->bl_command);
+
+ message->bl_command.const1 = cpu_to_le16(0x01B0);
+ message->bl_command.level =
+ cpu_to_le16(applespi->have_bl_level);
+
+ if (applespi->have_bl_level > 0)
+ message->bl_command.const2 = cpu_to_le16(0x01F4);
+ else
+ message->bl_command.const2 = cpu_to_le16(0x0001);
+
+ /* everything's up-to-date */
+ } else {
+ return 0;
+ }
+
+ /* finalize packet */
+ packet->flags = PACKET_TYPE_WRITE;
+ packet->device = device;
+ packet->length = cpu_to_le16(MSG_HEADER_SIZE + msg_len);
+
+ message->counter = applespi->cmd_msg_cntr++ % (U8_MAX + 1);
+
+ message->length = cpu_to_le16(msg_len - 2);
+ if (!message->rsp_buf_len)
+ message->rsp_buf_len = message->length;
+
+ crc = crc16(0, (u8 *)message, le16_to_cpu(packet->length) - 2);
+ put_unaligned_le16(crc, &message->data[msg_len - 2]);
+
+ crc = crc16(0, (u8 *)packet, sizeof(*packet) - 2);
+ packet->crc16 = cpu_to_le16(crc);
+
+ /* send command */
+ sts = applespi_async(applespi, &applespi->wr_m,
+ applespi_async_write_complete);
+ if (sts) {
+ dev_warn(&applespi->spi->dev,
+ "Error queueing async write to device: %d\n", sts);
+ return sts;
+ }
+
+ applespi->cmd_msg_queued = true;
+ applespi->write_active = true;
+
+ return 0;
+}
+
+static void applespi_init(struct applespi_data *applespi, bool is_resume)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ if (is_resume)
+ applespi->want_mt_init_cmd = true;
+ else
+ applespi->want_tp_info_cmd = true;
+ applespi_send_cmd_msg(applespi);
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_set_capsl_led(struct applespi_data *applespi,
+ bool capslock_on)
+{
+ unsigned long flags;
+ int sts;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ applespi->want_cl_led_on = capslock_on;
+ sts = applespi_send_cmd_msg(applespi);
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+ return sts;
+}
+
+static void applespi_set_bl_level(struct led_classdev *led_cdev,
+ enum led_brightness value)
+{
+ struct applespi_data *applespi =
+ container_of(led_cdev, struct applespi_data, backlight_info);
+ unsigned long flags;
+ int sts;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ if (value == 0) {
+ applespi->want_bl_level = value;
+ } else {
+ /*
+ * The backlight does not turn on till level 32, so we scale
+ * the range here so that from a user's perspective it turns
+ * on at 1.
+ */
+ applespi->want_bl_level =
+ ((value * KBD_BL_LEVEL_ADJ) / KBD_BL_LEVEL_SCALE +
+ KBD_BL_LEVEL_MIN);
+ }
+
+ sts = applespi_send_cmd_msg(applespi);
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct applespi_data *applespi = input_get_drvdata(dev);
+
+ switch (type) {
+ case EV_LED:
+ applespi_set_capsl_led(applespi, !!test_bit(LED_CAPSL, dev->led));
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+/* lifted from the BCM5974 driver and renamed from raw2int */
+/* convert 16-bit little endian to signed integer */
+static inline int le16_to_int(__le16 x)
+{
+ return (signed short)le16_to_cpu(x);
+}
+
+static void applespi_debug_update_dimensions(struct applespi_data *applespi,
+ const struct tp_finger *f)
+{
+ applespi->tp_dim_min_x = min_t(int, applespi->tp_dim_min_x, f->abs_x);
+ applespi->tp_dim_max_x = max_t(int, applespi->tp_dim_max_x, f->abs_x);
+ applespi->tp_dim_min_y = min_t(int, applespi->tp_dim_min_y, f->abs_y);
+ applespi->tp_dim_max_y = max_t(int, applespi->tp_dim_max_y, f->abs_y);
+}
+
+static int applespi_tp_dim_open(struct inode *inode, struct file *file)
+{
+ struct applespi_data *applespi = inode->i_private;
+
+ file->private_data = applespi;
+
+ snprintf(applespi->tp_dim_val, sizeof(applespi->tp_dim_val),
+ "0x%.4x %dx%d+%u+%u\n",
+ applespi->touchpad_input_dev->id.product,
+ applespi->tp_dim_min_x, applespi->tp_dim_min_y,
+ applespi->tp_dim_max_x - applespi->tp_dim_min_x,
+ applespi->tp_dim_max_y - applespi->tp_dim_min_y);
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t applespi_tp_dim_read(struct file *file, char __user *buf,
+ size_t len, loff_t *off)
+{
+ struct applespi_data *applespi = file->private_data;
+
+ return simple_read_from_buffer(buf, len, off, applespi->tp_dim_val,
+ strlen(applespi->tp_dim_val));
+}
+
+static const struct file_operations applespi_tp_dim_fops = {
+ .owner = THIS_MODULE,
+ .open = applespi_tp_dim_open,
+ .read = applespi_tp_dim_read,
+ .llseek = no_llseek,
+};
+
+static void report_finger_data(struct input_dev *input, int slot,
+ const struct input_mt_pos *pos,
+ const struct tp_finger *f)
+{
+ input_mt_slot(input, slot);
+ input_mt_report_slot_state(input, MT_TOOL_FINGER, true);
+
+ input_report_abs(input, ABS_MT_TOUCH_MAJOR,
+ le16_to_int(f->touch_major) << 1);
+ input_report_abs(input, ABS_MT_TOUCH_MINOR,
+ le16_to_int(f->touch_minor) << 1);
+ input_report_abs(input, ABS_MT_WIDTH_MAJOR,
+ le16_to_int(f->tool_major) << 1);
+ input_report_abs(input, ABS_MT_WIDTH_MINOR,
+ le16_to_int(f->tool_minor) << 1);
+ input_report_abs(input, ABS_MT_ORIENTATION,
+ MAX_FINGER_ORIENTATION - le16_to_int(f->orientation));
+ input_report_abs(input, ABS_MT_POSITION_X, pos->x);
+ input_report_abs(input, ABS_MT_POSITION_Y, pos->y);
+}
+
+static void report_tp_state(struct applespi_data *applespi,
+ struct touchpad_protocol *t)
+{
+ const struct tp_finger *f;
+ struct input_dev *input;
+ const struct applespi_tp_info *tp_info = &applespi->tp_info;
+ int i, n;
+
+ /* touchpad_input_dev is set async in worker */
+ input = smp_load_acquire(&applespi->touchpad_input_dev);
+ if (!input)
+ return; /* touchpad isn't initialized yet */
+
+ n = 0;
+
+ for (i = 0; i < t->number_of_fingers; i++) {
+ f = &t->fingers[i];
+ if (le16_to_int(f->touch_major) == 0)
+ continue;
+ applespi->pos[n].x = le16_to_int(f->abs_x);
+ applespi->pos[n].y = tp_info->y_min + tp_info->y_max -
+ le16_to_int(f->abs_y);
+ n++;
+
+ if (applespi->debug_tp_dim)
+ applespi_debug_update_dimensions(applespi, f);
+ }
+
+ input_mt_assign_slots(input, applespi->slots, applespi->pos, n, 0);
+
+ for (i = 0; i < n; i++)
+ report_finger_data(input, applespi->slots[i],
+ &applespi->pos[i], &t->fingers[i]);
+
+ input_mt_sync_frame(input);
+ input_report_key(input, BTN_LEFT, t->clicked);
+
+ input_sync(input);
+}
+
+static const struct applespi_key_translation *
+applespi_find_translation(const struct applespi_key_translation *table, u16 key)
+{
+ const struct applespi_key_translation *trans;
+
+ for (trans = table; trans->from; trans++)
+ if (trans->from == key)
+ return trans;
+
+ return NULL;
+}
+
+static unsigned int applespi_translate_fn_key(unsigned int key, int fn_pressed)
+{
+ const struct applespi_key_translation *trans;
+ int do_translate;
+
+ trans = applespi_find_translation(applespi_fn_codes, key);
+ if (trans) {
+ if (trans->flags & APPLE_FLAG_FKEY)
+ do_translate = (fnmode == 2 && fn_pressed) ||
+ (fnmode == 1 && !fn_pressed);
+ else
+ do_translate = fn_pressed;
+
+ if (do_translate)
+ key = trans->to;
+ }
+
+ return key;
+}
+
+static unsigned int applespi_translate_iso_layout(unsigned int key)
+{
+ const struct applespi_key_translation *trans;
+
+ trans = applespi_find_translation(apple_iso_keyboard, key);
+ if (trans)
+ key = trans->to;
+
+ return key;
+}
+
+static unsigned int applespi_code_to_key(u8 code, int fn_pressed)
+{
+ unsigned int key = applespi_scancodes[code];
+
+ if (fnmode)
+ key = applespi_translate_fn_key(key, fn_pressed);
+ if (iso_layout)
+ key = applespi_translate_iso_layout(key);
+ return key;
+}
+
+static void
+applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol)
+{
+ unsigned char tmp;
+ u8 bit = BIT((fnremap - 1) & 0x07);
+
+ if (!fnremap || fnremap > ARRAY_SIZE(applespi_controlcodes) ||
+ !applespi_controlcodes[fnremap - 1])
+ return;
+
+ tmp = keyboard_protocol->fn_pressed;
+ keyboard_protocol->fn_pressed = !!(keyboard_protocol->modifiers & bit);
+ if (tmp)
+ keyboard_protocol->modifiers |= bit;
+ else
+ keyboard_protocol->modifiers &= ~bit;
+}
+
+static void
+applespi_handle_keyboard_event(struct applespi_data *applespi,
+ struct keyboard_protocol *keyboard_protocol)
+{
+ unsigned int key;
+ int i, j;
+
+ compiletime_assert(ARRAY_SIZE(applespi_controlcodes) ==
+ sizeof_field(struct keyboard_protocol, modifiers) * 8,
+ "applespi_controlcodes has wrong number of entries");
+
+ /* check for rollover overflow, which is signalled by all keys == 1 */
+ if (!memchr_inv(keyboard_protocol->keys_pressed, 1, MAX_ROLLOVER))
+ return;
+
+ /* remap fn key if desired */
+ applespi_remap_fn_key(keyboard_protocol);
+
+ /* check released keys */
+ for (i = 0; i < MAX_ROLLOVER; i++) {
+ if (memchr(keyboard_protocol->keys_pressed,
+ applespi->last_keys_pressed[i], MAX_ROLLOVER))
+ continue; /* key is still pressed */
+
+ key = applespi_code_to_key(applespi->last_keys_pressed[i],
+ applespi->last_keys_fn_pressed[i]);
+ input_report_key(applespi->keyboard_input_dev, key, 0);
+ applespi->last_keys_fn_pressed[i] = 0;
+ }
+
+ /* check pressed keys */
+ for (i = 0; i < MAX_ROLLOVER; i++) {
+ if (keyboard_protocol->keys_pressed[i] <
+ ARRAY_SIZE(applespi_scancodes) &&
+ keyboard_protocol->keys_pressed[i] > 0) {
+ key = applespi_code_to_key(
+ keyboard_protocol->keys_pressed[i],
+ keyboard_protocol->fn_pressed);
+ input_report_key(applespi->keyboard_input_dev, key, 1);
+ applespi->last_keys_fn_pressed[i] =
+ keyboard_protocol->fn_pressed;
+ }
+ }
+
+ /* check control keys */
+ for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++) {
+ if (keyboard_protocol->modifiers & BIT(i))
+ input_report_key(applespi->keyboard_input_dev,
+ applespi_controlcodes[i], 1);
+ else
+ input_report_key(applespi->keyboard_input_dev,
+ applespi_controlcodes[i], 0);
+ }
+
+ /* check function key */
+ if (keyboard_protocol->fn_pressed && !applespi->last_fn_pressed)
+ input_report_key(applespi->keyboard_input_dev, KEY_FN, 1);
+ else if (!keyboard_protocol->fn_pressed && applespi->last_fn_pressed)
+ input_report_key(applespi->keyboard_input_dev, KEY_FN, 0);
+ applespi->last_fn_pressed = keyboard_protocol->fn_pressed;
+
+ /* done */
+ input_sync(applespi->keyboard_input_dev);
+ memcpy(&applespi->last_keys_pressed, keyboard_protocol->keys_pressed,
+ sizeof(applespi->last_keys_pressed));
+}
+
+static const struct applespi_tp_info *applespi_find_touchpad_info(__u8 model)
+{
+ const struct applespi_tp_model_info *info;
+
+ for (info = applespi_tp_models; info->model; info++) {
+ if (info->model == model)
+ return &info->tp_info;
+ }
+
+ return NULL;
+}
+
+static int
+applespi_register_touchpad_device(struct applespi_data *applespi,
+ struct touchpad_info_protocol *rcvd_tp_info)
+{
+ const struct applespi_tp_info *tp_info;
+ struct input_dev *touchpad_input_dev;
+ int sts;
+
+ /* set up touchpad dimensions */
+ tp_info = applespi_find_touchpad_info(rcvd_tp_info->model_no);
+ if (!tp_info) {
+ dev_warn(&applespi->spi->dev,
+ "Unknown touchpad model %x - falling back to MB8 touchpad\n",
+ rcvd_tp_info->model_no);
+ tp_info = &applespi_tp_models[0].tp_info;
+ }
+
+ applespi->tp_info = *tp_info;
+
+ if (touchpad_dimensions[0]) {
+ int x, y, w, h;
+
+ sts = sscanf(touchpad_dimensions, "%dx%d+%u+%u", &x, &y, &w, &h);
+ if (sts == 4) {
+ dev_info(&applespi->spi->dev,
+ "Overriding touchpad dimensions from module param\n");
+ applespi->tp_info.x_min = x;
+ applespi->tp_info.y_min = y;
+ applespi->tp_info.x_max = x + w;
+ applespi->tp_info.y_max = y + h;
+ } else {
+ dev_warn(&applespi->spi->dev,
+ "Invalid touchpad dimensions '%s': must be in the form XxY+W+H\n",
+ touchpad_dimensions);
+ touchpad_dimensions[0] = '\0';
+ }
+ }
+ if (!touchpad_dimensions[0]) {
+ snprintf(touchpad_dimensions, sizeof(touchpad_dimensions),
+ "%dx%d+%u+%u",
+ applespi->tp_info.x_min,
+ applespi->tp_info.y_min,
+ applespi->tp_info.x_max - applespi->tp_info.x_min,
+ applespi->tp_info.y_max - applespi->tp_info.y_min);
+ }
+
+ /* create touchpad input device */
+ touchpad_input_dev = devm_input_allocate_device(&applespi->spi->dev);
+ if (!touchpad_input_dev) {
+ dev_err(&applespi->spi->dev,
+ "Failed to allocate touchpad input device\n");
+ return -ENOMEM;
+ }
+
+ touchpad_input_dev->name = "Apple SPI Touchpad";
+ touchpad_input_dev->phys = "applespi/input1";
+ touchpad_input_dev->dev.parent = &applespi->spi->dev;
+ touchpad_input_dev->id.bustype = BUS_SPI;
+ touchpad_input_dev->id.vendor = SYNAPTICS_VENDOR_ID;
+ touchpad_input_dev->id.product =
+ rcvd_tp_info->model_no << 8 | rcvd_tp_info->model_flags;
+
+ /* basic properties */
+ input_set_capability(touchpad_input_dev, EV_REL, REL_X);
+ input_set_capability(touchpad_input_dev, EV_REL, REL_Y);
+
+ __set_bit(INPUT_PROP_POINTER, touchpad_input_dev->propbit);
+ __set_bit(INPUT_PROP_BUTTONPAD, touchpad_input_dev->propbit);
+
+ /* finger touch area */
+ input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MAJOR,
+ 0, 5000, 0, 0);
+ input_set_abs_params(touchpad_input_dev, ABS_MT_TOUCH_MINOR,
+ 0, 5000, 0, 0);
+
+ /* finger approach area */
+ input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MAJOR,
+ 0, 5000, 0, 0);
+ input_set_abs_params(touchpad_input_dev, ABS_MT_WIDTH_MINOR,
+ 0, 5000, 0, 0);
+
+ /* finger orientation */
+ input_set_abs_params(touchpad_input_dev, ABS_MT_ORIENTATION,
+ -MAX_FINGER_ORIENTATION, MAX_FINGER_ORIENTATION,
+ 0, 0);
+
+ /* finger position */
+ input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_X,
+ applespi->tp_info.x_min, applespi->tp_info.x_max,
+ 0, 0);
+ input_set_abs_params(touchpad_input_dev, ABS_MT_POSITION_Y,
+ applespi->tp_info.y_min, applespi->tp_info.y_max,
+ 0, 0);
+
+ /* touchpad button */
+ input_set_capability(touchpad_input_dev, EV_KEY, BTN_LEFT);
+
+ /* multitouch */
+ input_mt_init_slots(touchpad_input_dev, MAX_FINGERS,
+ INPUT_MT_POINTER | INPUT_MT_DROP_UNUSED |
+ INPUT_MT_TRACK);
+
+ /* register input device */
+ sts = input_register_device(touchpad_input_dev);
+ if (sts) {
+ dev_err(&applespi->spi->dev,
+ "Unable to register touchpad input device (%d)\n", sts);
+ return sts;
+ }
+
+ /* touchpad_input_dev is read async in spi callback */
+ smp_store_release(&applespi->touchpad_input_dev, touchpad_input_dev);
+
+ return 0;
+}
+
+static void applespi_worker(struct work_struct *work)
+{
+ struct applespi_data *applespi =
+ container_of(work, struct applespi_data, work);
+
+ applespi_register_touchpad_device(applespi, &applespi->rcvd_tp_info);
+}
+
+static void applespi_handle_cmd_response(struct applespi_data *applespi,
+ struct spi_packet *packet,
+ struct message *message)
+{
+ if (packet->device == PACKET_DEV_INFO &&
+ le16_to_cpu(message->type) == 0x1020) {
+ /*
+ * We're not allowed to sleep here, but registering an input
+ * device can sleep.
+ */
+ applespi->rcvd_tp_info = message->tp_info;
+ schedule_work(&applespi->work);
+ return;
+ }
+
+ if (le16_to_cpu(message->length) != 0x0000) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received unexpected write response: length=%x\n",
+ le16_to_cpu(message->length));
+ return;
+ }
+
+ if (packet->device == PACKET_DEV_TPAD &&
+ le16_to_cpu(message->type) == 0x0252 &&
+ le16_to_cpu(message->rsp_buf_len) == 0x0002)
+ dev_info(&applespi->spi->dev, "modeswitch done.\n");
+}
+
+static bool applespi_verify_crc(struct applespi_data *applespi, u8 *buffer,
+ size_t buflen)
+{
+ u16 crc;
+
+ crc = crc16(0, buffer, buflen);
+ if (crc) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received corrupted packet (crc mismatch)\n");
+ trace_applespi_bad_crc(ET_RD_CRC, READ, buffer, buflen);
+
+ return false;
+ }
+
+ return true;
+}
+
+static void applespi_debug_print_read_packet(struct applespi_data *applespi,
+ struct spi_packet *packet)
+{
+ unsigned int evt_type;
+
+ if (packet->flags == PACKET_TYPE_READ &&
+ packet->device == PACKET_DEV_KEYB)
+ evt_type = ET_RD_KEYB;
+ else if (packet->flags == PACKET_TYPE_READ &&
+ packet->device == PACKET_DEV_TPAD)
+ evt_type = ET_RD_TPAD;
+ else if (packet->flags == PACKET_TYPE_WRITE)
+ evt_type = applespi->cmd_evt_type;
+ else
+ evt_type = ET_RD_UNKN;
+
+ applespi_get_trace_fun(evt_type)(evt_type, PT_READ, applespi->rx_buffer,
+ APPLESPI_PACKET_SIZE);
+}
+
+static void applespi_got_data(struct applespi_data *applespi)
+{
+ struct spi_packet *packet;
+ struct message *message;
+ unsigned int msg_len;
+ unsigned int off;
+ unsigned int rem;
+ unsigned int len;
+
+ /* process packet header */
+ if (!applespi_verify_crc(applespi, applespi->rx_buffer,
+ APPLESPI_PACKET_SIZE)) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ if (applespi->drain) {
+ applespi->read_active = false;
+ applespi->write_active = false;
+
+ wake_up_all(&applespi->drain_complete);
+ }
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+ return;
+ }
+
+ packet = (struct spi_packet *)applespi->rx_buffer;
+
+ applespi_debug_print_read_packet(applespi, packet);
+
+ off = le16_to_cpu(packet->offset);
+ rem = le16_to_cpu(packet->remaining);
+ len = le16_to_cpu(packet->length);
+
+ if (len > sizeof(packet->data)) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received corrupted packet (invalid packet length %u)\n",
+ len);
+ goto msg_complete;
+ }
+
+ /* handle multi-packet messages */
+ if (rem > 0 || off > 0) {
+ if (off != applespi->saved_msg_len) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received unexpected offset (got %u, expected %u)\n",
+ off, applespi->saved_msg_len);
+ goto msg_complete;
+ }
+
+ if (off + rem > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received message too large (size %u)\n",
+ off + rem);
+ goto msg_complete;
+ }
+
+ if (off + len > MAX_PKTS_PER_MSG * APPLESPI_PACKET_SIZE) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received message too large (size %u)\n",
+ off + len);
+ goto msg_complete;
+ }
+
+ memcpy(applespi->msg_buf + off, &packet->data, len);
+ applespi->saved_msg_len += len;
+
+ if (rem > 0)
+ return;
+
+ message = (struct message *)applespi->msg_buf;
+ msg_len = applespi->saved_msg_len;
+ } else {
+ message = (struct message *)&packet->data;
+ msg_len = len;
+ }
+
+ /* got complete message - verify */
+ if (!applespi_verify_crc(applespi, (u8 *)message, msg_len))
+ goto msg_complete;
+
+ if (le16_to_cpu(message->length) != msg_len - MSG_HEADER_SIZE - 2) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received corrupted packet (invalid message length %u - expected %u)\n",
+ le16_to_cpu(message->length),
+ msg_len - MSG_HEADER_SIZE - 2);
+ goto msg_complete;
+ }
+
+ /* handle message */
+ if (packet->flags == PACKET_TYPE_READ &&
+ packet->device == PACKET_DEV_KEYB) {
+ applespi_handle_keyboard_event(applespi, &message->keyboard);
+
+ } else if (packet->flags == PACKET_TYPE_READ &&
+ packet->device == PACKET_DEV_TPAD) {
+ struct touchpad_protocol *tp;
+ size_t tp_len;
+
+ tp = &message->touchpad;
+ tp_len = sizeof(*tp) +
+ tp->number_of_fingers * sizeof(tp->fingers[0]);
+
+ if (le16_to_cpu(message->length) + 2 != tp_len) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Received corrupted packet (invalid message length %u - num-fingers %u, tp-len %zu)\n",
+ le16_to_cpu(message->length),
+ tp->number_of_fingers, tp_len);
+ goto msg_complete;
+ }
+
+ if (tp->number_of_fingers > MAX_FINGERS) {
+ dev_warn_ratelimited(&applespi->spi->dev,
+ "Number of reported fingers (%u) exceeds max (%u))\n",
+ tp->number_of_fingers,
+ MAX_FINGERS);
+ tp->number_of_fingers = MAX_FINGERS;
+ }
+
+ report_tp_state(applespi, tp);
+
+ } else if (packet->flags == PACKET_TYPE_WRITE) {
+ applespi_handle_cmd_response(applespi, packet, message);
+ }
+
+msg_complete:
+ applespi->saved_msg_len = 0;
+
+ applespi_msg_complete(applespi, packet->flags == PACKET_TYPE_WRITE,
+ true);
+}
+
+static void applespi_async_read_complete(void *context)
+{
+ struct applespi_data *applespi = context;
+
+ if (applespi->rd_m.status < 0) {
+ dev_warn(&applespi->spi->dev, "Error reading from device: %d\n",
+ applespi->rd_m.status);
+ /*
+ * We don't actually know if this was a pure read, or a response
+ * to a write. But this is a rare error condition that should
+ * never occur, so clearing both flags to avoid deadlock.
+ */
+ applespi_msg_complete(applespi, true, true);
+ } else {
+ applespi_got_data(applespi);
+ }
+
+ acpi_finish_gpe(NULL, applespi->gpe);
+}
+
+static u32 applespi_notify(acpi_handle gpe_device, u32 gpe, void *context)
+{
+ struct applespi_data *applespi = context;
+ int sts;
+ unsigned long flags;
+
+ trace_applespi_irq_received(ET_RD_IRQ, PT_READ);
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ if (!applespi->suspended) {
+ sts = applespi_async(applespi, &applespi->rd_m,
+ applespi_async_read_complete);
+ if (sts)
+ dev_warn(&applespi->spi->dev,
+ "Error queueing async read to device: %d\n",
+ sts);
+ else
+ applespi->read_active = true;
+ }
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+ return ACPI_INTERRUPT_HANDLED;
+}
+
+static int applespi_get_saved_bl_level(struct applespi_data *applespi)
+{
+ struct efivar_entry *efivar_entry;
+ u16 efi_data = 0;
+ unsigned long efi_data_len;
+ int sts;
+
+ efivar_entry = kmalloc(sizeof(*efivar_entry), GFP_KERNEL);
+ if (!efivar_entry)
+ return -ENOMEM;
+
+ memcpy(efivar_entry->var.VariableName, EFI_BL_LEVEL_NAME,
+ sizeof(EFI_BL_LEVEL_NAME));
+ efivar_entry->var.VendorGuid = EFI_BL_LEVEL_GUID;
+ efi_data_len = sizeof(efi_data);
+
+ sts = efivar_entry_get(efivar_entry, NULL, &efi_data_len, &efi_data);
+ if (sts && sts != -ENOENT)
+ dev_warn(&applespi->spi->dev,
+ "Error getting backlight level from EFI vars: %d\n",
+ sts);
+
+ kfree(efivar_entry);
+
+ return sts ? sts : efi_data;
+}
+
+static void applespi_save_bl_level(struct applespi_data *applespi,
+ unsigned int level)
+{
+ efi_guid_t efi_guid;
+ u32 efi_attr;
+ unsigned long efi_data_len;
+ u16 efi_data;
+ int sts;
+
+ /* Save keyboard backlight level */
+ efi_guid = EFI_BL_LEVEL_GUID;
+ efi_data = (u16)level;
+ efi_data_len = sizeof(efi_data);
+ efi_attr = EFI_VARIABLE_NON_VOLATILE | EFI_VARIABLE_BOOTSERVICE_ACCESS |
+ EFI_VARIABLE_RUNTIME_ACCESS;
+
+ sts = efivar_entry_set_safe(EFI_BL_LEVEL_NAME, efi_guid, efi_attr, true,
+ efi_data_len, &efi_data);
+ if (sts)
+ dev_warn(&applespi->spi->dev,
+ "Error saving backlight level to EFI vars: %d\n", sts);
+}
+
+static int applespi_probe(struct spi_device *spi)
+{
+ struct applespi_data *applespi;
+ acpi_handle spi_handle = ACPI_HANDLE(&spi->dev);
+ acpi_status acpi_sts;
+ int sts, i;
+ unsigned long long gpe, usb_status;
+
+ /* check if the USB interface is present and enabled already */
+ acpi_sts = acpi_evaluate_integer(spi_handle, "UIST", NULL, &usb_status);
+ if (ACPI_SUCCESS(acpi_sts) && usb_status) {
+ /* let the USB driver take over instead */
+ dev_info(&spi->dev, "USB interface already enabled\n");
+ return -ENODEV;
+ }
+
+ /* allocate driver data */
+ applespi = devm_kzalloc(&spi->dev, sizeof(*applespi), GFP_KERNEL);
+ if (!applespi)
+ return -ENOMEM;
+
+ applespi->spi = spi;
+
+ INIT_WORK(&applespi->work, applespi_worker);
+
+ /* store the driver data */
+ spi_set_drvdata(spi, applespi);
+
+ /* create our buffers */
+ applespi->tx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE,
+ GFP_KERNEL);
+ applespi->tx_status = devm_kmalloc(&spi->dev, APPLESPI_STATUS_SIZE,
+ GFP_KERNEL);
+ applespi->rx_buffer = devm_kmalloc(&spi->dev, APPLESPI_PACKET_SIZE,
+ GFP_KERNEL);
+ applespi->msg_buf = devm_kmalloc_array(&spi->dev, MAX_PKTS_PER_MSG,
+ APPLESPI_PACKET_SIZE,
+ GFP_KERNEL);
+
+ if (!applespi->tx_buffer || !applespi->tx_status ||
+ !applespi->rx_buffer || !applespi->msg_buf)
+ return -ENOMEM;
+
+ /* set up our spi messages */
+ applespi_setup_read_txfrs(applespi);
+ applespi_setup_write_txfrs(applespi);
+
+ /* cache ACPI method handles */
+ acpi_sts = acpi_get_handle(spi_handle, "SIEN", &applespi->sien);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev,
+ "Failed to get SIEN ACPI method handle: %s\n",
+ acpi_format_exception(acpi_sts));
+ return -ENODEV;
+ }
+
+ acpi_sts = acpi_get_handle(spi_handle, "SIST", &applespi->sist);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev,
+ "Failed to get SIST ACPI method handle: %s\n",
+ acpi_format_exception(acpi_sts));
+ return -ENODEV;
+ }
+
+ /* switch on the SPI interface */
+ sts = applespi_setup_spi(applespi);
+ if (sts)
+ return sts;
+
+ sts = applespi_enable_spi(applespi);
+ if (sts)
+ return sts;
+
+ /* setup the keyboard input dev */
+ applespi->keyboard_input_dev = devm_input_allocate_device(&spi->dev);
+
+ if (!applespi->keyboard_input_dev)
+ return -ENOMEM;
+
+ applespi->keyboard_input_dev->name = "Apple SPI Keyboard";
+ applespi->keyboard_input_dev->phys = "applespi/input0";
+ applespi->keyboard_input_dev->dev.parent = &spi->dev;
+ applespi->keyboard_input_dev->id.bustype = BUS_SPI;
+
+ applespi->keyboard_input_dev->evbit[0] =
+ BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) | BIT_MASK(EV_REP);
+ applespi->keyboard_input_dev->ledbit[0] = BIT_MASK(LED_CAPSL);
+
+ input_set_drvdata(applespi->keyboard_input_dev, applespi);
+ applespi->keyboard_input_dev->event = applespi_event;
+
+ for (i = 0; i < ARRAY_SIZE(applespi_scancodes); i++)
+ if (applespi_scancodes[i])
+ input_set_capability(applespi->keyboard_input_dev,
+ EV_KEY, applespi_scancodes[i]);
+
+ for (i = 0; i < ARRAY_SIZE(applespi_controlcodes); i++)
+ if (applespi_controlcodes[i])
+ input_set_capability(applespi->keyboard_input_dev,
+ EV_KEY, applespi_controlcodes[i]);
+
+ for (i = 0; i < ARRAY_SIZE(applespi_fn_codes); i++)
+ if (applespi_fn_codes[i].to)
+ input_set_capability(applespi->keyboard_input_dev,
+ EV_KEY, applespi_fn_codes[i].to);
+
+ input_set_capability(applespi->keyboard_input_dev, EV_KEY, KEY_FN);
+
+ sts = input_register_device(applespi->keyboard_input_dev);
+ if (sts) {
+ dev_err(&applespi->spi->dev,
+ "Unable to register keyboard input device (%d)\n", sts);
+ return -ENODEV;
+ }
+
+ /*
+ * The applespi device doesn't send interrupts normally (as is described
+ * in its DSDT), but rather seems to use ACPI GPEs.
+ */
+ acpi_sts = acpi_evaluate_integer(spi_handle, "_GPE", NULL, &gpe);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev,
+ "Failed to obtain GPE for SPI slave device: %s\n",
+ acpi_format_exception(acpi_sts));
+ return -ENODEV;
+ }
+ applespi->gpe = (int)gpe;
+
+ acpi_sts = acpi_install_gpe_handler(NULL, applespi->gpe,
+ ACPI_GPE_LEVEL_TRIGGERED,
+ applespi_notify, applespi);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev,
+ "Failed to install GPE handler for GPE %d: %s\n",
+ applespi->gpe, acpi_format_exception(acpi_sts));
+ return -ENODEV;
+ }
+
+ applespi->suspended = false;
+
+ acpi_sts = acpi_enable_gpe(NULL, applespi->gpe);
+ if (ACPI_FAILURE(acpi_sts)) {
+ dev_err(&applespi->spi->dev,
+ "Failed to enable GPE handler for GPE %d: %s\n",
+ applespi->gpe, acpi_format_exception(acpi_sts));
+ acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
+ return -ENODEV;
+ }
+
+ /* trigger touchpad setup */
+ applespi_init(applespi, false);
+
+ /*
+ * By default this device is not enabled for wakeup; but USB keyboards
+ * generally are, so the expectation is that by default the keyboard
+ * will wake the system.
+ */
+ device_wakeup_enable(&spi->dev);
+
+ /* set up keyboard-backlight */
+ sts = applespi_get_saved_bl_level(applespi);
+ if (sts >= 0)
+ applespi_set_bl_level(&applespi->backlight_info, sts);
+
+ applespi->backlight_info.name = "spi::kbd_backlight";
+ applespi->backlight_info.default_trigger = "kbd-backlight";
+ applespi->backlight_info.brightness_set = applespi_set_bl_level;
+
+ sts = devm_led_classdev_register(&spi->dev, &applespi->backlight_info);
+ if (sts)
+ dev_warn(&applespi->spi->dev,
+ "Unable to register keyboard backlight class dev (%d)\n",
+ sts);
+
+ /* set up debugfs entries for touchpad dimensions logging */
+ applespi->debugfs_root = debugfs_create_dir("applespi", NULL);
+ if (IS_ERR(applespi->debugfs_root)) {
+ if (PTR_ERR(applespi->debugfs_root) != -ENODEV)
+ dev_warn(&applespi->spi->dev,
+ "Error creating debugfs root entry (%ld)\n",
+ PTR_ERR(applespi->debugfs_root));
+ } else {
+ struct dentry *ret;
+
+ ret = debugfs_create_bool("enable_tp_dim", 0600,
+ applespi->debugfs_root,
+ &applespi->debug_tp_dim);
+ if (IS_ERR(ret))
+ dev_dbg(&applespi->spi->dev,
+ "Error creating debugfs entry enable_tp_dim (%ld)\n",
+ PTR_ERR(ret));
+
+ ret = debugfs_create_file("tp_dim", 0400,
+ applespi->debugfs_root, applespi,
+ &applespi_tp_dim_fops);
+ if (IS_ERR(ret))
+ dev_dbg(&applespi->spi->dev,
+ "Error creating debugfs entry tp_dim (%ld)\n",
+ PTR_ERR(ret));
+ }
+
+ return 0;
+}
+
+static void applespi_drain_writes(struct applespi_data *applespi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ applespi->drain = true;
+ wait_event_lock_irq(applespi->drain_complete, !applespi->write_active,
+ applespi->cmd_msg_lock);
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static void applespi_drain_reads(struct applespi_data *applespi)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ wait_event_lock_irq(applespi->drain_complete, !applespi->read_active,
+ applespi->cmd_msg_lock);
+
+ applespi->suspended = true;
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+}
+
+static int applespi_remove(struct spi_device *spi)
+{
+ struct applespi_data *applespi = spi_get_drvdata(spi);
+
+ applespi_drain_writes(applespi);
+
+ acpi_disable_gpe(NULL, applespi->gpe);
+ acpi_remove_gpe_handler(NULL, applespi->gpe, applespi_notify);
+ device_wakeup_disable(&spi->dev);
+
+ applespi_drain_reads(applespi);
+
+ debugfs_remove_recursive(applespi->debugfs_root);
+
+ return 0;
+}
+
+static void applespi_shutdown(struct spi_device *spi)
+{
+ struct applespi_data *applespi = spi_get_drvdata(spi);
+
+ applespi_save_bl_level(applespi, applespi->have_bl_level);
+}
+
+static int applespi_poweroff_late(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct applespi_data *applespi = spi_get_drvdata(spi);
+
+ applespi_save_bl_level(applespi, applespi->have_bl_level);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int applespi_suspend(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct applespi_data *applespi = spi_get_drvdata(spi);
+ acpi_status acpi_sts;
+ int sts;
+
+ /* turn off caps-lock - it'll stay on otherwise */
+ sts = applespi_set_capsl_led(applespi, false);
+ if (sts)
+ dev_warn(&applespi->spi->dev,
+ "Failed to turn off caps-lock led (%d)\n", sts);
+
+ applespi_drain_writes(applespi);
+
+ /* disable the interrupt */
+ acpi_sts = acpi_disable_gpe(NULL, applespi->gpe);
+ if (ACPI_FAILURE(acpi_sts))
+ dev_err(&applespi->spi->dev,
+ "Failed to disable GPE handler for GPE %d: %s\n",
+ applespi->gpe, acpi_format_exception(acpi_sts));
+
+ applespi_drain_reads(applespi);
+
+ return 0;
+}
+
+static int applespi_resume(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct applespi_data *applespi = spi_get_drvdata(spi);
+ acpi_status acpi_sts;
+ unsigned long flags;
+
+ /* ensure our flags and state reflect a newly resumed device */
+ spin_lock_irqsave(&applespi->cmd_msg_lock, flags);
+
+ applespi->drain = false;
+ applespi->have_cl_led_on = false;
+ applespi->have_bl_level = 0;
+ applespi->cmd_msg_queued = false;
+ applespi->read_active = false;
+ applespi->write_active = false;
+
+ applespi->suspended = false;
+
+ spin_unlock_irqrestore(&applespi->cmd_msg_lock, flags);
+
+ /* switch on the SPI interface */
+ applespi_enable_spi(applespi);
+
+ /* re-enable the interrupt */
+ acpi_sts = acpi_enable_gpe(NULL, applespi->gpe);
+ if (ACPI_FAILURE(acpi_sts))
+ dev_err(&applespi->spi->dev,
+ "Failed to re-enable GPE handler for GPE %d: %s\n",
+ applespi->gpe, acpi_format_exception(acpi_sts));
+
+ /* switch the touchpad into multitouch mode */
+ applespi_init(applespi, true);
+
+ return 0;
+}
+#endif
+
+static const struct acpi_device_id applespi_acpi_match[] = {
+ { "APP000D", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, applespi_acpi_match);
+
+const struct dev_pm_ops applespi_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(applespi_suspend, applespi_resume)
+ .poweroff_late = applespi_poweroff_late,
+};
+
+static struct spi_driver applespi_driver = {
+ .driver = {
+ .name = "applespi",
+ .acpi_match_table = applespi_acpi_match,
+ .pm = &applespi_pm_ops,
+ },
+ .probe = applespi_probe,
+ .remove = applespi_remove,
+ .shutdown = applespi_shutdown,
+};
+
+module_spi_driver(applespi_driver)
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MacBook(Pro) SPI Keyboard/Touchpad driver");
+MODULE_AUTHOR("Federico Lorenzi");
+MODULE_AUTHOR("Ronald Tschalär");
diff --git a/drivers/input/keyboard/applespi.h b/drivers/input/keyboard/applespi.h
new file mode 100644
index 000000000000..7f5ab10c597a
--- /dev/null
+++ b/drivers/input/keyboard/applespi.h
@@ -0,0 +1,29 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2019 Federico Lorenzi
+ * Copyright (c) 2017-2019 Ronald Tschalär
+ */
+
+#ifndef _APPLESPI_H_
+#define _APPLESPI_H_
+
+enum applespi_evt_type {
+ ET_CMD_TP_INI = BIT(0),
+ ET_CMD_BL = BIT(1),
+ ET_CMD_CL = BIT(2),
+ ET_RD_KEYB = BIT(8),
+ ET_RD_TPAD = BIT(9),
+ ET_RD_UNKN = BIT(10),
+ ET_RD_IRQ = BIT(11),
+ ET_RD_CRC = BIT(12),
+};
+
+enum applespi_pkt_type {
+ PT_READ,
+ PT_WRITE,
+ PT_STATUS,
+};
+
+#endif /* _APPLESPI_H_ */
diff --git a/drivers/input/keyboard/applespi_trace.h b/drivers/input/keyboard/applespi_trace.h
new file mode 100644
index 000000000000..5e965e1974c7
--- /dev/null
+++ b/drivers/input/keyboard/applespi_trace.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MacBook (Pro) SPI keyboard and touchpad driver
+ *
+ * Copyright (c) 2015-2019 Federico Lorenzi
+ * Copyright (c) 2017-2019 Ronald Tschalär
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM applespi
+
+#if !defined(_APPLESPI_TRACE_H_) || defined(TRACE_HEADER_MULTI_READ)
+#define _APPLESPI_TRACE_H_
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+
+#include "applespi.h"
+
+DECLARE_EVENT_CLASS(dump_message_template,
+ TP_PROTO(enum applespi_evt_type evt_type,
+ enum applespi_pkt_type pkt_type,
+ u8 *buf,
+ size_t len),
+
+ TP_ARGS(evt_type, pkt_type, buf, len),
+
+ TP_STRUCT__entry(
+ __field(enum applespi_evt_type, evt_type)
+ __field(enum applespi_pkt_type, pkt_type)
+ __field(size_t, len)
+ __dynamic_array(u8, buf, len)
+ ),
+
+ TP_fast_assign(
+ __entry->evt_type = evt_type;
+ __entry->pkt_type = pkt_type;
+ __entry->len = len;
+ memcpy(__get_dynamic_array(buf), buf, len);
+ ),
+
+ TP_printk("%-6s: %s",
+ __print_symbolic(__entry->pkt_type,
+ { PT_READ, "read" },
+ { PT_WRITE, "write" },
+ { PT_STATUS, "status" }
+ ),
+ __print_hex(__get_dynamic_array(buf), __entry->len))
+);
+
+#define DEFINE_DUMP_MESSAGE_EVENT(name) \
+DEFINE_EVENT(dump_message_template, name, \
+ TP_PROTO(enum applespi_evt_type evt_type, \
+ enum applespi_pkt_type pkt_type, \
+ u8 *buf, \
+ size_t len), \
+ TP_ARGS(evt_type, pkt_type, buf, len) \
+)
+
+DEFINE_DUMP_MESSAGE_EVENT(applespi_tp_ini_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_backlight_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_caps_lock_cmd);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_keyboard_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_touchpad_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_unknown_data);
+DEFINE_DUMP_MESSAGE_EVENT(applespi_bad_crc);
+
+TRACE_EVENT(applespi_irq_received,
+ TP_PROTO(enum applespi_evt_type evt_type,
+ enum applespi_pkt_type pkt_type),
+
+ TP_ARGS(evt_type, pkt_type),
+
+ TP_STRUCT__entry(
+ __field(enum applespi_evt_type, evt_type)
+ __field(enum applespi_pkt_type, pkt_type)
+ ),
+
+ TP_fast_assign(
+ __entry->evt_type = evt_type;
+ __entry->pkt_type = pkt_type;
+ ),
+
+ "\n"
+);
+
+#endif /* _APPLESPI_TRACE_H_ */
+
+/* This part must be outside protection */
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH ../../drivers/input/keyboard
+#define TRACE_INCLUDE_FILE applespi_trace
+#include <trace/define_trace.h>
+
--
2.20.1
^ permalink raw reply related
* [PATCH v6 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Ronald Tschalär @ 2019-04-16 10:26 UTC (permalink / raw)
To: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Andrzej Hajda,
Inki Dae, Greg Kroah-Hartman
Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
dri-devel, linux-kernel
In-Reply-To: <20190416102647.5602-1-ronald@innovation.ch>
commit d6abe6df706c (drm/bridge: sil_sii8620: do not have a dependency
of RC_CORE) changed the driver to select both RC_CORE and INPUT.
However, this causes problems with other drivers, in particular an input
driver that depends on MFD_INTEL_LPSS_PCI (to be added in a separate
commit):
drivers/clk/Kconfig:9:error: recursive dependency detected!
drivers/clk/Kconfig:9: symbol COMMON_CLK is selected by MFD_INTEL_LPSS
drivers/mfd/Kconfig:566: symbol MFD_INTEL_LPSS is selected by MFD_INTEL_LPSS_PCI
drivers/mfd/Kconfig:580: symbol MFD_INTEL_LPSS_PCI is implied by KEYBOARD_APPLESPI
drivers/input/keyboard/Kconfig:73: symbol KEYBOARD_APPLESPI depends on INPUT
drivers/input/Kconfig:8: symbol INPUT is selected by DRM_SIL_SII8620
drivers/gpu/drm/bridge/Kconfig:83: symbol DRM_SIL_SII8620 depends on DRM_BRIDGE
drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_PL111
drivers/gpu/drm/pl111/Kconfig:1: symbol DRM_PL111 depends on COMMON_CLK
According to the docs and general consensus, select should only be used
for non user-visible symbols, but both RC_CORE and INPUT are
user-visible. Furthermore almost all other references to INPUT
throughout the kernel config are depends, not selects. For this reason
the first part of this change reverts commit d6abe6df706c.
In order to address the original reason for commit d6abe6df706c, namely
that not all boards use the remote controller functionality and hence
should not need have to deal with RC_CORE, the second part of this
change now makes the remote control support in the driver optional and
contingent on RC_CORE being defined. And with this the hard dependency
on INPUT also goes away as that is only needed if RC_CORE is defined
(which in turn already depends on INPUT).
CC: Inki Dae <inki.dae@samsung.com>
CC: Andrzej Hajda <a.hajda@samsung.com>
CC: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
---
drivers/gpu/drm/bridge/Kconfig | 3 +--
drivers/gpu/drm/bridge/sil-sii8620.c | 10 +++++++---
2 files changed, 8 insertions(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
index 2fee47b0d50b..9cf07105b73a 100644
--- a/drivers/gpu/drm/bridge/Kconfig
+++ b/drivers/gpu/drm/bridge/Kconfig
@@ -85,8 +85,7 @@ config DRM_SIL_SII8620
depends on OF
select DRM_KMS_HELPER
imply EXTCON
- select INPUT
- select RC_CORE
+ imply RC_CORE
help
Silicon Image SII8620 HDMI/MHL bridge chip driver.
diff --git a/drivers/gpu/drm/bridge/sil-sii8620.c b/drivers/gpu/drm/bridge/sil-sii8620.c
index a6e8f4591e63..cff3131aae6c 100644
--- a/drivers/gpu/drm/bridge/sil-sii8620.c
+++ b/drivers/gpu/drm/bridge/sil-sii8620.c
@@ -1763,10 +1763,8 @@ static bool sii8620_rcp_consume(struct sii8620 *ctx, u8 scancode)
scancode &= MHL_RCP_KEY_ID_MASK;
- if (!ctx->rc_dev) {
- dev_dbg(ctx->dev, "RCP input device not initialized\n");
+ if (!IS_ENABLED(CONFIG_RC_CORE) || !ctx->rc_dev)
return false;
- }
if (pressed)
rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
@@ -2103,6 +2101,9 @@ static void sii8620_init_rcp_input_dev(struct sii8620 *ctx)
struct rc_dev *rc_dev;
int ret;
+ if (!IS_ENABLED(CONFIG_RC_CORE))
+ return;
+
rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
if (!rc_dev) {
dev_err(ctx->dev, "Failed to allocate RC device\n");
@@ -2217,6 +2218,9 @@ static void sii8620_detach(struct drm_bridge *bridge)
{
struct sii8620 *ctx = bridge_to_sii8620(bridge);
+ if (!IS_ENABLED(CONFIG_RC_CORE))
+ return;
+
rc_unregister_device(ctx->rc_dev);
}
--
2.20.1
^ permalink raw reply related
* [PATCH v6 0/2] Add Apple SPI keyboard and trackpad driver
From: Ronald Tschalär @ 2019-04-16 10:26 UTC (permalink / raw)
To: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Andrzej Hajda,
Inki Dae, Greg Kroah-Hartman
Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
dri-devel, linux-kernel
This changeset adds a driver for the SPI keyboard and trackpad on recent
MacBook's and MacBook Pro's. The driver has seen a fair amount of use
over the last 2 years (basically anybody running linux on these
machines), with only relatively small changes in the last year or so.
For those interested, the driver development has been hosted at
https://github.com/cb22/macbook12-spi-driver/ (as well as my clone at
https://github.com/roadrunner2/macbook12-spi-driver/).
The first patch fixes a problem during config. While it affects the drm
tree, Andrzej Hajda has given his ok for this patch to be taken via the
input tree because the second patch here depends on it.
The second patch contains the new applespi driver.
Changes in v6:
Applied all feedback from review by Andy Shevchenko:
- use memchr() and friends instead of hand-rolled loops
- minor code tweak
The full set of changes to applespi can be viewed at
https://github.com/roadrunner2/macbook12-spi-driver/ as individual
commits 36afd70..fa81d40 in the upstreaming-review branch.
Ronald Tschalär (2):
drm/bridge: sil_sii8620: make remote control optional.
Input: add Apple SPI keyboard and trackpad driver.
drivers/gpu/drm/bridge/Kconfig | 3 +-
drivers/gpu/drm/bridge/sil-sii8620.c | 10 +-
drivers/input/keyboard/Kconfig | 15 +
drivers/input/keyboard/Makefile | 1 +
drivers/input/keyboard/applespi.c | 1975 +++++++++++++++++++++++
drivers/input/keyboard/applespi.h | 29 +
drivers/input/keyboard/applespi_trace.h | 94 ++
7 files changed, 2122 insertions(+), 5 deletions(-)
create mode 100644 drivers/input/keyboard/applespi.c
create mode 100644 drivers/input/keyboard/applespi.h
create mode 100644 drivers/input/keyboard/applespi_trace.h
--
2.20.1
^ permalink raw reply
* Re: [PATCH v5 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Life is hard, and then you die @ 2019-04-16 10:25 UTC (permalink / raw)
To: Andrzej Hajda
Cc: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Inki Dae,
Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi,
Laurent Pinchart, linux-input, dri-devel, linux-kernel
In-Reply-To: <dd5189a2-8422-5e3c-1c79-d68680117deb@samsung.com>
Hi Andrzej,
On Tue, Apr 16, 2019 at 07:56:31AM +0200, Andrzej Hajda wrote:
> On 16.04.2019 01:24, Life is hard, and then you die wrote:
> > Hi Andrzej,
> >
> > On Mon, Apr 15, 2019 at 10:58:09AM +0200, Andrzej Hajda wrote:
> >> On 15.04.2019 10:12, Ronald Tschalär wrote:
> >>> commit d6abe6df706c (drm/bridge: sil_sii8620: do not have a dependency
> >>> of RC_CORE) changed the driver to select both RC_CORE and INPUT.
> >>> However, this causes problems with other drivers, in particular an input
> >>> driver that depends on MFD_INTEL_LPSS_PCI (to be added in a separate
> >>> commit):
> >>>
> >>> drivers/clk/Kconfig:9:error: recursive dependency detected!
> >>> drivers/clk/Kconfig:9: symbol COMMON_CLK is selected by MFD_INTEL_LPSS
> >>> drivers/mfd/Kconfig:566: symbol MFD_INTEL_LPSS is selected by MFD_INTEL_LPSS_PCI
> >>> drivers/mfd/Kconfig:580: symbol MFD_INTEL_LPSS_PCI is implied by KEYBOARD_APPLESPI
> >>> drivers/input/keyboard/Kconfig:73: symbol KEYBOARD_APPLESPI depends on INPUT
> >>> drivers/input/Kconfig:8: symbol INPUT is selected by DRM_SIL_SII8620
> >>> drivers/gpu/drm/bridge/Kconfig:83: symbol DRM_SIL_SII8620 depends on DRM_BRIDGE
> >>> drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_PL111
> >>> drivers/gpu/drm/pl111/Kconfig:1: symbol DRM_PL111 depends on COMMON_CLK
> >>>
> >>> According to the docs and general consensus, select should only be used
> >>> for non user-visible symbols, but both RC_CORE and INPUT are
> >>> user-visible. Furthermore almost all other references to INPUT
> >>> throughout the kernel config are depends, not selects. For this reason
> >>> the first part of this change reverts commit d6abe6df706c.
> >>>
> >>> In order to address the original reason for commit d6abe6df706c, namely
> >>> that not all boards use the remote controller functionality and hence
> >>> should not need have to deal with RC_CORE, the second part of this
> >>> change now makes the remote control support in the driver optional and
> >>> contingent on RC_CORE being defined. And with this the hard dependency
> >>> on INPUT also goes away as that is only needed if RC_CORE is defined
> >>> (which in turn already depends on INPUT).
> >>>
> >>> CC: Inki Dae <inki.dae@samsung.com>
> >>> CC: Andrzej Hajda <a.hajda@samsung.com>
> >>> CC: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >>> CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> >>> Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
> >>
> >> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
> > Thanks for your reviews!
> >
> >> If there are no objections I will take it to drm-misc tomorrow.
> > This brings us back to the discussion started in response to the first
> > version of my patch (see
> > https://lore.kernel.org/lkml/20190124082423.23139-1-ronald@innovation.ch/T/#m24f45fecd987a787a9554c8088f463fd10de2b00).
> > To recap: the problem is that the applespi patch depends on this patch
> > here, as make-config will break as described above otherwise. So if
> > this patch is submitted through drm-misc, then it's unclear to me how
> > to ensure that the two patches make it upstream in proper order,
> > unless the applespi patch is also upstreamed through drm-misc, or the
> > Kconfig for applespi is (temporarily) modified to not trigger the
> > config error and another patch is later submitted to fix the Kconfig
> > again (which seems somewhat ugly to me). Assuming that consensus is to
> > merge both patches through one tree, then it would seem that because
> > this patch here is relatively small that maybe it could be merged
> > through the input tree along with the applespi patch?
>
>
> Oh, I have forgot. Please take it then via input tree.
Thank you!
Cheers,
Ronald
^ permalink raw reply
* Re: [PATCH v5 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Andrzej Hajda @ 2019-04-16 5:56 UTC (permalink / raw)
To: Life is hard, and then you die
Cc: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Inki Dae,
Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi,
Laurent Pinchart, linux-input, dri-devel, linux-kernel
In-Reply-To: <20190415232444.GB13033@innovation.ch>
On 16.04.2019 01:24, Life is hard, and then you die wrote:
> Hi Andrzej,
>
> On Mon, Apr 15, 2019 at 10:58:09AM +0200, Andrzej Hajda wrote:
>> On 15.04.2019 10:12, Ronald Tschalär wrote:
>>> commit d6abe6df706c (drm/bridge: sil_sii8620: do not have a dependency
>>> of RC_CORE) changed the driver to select both RC_CORE and INPUT.
>>> However, this causes problems with other drivers, in particular an input
>>> driver that depends on MFD_INTEL_LPSS_PCI (to be added in a separate
>>> commit):
>>>
>>> drivers/clk/Kconfig:9:error: recursive dependency detected!
>>> drivers/clk/Kconfig:9: symbol COMMON_CLK is selected by MFD_INTEL_LPSS
>>> drivers/mfd/Kconfig:566: symbol MFD_INTEL_LPSS is selected by MFD_INTEL_LPSS_PCI
>>> drivers/mfd/Kconfig:580: symbol MFD_INTEL_LPSS_PCI is implied by KEYBOARD_APPLESPI
>>> drivers/input/keyboard/Kconfig:73: symbol KEYBOARD_APPLESPI depends on INPUT
>>> drivers/input/Kconfig:8: symbol INPUT is selected by DRM_SIL_SII8620
>>> drivers/gpu/drm/bridge/Kconfig:83: symbol DRM_SIL_SII8620 depends on DRM_BRIDGE
>>> drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_PL111
>>> drivers/gpu/drm/pl111/Kconfig:1: symbol DRM_PL111 depends on COMMON_CLK
>>>
>>> According to the docs and general consensus, select should only be used
>>> for non user-visible symbols, but both RC_CORE and INPUT are
>>> user-visible. Furthermore almost all other references to INPUT
>>> throughout the kernel config are depends, not selects. For this reason
>>> the first part of this change reverts commit d6abe6df706c.
>>>
>>> In order to address the original reason for commit d6abe6df706c, namely
>>> that not all boards use the remote controller functionality and hence
>>> should not need have to deal with RC_CORE, the second part of this
>>> change now makes the remote control support in the driver optional and
>>> contingent on RC_CORE being defined. And with this the hard dependency
>>> on INPUT also goes away as that is only needed if RC_CORE is defined
>>> (which in turn already depends on INPUT).
>>>
>>> CC: Inki Dae <inki.dae@samsung.com>
>>> CC: Andrzej Hajda <a.hajda@samsung.com>
>>> CC: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>>> CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
>>> Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
>>
>> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
> Thanks for your reviews!
>
>> If there are no objections I will take it to drm-misc tomorrow.
> This brings us back to the discussion started in response to the first
> version of my patch (see
> https://lore.kernel.org/lkml/20190124082423.23139-1-ronald@innovation.ch/T/#m24f45fecd987a787a9554c8088f463fd10de2b00).
> To recap: the problem is that the applespi patch depends on this patch
> here, as make-config will break as described above otherwise. So if
> this patch is submitted through drm-misc, then it's unclear to me how
> to ensure that the two patches make it upstream in proper order,
> unless the applespi patch is also upstreamed through drm-misc, or the
> Kconfig for applespi is (temporarily) modified to not trigger the
> config error and another patch is later submitted to fix the Kconfig
> again (which seems somewhat ugly to me). Assuming that consensus is to
> merge both patches through one tree, then it would seem that because
> this patch here is relatively small that maybe it could be merged
> through the input tree along with the applespi patch?
Oh, I have forgot. Please take it then via input tree.
Regards
Andrzej
>
>
> Cheers,
>
> Ronald
>
>
>
^ permalink raw reply
* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Kai-Heng Feng @ 2019-04-16 3:59 UTC (permalink / raw)
To: Hans de Goede
Cc: hotwater438, Dmitry Torokhov, Vladislav Dalechyn,
Benjamin Tissoires, Jiri Kosina, Swboyd, Bigeasy,
open list:HID CORE LAYER, lkml
In-Reply-To: <b495a521-16b5-8005-16be-e0b899f954f7@redhat.com>
at 19:42, Hans de Goede <hdegoede@redhat.com> wrote:
> Hi,
>
> On 15-04-19 13:36, hotwater438@tutanota.com wrote:
>> Sorry for the delay.
>> By applying this patch I get next results:
>> Five finger tap and two finger scroll issues disappear, but after
>> suspend touchpad dies. Restarting module doesn't help.
>
> So bascally the same results as with the edge-triggered interrupt
> patch/hack,
> right?
>
> Are you still using the edge-triggered interrupt patch, or just the new
> patch Kai-Heng Feng provided.
>
> To me it sounds like the patch Kai-Heng Feng provided at least removes
> the need for the edge-triggered interrupt patch/hack and what remains to
> be solved is the suspend/resume issues.
Great! I’ll send a patch to address this issue.
Kai-Heng
>
> Regards,
>
> Hans
>
>
>
>> Here's the log:
>> Apr 15 14:35:54 parrot sudo[3473]: h0tw4t3r : TTY=pts/1 ;
>> PWD=/home/h0tw4t3r ; USER=root ; COMMAND=/sbin/rmmod i2c_hid
>> Apr 15 14:35:54 parrot sudo[3478]: h0tw4t3r : TTY=pts/1 ;
>> PWD=/home/h0tw4t3r ; USER=root ; COMMAND=/sbin/modprobe i2c_hid
>> Apr 15 14:35:54 parrot kernel: i2c_hid i2c-ELAN1200:00: i2c-ELAN1200:00
>> supply vdd not found, using dummy regulator
>> Apr 15 14:35:54 parrot kernel: i2c_hid i2c-ELAN1200:00: i2c-ELAN1200:00
>> supply vddl not found, using dummy regulator
>> Could you please explain what this patch does?
>> Regards,
>> Vladislav.
>> Apr 13, 2019, 11:42 AM by kai.heng.feng@canonical.com:
>> at 16:40, <hotwater438@tutanota.com <mailto:hotwater438@tutanota.com>> <hotwater438@tutanota.com <mailto:hotwater438@tutanota.com>> wrote:
>> Hi.
>> I've applied this patch, but still getting incomplete report messages.
>> Does the patch fix the other two issues:
>> - Five finger tap kill's module so you have to restart it;
>> - Two finger scoll is working incorrect and sometimes even when you
>> raised one of two finger still thinks that you are scrolling.
>> Kai-Heng
>> Regards,
>> Vladislav
>> Apr 11, 2019, 7:17 PM by kai.heng.feng@canonical.com <mailto:kai.heng.feng@canonical.com>:
>> Hi,
>> at 05:18, <hotwater438@tutanota.com <mailto:hotwater438@tutanota.com>> <hotwater438@tutanota.com <mailto:hotwater438@tutanota.com>> wrote:
>> Hi.
>> 1) Run "cat /proc/interrupts | grep ELAN" , note down the value
>> 2) Very quickly/briefly touch the touchpad once
>> 3) Run "cat /proc/interrupts | grep ELAN" , note down the value again
>> 4) Subtract result from 1. from result from 3, this difference is
>> the value we are interested in. E.g. my testing got 254 and 257, so
>> a difference of 3.
>> I've tested that, main diffs are 30, 24, 16 (the most frequent), 2 (the least frequent).
>> I was using 4.19.13 kernel, because I use ParrotOS (which happens to be Debian distribution).
>> But I've installed experimental 5.0.0 kernel and I can't say right now if suspend problem is resolved (i have to rebuild latest kernel with patch).
>> Can you try below fix?
>> This can solve what commit 1475af255e18 ("HID: i2c-hid: Ignore input report if there's no data present on Elan touchpanels”) tries to workaround.
>> diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c
>> index c19a4c45f7bb..30e3664f1ae5 100644
>> --- a/drivers/pinctrl/intel/pinctrl-intel.c
>> +++ b/drivers/pinctrl/intel/pinctrl-intel.c
>> @@ -957,6 +957,10 @@ static void intel_gpio_irq_mask_unmask(struct irq_data *d, bool mask)
>> reg = community->regs + community->ie_offset + gpp * 4;
>> raw_spin_lock_irqsave(&pctrl->lock, flags);
>> +
>> + if (!mask)
>> + writel(BIT(gpp_offset), community->regs + community->is_offset + gpp * 4);
>> +
>> value = readl(reg);
>> if (mask)
>> value &= ~BIT(gpp_offset);
>> Regards,
>> Vladislav.
>> Apr 3, 2019, 2:18 PM by hdegoede@redhat.com <mailto:hdegoede@redhat.com>:
>> Hi,
>> On 31-03-19 11:50, hotwater438@tutanota.com <mailto:hotwater438@tutanota.com> wrote:
>> Hi. I've done everything you said, here are results:
>> Vladislav can you check the output of /cat/interrupts on a kernel
>> without the patch and while *not* using the touchpad; and check
>> if the amount of touchpads-interrupts still keeps increasing in this
>> case?
>> IWI or IRQ work interrupts keep increasing with speed at least 3 interrupts/s.
>> I'm really only interested in the touchpad related IRQs, so e.g. the line
>> about "intel-gpio 129 ELAN1200:00", if you're seeing 3 interrupts/s on
>> some others that is fine, so I take it the ELAN1200:00 interrupt count
>> does not increase on an *unpatched* kernel, unless you use the touchpad?
>> Also when I am moving touchpad IR-IO-APIC 14-fasteoi INT345D:00 get's triggered and increased.
>> That is the GPIO controller interrupt, so that one increasing is normal.
>> If I understand things correctly then this all means that the IRQ indeed
>> is a normal level IRQ and Dmitry is likely correct that there is an
>> pinctrl / gpiochip driver problem here.
>> Can you try the following with an *unpatched* kernel? :
>> 1) Run "cat /proc/interrupts | grep ELAN" , note down the value
>> 2) Very quickly/briefly touch the touchpad once
>> 3) Run "cat /proc/interrupts | grep ELAN" , note down the value again
>> 4) Subtract result from 1. from result from 3, this difference is
>> the value we are interested in. E.g. my testing got 254 and 257, so
>> a difference of 3.
>> The goal here is to get an as low as possible difference. Feel free
>> to repeat this a couple of times.
>> On an Apollo Lake laptop with an I2C hid mt touchpad I can get the
>> amount of interrupts triggered for a single touch down to 3,
>> given the huge interrupt counts of 130000+ reported in:
>> https://bugzilla.redhat.com/show_bug.cgi?id=1543769
>> I expect you to get a much bigger smallest possible difference
>> between 2 "cat /proc/interrupts | grep ELAN" commands, note a
>> difference of 0 means your touch did not register.
>> Assuming you indeed see much more interrupts for a very quick
>> touch + release, then we indeed have an interrupt handling problem
>> we need to investigate further.
>> I don't know if it's important or not, but for some reason these interrupts keep popping only on CPU2 (i have 4cpu processor).
>> That does not matter.
>> 1) Suspending the machine by selecting suspend from a menu in your
>> desktop environment, or by briefly pressing the power-button, do
>> not close the lid
>> 2) As soon as the system starts suspending and while it is suspended, move
>> your finger around the touchpad
>> 3) Wake the system up with the powerbutton while moving your finger around
>> 4) Check if the touchpad still works after this
>> It works, but as it seems, looses edge. JournalCTL is being flooded with i2c_hid_get_input: incomplete report (16/65535)
>> That is probably a different issue. If you loose the edge IRQ, then the touchpad
>> would stop working without any messages. I believe that the suspend / resume
>> issue may be fixed by:
>> https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=52cf93e63ee672a92f349edc6ddad86ec8808fd8
>> Does your kernel have this commit? (please always use the latest kernel while
>> testing).
>> Also a thing to notice, that after manually removing and modprobing i2c_hid module, it says next in journalctl:
>> i2c_hid i2c-ELAN1200:00: i2c-ELAN1200:00 supply vdd not found, using dummy regulator
>> i2c_hid i2c-ELAN1200:00: i2c-ELAN1200:00 supply vddl not found, using dummy regulator
>> Those messages can safely be ignored.
>> Regards,
>> Hans
^ permalink raw reply
* Re: [PATCH v5 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Life is hard, and then you die @ 2019-04-15 23:24 UTC (permalink / raw)
To: Andrzej Hajda
Cc: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko, Inki Dae,
Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi,
Laurent Pinchart, linux-input, dri-devel, linux-kernel
In-Reply-To: <76cf3079-f190-bed4-7a00-149d7fa0a650@samsung.com>
Hi Andrzej,
On Mon, Apr 15, 2019 at 10:58:09AM +0200, Andrzej Hajda wrote:
> On 15.04.2019 10:12, Ronald Tschalär wrote:
> > commit d6abe6df706c (drm/bridge: sil_sii8620: do not have a dependency
> > of RC_CORE) changed the driver to select both RC_CORE and INPUT.
> > However, this causes problems with other drivers, in particular an input
> > driver that depends on MFD_INTEL_LPSS_PCI (to be added in a separate
> > commit):
> >
> > drivers/clk/Kconfig:9:error: recursive dependency detected!
> > drivers/clk/Kconfig:9: symbol COMMON_CLK is selected by MFD_INTEL_LPSS
> > drivers/mfd/Kconfig:566: symbol MFD_INTEL_LPSS is selected by MFD_INTEL_LPSS_PCI
> > drivers/mfd/Kconfig:580: symbol MFD_INTEL_LPSS_PCI is implied by KEYBOARD_APPLESPI
> > drivers/input/keyboard/Kconfig:73: symbol KEYBOARD_APPLESPI depends on INPUT
> > drivers/input/Kconfig:8: symbol INPUT is selected by DRM_SIL_SII8620
> > drivers/gpu/drm/bridge/Kconfig:83: symbol DRM_SIL_SII8620 depends on DRM_BRIDGE
> > drivers/gpu/drm/bridge/Kconfig:1: symbol DRM_BRIDGE is selected by DRM_PL111
> > drivers/gpu/drm/pl111/Kconfig:1: symbol DRM_PL111 depends on COMMON_CLK
> >
> > According to the docs and general consensus, select should only be used
> > for non user-visible symbols, but both RC_CORE and INPUT are
> > user-visible. Furthermore almost all other references to INPUT
> > throughout the kernel config are depends, not selects. For this reason
> > the first part of this change reverts commit d6abe6df706c.
> >
> > In order to address the original reason for commit d6abe6df706c, namely
> > that not all boards use the remote controller functionality and hence
> > should not need have to deal with RC_CORE, the second part of this
> > change now makes the remote control support in the driver optional and
> > contingent on RC_CORE being defined. And with this the hard dependency
> > on INPUT also goes away as that is only needed if RC_CORE is defined
> > (which in turn already depends on INPUT).
> >
> > CC: Inki Dae <inki.dae@samsung.com>
> > CC: Andrzej Hajda <a.hajda@samsung.com>
> > CC: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> > CC: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> > Signed-off-by: Ronald Tschalär <ronald@innovation.ch>
>
>
> Reviewed-by: Andrzej Hajda <a.hajda@samsung.com>
Thanks for your reviews!
> If there are no objections I will take it to drm-misc tomorrow.
This brings us back to the discussion started in response to the first
version of my patch (see
https://lore.kernel.org/lkml/20190124082423.23139-1-ronald@innovation.ch/T/#m24f45fecd987a787a9554c8088f463fd10de2b00).
To recap: the problem is that the applespi patch depends on this patch
here, as make-config will break as described above otherwise. So if
this patch is submitted through drm-misc, then it's unclear to me how
to ensure that the two patches make it upstream in proper order,
unless the applespi patch is also upstreamed through drm-misc, or the
Kconfig for applespi is (temporarily) modified to not trigger the
config error and another patch is later submitted to fix the Kconfig
again (which seems somewhat ugly to me). Assuming that consensus is to
merge both patches through one tree, then it would seem that because
this patch here is relatively small that maybe it could be merged
through the input tree along with the applespi patch?
Cheers,
Ronald
^ permalink raw reply
* Re: [PATCH v5 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Life is hard, and then you die @ 2019-04-15 23:09 UTC (permalink / raw)
To: Andy Shevchenko
Cc: Dmitry Torokhov, Henrik Rydberg, Andrzej Hajda, Inki Dae,
Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi,
Laurent Pinchart, linux-input, dri-devel, linux-kernel
In-Reply-To: <20190415090346.GL9224@smile.fi.intel.com>
Hi Andy,
On Mon, Apr 15, 2019 at 12:03:46PM +0300, Andy Shevchenko wrote:
> On Mon, Apr 15, 2019 at 01:13:00AM -0700, Ronald Tschalär wrote:
> > The keyboard and trackpad on recent MacBook's (since 8,1) and
> > MacBookPro's (13,* and 14,*) are attached to an SPI controller instead
> > of USB, as previously. The higher level protocol is not publicly
> > documented and hence has been reverse engineered. As a consequence there
> > are still a number of unknown fields and commands. However, the known
> > parts have been working well and received extensive testing and use.
> >
> > In order for this driver to work, the proper SPI drivers need to be
> > loaded too; for MB8,1 these are spi_pxa2xx_platform and spi_pxa2xx_pci;
> > for all others they are spi_pxa2xx_platform and intel_lpss_pci. For this
> > reason enabling this driver in the config implies enabling the above
> > drivers.
>
> Thank you for an update.
> I suddenly realized couple of places where something maybe optimized.
>
> Nevertheless, FWIW,
> Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Many thanks for all your reviews!
[snip]
> > +static void
> > +applespi_remap_fn_key(struct keyboard_protocol *keyboard_protocol)
> > +{
> > + unsigned char tmp;
>
> > + u8 bit = BIT(fnremap - 1);
>
> The above is UB and I'm sorry I didn't find this earlier.
>
> So, something like this would work
>
> u8 bit = BIT((fnremap - 1) & 0x07);
fnremap is already constrained by the following:
> > +
> > + if (!fnremap || fnremap > ARRAY_SIZE(applespi_controlcodes) ||
> > + !applespi_controlcodes[fnremap - 1])
> > + return;
and the array-size of applespi_controlcodes is constrained to the
number of bits in u8 according to this assertion
> > + compiletime_assert(ARRAY_SIZE(applespi_controlcodes) ==
> > + sizeof_field(struct keyboard_protocol, modifiers) * 8,
> > + "applespi_controlcodes has wrong number of entries");
So I don't see that the masking buys anything new.
[snip]
> > + /* check for rollover overflow, which is signalled by all keys == 1 */
> > + for (i = 0; i < MAX_ROLLOVER; i++) {
> > + if (keyboard_protocol->keys_pressed[i] != 1)
> > + break;
> > + }
> > +
> > + if (i == MAX_ROLLOVER) /* all keys were 1 */
> > + return;
>
> Since keys_pressed is an array of byte values, it may be replaced with
> memchr_inv().
>
> > +
> > + /* remap fn key if desired */
> > + applespi_remap_fn_key(keyboard_protocol);
> > +
> > + /* check released keys */
> > + for (i = 0; i < MAX_ROLLOVER; i++) {
> > + for (j = 0; j < MAX_ROLLOVER; j++) {
> > + if (applespi->last_keys_pressed[i] ==
> > + keyboard_protocol->keys_pressed[j])
> > + break;
> > + }
> > +
> > + if (j < MAX_ROLLOVER) /* key is still pressed */
> > + continue;
>
> And memchr() here.
Ah, yes, excellent suggestion. Thanks.
Cheers,
Ronald
^ permalink raw reply
* [RFC v3] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: H. Nikolaus Schaller @ 2019-04-15 21:05 UTC (permalink / raw)
To: Jonathan Cameron, Dmitry Torokhov
Cc: Eric Piel, linux-input, letux-kernel, kernel, Hartmut Knaack,
Lars-Peter Clausen, Peter Meerwald-Stadler, linux-kernel,
linux-iio, H. Nikolaus Schaller
Some user spaces (e.g. some Android devices) use /dev/input/event* for handling
the 3D position of the device with respect to the center of gravity (earth).
This can be used for gaming input, auto-rotation of screens etc.
This interface should be the standard for such use cases because it is an abstraction
of how orientation data is acquired from sensor chips. Sensor chips may be connected
through different interfaces and in different positions. They may also have different
parameters. And, if a chip is replaced by a different one, the values reported by
the device position interface should remain the same, provided the device tree reflects
the changed chip.
This did initially lead to input accelerometer drivers like drivers/input/misc/bma150.c
or drivers/misc/lis3lv02d/
But nowadays, new accelerometer chips mostly get iio drivers and rarely input drivers.
Therefore we need something like a protocol stack which bridges raw data and input devices.
It can be seen as a similar layering like TCP/IP vs. bare Ethernet. Or keyboard
input events vs. raw gpio or raw USB access.
This patch bridges the gap between raw iio data and the input device abstraction
so that accelerometer measurements can additionally be presented as X/Y/Z accelerometer
channels (INPUT_PROP_ACCELEROMETER) through /dev/input/event*.
There are no special requirements or changes needed for an iio driver.
There is no need to define a mapping (e.g. in device tree).
This driver simply collects the first 3 accelerometer channels as X, Y and Z.
If only 1 or 2 channels are available, they are used for X and Y only. Additional
channels are ignored.
Scaling is done automatically so that 1g is represented by value 256 and
range is assumed to be -511 .. +511 which gives a reasonable precision as an
input device.
If a mount-matrix is provided by the iio driver, it is also taken into account
so that the input event automatically gets the correct orientation with respect
to the device.
If this extension is not configured into the kernel it takes no resources (except
source code).
If it is configured, but there is no accelerometer, there is only a tiny penalty
for scanning for accelerometer channels once during probe of each iio device.
If it runs, the driver polls the device(s) once every 100 ms. A mode where the
iio device defines the update rate is not implemented and for further study.
If there is no user-space client, polling is not running.
The driver is capable to handle multiple iio accelerometers and they are
presented by unique /dev/input/event* files. The iio chip name is used to define
the input device name so that it can be identified (e.g. by udev rules or evtest).
Here is some example what you can expect from the driver (device:
arch/arm/boot/dts/omap3-gta04a5.dts):
root@letux:~# dmesg|fgrep iio
[ 6.324584] input: iio-bridge: bmc150_accel as /devices/platform/68000000.ocp/48072000.i2c/i2c-1/1-0010/iio:device1/input/input5
[ 6.516632] input: iio-bridge: bno055 as /devices/platform/68000000.ocp/48072000.i2c/i2c-1/1-0029/iio:device3/input/input7
root@letux:~# evtest /dev/input/event5 | head -19
Input driver version is 1.0.1
Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
Input device name: "iio-bridge: bmc150_accel"
Supported events:
Event type 0 (EV_SYN)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value 8
Min -511
Max 511
Event code 1 (ABS_Y)
Value -44
Min -511
Max 511
Event code 2 (ABS_Z)
Value -265
Min -511
Max 511
Properties:
root@letux:~# evtest /dev/input/event7 | head -19
Input driver version is 1.0.1
Input device ID: bus 0x0 vendor 0x0 product 0x0 version 0x0
Input device name: "iio-bridge: bno055"
Supported events:
Event type 0 (EV_SYN)
Event type 3 (EV_ABS)
Event code 0 (ABS_X)
Value -6
Min -511
Max 511
Event code 1 (ABS_Y)
Value 17
Min -511
Max 511
Event code 2 (ABS_Z)
Value -250
Min -511
Max 511
Properties:
root@letux:~#
Although the sensor chips are mounted with different axis orientation,
the application of the mount matrix provides equivalent (despite noise
and precision) information on device orientation.
Signed-off-by: H. Nikolaus Schaller <hns@goldelico.com>
---
V1: initial RFC version
V2:
- rework based on comments by Jonathan Cameron
- mainly: use input_polldev instead of using own polling timer
- no need for checking number of open()/close()
- no need for locks (already handled by input framework)
V3:
- use new iio_dev->input_mapping instead of mis-using iio_dev->private
- removed some spurious printk from debugging
- collect channels first and then register them all in one step
- fix issue with unsigned int type propagation in atofix()
- simplify code for handling negative numbers
- fix sequence in unregister
---
drivers/iio/Kconfig | 8 +
drivers/iio/Makefile | 1 +
drivers/iio/industrialio-core.c | 12 ++
drivers/iio/industrialio-inputbridge.c | 270 +++++++++++++++++++++++++
drivers/iio/industrialio-inputbridge.h | 28 +++
include/linux/iio/iio.h | 4 +
6 files changed, 323 insertions(+)
create mode 100644 drivers/iio/industrialio-inputbridge.c
create mode 100644 drivers/iio/industrialio-inputbridge.h
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index d08aeb41cd07..2f0295da6ebc 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -68,6 +68,14 @@ config IIO_TRIGGERED_EVENT
help
Provides helper functions for setting up triggered events.
+config IIO_INPUT_BRIDGE
+ depends on INPUT
+ bool "Enable accelerometer bridge to input driver"
+ help
+ Provides a /dev/input/event* device for accelerometers
+ to use as a 3D input device, e.g. for gaming or auto-rotation
+ of screen contents.
+
source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/afe/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index cb5993251381..d695e5a27da5 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -7,6 +7,7 @@ obj-$(CONFIG_IIO) += industrialio.o
industrialio-y := industrialio-core.o industrialio-event.o inkern.o
industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o
industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o
+industrialio-$(CONFIG_IIO_INPUT_BRIDGE) += industrialio-inputbridge.o
obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o
obj-$(CONFIG_IIO_SW_DEVICE) += industrialio-sw-device.o
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 4700fd5d8c90..81f412b41a78 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -29,6 +29,7 @@
#include <linux/iio/iio.h>
#include "iio_core.h"
#include "iio_core_trigger.h"
+#include "industrialio-inputbridge.h"
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>
@@ -1723,6 +1724,15 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
if (ret < 0)
goto error_unreg_eventset;
+ ret = iio_device_register_inputbridge(indio_dev);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "Failed to register as input driver\n");
+ device_del(&indio_dev->dev);
+
+ return ret;
+ }
+
return 0;
error_unreg_eventset:
@@ -1745,6 +1755,8 @@ void iio_device_unregister(struct iio_dev *indio_dev)
{
mutex_lock(&indio_dev->info_exist_lock);
+ iio_device_unregister_inputbridge(indio_dev);
+
cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
iio_device_unregister_debugfs(indio_dev);
diff --git a/drivers/iio/industrialio-inputbridge.c b/drivers/iio/industrialio-inputbridge.c
new file mode 100644
index 000000000000..592d5ee91a30
--- /dev/null
+++ b/drivers/iio/industrialio-inputbridge.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * The Industrial I/O core, bridge to input devices
+ *
+ * Copyright (c) 2016-2019 Golden Delicious Computers GmbH&Co. KG
+ */
+
+#include <linux/iio/consumer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "industrialio-inputbridge.h"
+
+/* currently, only polling is implemented */
+#define POLLING_MSEC 100
+
+struct iio_input_map {
+ struct input_polled_dev *poll_dev; /* the input device */
+ struct iio_channel channels[3]; /* x, y, z channels */
+ struct matrix {
+ int mxx, myx, mzx; /* fixed point mount-matrix */
+ int mxy, myy, mzy;
+ int mxz, myz, mzz;
+ } matrix;
+};
+
+static inline struct iio_input_map *to_iio_input_map(
+ struct iio_channel *channel)
+{
+ return (struct iio_input_map *) channel->data;
+}
+
+/* minimum and maximum range we want to report */
+#define ABSMAX_ACC_VAL (512 - 1)
+#define ABSMIN_ACC_VAL -(ABSMAX_ACC_VAL)
+
+/* scale processed iio values so that 1g maps to ABSMAX_ACC_VAL / 2 */
+#define SCALE ((100 * ABSMAX_ACC_VAL) / (2 * 981))
+
+/*
+ * convert float string to scaled fixed point format, e.g.
+ * 1 -> 1000 (value passed as unit)
+ * 1.23 -> 1230
+ * 0.1234 -> 123
+ * -.01234 -> -12
+ */
+
+static int32_t atofix(const char *str, uint32_t unit)
+{
+ int32_t mantissa = 0;
+ bool decimal = false;
+ int divisor = 1;
+
+ if (*str == '-')
+ divisor = -1, str++;
+ while (*str && abs(divisor) < unit) {
+ if (*str >= '0' && *str <= '9') {
+ mantissa = 10 * mantissa + (*str - '0');
+ if (decimal)
+ divisor *= 10;
+ } else if (*str == '.')
+ decimal = true;
+ else
+ return 0; /* error */
+ str++;
+ }
+
+ return (mantissa * (int32_t) unit) / divisor;
+}
+
+static void iio_apply_matrix(struct matrix *m, int *in, int *out, int unit)
+{
+ /* apply mount matrix */
+ out[0] = (m->mxx * in[0] + m->myx * in[1] + m->mzx * in[2]) / unit;
+ out[1] = (m->mxy * in[0] + m->myy * in[1] + m->mzy * in[2]) / unit;
+ out[2] = (m->mxz * in[0] + m->myz * in[1] + m->mzz * in[2]) / unit;
+}
+
+#define FIXED_POINT_UNIT 1000 /* seems reasonable for accelerometer input */
+
+static void iio_accel_poll(struct input_polled_dev *dev)
+{
+ struct iio_input_map *map = dev->private;
+ struct input_dev *input = dev->input;
+
+ int values[3]; /* values while processing */
+ int aligned_values[3]; /* mount matrix applied */
+
+ int cindex = 0;
+
+ while (cindex < ARRAY_SIZE(values)) {
+ struct iio_channel *channel =
+ &map->channels[cindex];
+ int val;
+ int ret;
+
+ if (!channel) {
+ values[cindex] = 0;
+ continue;
+ }
+
+ ret = iio_read_channel_raw(channel, &val);
+
+ if (ret < 0) {
+ pr_err("%s(): channel read error %d\n",
+ __func__, cindex);
+ return;
+ }
+
+ ret = iio_convert_raw_to_processed(channel, val,
+ &values[cindex], SCALE);
+
+ if (ret < 0) {
+ pr_err("%s(): channel processing error\n",
+ __func__);
+ return;
+ }
+
+ cindex++;
+ }
+
+ iio_apply_matrix(&map->matrix, values, aligned_values, FIXED_POINT_UNIT);
+
+ input_report_abs(input, ABS_X, aligned_values[0]);
+ input_report_abs(input, ABS_Y, aligned_values[1]);
+ input_report_abs(input, ABS_Z, aligned_values[2]);
+ input_sync(input);
+}
+
+static int iio_input_register_accel_channels(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan[], int num_channels)
+{ /* we found some accelerometer channel */
+ int ret;
+ int cindex;
+ struct input_polled_dev *poll_dev;
+ struct iio_input_map *map = indio_dev->input_mapping;
+ const struct iio_chan_spec_ext_info *ext_info;
+
+ if (unlikely(map))
+ return -EINVAL; /* already registered */
+
+ if (num_channels < 1)
+ return 0; /* silently ignore */
+
+ map = devm_kzalloc(&indio_dev->dev, sizeof(struct iio_input_map), GFP_KERNEL);
+ if (!map)
+ return -ENOMEM;
+
+ indio_dev->input_mapping = map;
+
+ poll_dev = devm_input_allocate_polled_device(&indio_dev->dev);
+ if (!poll_dev)
+ return -ENOMEM;
+
+ poll_dev->private = map;
+ poll_dev->poll = iio_accel_poll;
+ poll_dev->poll_interval = POLLING_MSEC;
+
+ poll_dev->input->name = kasprintf(GFP_KERNEL, "iio-bridge: %s",
+ indio_dev->name);
+ poll_dev->input->phys = kasprintf(GFP_KERNEL, "iio:device%d",
+ indio_dev->id);
+
+// do we need something like this?
+// poll_dev->input->id.bustype = BUS_IIO;
+// poll_dev->input->id.vendor = 0x0001;
+// poll_dev->input->id.product = 0x0001;
+// poll_dev->input->id.version = 0x0001;
+
+ set_bit(INPUT_PROP_ACCELEROMETER, poll_dev->input->propbit);
+ poll_dev->input->evbit[0] = BIT_MASK(EV_ABS);
+ input_alloc_absinfo(poll_dev->input);
+ input_set_abs_params(poll_dev->input, ABS_X, ABSMIN_ACC_VAL,
+ ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(poll_dev->input, ABS_Y, ABSMIN_ACC_VAL,
+ ABSMAX_ACC_VAL, 0, 0);
+ input_set_abs_params(poll_dev->input, ABS_Z, ABSMIN_ACC_VAL,
+ ABSMAX_ACC_VAL, 0, 0);
+
+ map->poll_dev = poll_dev;
+
+ ret = input_register_polled_device(poll_dev);
+
+ if (ret < 0) {
+ kfree(poll_dev->input->name);
+ kfree(poll_dev->input->phys);
+ return ret;
+ }
+
+ /* assume all channels of a device share the same matrix */
+
+ ext_info = chan[0]->ext_info;
+ while (ext_info && ext_info->name) {
+ if (strcmp(ext_info->name, "mount_matrix") == 0)
+ break; /* found */
+ ext_info++;
+ }
+
+ if (ext_info && ext_info->name) {
+ uintptr_t priv = ext_info->private;
+ const struct iio_mount_matrix *mtx;
+
+ mtx = ((iio_get_mount_matrix_t *) priv)(indio_dev,
+ chan[0]);
+
+ map->matrix.mxx = atofix(mtx->rotation[0], FIXED_POINT_UNIT);
+ map->matrix.myx = atofix(mtx->rotation[1], FIXED_POINT_UNIT);
+ map->matrix.mzx = atofix(mtx->rotation[2], FIXED_POINT_UNIT);
+ map->matrix.mxy = atofix(mtx->rotation[3], FIXED_POINT_UNIT);
+ map->matrix.myy = atofix(mtx->rotation[4], FIXED_POINT_UNIT);
+ map->matrix.mzy = atofix(mtx->rotation[5], FIXED_POINT_UNIT);
+ map->matrix.mxz = atofix(mtx->rotation[6], FIXED_POINT_UNIT);
+ map->matrix.myz = atofix(mtx->rotation[7], FIXED_POINT_UNIT);
+ map->matrix.mzz = atofix(mtx->rotation[8], FIXED_POINT_UNIT);
+ } else {
+ map->matrix.mxx = FIXED_POINT_UNIT;
+ map->matrix.myx = 0;
+ map->matrix.mzx = 0;
+ map->matrix.mxy = 0;
+ map->matrix.myy = FIXED_POINT_UNIT;
+ map->matrix.mzy = 0;
+ map->matrix.mxz = 0;
+ map->matrix.myz = 0;
+ map->matrix.mzz = FIXED_POINT_UNIT;
+ }
+
+ for (cindex = 0; cindex < ARRAY_SIZE(map->channels); cindex++) {
+ if (cindex < num_channels)
+ map->channels[cindex].channel = chan[cindex];
+ map->channels[cindex].indio_dev = indio_dev;
+ map->channels[cindex].data = map;
+ }
+
+ return 0;
+}
+
+int iio_device_register_inputbridge(struct iio_dev *indio_dev)
+{
+ int cindex;
+ int num_channels = 0;
+ const struct iio_chan_spec *channels[3];
+
+ for (cindex = 0; cindex < indio_dev->num_channels; cindex++) {
+ const struct iio_chan_spec *chan =
+ &indio_dev->channels[cindex];
+
+ if (chan->type == IIO_ACCEL && num_channels < ARRAY_SIZE(channels))
+ channels[num_channels++] = chan;
+ }
+
+ return iio_input_register_accel_channels(indio_dev, channels, num_channels);
+}
+
+void iio_device_unregister_inputbridge(struct iio_dev *indio_dev)
+{
+ struct iio_input_map *map = iio_device_get_drvdata(indio_dev);
+ struct input_dev *input = map->poll_dev->input;
+
+ input_unregister_polled_device(map->poll_dev);
+ kfree(input->name);
+ kfree(input->phys);
+}
+
+MODULE_AUTHOR("H. Nikolaus Schaller <hns@goldelico.com>");
+MODULE_DESCRIPTION("Bridge to present Industrial I/O accelerometers as properly oriented Input devices");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/industrialio-inputbridge.h b/drivers/iio/industrialio-inputbridge.h
new file mode 100644
index 000000000000..1363b10ab3f7
--- /dev/null
+++ b/drivers/iio/industrialio-inputbridge.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * The Industrial I/O core, bridge to input devices
+ *
+ * Copyright (c) 2016-2019 Golden Delicious Computers GmbH&Co. KG
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+#if defined(CONFIG_IIO_INPUT_BRIDGE)
+
+extern int iio_device_register_inputbridge(struct iio_dev *indio_dev);
+extern void iio_device_unregister_inputbridge(struct iio_dev *indio_dev);
+
+#else
+
+static inline int iio_device_register_inputbridge(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+static inline void iio_device_unregister_inputbridge(struct iio_dev *indio_dev)
+{
+}
+
+#endif
diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h
index a74cb177dc6f..a4d2f11384e9 100644
--- a/include/linux/iio/iio.h
+++ b/include/linux/iio/iio.h
@@ -524,6 +524,7 @@ struct iio_buffer_setup_ops {
* @flags: [INTERN] file ops related flags including busy flag.
* @debugfs_dentry: [INTERN] device specific debugfs dentry.
* @cached_reg_addr: [INTERN] cached register address for debugfs reads.
+ * @input_mapping: [INTERN] mapping for input device
*/
struct iio_dev {
int id;
@@ -570,6 +571,9 @@ struct iio_dev {
struct dentry *debugfs_dentry;
unsigned cached_reg_addr;
#endif
+#if defined(CONFIG_IIO_INPUT_BRIDGE)
+ void *input_mapping;
+#endif
};
const struct iio_chan_spec
--
2.19.1
^ permalink raw reply related
* Re: [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Guenter Roeck @ 2019-04-15 20:01 UTC (permalink / raw)
To: Alexander Sverdlin
Cc: Arnd Bergmann, Hartley Sweeten, Linus Walleij, Dmitry Torokhov,
Stefan Agner, Enric Balletbo i Serra, Guenter Roeck,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <3665a5ad-ff6d-97a1-ca31-971973a7167f@gmail.com>
On Mon, Apr 15, 2019 at 12:56 PM Alexander Sverdlin
<alexander.sverdlin@gmail.com> wrote:
>
> Hi!
>
> On 15/04/2019 21:47, Arnd Bergmann wrote:
> >>> We can communicate the clock rate using platform data rather than setting a
> >>> flag to use a particular value in the driver, which is cleaner and avoids the dependency.
> >>>
> >>> No platform in the kernel currently defines the ep93xx keypad device structure, so this
> >>> is a rather pointless excercise. Any out of tree users are probably dead now, but if not,
> >>> they have to change their platform code to match the new platform_data structure.
> >> <snip>
> >>
> >>> diff --git a/include/linux/platform_data/keypad-ep93xx.h b/include/linux/platform_data/keypad-ep93xx.h
> >>> index 0e36818e3680..3054fced8509 100644
> >>> --- a/include/linux/platform_data/keypad-ep93xx.h
> >>> +++ b/include/linux/platform_data/keypad-ep93xx.h
> >>> @@ -9,8 +9,7 @@ struct matrix_keymap_data;
> >>> #define EP93XX_KEYPAD_DIAG_MODE (1<<1) /* diagnostic mode */
> >>> #define EP93XX_KEYPAD_BACK_DRIVE (1<<2) /* back driving mode */
> >>> #define EP93XX_KEYPAD_TEST_MODE (1<<3) /* scan only column 0 */
> >>> -#define EP93XX_KEYPAD_KDIV (1<<4) /* 1/4 clock or 1/16 clock */
> >>> -#define EP93XX_KEYPAD_AUTOREPEAT (1<<5) /* enable key autorepeat */
> >>> +#define EP93XX_KEYPAD_AUTOREPEAT (1<<4) /* enable key autorepeat */
> >> You have re-defined the keypad register bits here.
> >>
> >> The KDIV bit changes the clock rate. The AUTOREPEAT bit enables key autorepeat.
> > As far as I can tell, they are not register bits, just software flags
> > for communicating between a board file and the driver, so I
> > assumed I could freely reorganize them.
> >
> > Did I miss something?
>
> They are indeed only software flags (just checked datasheet), so you are only changing
> platform data format. But as I do not know any keypad user in person, I'd rely on
> Hartley's opinion in this case (if it's a good idea to change platform data or not).
>
If there are out-of-tree users, it would be their responsibility to
upstream their code. If they don't, any changes in platform data is
their problem, not ours. Either case, platform data does, if anything,
reflect an in-kernel API, and thus is fair game for cleanup.
Guenter
^ permalink raw reply
* RE: [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Hartley Sweeten @ 2019-04-15 19:58 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Alexander Sverdlin, Linus Walleij, Dmitry Torokhov, Stefan Agner,
Enric Balletbo i Serra, Guenter Roeck,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CAK8P3a1DSN3jcfGngrJFp93Mt98qsibvKvtJGtjHYjOmcM1kNQ@mail.gmail.com>
On Monday, April 15, 2019 12:47 PM, Arnd Bergmann wrote:
> On Mon, Apr 15, 2019 at 9:39 PM Hartley Sweeten <HartleyS@visionengravers.com> wrote:
>>> -#define EP93XX_KEYPAD_KDIV (1<<4) /* 1/4 clock or 1/16 clock */
>>> -#define EP93XX_KEYPAD_AUTOREPEAT (1<<5) /* enable key autorepeat */
>>> +#define EP93XX_KEYPAD_AUTOREPEAT (1<<4) /* enable key autorepeat */
>>
>> You have re-defined the keypad register bits here.
>>
>> The KDIV bit changes the clock rate. The AUTOREPEAT bit enables key autorepeat.
>
> As far as I can tell, they are not register bits, just software flags for communicating between a
> board file and the driver, so I assumed I could freely reorganize them.
>
> Did I miss something?
Ugh.. It's been quite a while since I looked at that code...
Your correct, these are just flags to the driver.
The KeyScanInit register does have some bits that these flags effect but the driver deals
with them.
I have been buried updating an old PowerPC hardware/software design and haven't looked
at the EP93xx lately. My EP9307 board does have the keypad interface. Hopefully I will get
some time to check the latest mainline on it sometime soon.
Overall these patches look good. So, for the series...
Acked-by: H Hartley Sweeten <hsweeten@visionengravers.com>
^ permalink raw reply
* Re: [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Alexander Sverdlin @ 2019-04-15 19:54 UTC (permalink / raw)
To: Arnd Bergmann, Hartley Sweeten
Cc: Linus Walleij, Dmitry Torokhov, Stefan Agner,
Enric Balletbo i Serra, Guenter Roeck,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CAK8P3a1DSN3jcfGngrJFp93Mt98qsibvKvtJGtjHYjOmcM1kNQ@mail.gmail.com>
Hi!
On 15/04/2019 21:47, Arnd Bergmann wrote:
>>> We can communicate the clock rate using platform data rather than setting a
>>> flag to use a particular value in the driver, which is cleaner and avoids the dependency.
>>>
>>> No platform in the kernel currently defines the ep93xx keypad device structure, so this
>>> is a rather pointless excercise. Any out of tree users are probably dead now, but if not,
>>> they have to change their platform code to match the new platform_data structure.
>> <snip>
>>
>>> diff --git a/include/linux/platform_data/keypad-ep93xx.h b/include/linux/platform_data/keypad-ep93xx.h
>>> index 0e36818e3680..3054fced8509 100644
>>> --- a/include/linux/platform_data/keypad-ep93xx.h
>>> +++ b/include/linux/platform_data/keypad-ep93xx.h
>>> @@ -9,8 +9,7 @@ struct matrix_keymap_data;
>>> #define EP93XX_KEYPAD_DIAG_MODE (1<<1) /* diagnostic mode */
>>> #define EP93XX_KEYPAD_BACK_DRIVE (1<<2) /* back driving mode */
>>> #define EP93XX_KEYPAD_TEST_MODE (1<<3) /* scan only column 0 */
>>> -#define EP93XX_KEYPAD_KDIV (1<<4) /* 1/4 clock or 1/16 clock */
>>> -#define EP93XX_KEYPAD_AUTOREPEAT (1<<5) /* enable key autorepeat */
>>> +#define EP93XX_KEYPAD_AUTOREPEAT (1<<4) /* enable key autorepeat */
>> You have re-defined the keypad register bits here.
>>
>> The KDIV bit changes the clock rate. The AUTOREPEAT bit enables key autorepeat.
> As far as I can tell, they are not register bits, just software flags
> for communicating between a board file and the driver, so I
> assumed I could freely reorganize them.
>
> Did I miss something?
They are indeed only software flags (just checked datasheet), so you are only changing
platform data format. But as I do not know any keypad user in person, I'd rely on
Hartley's opinion in this case (if it's a good idea to change platform data or not).
--
Alex.
^ permalink raw reply
* Re: [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Arnd Bergmann @ 2019-04-15 19:47 UTC (permalink / raw)
To: Hartley Sweeten
Cc: Alexander Sverdlin, Linus Walleij, Dmitry Torokhov, Stefan Agner,
Enric Balletbo i Serra, Guenter Roeck,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org
In-Reply-To: <CO2PR01MB21683E465191EA8246503774D02B0@CO2PR01MB2168.prod.exchangelabs.com>
On Mon, Apr 15, 2019 at 9:39 PM Hartley Sweeten
<HartleyS@visionengravers.com> wrote:
>
> On Monday, April 15, 2019 12:25 PM, Arnd Bergmann wrote:
> > We can communicate the clock rate using platform data rather than setting a
> > flag to use a particular value in the driver, which is cleaner and avoids the dependency.
> >
> > No platform in the kernel currently defines the ep93xx keypad device structure, so this
> > is a rather pointless excercise. Any out of tree users are probably dead now, but if not,
> > they have to change their platform code to match the new platform_data structure.
>
> <snip>
>
> > diff --git a/include/linux/platform_data/keypad-ep93xx.h b/include/linux/platform_data/keypad-ep93xx.h
> > index 0e36818e3680..3054fced8509 100644
> > --- a/include/linux/platform_data/keypad-ep93xx.h
> > +++ b/include/linux/platform_data/keypad-ep93xx.h
> > @@ -9,8 +9,7 @@ struct matrix_keymap_data;
> > #define EP93XX_KEYPAD_DIAG_MODE (1<<1) /* diagnostic mode */
> > #define EP93XX_KEYPAD_BACK_DRIVE (1<<2) /* back driving mode */
> > #define EP93XX_KEYPAD_TEST_MODE (1<<3) /* scan only column 0 */
> > -#define EP93XX_KEYPAD_KDIV (1<<4) /* 1/4 clock or 1/16 clock */
> > -#define EP93XX_KEYPAD_AUTOREPEAT (1<<5) /* enable key autorepeat */
> > +#define EP93XX_KEYPAD_AUTOREPEAT (1<<4) /* enable key autorepeat */
>
> You have re-defined the keypad register bits here.
>
> The KDIV bit changes the clock rate. The AUTOREPEAT bit enables key autorepeat.
As far as I can tell, they are not register bits, just software flags
for communicating between a board file and the driver, so I
assumed I could freely reorganize them.
Did I miss something?
Arnd
^ 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