Linux Input/HID development
 help / color / mirror / Atom feed
* [PATCH v3 3/3] ARM: dts: msm8974-FP2: Add vibration motor
From: Luca Weiss @ 2019-04-12 15:06 UTC (permalink / raw)
  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, Luca Weiss
In-Reply-To: <20190412150625.28384-1-luca@z3ntu.xyz>

Signed-off-by: Luca Weiss <luca@z3ntu.xyz>
---
 arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts b/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
index 643c57f84818..bf402ae39226 100644
--- a/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
+++ b/arch/arm/boot/dts/qcom-msm8974-fairphone-fp2.dts
@@ -50,6 +50,12 @@
 		};
 	};
 
+	vibrator {
+		compatible = "gpio-vibrator";
+		enable-gpios = <&msmgpio 86 GPIO_ACTIVE_HIGH>;
+		vcc-supply = <&pm8941_l18>;
+	};
+
 	smd {
 		rpm {
 			rpm_requests {
-- 
2.21.0

^ permalink raw reply related

* Re: [PATCH v3 2/3] Input: add a driver for GPIO controllable vibrators
From: Stephen Boyd @ 2019-04-12 17:43 UTC (permalink / raw)
  To: Luca Weiss
  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: <20190412150625.28384-2-luca@z3ntu.xyz>

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.

> +               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.

> +};
> +MODULE_DEVICE_TABLE(of, gpio_vibra_dt_match_table);
> +#endif
> +

^ permalink raw reply

* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Kai-Heng Feng @ 2019-04-13  8:42 UTC (permalink / raw)
  To: hotwater438
  Cc: Hans de Goede, Dmitry Torokhov, Vladislav Dalechyn,
	Benjamin Tissoires, Jiri Kosina, Swboyd, Bigeasy,
	open list:HID CORE LAYER, lkml
In-Reply-To: <LcKqhgD--3-1@tutanota.com>

at 16:40, <hotwater438@tutanota.com> <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:
> Hi,
>
> at 05:18, <hotwater438@tutanota.com> <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:
> Hi,
>
> On 31-03-19 11:50, 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

* [PATCH] HID: fix compiling error in u2fzero_probe()
From: Mao Wenan @ 2019-04-13 13:44 UTC (permalink / raw)
  To: jikos, benjamin.tissoires, maowenan
  Cc: linux-input, linux-kernel, kernel-janitors, Hulk Robot

There is one compiling error in u2fzero_probe()->u2fzero_init_hwrng(),
this is because HW_RANDOM is not set.

drivers/hid/hid-u2fzero.o: In function `u2fzero_probe':
hid-u2fzero.c:(.text+0xc70): undefined reference to `devm_hwrng_register'

Fixes: 42337b9d4d958("HID: add driver for U2F Zero built-in LED and RNG")
Reported-by: Hulk Robot <hulkci@huawei.com>
Signed-off-by: Mao Wenan <maowenan@huawei.com>
---
 drivers/hid/Kconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
index 4dc1035..76d8206 100644
--- a/drivers/hid/Kconfig
+++ b/drivers/hid/Kconfig
@@ -1017,6 +1017,7 @@ config HID_U2FZERO
 	tristate "U2F Zero LED and RNG support"
 	depends on USB_HID
 	depends on LEDS_CLASS
+	depends on HW_RANDOM
 	help
 	  Support for the LED of the U2F Zero device.
 
-- 
2.7.4

^ permalink raw reply related

* Re: [RFC v2] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: Jonathan Cameron @ 2019-04-14 11:40 UTC (permalink / raw)
  To: H. Nikolaus Schaller
  Cc: Dmitry Torokhov, Eric Piel, linux-input, letux-kernel, kernel,
	Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	linux-kernel, linux-iio
In-Reply-To: <CD44AFA0-6676-4842-9C80-61BB363DD556@goldelico.com>

On Mon, 8 Apr 2019 15:15:56 +0200
H. Nikolaus Schaller <hns@goldelico.com> wrote:

> Hi Jonathan,
> 
> > Am 07.04.2019 um 14:30 schrieb Jonathan Cameron <jic23@kernel.org>:
> > 
> > On Sun, 31 Mar 2019 12:09:46 +0200
> > "H. Nikolaus Schaller" <hns@goldelico.com> wrote:
> > 
> > Hi Nikolaus,
> > 
> > I'm probably going to repeat a few things I sent for v1 as the audience has
> > expanded somewhat!
> > 
> > Good to see this moving forwards though as there has been at least some demand
> > for it going way back to the early days of IIO.
> >   
> >> 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 worries me, as it inherently means we end up with this interface being
> > registered in cases where it makes no sense.  A lot of generic distros get
> > used across widely differing use cases.  
> 
> I still do not fully understand what is worrying you here.

> 
> Do you worry about functionality, flexibility or resources or something else?

Two main things:
1) Lack of generality of the approach. 
   This is a single use trick for input devices. Why does it make sense for
   input devices?  There are lots of other in kernel users and potential
   ones in the future.  The ability to register additional IIO consumers like
   this is useful, lets make it useful to everyone.

2) To much generality of the specific usecase.  I don't want to put an Input
   interface on accelerometers where it makes no sense.  The rule of it has
   2-3 axis so it must make sense isn't good enough to my mind.  How
   does userspace know which accelerometer to use (more and more devices have
   multiple?)  You could do something like looking at the location info from
   DT / ACPI in your driver and pick the 'best' but that's policy. Should be
   in userspace.  Sure you can just use the right input driver, but the moment
   we do that, we need aware userspace, if that's the case why not make it
   aware from the start.

Believe me I've been round this one a good few times and thought about it
a lot.  I'll take a lot of convincing that this isn't a problem that
should be pushed into userspace.

> 
> I think having them mapped always does not need much resources (except a handful of bytes
> in memory and some µs during probing) unless the event device is opened and really used.
> Only then it starts e.g. I2C traffic to read the sensors.

The bytes don't really mater. The userspace ABI additions do.

> 
> So it is just some unused file sitting around in /dev. Or 2 or could be even 100.
> For devices which have no iio accelerometers configured, there will be no /dev/input
> file. So we are discussing the rare case of devices with more than one or two accelerometers.

Well they aren't exactly rare in IIO using systems ;)

> 
> Now, on every system there are many interfaces and files that are not used because it makes
> no sense to look at them. If I check on one of my systems, I find for example a lot of
> /dev/tty and only a very small portion is used and generic distros have no issue with it.
> 
> There is even /dev/iio:device0 to /dev/iio:device5 representing the raw iio devices.
> Not all of them are actively used, but they are simply there and can be scanned for.

Agreed, in the ideal case we wouldn't have had that either, but we are
stuck with it.  The long term plan is to allow use of IIO backends without the
front end being there at all. Lots of SoC ADC users would prefer this. We are
stuck with the legacy intertwining fo the front end and back end of IIO so
this isn't as easy to do as I would like.

> 
> So I do not see a resource problem if every accelerometer /dev/iio:device* gets
> some companion /dev/input/event* for being used on demand - but only if this bridge
> is configured at all.

That argument does not apply. If we add a config option, distros will enable it.
So the vast majority of systems will ship with this turned on.  You cannot
use a config variable to control policy and expect it to be change by anyone
but a very very small subset of users.  So please drop the 'you can just not
build it argument'.

Userspace configuration changing is a lot easier if people actually care.
Sure, many distros will ship the same script to everyone.

> 
> > I think we need some deliberate userspace interaction to instantiate
> > one of these rather than 'always doing it'.  
> 
> My gut feeling is that this additional user-space interaction needs more resources and
> adds a lot of complexity, independently of how it is done.

Trivial resources and actually fairly trivial complexity.  Key thing is
it puts the burden on the users of this functionality to configure what they
want.

> 
> And I think is even less flexible than "always doing it". Let me explain this claim.
> 
> For me, the kernel should present everything the hardware supports to user-space
> in better digestable device files or APIs (without making any assumptions about the
> user-space code).

Agreed, we just have a different view on how this should be done. I want
it to be dynamic and extremely flexible, you want the easy way of just
putting a fixed set out all the time.

> 
> Then, every user-space that will be installed can find out what the hardware supports
> by looking at standard places.
> 
> E.g. it can scan for all mice and keyboards. And for all input accelerometers.

Or, you an have the correct 'fairly trivial' userspace setup to scan for all
registered accelerometers and 'on demand' create the bindings to bring them up as
Input accelerometers if that is what makes sense for your platform.

> 
> If the kernel is hiding some chips and needs some initial user-space action before
> presenting them all, this requires that the user-space has some a-priori knowledge
> about which specific devices it should ask for.

No more that it needs to know which accelerometer to use?

> So it does not really need to scan
> for them. Because it must already know. Obviously in some mapping table stored at
> a well known location inside the rootfs image.

No. Let me give some more details of how this would work.  It's really just
a more flexible version of what you have.

A distro, or individual user decides to put the relevant script in place for the
following:

1. Userspace detects a new accelerometer driver, via the standard methods (uevent)
2. Userspace looks to see if it has the required properties. Now this includes things
like detecting that it is the accelerometer in the lid of a laptop - if so do not
register it as an input device.  If it's in the keyboard then do register it.
3. Userspace script then creates the files in configfs
/sys/kernel/config/iio/maps/
(this interface needs appropriate definition)
Maybe...
/sys/kernel/config/iio/maps/iio_input/iio_device:X/accel_x, accel_y, etc
When done it writes to the bind file
/sys/kernel/config/iio/maps/iio_input/iio_device:X/bind
which instantiates the input driver.

This moves all of the policy decision into userspace, where it belongs.  If
we want to enable a particular accelerometer on a particular board because it
actually works better than the one the default policy says to use, then we can
do so.

The resulting infrastructure is much more general, because it lets us do the
same for any IIO consumer.  This input bridge is not a special case. It works
equally well for the existing hwmon bridge any would even let us do things
like provide the information from userspace that we have an analog accelerometer
wired up to an ADC on some hacker board.


> 
> This seems to make it impossible to develop a generic distro rootfs image - without
> asking the user for manual configuration. And that where the kernel already knows
> this (which iio accelerometers do exist for a specific piece of hardware).
> 
> This is why I believe a mechanism to instantiate only on demand isn't adding but
> removing flexibility because it prevents copying a rootfs from one device to another.

I disagree, see above.

> 
> > 
> > As I mentioned in V1, look at the possibility of a configfs based method
> > to build the map.  It's easy for userspace to work out what makes sense to
> > map in principle.  There may be some missing info that we also need to
> > look to expose.  
> 
> With a "may be missing" it is impossible to write code for it...
> Can you please name which information is missing on the input accelerometer
> API?

See above. It's not the input accelerometer ABI, it's the missing ability
to instantiate IIO maps from user space.

> 
> > 
> > In general, userspace created channel maps would be very useful for
> > other things such as maker type boards where they can plug all sorts
> > of odd things into ADC channels for example.  
> 
> Ok, I understand, but this is a different problem where this iio-input-bridge is not
> intended to be a solution. Generic ADCs are not input devices. Like SD cards are not
> keyboards.
> 
> So we should not try to mix the idea of general mapping with this input-bridge for
> input accelerometers.
Yes we should. You are proposing a solution that is a subset of the larger
problem set.  Why introduce a stop gap like this when we can do it correctly
and provide something useful for all those other use cases.

The only difference here is the uevent triggered script that creates those maps
for your particular usecase.


> 
> BTW, there is a way to define additional mapping using udev rules which symlink the
> /dev/input/event* paths to stable names like /dev/input/accelerometer.
> 
> This comes without additional code and is already provided by udev and the input system.
> 
> So in summary, I have not yet seen a convincing scenario where being able to dynamically
> map iio channels to input devices seems beneficial.

That is true for the narrow case you are talking about. I don't want to see that
narrow case solved in a fashion that effectively breaks solving it properly.
If we add this, we have to export all accelerometers for ever under all circumstances
to userspace, because to remove it will break existing userspace.

If we stand back and work out if we can do the general solution now, we avoid
this problem.

> 
> >   
> >> 
> >> 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.  
> > 
> > Why do we do this, rather than letting input deal with it?  Input is used
> > to widely differing scales IIRC  
> 
> Well, it can't be done differently... And what I call scale here is nothing more than
> defining ABSMIN_ACC_VAL and ABSMAX_ACC_VAL.
> 
> We need to apply some scale since iio reports in (fractional) units of 1g, i.e. values
> of magnitude 1.

m/s^2 not g, but doesn't matter for the point of view of this discussion.

> These are not adaequate for input events which use integers. So we must
> define some factor for iio_convert_raw_to_processed() to scale from raw value range
> to int value range. We could report raw values but this would be an improper abstraction
> from chip specific differences.

Hmm. I can see we perhaps need some mapping, but is there a concept of standard scale
for existing input accelerometers?  How is this done to give for other input devices
such as touch screens?  I'd expect to see a separation between scale, and range.


> 
> BTW: the range (and therefore the factor) is reported through the evdev driver to user-space
> (evtest reports Min and Max as you can see in the example).
> 
> The most important thing is that this is a hardware independent definition. Every accelerometer
> chip will report this range. So you can easily upgrade hardware or switch accelerometers
> without touching user-space calibration. Like you can replace ethernet controller chips but
> networking works the same with all of them.

Agreed, it needs to be hardware independent by the time it hits userspace, but I would
have thought that scaling would be done in input, rather than IIO. It's hardly
a problem unique to our usecase!

Perhaps Dmitry can give some advice on this.

> 
> >   
> >> 
> >> 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>
> >> ---
> >> drivers/iio/Kconfig                    |   7 +
> >> drivers/iio/Makefile                   |   1 +
> >> drivers/iio/industrialio-core.c        |  12 +
> >> drivers/iio/industrialio-inputbridge.c | 295 +++++++++++++++++++++++++
> >> drivers/iio/industrialio-inputbridge.h |  28 +++
> >> 5 files changed, 343 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..d85afe002613 100644
> >> --- a/drivers/iio/Kconfig
> >> +++ b/drivers/iio/Kconfig
> >> @@ -68,6 +68,13 @@ config IIO_TRIGGERED_EVENT
> >> 	help
> >> 	  Provides helper functions for setting up triggered events.
> >> 
> >> +config IIO_INPUT_BRIDGE
> >> +	bool "Enable accelerometer bridge to input driver"  
> > 
> > Dependency on input?  
> 
> Yes, should be added.
> 
> >   
> >> +	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);
> >> +  
> > This doesn't look like balanced error handling given the goto in the previous
> > case.  If we are treating this as an error we need to unwind the whole
> > of this function properly.  
> 
> Will to check.
> 
> >   
> >> +		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..dd672e25bc70
> >> --- /dev/null
> >> +++ b/drivers/iio/industrialio-inputbridge.c
> >> @@ -0,0 +1,295 @@
> >> +// 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.  
> > No need for the boiler plate if you have SPDX.  That is one of the
> > advantages!  
> 
> Ok.
> 
> >   
> >> + */
> >> +
> >> +#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 sign = false;
> >> +	bool decimal = false;
> >> +	int32_t divisor = 1;
> >> +
> >> +	if (*str == '-')
> >> +		sign = true, str++;
> >> +	while (*str && 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++;
> >> +	}
> >> +
> >> +	mantissa = (mantissa * unit) / divisor;
> >> +	if (sign)
> >> +		mantissa = -mantissa;
> >> +
> >> +	return mantissa;
> >> +}
> >> +
> >> +static void iio_apply_matrix(struct matrix *m, int *in, int *out, uint32_t 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;
> >> +
> >> +printk("%s: map=%px input=%px\n", __func__, map, input);  
> > Remember to tidy these debug statements up at some point.  
> 
> Oops... Shouldn't be there. Maybe I did squash in some commit from my debugging branch.
> 
> >   
> >> +
> >> +	while (cindex < ARRAY_SIZE(values)) {
> >> +		struct iio_channel *channel =
> >> +			&map->channels[cindex];
> >> +		int val;
> >> +		int ret;
> >> +
> >> +		if (!channel->indio_dev) {
> >> +			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 dindex=0;	/* assign unique names to accel/input devices */  
> > Build something from the iio device IDA perhaps?  Those are unique
> > as well.  Useful to know which one this is linked to.  
> 
> We just use this unique number in the poll_dev->input->phys name.
> Mainly this number gives them unique names in /sys/class/input/input*/phys
>  
> Using the iio device IDA seems to be a very good replacement.
> 
> >   
> >> +
> >> +static int iio_input_register_accel_channel(struct iio_dev *indio_dev,
> >> +		 const struct iio_chan_spec *chan)
> >> +{ /* we found some accelerometer channel */
> >> +	int ret;
> >> +	int cindex;
> >> +	struct iio_input_map *map = iio_device_get_drvdata(indio_dev);  
> > 
> > Don't do that.   That is in the domain of the device driver and so
> > will sometimes already be in use.   
> 
> Hm. Is there an alternative to attach such private data to an struct iio_dev
> allocated by someone else? I have not found one yet.
> 
> Or can I add some void *input_mapping; to struct iio_dev? Depending on
> #if defined(CONFIG_IIO_INPUT_BRIDGE)?

Yes, add a new element.

> 
> >   
> >> +
> >> +printk("%s: map=%px\n", __func__, map);
> >> +
> >> +	if (!map) {
> >> +		struct input_polled_dev *poll_dev;
> >> +		const struct iio_chan_spec_ext_info *ext_info;
> >> +
> >> +		map = devm_kzalloc(&indio_dev->dev, sizeof(struct iio_input_map), GFP_KERNEL);
> >> +		if (!map)
> >> +			return -ENOMEM;
> >> +
> >> +		iio_device_set_drvdata(indio_dev, 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, "accel/input%d",  
> 
> If we use the IDA, we can define phys as "iio:device%d", indio_dev->id)
> which makes it easy to locate the associated iio device file name (although
> there are other mechanisms in sysfs tree).
> 
> >> +						    dindex++);
> >> +
> >> +// 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->ext_info;
> >> +		for (; ext_info && ext_info->name; ext_info++) {
> >> +			if (strcmp(ext_info->name, "mount_matrix") == 0)
> >> +				break;
> >> +		}
> >> +
> >> +		if (ext_info && ext_info->name) {
> >> +			/* matrix found */
> >> +			uintptr_t priv = ext_info->private;
> >> +			const struct iio_mount_matrix *mtx;
> >> +
> >> +			mtx = ((iio_get_mount_matrix_t *) priv)(indio_dev,
> >> +								chan);
> >> +
> >> +			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;
> >> +		}
> >> +	}
> >> +
> >> +// brauchen wir das noch? Oder nehmen wir einfach an dass es 3 Kanäle gibt?
> >> +
> >> +	/* find free channel within this device */
> >> +
> >> +	for (cindex = 0; cindex < ARRAY_SIZE(map->channels); cindex++) {
> >> +		if (!map->channels[cindex].indio_dev)
> >> +			break;
> >> +	}
> >> +
> >> +	/* check if we already have collected enough channels */
> >> +	if (cindex == ARRAY_SIZE(map->channels))
> >> +		return 0;	/* silently ignore */
> >> +
> >> +	map->channels[cindex].indio_dev = indio_dev;
> >> +	map->channels[cindex].channel = chan;
> >> +	map->channels[cindex].data = map;
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +int iio_device_register_inputbridge(struct iio_dev *indio_dev)
> >> +{
> >> +	int i;
> >> +
> >> +	for (i = 0; i < indio_dev->num_channels; i++) {
> >> +		const struct iio_chan_spec *chan =
> >> +				&indio_dev->channels[i];
> >> +
> >> +		if (chan->type == IIO_ACCEL) {
> >> +			int r = iio_input_register_accel_channel(indio_dev,
> >> +								 chan);  
> > It would be cleaner (and safer) to go find all the necessary channels then
> > set up the map in one go, rather that iterating and trying to build it
> > in a sequential fashion.
> > 
> > So move the search loop inside and have something like.
> > 
> > iio_input_find_accel_channel(indio_dev, chan, &numchans);
> > iio_input_register_device(indio_dev, chan, numchans);  
> 
> Well, that looks like it needs some temporary storage of dynamic size
> and loop twice over channels for no functional benefit.

Use fixed size. The worst that happens is we end up with it being
an entry larger that it needs to be.

> And handle the
> special case of numchans == 0 (the proposed code simply does not call
> iio_input_register_accel_channel and does not register anything).
> 
> So I'd prefer to follow the "KISS" principle and register single channels
> instead of a set of channels.

Well we disagree on this.  A singleton approach like used here
is to my mind not KISS.  I would rather see what is there then
act as two simple steps, rather than interleave two different
actions with a totally different path for the first channel found.
If there is only one channel you just built a load of infrastructure
that makes no sense.  If you scan first then you can know that
before building anything.


> 
> >   
> >> +
> >> +			if (r < 0)
> >> +				return r;
> >> +		}
> >> +	}
> >> +
> >> +	return 0;
> >> +}
> >> +
> >> +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;
> >> +
> >> +	kfree(input->name);
> >> +	kfree(input->phys);
> >> +	input_unregister_polled_device(map->poll_dev);
> >> +}
> >> +
> >> +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  
> >   
> 
> BR and thanks,
> Nikolaus
> 
Thanks,

Jonathan

^ permalink raw reply

* Re: [RFC v2] iio: input-bridge: optionally bridge iio acceleometers to create a /dev/input interface
From: Roderick Colenbrander @ 2019-04-14 16:26 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: H. Nikolaus Schaller, Dmitry Torokhov, Eric Piel, linux-input,
	letux-kernel, kernel, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, lkml, linux-iio
In-Reply-To: <20190414124029.1f1f6084@archlinux>

Hi Jonathan and Nikolaus,

I would like to jump into this discussion a bit as well. Might be
slightly off topic or not... I'm not too familiar with the details of
IIO, but as Sony we do report accelerometer / gyroscope data through
/dev/input for our DualShock devices through hid-sony. This is among
reasons done for Android, which Nikolaus showed interest in as well.
We are working on exposing this data through user space through
standard Android APIs (see,
https://android-review.googlesource.com/c/platform/hardware/libhardware/+/571762/).
The work will likely be merged later this year and it is already used
in some devices. Main reason it wasn't merged yet it that in our case
we have multiple evdev devices (gamepad, touchpad, motion sensors) and
need to tie those together for applications. Android doesn't support a
good mechanism for that yet.

Since the IIO input work relates to our Android work as well, I will
jump in more from the sidelines to make sure things are done in a
consistent manner. The main concern are resolution and value ranges.

Thanks,
Roderick

On Sun, Apr 14, 2019 at 4:41 AM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Mon, 8 Apr 2019 15:15:56 +0200
> H. Nikolaus Schaller <hns@goldelico.com> wrote:
>
> > Hi Jonathan,
> >
> > > Am 07.04.2019 um 14:30 schrieb Jonathan Cameron <jic23@kernel.org>:
> > >
> > > On Sun, 31 Mar 2019 12:09:46 +0200
> > > "H. Nikolaus Schaller" <hns@goldelico.com> wrote:
> > >
> > > Hi Nikolaus,
> > >
> > > I'm probably going to repeat a few things I sent for v1 as the audience has
> > > expanded somewhat!
> > >
> > > Good to see this moving forwards though as there has been at least some demand
> > > for it going way back to the early days of IIO.
> > >
> > >> 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 worries me, as it inherently means we end up with this interface being
> > > registered in cases where it makes no sense.  A lot of generic distros get
> > > used across widely differing use cases.
> >
> > I still do not fully understand what is worrying you here.
>
> >
> > Do you worry about functionality, flexibility or resources or something else?
>
> Two main things:
> 1) Lack of generality of the approach.
>    This is a single use trick for input devices. Why does it make sense for
>    input devices?  There are lots of other in kernel users and potential
>    ones in the future.  The ability to register additional IIO consumers like
>    this is useful, lets make it useful to everyone.
>
> 2) To much generality of the specific usecase.  I don't want to put an Input
>    interface on accelerometers where it makes no sense.  The rule of it has
>    2-3 axis so it must make sense isn't good enough to my mind.  How
>    does userspace know which accelerometer to use (more and more devices have
>    multiple?)  You could do something like looking at the location info from
>    DT / ACPI in your driver and pick the 'best' but that's policy. Should be
>    in userspace.  Sure you can just use the right input driver, but the moment
>    we do that, we need aware userspace, if that's the case why not make it
>    aware from the start.
>
> Believe me I've been round this one a good few times and thought about it
> a lot.  I'll take a lot of convincing that this isn't a problem that
> should be pushed into userspace.
>
> >
> > I think having them mapped always does not need much resources (except a handful of bytes
> > in memory and some µs during probing) unless the event device is opened and really used.
> > Only then it starts e.g. I2C traffic to read the sensors.
>
> The bytes don't really mater. The userspace ABI additions do.
>
> >
> > So it is just some unused file sitting around in /dev. Or 2 or could be even 100.
> > For devices which have no iio accelerometers configured, there will be no /dev/input
> > file. So we are discussing the rare case of devices with more than one or two accelerometers.
>
> Well they aren't exactly rare in IIO using systems ;)
>
> >
> > Now, on every system there are many interfaces and files that are not used because it makes
> > no sense to look at them. If I check on one of my systems, I find for example a lot of
> > /dev/tty and only a very small portion is used and generic distros have no issue with it.
> >
> > There is even /dev/iio:device0 to /dev/iio:device5 representing the raw iio devices.
> > Not all of them are actively used, but they are simply there and can be scanned for.
>
> Agreed, in the ideal case we wouldn't have had that either, but we are
> stuck with it.  The long term plan is to allow use of IIO backends without the
> front end being there at all. Lots of SoC ADC users would prefer this. We are
> stuck with the legacy intertwining fo the front end and back end of IIO so
> this isn't as easy to do as I would like.
>
> >
> > So I do not see a resource problem if every accelerometer /dev/iio:device* gets
> > some companion /dev/input/event* for being used on demand - but only if this bridge
> > is configured at all.
>
> That argument does not apply. If we add a config option, distros will enable it.
> So the vast majority of systems will ship with this turned on.  You cannot
> use a config variable to control policy and expect it to be change by anyone
> but a very very small subset of users.  So please drop the 'you can just not
> build it argument'.
>
> Userspace configuration changing is a lot easier if people actually care.
> Sure, many distros will ship the same script to everyone.
>
> >
> > > I think we need some deliberate userspace interaction to instantiate
> > > one of these rather than 'always doing it'.
> >
> > My gut feeling is that this additional user-space interaction needs more resources and
> > adds a lot of complexity, independently of how it is done.
>
> Trivial resources and actually fairly trivial complexity.  Key thing is
> it puts the burden on the users of this functionality to configure what they
> want.
>
> >
> > And I think is even less flexible than "always doing it". Let me explain this claim.
> >
> > For me, the kernel should present everything the hardware supports to user-space
> > in better digestable device files or APIs (without making any assumptions about the
> > user-space code).
>
> Agreed, we just have a different view on how this should be done. I want
> it to be dynamic and extremely flexible, you want the easy way of just
> putting a fixed set out all the time.
>
> >
> > Then, every user-space that will be installed can find out what the hardware supports
> > by looking at standard places.
> >
> > E.g. it can scan for all mice and keyboards. And for all input accelerometers.
>
> Or, you an have the correct 'fairly trivial' userspace setup to scan for all
> registered accelerometers and 'on demand' create the bindings to bring them up as
> Input accelerometers if that is what makes sense for your platform.
>
> >
> > If the kernel is hiding some chips and needs some initial user-space action before
> > presenting them all, this requires that the user-space has some a-priori knowledge
> > about which specific devices it should ask for.
>
> No more that it needs to know which accelerometer to use?
>
> > So it does not really need to scan
> > for them. Because it must already know. Obviously in some mapping table stored at
> > a well known location inside the rootfs image.
>
> No. Let me give some more details of how this would work.  It's really just
> a more flexible version of what you have.
>
> A distro, or individual user decides to put the relevant script in place for the
> following:
>
> 1. Userspace detects a new accelerometer driver, via the standard methods (uevent)
> 2. Userspace looks to see if it has the required properties. Now this includes things
> like detecting that it is the accelerometer in the lid of a laptop - if so do not
> register it as an input device.  If it's in the keyboard then do register it.
> 3. Userspace script then creates the files in configfs
> /sys/kernel/config/iio/maps/
> (this interface needs appropriate definition)
> Maybe...
> /sys/kernel/config/iio/maps/iio_input/iio_device:X/accel_x, accel_y, etc
> When done it writes to the bind file
> /sys/kernel/config/iio/maps/iio_input/iio_device:X/bind
> which instantiates the input driver.
>
> This moves all of the policy decision into userspace, where it belongs.  If
> we want to enable a particular accelerometer on a particular board because it
> actually works better than the one the default policy says to use, then we can
> do so.
>
> The resulting infrastructure is much more general, because it lets us do the
> same for any IIO consumer.  This input bridge is not a special case. It works
> equally well for the existing hwmon bridge any would even let us do things
> like provide the information from userspace that we have an analog accelerometer
> wired up to an ADC on some hacker board.
>
>
> >
> > This seems to make it impossible to develop a generic distro rootfs image - without
> > asking the user for manual configuration. And that where the kernel already knows
> > this (which iio accelerometers do exist for a specific piece of hardware).
> >
> > This is why I believe a mechanism to instantiate only on demand isn't adding but
> > removing flexibility because it prevents copying a rootfs from one device to another.
>
> I disagree, see above.
>
> >
> > >
> > > As I mentioned in V1, look at the possibility of a configfs based method
> > > to build the map.  It's easy for userspace to work out what makes sense to
> > > map in principle.  There may be some missing info that we also need to
> > > look to expose.
> >
> > With a "may be missing" it is impossible to write code for it...
> > Can you please name which information is missing on the input accelerometer
> > API?
>
> See above. It's not the input accelerometer ABI, it's the missing ability
> to instantiate IIO maps from user space.
>
> >
> > >
> > > In general, userspace created channel maps would be very useful for
> > > other things such as maker type boards where they can plug all sorts
> > > of odd things into ADC channels for example.
> >
> > Ok, I understand, but this is a different problem where this iio-input-bridge is not
> > intended to be a solution. Generic ADCs are not input devices. Like SD cards are not
> > keyboards.
> >
> > So we should not try to mix the idea of general mapping with this input-bridge for
> > input accelerometers.
> Yes we should. You are proposing a solution that is a subset of the larger
> problem set.  Why introduce a stop gap like this when we can do it correctly
> and provide something useful for all those other use cases.
>
> The only difference here is the uevent triggered script that creates those maps
> for your particular usecase.
>
>
> >
> > BTW, there is a way to define additional mapping using udev rules which symlink the
> > /dev/input/event* paths to stable names like /dev/input/accelerometer.
> >
> > This comes without additional code and is already provided by udev and the input system.
> >
> > So in summary, I have not yet seen a convincing scenario where being able to dynamically
> > map iio channels to input devices seems beneficial.
>
> That is true for the narrow case you are talking about. I don't want to see that
> narrow case solved in a fashion that effectively breaks solving it properly.
> If we add this, we have to export all accelerometers for ever under all circumstances
> to userspace, because to remove it will break existing userspace.
>
> If we stand back and work out if we can do the general solution now, we avoid
> this problem.
>
> >
> > >
> > >>
> > >> 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.
> > >
> > > Why do we do this, rather than letting input deal with it?  Input is used
> > > to widely differing scales IIRC
> >
> > Well, it can't be done differently... And what I call scale here is nothing more than
> > defining ABSMIN_ACC_VAL and ABSMAX_ACC_VAL.
> >
> > We need to apply some scale since iio reports in (fractional) units of 1g, i.e. values
> > of magnitude 1.
>
> m/s^2 not g, but doesn't matter for the point of view of this discussion.

m/s^2 is conceptually right, but others have been using 'g' for input,
so I would consider that the standard now. Also leaves handling of
location on Earth to user space instead of assuming a fixed conversion
to 9.81 m/s^2.

> > These are not adaequate for input events which use integers. So we must
> > define some factor for iio_convert_raw_to_processed() to scale from raw value range
> > to int value range. We could report raw values but this would be an improper abstraction
> > from chip specific differences.
>
> Hmm. I can see we perhaps need some mapping, but is there a concept of standard scale
> for existing input accelerometers?  How is this done to give for other input devices
> such as touch screens?  I'd expect to see a separation between scale, and range.
>

We at the time were one of the first to expose acceleration and gyro
data through /dev/input for DualShock 4 as supported by hid-sony. We
report acceleration in 'g' and angular velocity in 'degree / s'. We
set the resolution to respectively '1 g' and '1 degree / s'. The range
we set to the range of the device e.g. for DS4 -4g to +4g for
acceleration. I need to check though what coordinate system we use,
but I think it is right handed (gyro counter clockwise relative to
acceleration axes).

The two other drivers using INPUT_PROC_ACCELEROMETER are hid-wacom and
hid-udraw-ps3 Wacom. Both seem to report resolution in 'g'  as well.

Thanks,
Roderick

^ permalink raw reply

* Re: [PATCH v4 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Life is hard, and then you die @ 2019-04-15  6:50 UTC (permalink / raw)
  To: Andrzej Hajda
  Cc: Dmitry Torokhov, Henrik Rydberg, Andy Shevchenko,
	Greg Kroah-Hartman, Lukas Wunner, Federico Lorenzi, linux-input,
	linux-kernel, Inki Dae, Laurent Pinchart
In-Reply-To: <c861091d-f659-614d-65f4-6fe983885e09@samsung.com>


  Hi Andrzej,

On Wed, Apr 10, 2019 at 11:42:50AM +0200, Andrzej Hajda wrote:
> On 07.04.2019 07:03, 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).
[snip]
> What about:
> 
> ---------
> 
> diff --git a/drivers/gpu/drm/bridge/Kconfig b/drivers/gpu/drm/bridge/Kconfig
> index 8840f396a7b6..298189067929 100644
> --- a/drivers/gpu/drm/bridge/Kconfig
> +++ b/drivers/gpu/drm/bridge/Kconfig
> @@ -86,8 +86,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 0cc293a6ac24..df0f9dbbe839 100644
> --- a/drivers/gpu/drm/bridge/sil-sii8620.c
> +++ b/drivers/gpu/drm/bridge/sil-sii8620.c
> @@ -1762,10 +1762,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(RC_CORE) || !ctx->rc_dev)
>                 return false;
> -       }
>  
>         if (pressed)
>                 rc_keydown(ctx->rc_dev, RC_PROTO_CEC, scancode, 0);
> @@ -2102,6 +2100,9 @@ static void sii8620_init_rcp_input_dev(struct
> sii8620 *ctx)
>         struct rc_dev *rc_dev;
>         int ret;
>  
> +       if (!IS_ENABLED(RC_CORE))
> +               return;
> +
>         rc_dev = rc_allocate_device(RC_DRIVER_SCANCODE);
>         if (!rc_dev) {
>                 dev_err(ctx->dev, "Failed to allocate RC device\n");
> @@ -2216,6 +2217,9 @@ static void sii8620_detach(struct drm_bridge *bridge)
>  {
>         struct sii8620 *ctx = bridge_to_sii8620(bridge);
>  
> +       if (!IS_ENABLED(RC_CORE))
> +               return;
> +
>         rc_unregister_device(ctx->rc_dev);
>  }
> -----------
> 
> Less changes, no conditional compilation - better compiler coverage,
> more readable.

This works for me (with RC_CORE replaced with CONFIG_RC_CORE). Sending
a new patch that includes this shortly.


  Cheers,

  Ronald

^ permalink raw reply

* [PATCH v5 0/2] Add Apple SPI keyboard and trackpad driver
From: Ronald Tschalär @ 2019-04-15  8:12 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.

The second patch contains the new applespi driver.

Changes in v5:
  Applied all feedback from review by Andy Shevchenko (applespi) and
  Andrzej Hajda (sil-sii8620), including:
  - (sil_sii8620) use 'if (IS_DEFINED(...))' instead of conditional
    compilation to handle RC_CORE being undefined
  - (applespi) a number of cleanups of leftovers from changes to earlier
    versions
  - (applespi) replace some bool flags with loop-end detection
  - (applespi) improve error reporting in a couple places
  The full set of changes to applespi can be viewed at
  https://github.com/roadrunner2/macbook12-spi-driver/ as individual
  commits daf60f8..36afd70 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       | 1985 +++++++++++++++++++++++
 drivers/input/keyboard/applespi.h       |   29 +
 drivers/input/keyboard/applespi_trace.h |   94 ++
 7 files changed, 2132 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

* [PATCH v5 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Ronald Tschalär @ 2019-04-15  8:12 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: <20190415081300.24831-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>
---
 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 v5 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Ronald Tschalär @ 2019-04-15  8:13 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: <20190415081300.24831-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>
---
 drivers/input/keyboard/Kconfig          |   15 +
 drivers/input/keyboard/Makefile         |    1 +
 drivers/input/keyboard/applespi.c       | 1985 +++++++++++++++++++++++
 drivers/input/keyboard/applespi.h       |   29 +
 drivers/input/keyboard/applespi_trace.h |   94 ++
 5 files changed, 2124 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..5c3d7687e346
--- /dev/null
+++ b/drivers/input/keyboard/applespi.c
@@ -0,0 +1,1985 @@
+// 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);
+
+	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 */
+	for (i = 0; i < MAX_ROLLOVER; i++) {
+		if (keyboard_protocol->keys_pressed[i] != 1)
+			break;
+	}
+
+	if (i == MAX_ROLLOVER)	/* all keys were 1 */
+		return;
+
+	/* 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;
+
+		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

* Re: [PATCH v5 1/2] drm/bridge: sil_sii8620: make remote control optional.
From: Andrzej Hajda @ 2019-04-15  8:58 UTC (permalink / raw)
  To: Ronald Tschalär, Dmitry Torokhov, Henrik Rydberg,
	Andy Shevchenko, Inki Dae, Greg Kroah-Hartman
  Cc: Lukas Wunner, Federico Lorenzi, Laurent Pinchart, linux-input,
	dri-devel, linux-kernel
In-Reply-To: <20190415081300.24831-2-ronald@innovation.ch>

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>


If there are no objections I will take it to drm-misc tomorrow.


 --
Regards
Andrzej

^ permalink raw reply

* Re: [PATCH] HID: fix compiling error in u2fzero_probe()
From: Jiri Kosina @ 2019-04-15  9:03 UTC (permalink / raw)
  To: Mao Wenan
  Cc: benjamin.tissoires, linux-input, linux-kernel, kernel-janitors,
	Hulk Robot, Andrej Shadura
In-Reply-To: <20190413134459.76045-1-maowenan@huawei.com>

On Sat, 13 Apr 2019, Mao Wenan wrote:

> There is one compiling error in u2fzero_probe()->u2fzero_init_hwrng(),
> this is because HW_RANDOM is not set.
> 
> drivers/hid/hid-u2fzero.o: In function `u2fzero_probe':
> hid-u2fzero.c:(.text+0xc70): undefined reference to `devm_hwrng_register'
> 
> Fixes: 42337b9d4d958("HID: add driver for U2F Zero built-in LED and RNG")
> Reported-by: Hulk Robot <hulkci@huawei.com>
> Signed-off-by: Mao Wenan <maowenan@huawei.com>
> ---
>  drivers/hid/Kconfig | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig
> index 4dc1035..76d8206 100644
> --- a/drivers/hid/Kconfig
> +++ b/drivers/hid/Kconfig
> @@ -1017,6 +1017,7 @@ config HID_U2FZERO
>  	tristate "U2F Zero LED and RNG support"
>  	depends on USB_HID
>  	depends on LEDS_CLASS
> +	depends on HW_RANDOM

[ CCing Andrej ]

This was supposed to be sent by Andrej (together with other fixes), but 
you beat him to it. Applied, thanks.

-- 
Jiri Kosina
SUSE Labs

^ permalink raw reply

* Re: [PATCH v5 2/2] Input: add Apple SPI keyboard and trackpad driver.
From: Andy Shevchenko @ 2019-04-15  9:03 UTC (permalink / raw)
  To: Ronald Tschalär
  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: <20190415081300.24831-3-ronald@innovation.ch>

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>

> 
> 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>
> ---
>  drivers/input/keyboard/Kconfig          |   15 +
>  drivers/input/keyboard/Makefile         |    1 +
>  drivers/input/keyboard/applespi.c       | 1985 +++++++++++++++++++++++
>  drivers/input/keyboard/applespi.h       |   29 +
>  drivers/input/keyboard/applespi_trace.h |   94 ++
>  5 files changed, 2124 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..5c3d7687e346
> --- /dev/null
> +++ b/drivers/input/keyboard/applespi.c
> @@ -0,0 +1,1985 @@
> +// 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);

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);

> +
> +	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 */
> +	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.

> +
> +		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
> 

-- 
With Best Regards,
Andy Shevchenko

^ permalink raw reply

* Re: [PATCH v2] Input: uinput: Avoid Object-Already-Free with a global lock
From: Mukesh Ojha @ 2019-04-15 10:05 UTC (permalink / raw)
  To: linux-input, linux-kernel
  Cc: Gaurav Kohli, Peter Hutterer, Martin Kepplinger, Paul E. McKenney,
	dmitry.torokhov@gmail.com
In-Reply-To: <1554883176-24318-1-git-send-email-mojha@codeaurora.org>


Hi Dmitry,

Can you please have a look at this patch ? as this seems to reproducing 
quite frequently

Thanks,
Mukesh

On 4/10/2019 1:29 PM, Mukesh Ojha wrote:
> uinput_destroy_device() gets called from two places. In one place,
> uinput_ioctl_handler() where it is protected under a lock
> udev->mutex but there is no protection on udev device from freeing
> inside uinput_release().
>
> This can result in Object-Already-Free case where uinput parent
> device already got freed while a child being inserted inside it.
> That result in a double free case for parent while kernfs_put()
> being done for child in a failure path of adding a node.
>
> [  160.093398] Call trace:
> [  160.093417]  kernfs_get+0x64/0x88
> [  160.093438]  kernfs_new_node+0x94/0xc8
> [  160.093450]  kernfs_create_dir_ns+0x44/0xfc
> [  160.093463]  sysfs_create_dir_ns+0xa8/0x130
> [  160.093479]  kobject_add_internal+0x278/0x650
> [  160.093491]  kobject_add_varg+0xe0/0x130
> [  160.093502]  kobject_add+0x15c/0x1d0
> [  160.093518]  device_add+0x2bc/0xde0
> [  160.093533]  input_register_device+0x5f4/0xa0c
> [  160.093547]  uinput_ioctl_handler+0x1184/0x2198
> [  160.093560]  uinput_ioctl+0x38/0x48
> [  160.093573]  vfs_ioctl+0x7c/0xb4
> [  160.093585]  do_vfs_ioctl+0x9ec/0x2350
> [  160.093597]  SyS_ioctl+0x6c/0xa4
> [  160.093610]  el0_svc_naked+0x34/0x38
> [  160.093621] ---[ end trace bccf0093cda2c538 ]---
> [  160.099041] =============================================================================
> [  160.107459] BUG kernfs_node_cache (Tainted: G S      W  O   ): Object already free
> [  160.115235] -----------------------------------------------------------------------------
> [  160.115235]
> [  160.125151] Disabling lock debugging due to kernel taint
> [  160.130626] INFO: Allocated in __kernfs_new_node+0x8c/0x3c0 age=11 cpu=2 pid=7098
> [  160.138314] 	kmem_cache_alloc+0x358/0x388
> [  160.142445] 	__kernfs_new_node+0x8c/0x3c0
> [  160.146590] 	kernfs_new_node+0x80/0xc8
> [  160.150462] 	kernfs_create_dir_ns+0x44/0xfc
> [  160.154777] 	sysfs_create_dir_ns+0xa8/0x130
> [  160.158416] CPU5: update max cpu_capacity 1024
> [  160.159085] 	kobject_add_internal+0x278/0x650
> [  160.163567] 	kobject_add_varg+0xe0/0x130
> [  160.167606] 	kobject_add+0x15c/0x1d0
> [  160.168452] CPU5: update max cpu_capacity 780
> [  160.171287] 	get_device_parent+0x2d0/0x34c
> [  160.175510] 	device_add+0x240/0xde0
> [  160.178371] CPU6: update max cpu_capacity 916
> [  160.179108] 	input_register_device+0x5f4/0xa0c
> [  160.183686] 	uinput_ioctl_handler+0x1184/0x2198
> [  160.188346] 	uinput_ioctl+0x38/0x48
> [  160.191941] 	vfs_ioctl+0x7c/0xb4
> [  160.195261] 	do_vfs_ioctl+0x9ec/0x2350
> [  160.199111] 	SyS_ioctl+0x6c/0xa4
> [  160.202436] INFO: Freed in kernfs_put+0x2c8/0x434 age=14 cpu=0 pid=7096
> [  160.209230] 	kernfs_put+0x2c8/0x434
> [  160.212825] 	kobject_del+0x50/0xcc
> [  160.216332] 	cleanup_glue_dir+0x124/0x16c
> [  160.220456] 	device_del+0x55c/0x5c8
> [  160.224047] 	__input_unregister_device+0x274/0x2a8
> [  160.228974] 	input_unregister_device+0x90/0xd0
> [  160.233553] 	uinput_destroy_device+0x15c/0x1dc
> [  160.238131] 	uinput_release+0x44/0x5c
> [  160.241898] 	__fput+0x1f4/0x4e4
> [  160.245127] 	____fput+0x20/0x2c
> [  160.248358] 	task_work_run+0x9c/0x174
> [  160.252127] 	do_notify_resume+0x104/0x6bc
> [  160.256253] 	work_pending+0x8/0x14
> [  160.259751] INFO: Slab 0xffffffbf0215ff00 objects=33 used=11 fp=0xffffffc0857ffd08 flags=0x8101
> [  160.268693] INFO: Object 0xffffffc0857ffd08 @offset=15624 fp=0xffffffc0857fefb0
> [  160.268693]
> [  160.277721] Redzone ffffffc0857ffd00: bb bb bb bb bb bb bb bb                          ........
> [  160.286656] Object ffffffc0857ffd08: 00 00 00 00 01 00 00 80 58 a2 37 45 c1 ff ff ff  ........X.7E....
> [  160.296207] Object ffffffc0857ffd18: ae 21 10 0b 90 ff ff ff 20 fd 7f 85 c0 ff ff ff  .!...... .......
> [  160.305780] Object ffffffc0857ffd28: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
> [  160.315342] Object ffffffc0857ffd38: 00 00 00 00 00 00 00 00 7d a3 25 69 00 00 00 00  ........}.%i....
> [  160.324896] Object ffffffc0857ffd48: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00  ................
> [  160.334446] Object ffffffc0857ffd58: 80 c0 28 47 c1 ff ff ff 00 00 00 00 00 00 00 00  ..(G............
> [  160.344000] Object ffffffc0857ffd68: 80 4a ce d1 c0 ff ff ff dc 32 01 00 01 00 00 00  .J.......2......
> [  160.353554] Object ffffffc0857ffd78: 11 00 ed 41 00 00 00 00 00 00 00 00 00 00 00 00  ...A............
> [  160.363099] Redzone ffffffc0857ffd88: bb bb bb bb bb bb bb bb                          ........
> [  160.372032] Padding ffffffc0857ffee0: 5a 5a 5a 5a 5a 5a 5a 5a                          ZZZZZZZZ
> [  160.378299] CPU6: update max cpu_capacity 780
> [  160.380971] CPU: 4 PID: 7098 Comm: syz-executor Tainted: G S  B   W  O    4.14.98+ #1
>
> So, avoid the race by taking a global lock inside uinput_release().
>
> Signed-off-by: Mukesh Ojha <mojha@codeaurora.org>
> Cc: Gaurav Kohli <gkohli@codeaurora.org>
> Cc: Peter Hutterer <peter.hutterer@who-t.net>
> Cc: Martin Kepplinger <martink@posteo.de>
> Cc: "Paul E. McKenney" <paulmck@linux.ibm.com>
> ---
> Also, if this looks good we can further use this global lock inside
> read/write in separate patches and release can happen at any time.
>
> Changes from v1->v2:
>   - Mistakenly added udev lock replaced by global lock.
>
>
>
>
>   drivers/input/misc/uinput.c | 28 ++++++++++++++++++++++++++--
>   1 file changed, 26 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
> index 26ec603f..2e7e096 100644
> --- a/drivers/input/misc/uinput.c
> +++ b/drivers/input/misc/uinput.c
> @@ -81,6 +81,8 @@ struct uinput_device {
>   	spinlock_t		requests_lock;
>   };
>   
> +static DEFINE_MUTEX(uinput_glb_mutex);
> +
>   static int uinput_dev_event(struct input_dev *dev,
>   			    unsigned int type, unsigned int code, int value)
>   {
> @@ -714,10 +716,18 @@ static __poll_t uinput_poll(struct file *file, poll_table *wait)
>   static int uinput_release(struct inode *inode, struct file *file)
>   {
>   	struct uinput_device *udev = file->private_data;
> +	ssize_t retval;
> +
> +	retval = mutex_lock_interruptible(&uinput_glb_mutex);
> +	if (retval)
> +		return retval;
>   
>   	uinput_destroy_device(udev);
> +	file->private_data = NULL;
>   	kfree(udev);
>   
> +	mutex_unlock(&uinput_glb_mutex);
> +
>   	return 0;
>   }
>   
> @@ -848,7 +858,7 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>   				 unsigned long arg, void __user *p)
>   {
>   	int			retval;
> -	struct uinput_device	*udev = file->private_data;
> +	struct uinput_device	*udev;
>   	struct uinput_ff_upload ff_up;
>   	struct uinput_ff_erase  ff_erase;
>   	struct uinput_request   *req;
> @@ -856,10 +866,20 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>   	const char		*name;
>   	unsigned int		size;
>   
> -	retval = mutex_lock_interruptible(&udev->mutex);
> +	retval = mutex_lock_interruptible(&uinput_glb_mutex);
>   	if (retval)
>   		return retval;
>   
> +	udev = file->private_data;
> +	if (!udev) {
> +		retval = -EINVAL;
> +		goto unlock_glb_mutex;
> +	}
> +
> +	retval = mutex_lock_interruptible(&udev->mutex);
> +	if (retval)
> +		goto unlock_glb_mutex;
> +
>   	if (!udev->dev) {
>   		udev->dev = input_allocate_device();
>   		if (!udev->dev) {
> @@ -1039,8 +1059,12 @@ static long uinput_ioctl_handler(struct file *file, unsigned int cmd,
>   	}
>   
>   	retval = -EINVAL;
> +
>    out:
>   	mutex_unlock(&udev->mutex);
> +
> + unlock_glb_mutex:
> +	mutex_unlock(&uinput_glb_mutex);
>   	return retval;
>   }
>   

^ permalink raw reply

* Re: [PATCH] ELAN touchpad i2c_hid bugs fix
From: Hans de Goede @ 2019-04-15 11:42 UTC (permalink / raw)
  To: hotwater438, Kai-Heng Feng
  Cc: Dmitry Torokhov, Vladislav Dalechyn, Benjamin Tissoires,
	Jiri Kosina, Swboyd, Bigeasy, open list:HID CORE LAYER, lkml
In-Reply-To: <LcVmBjG--3-1@tutanota.com>

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.

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

* [PATCH 1/1] input: adp5589: Add gpio_set_multiple interface
From: Bogdan Togorean @ 2019-04-15 12:25 UTC (permalink / raw)
  To: linux-input; +Cc: dmitry.torokhov, gustavo, linux-kernel, Bogdan Togorean

This patch implements the gpio_set_multiple interface for ADP558x chip.

Signed-off-by: Bogdan Togorean <bogdan.togorean@analog.com>
---
 drivers/input/keyboard/adp5589-keys.c | 25 +++++++++++++++++++++++++
 1 file changed, 25 insertions(+)

diff --git a/drivers/input/keyboard/adp5589-keys.c b/drivers/input/keyboard/adp5589-keys.c
index 2835fba71c33..143871bd60ef 100644
--- a/drivers/input/keyboard/adp5589-keys.c
+++ b/drivers/input/keyboard/adp5589-keys.c
@@ -416,6 +416,30 @@ static void adp5589_gpio_set_value(struct gpio_chip *chip,
 	mutex_unlock(&kpad->gpio_lock);
 }
 
+static void adp5589_gpio_set_multiple(struct gpio_chip *chip,
+				      unsigned long *mask, unsigned long *bits)
+{
+	struct adp5589_kpad *kpad = container_of(chip, struct adp5589_kpad, gc);
+	u8 bank, reg_mask, reg_bits;
+
+	mutex_lock(&kpad->gpio_lock);
+
+	for (bank = 0; bank <= kpad->var->bank(kpad->var->maxgpio); bank++) {
+		if (bank > kpad->var->bank(get_bitmask_order(*mask) - 1))
+			break;
+		reg_mask = mask[bank / sizeof(*mask)] >>
+			   ((bank % sizeof(*mask)) * BITS_PER_BYTE);
+		reg_bits = bits[bank / sizeof(*bits)] >>
+			   ((bank % sizeof(*bits)) * BITS_PER_BYTE);
+		kpad->dat_out[bank] &= ~reg_mask;
+		kpad->dat_out[bank] |= reg_bits & reg_mask;
+		adp5589_write(kpad->client, kpad->var->reg(ADP5589_GPO_DATA_OUT_A) + bank,
+			      kpad->dat_out[bank]);
+	}
+
+	mutex_unlock(&kpad->gpio_lock);
+}
+
 static int adp5589_gpio_direction_input(struct gpio_chip *chip, unsigned off)
 {
 	struct adp5589_kpad *kpad = gpiochip_get_data(chip);
@@ -517,6 +541,7 @@ static int adp5589_gpio_add(struct adp5589_kpad *kpad)
 	kpad->gc.direction_output = adp5589_gpio_direction_output;
 	kpad->gc.get = adp5589_gpio_get_value;
 	kpad->gc.set = adp5589_gpio_set_value;
+	kpad->gc.set_multiple = adp5589_gpio_set_multiple;
 	kpad->gc.can_sleep = 1;
 
 	kpad->gc.base = gpio_data->gpio_start;
-- 
2.21.0

^ permalink raw reply related

* [PATCH v3 0/3] Basic DT support for Lenovo Miix 630
From: Jeffrey Hugo @ 2019-04-15 16:09 UTC (permalink / raw)
  Cc: lee.jones, bjorn.andersson, dmitry.torokhov, robh+dt,
	mark.rutland, agross, david.brown, jikos, benjamin.tissoires,
	linux-input, devicetree, linux-arm-msm, linux-kernel,
	Jeffrey Hugo

The Lenovo Miix 630 is one of three ARM based (specifically Qualcomm
MSM8998) laptops that comes with Windows, and seems to have a dedicated
following of folks intrested to get Linux up and running on it.

This series adds support for the basic functionality this is validated
towork using devicetree.  Although the laptops do feed ACPI to Windows,
the existing MSM8998 support in mainline is DT based, so DT provides a
quick path to functionality while ACPI support is investigated.

The three devices are very similar, but do have differences in the set
of peripherals supported, so the idea is that the vast majority of the
support for all three can live in a common include, which should reduce
overall duplication.  Adding support for the other two devices as a
follow on should involve minimal work.

The bleeding edge work for these laptops and work in progress can be
found at https://github.com/aarch64-laptops/prebuilt

v3:
-Changed "clam" to "clamshell"
-Defined a dt binding for the combo Elan keyboard + touchpad device
-Adjusted the HID quirk to be correct for dt boot
-Removed extranious comment in board dts
-Fixed board level compatible

v2:
-Changed "cls" to "clam" since feedback indicated "cls" is too opaque,
but
"clamshell" is a mouthfull.  "clam" seems to be a happy medium.

Jeffrey Hugo (3):
  dt-bindings: input: add Elan 400 combo keyboard/touchpad over i2c
  HID: quirks: Fix keyboard + touchpad on Lenovo Miix 630 for DT
  arm64: dts: qcom: Add Lenovo Miix 630

 .../bindings/input/elan,combo400-i2c.txt      |  11 +
 arch/arm64/boot/dts/qcom/Makefile             |   1 +
 .../boot/dts/qcom/msm8998-clamshell.dtsi      | 278 ++++++++++++++++++
 .../boot/dts/qcom/msm8998-lenovo-miix-630.dts |  30 ++
 drivers/hid/hid-quirks.c                      |   3 +-
 5 files changed, 322 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/input/elan,combo400-i2c.txt
 create mode 100644 arch/arm64/boot/dts/qcom/msm8998-clamshell.dtsi
 create mode 100644 arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dts

-- 
2.17.1

^ permalink raw reply

* [PATCH v3 1/3] dt-bindings: input: add Elan 400 combo keyboard/touchpad over i2c
From: Jeffrey Hugo @ 2019-04-15 16:10 UTC (permalink / raw)
  To: robh+dt, mark.rutland
  Cc: lee.jones, bjorn.andersson, dmitry.torokhov, agross, david.brown,
	jikos, benjamin.tissoires, linux-input, devicetree, linux-arm-msm,
	linux-kernel, Jeffrey Hugo
In-Reply-To: <20190415160915.16324-1-jeffrey.l.hugo@gmail.com>

The Elan 400 combo keyboard/touchpad over i2c device is a distinct device
from the Elan 400 standalone touchpad device.  The combo device has been
found in the Lenovo Miix 630 and HP Envy x2 laptops.

Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 .../devicetree/bindings/input/elan,combo400-i2c.txt   | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/input/elan,combo400-i2c.txt

diff --git a/Documentation/devicetree/bindings/input/elan,combo400-i2c.txt b/Documentation/devicetree/bindings/input/elan,combo400-i2c.txt
new file mode 100644
index 000000000000..fb700a29148d
--- /dev/null
+++ b/Documentation/devicetree/bindings/input/elan,combo400-i2c.txt
@@ -0,0 +1,11 @@
+Elantech 0400 I2C combination Keyboard/Touchpad
+
+This binding describes an Elan device with pid 0x0400, that is a combination
+keyboard + touchpad device.  This binding does not cover an Elan device with
+pid 0x0400 that is solely a standalone touchpad device.
+
+Required properties:
+- compatible: should be "elan,combo400-i2c"
+
+This binding is compatible with the HID over I2C binding, which is specified
+in hid-over-i2c.txt in this directory.
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 2/3] HID: quirks: Fix keyboard + touchpad on Lenovo Miix 630 for DT
From: Jeffrey Hugo @ 2019-04-15 16:11 UTC (permalink / raw)
  To: robh+dt, mark.rutland, jikos, benjamin.tissoires
  Cc: lee.jones, bjorn.andersson, dmitry.torokhov, agross, david.brown,
	linux-input, devicetree, linux-arm-msm, linux-kernel,
	Jeffrey Hugo
In-Reply-To: <20190415160915.16324-1-jeffrey.l.hugo@gmail.com>

Following up on commit 2bafa1e96254 ("HID: quirks: Fix keyboard + touchpad
on Lenovo Miix 630"), the devicetree (DT) identifier for the combo keyboard
+ touchpad device is "elan,combo400-i2c", which differs from the ACPI ID,
thus if we want the quirk to work properly when booting via DT instead of
ACPI, we need to key off the DT id as well.

Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 drivers/hid/hid-quirks.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hid/hid-quirks.c b/drivers/hid/hid-quirks.c
index 77ffba48cc73..00c08f8318b8 100644
--- a/drivers/hid/hid-quirks.c
+++ b/drivers/hid/hid-quirks.c
@@ -997,7 +997,8 @@ bool hid_ignore(struct hid_device *hdev)
 			return true;
 		/* Same with product id 0x0400 */
 		if (hdev->product == 0x0400 &&
-		    strncmp(hdev->name, "QTEC0001", 8) != 0)
+		    (strncmp(hdev->name, "QTEC0001", 8) != 0 ||
+		     strncmp(hdev->name, "elan,combo400-i2c", 17) != 0))
 			return true;
 		break;
 	}
-- 
2.17.1

^ permalink raw reply related

* [PATCH v3 3/3] arm64: dts: qcom: Add Lenovo Miix 630
From: Jeffrey Hugo @ 2019-04-15 16:11 UTC (permalink / raw)
  To: bjorn.andersson, robh+dt, mark.rutland, agross, david.brown
  Cc: lee.jones, dmitry.torokhov, jikos, benjamin.tissoires,
	linux-input, devicetree, linux-arm-msm, linux-kernel,
	Jeffrey Hugo
In-Reply-To: <20190415160915.16324-1-jeffrey.l.hugo@gmail.com>

This adds the initial DT for the Lenovo Miix 630 laptop.  Supported
functionality includes USB (host), microSD-card, keyboard, and trackpad.

Signed-off-by: Jeffrey Hugo <jeffrey.l.hugo@gmail.com>
---
 arch/arm64/boot/dts/qcom/Makefile             |   1 +
 .../boot/dts/qcom/msm8998-clamshell.dtsi      | 278 ++++++++++++++++++
 .../boot/dts/qcom/msm8998-lenovo-miix-630.dts |  30 ++
 3 files changed, 309 insertions(+)
 create mode 100644 arch/arm64/boot/dts/qcom/msm8998-clamshell.dtsi
 create mode 100644 arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dts

diff --git a/arch/arm64/boot/dts/qcom/Makefile b/arch/arm64/boot/dts/qcom/Makefile
index 21d548f02d39..c3e4307bcbd4 100644
--- a/arch/arm64/boot/dts/qcom/Makefile
+++ b/arch/arm64/boot/dts/qcom/Makefile
@@ -6,6 +6,7 @@ dtb-$(CONFIG_ARCH_QCOM)	+= msm8916-mtp.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= msm8992-bullhead-rev-101.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= msm8994-angler-rev-101.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= msm8996-mtp.dtb
+dtb-$(CONFIG_ARCH_QCOM)	+= msm8998-lenovo-miix-630.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= msm8998-mtp.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= sdm845-mtp.dtb
 dtb-$(CONFIG_ARCH_QCOM)	+= qcs404-evb-1000.dtb
diff --git a/arch/arm64/boot/dts/qcom/msm8998-clamshell.dtsi b/arch/arm64/boot/dts/qcom/msm8998-clamshell.dtsi
new file mode 100644
index 000000000000..1a341d4b1597
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8998-clamshell.dtsi
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Jeffrey Hugo. All rights reserved. */
+
+/*
+ * Common include for MSM8998 clamshell devices, ie the Lenovo Miix 630,
+ * Asus NovaGo TP370QL, and HP Envy x2.  All three devices are basically the
+ * same, with differences in peripherals.
+ */
+
+#include "msm8998.dtsi"
+#include "pm8998.dtsi"
+#include "pm8005.dtsi"
+
+/ {
+	chosen {
+	};
+
+	thermal-zones {
+		battery-thermal {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&tsens0 0>;
+
+			trips {
+				battery_crit: trip0 {
+					temperature = <60000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+
+		skin-thermal {
+			polling-delay-passive = <250>;
+			polling-delay = <1000>;
+
+			thermal-sensors = <&tsens1 5>;
+
+			trips {
+				skin_alert: trip0 {
+					temperature = <44000>;
+					hysteresis = <2000>;
+					type = "passive";
+				};
+
+				skip_crit: trip1 {
+					temperature = <70000>;
+					hysteresis = <2000>;
+					type = "critical";
+				};
+			};
+		};
+	};
+
+	vph_pwr: vph-pwr-regulator {
+		compatible = "regulator-fixed";
+		regulator-name = "vph_pwr";
+		regulator-always-on;
+		regulator-boot-on;
+	};
+};
+
+&qusb2phy {
+	status = "okay";
+
+	vdda-pll-supply = <&vreg_l12a_1p8>;
+	vdda-phy-dpdm-supply = <&vreg_l24a_3p075>;
+};
+
+&rpm_requests {
+	pm8998-regulators {
+		compatible = "qcom,rpm-pm8998-regulators";
+
+		vdd_s1-supply = <&vph_pwr>;
+		vdd_s2-supply = <&vph_pwr>;
+		vdd_s3-supply = <&vph_pwr>;
+		vdd_s4-supply = <&vph_pwr>;
+		vdd_s5-supply = <&vph_pwr>;
+		vdd_s6-supply = <&vph_pwr>;
+		vdd_s7-supply = <&vph_pwr>;
+		vdd_s8-supply = <&vph_pwr>;
+		vdd_s9-supply = <&vph_pwr>;
+		vdd_s10-supply = <&vph_pwr>;
+		vdd_s11-supply = <&vph_pwr>;
+		vdd_s12-supply = <&vph_pwr>;
+		vdd_s13-supply = <&vph_pwr>;
+		vdd_l1_l27-supply = <&vreg_s7a_1p025>;
+		vdd_l2_l8_l17-supply = <&vreg_s3a_1p35>;
+		vdd_l3_l11-supply = <&vreg_s7a_1p025>;
+		vdd_l4_l5-supply = <&vreg_s7a_1p025>;
+		vdd_l6-supply = <&vreg_s5a_2p04>;
+		vdd_l7_l12_l14_l15-supply = <&vreg_s5a_2p04>;
+		vdd_l9-supply = <&vph_pwr>;
+		vdd_l10_l23_l25-supply = <&vph_pwr>;
+		vdd_l13_l19_l21-supply = <&vph_pwr>;
+		vdd_l16_l28-supply = <&vph_pwr>;
+		vdd_l18_l22-supply = <&vph_pwr>;
+		vdd_l20_l24-supply = <&vph_pwr>;
+		vdd_l26-supply = <&vreg_s3a_1p35>;
+		vdd_lvs1_lvs2-supply = <&vreg_s4a_1p8>;
+
+		vreg_s3a_1p35: s3 {
+			regulator-min-microvolt = <1352000>;
+			regulator-max-microvolt = <1352000>;
+		};
+		vreg_s4a_1p8: s4 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+			regulator-allow-set-load;
+		};
+		vreg_s5a_2p04: s5 {
+			regulator-min-microvolt = <1904000>;
+			regulator-max-microvolt = <2040000>;
+		};
+		vreg_s7a_1p025: s7 {
+			regulator-min-microvolt = <900000>;
+			regulator-max-microvolt = <1028000>;
+		};
+		vreg_l1a_0p875: l1 {
+			regulator-min-microvolt = <880000>;
+			regulator-max-microvolt = <880000>;
+			regulator-allow-set-load;
+		};
+		vreg_l2a_1p2: l2 {
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+			regulator-allow-set-load;
+		};
+		vreg_l3a_1p0: l3 {
+			regulator-min-microvolt = <1000000>;
+			regulator-max-microvolt = <1000000>;
+		};
+		vreg_l5a_0p8: l5 {
+			regulator-min-microvolt = <800000>;
+			regulator-max-microvolt = <800000>;
+		};
+		vreg_l6a_1p8: l6 {
+			regulator-min-microvolt = <1808000>;
+			regulator-max-microvolt = <1808000>;
+		};
+		vreg_l7a_1p8: l7 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+		vreg_l8a_1p2: l8 {
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+		};
+		vreg_l9a_1p8: l9 {
+			regulator-min-microvolt = <1808000>;
+			regulator-max-microvolt = <2960000>;
+		};
+		vreg_l10a_1p8: l10 {
+			regulator-min-microvolt = <1808000>;
+			regulator-max-microvolt = <2960000>;
+		};
+		vreg_l11a_1p0: l11 {
+			regulator-min-microvolt = <1000000>;
+			regulator-max-microvolt = <1000000>;
+		};
+		vreg_l12a_1p8: l12 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+		vreg_l13a_2p95: l13 {
+			regulator-min-microvolt = <1808000>;
+			regulator-max-microvolt = <2960000>;
+		};
+		vreg_l14a_1p88: l14 {
+			regulator-min-microvolt = <1880000>;
+			regulator-max-microvolt = <1880000>;
+		};
+		vreg_15a_1p8: l15 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+		vreg_l16a_2p7: l16 {
+			regulator-min-microvolt = <2704000>;
+			regulator-max-microvolt = <2704000>;
+		};
+		vreg_l17a_1p3: l17 {
+			regulator-min-microvolt = <1304000>;
+			regulator-max-microvolt = <1304000>;
+		};
+		vreg_l18a_2p7: l18 {
+			regulator-min-microvolt = <2704000>;
+			regulator-max-microvolt = <2704000>;
+		};
+		vreg_l19a_3p0: l19 {
+			regulator-min-microvolt = <3008000>;
+			regulator-max-microvolt = <3008000>;
+		};
+		vreg_l20a_2p95: l20 {
+			regulator-min-microvolt = <2960000>;
+			regulator-max-microvolt = <2960000>;
+			regulator-allow-set-load;
+		};
+		vreg_l21a_2p95: l21 {
+			regulator-min-microvolt = <2960000>;
+			regulator-max-microvolt = <2960000>;
+			regulator-allow-set-load;
+			regulator-system-load = <800000>;
+		};
+		vreg_l22a_2p85: l22 {
+			regulator-min-microvolt = <2864000>;
+			regulator-max-microvolt = <2864000>;
+		};
+		vreg_l23a_3p3: l23 {
+			regulator-min-microvolt = <3312000>;
+			regulator-max-microvolt = <3312000>;
+		};
+		vreg_l24a_3p075: l24 {
+			regulator-min-microvolt = <3088000>;
+			regulator-max-microvolt = <3088000>;
+		};
+		vreg_l25a_3p3: l25 {
+			regulator-min-microvolt = <3104000>;
+			regulator-max-microvolt = <3312000>;
+		};
+		vreg_l26a_1p2: l26 {
+			regulator-min-microvolt = <1200000>;
+			regulator-max-microvolt = <1200000>;
+		};
+		vreg_l28_3p0: l28 {
+			regulator-min-microvolt = <3008000>;
+			regulator-max-microvolt = <3008000>;
+		};
+
+		vreg_lvs1a_1p8: lvs1 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+
+		vreg_lvs2a_1p8: lvs2 {
+			regulator-min-microvolt = <1800000>;
+			regulator-max-microvolt = <1800000>;
+		};
+
+	};
+};
+
+&tlmm {
+	gpio-reserved-ranges = <0 4>, <81 4>;
+
+	touchpad: touchpad {
+		config {
+			pins = "gpio123";
+			bias-pull-up;           /* pull up */
+		};
+	};
+};
+
+&sdhc2 {
+	status = "okay";
+
+	vmmc-supply = <&vreg_l21a_2p95>;
+	vqmmc-supply = <&vreg_l13a_2p95>;
+
+	pinctrl-names = "default", "sleep";
+	pinctrl-0 = <&sdc2_clk_on  &sdc2_cmd_on  &sdc2_data_on  &sdc2_cd_on>;
+	pinctrl-1 = <&sdc2_clk_off &sdc2_cmd_off &sdc2_data_off &sdc2_cd_off>;
+};
+
+&usb3 {
+	status = "okay";
+};
+
+&usb3_dwc3 {
+	dr_mode = "host"; /* Force to host until we have Type-C hooked up */
+};
+
+&usb3phy {
+	status = "okay";
+
+	vdda-phy-supply = <&vreg_l1a_0p875>;
+	vdda-pll-supply = <&vreg_l2a_1p2>;
+};
diff --git a/arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dts b/arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dts
new file mode 100644
index 000000000000..c2b43f7ed137
--- /dev/null
+++ b/arch/arm64/boot/dts/qcom/msm8998-lenovo-miix-630.dts
@@ -0,0 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2019, Jeffrey Hugo. All rights reserved. */
+
+/dts-v1/;
+
+#include "msm8998-clamshell.dtsi"
+
+/ {
+	model = "Lenovo Miix 630";
+	compatible = "lenovo,miix-630", "qcom,msm8998";
+};
+
+&blsp1_i2c6 {
+	status = "okay";
+
+	keyboard@3a {
+		compatible = "elan,combo400-i2c", "hid-over-i2c";
+		interrupt-parent = <&tlmm>;
+		interrupts = <0x79 IRQ_TYPE_LEVEL_LOW>;
+		reg = <0x3a>;
+		hid-descr-addr = <0x0001>;
+
+		pinctrl-names = "default";
+		pinctrl-0 = <&touchpad>;
+	};
+};
+
+&sdhc2 {
+	cd-gpios = <&tlmm 95 GPIO_ACTIVE_HIGH>;
+};
-- 
2.17.1

^ permalink raw reply related

* [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Arnd Bergmann @ 2019-04-15 19:25 UTC (permalink / raw)
  To: Hartley Sweeten, Alexander Sverdlin
  Cc: Linus Walleij, Arnd Bergmann, Dmitry Torokhov, Stefan Agner,
	Enric Balletbo i Serra, Guenter Roeck, linux-input, linux-kernel
In-Reply-To: <20190415192734.935387-1-arnd@arndb.de>

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.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 drivers/input/keyboard/Kconfig              | 2 +-
 drivers/input/keyboard/ep93xx_keypad.c      | 5 +----
 include/linux/platform_data/keypad-ep93xx.h | 4 ++--
 3 files changed, 4 insertions(+), 7 deletions(-)

diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a878351f1643..b373f3274542 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -194,7 +194,7 @@ config KEYBOARD_LKKBD
 
 config KEYBOARD_EP93XX
 	tristate "EP93xx Matrix Keypad support"
-	depends on ARCH_EP93XX
+	depends on ARCH_EP93XX || COMPILE_TEST
 	select INPUT_MATRIXKMAP
 	help
 	  Say Y here to enable the matrix keypad on the Cirrus EP93XX.
diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c
index f77b295e0123..71472f6257c0 100644
--- a/drivers/input/keyboard/ep93xx_keypad.c
+++ b/drivers/input/keyboard/ep93xx_keypad.c
@@ -137,10 +137,7 @@ static void ep93xx_keypad_config(struct ep93xx_keypad *keypad)
 	struct ep93xx_keypad_platform_data *pdata = keypad->pdata;
 	unsigned int val = 0;
 
-	if (pdata->flags & EP93XX_KEYPAD_KDIV)
-		clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV4);
-	else
-		clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV16);
+	clk_set_rate(keypad->clk, pdata->clk_rate);
 
 	if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY)
 		val |= KEY_INIT_DIS3KY;
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 */
 
 /**
  * struct ep93xx_keypad_platform_data - platform specific device structure
@@ -24,6 +23,7 @@ struct ep93xx_keypad_platform_data {
 	unsigned int	debounce;
 	unsigned int	prescale;
 	unsigned int	flags;
+	unsigned int	clk_rate;
 };
 
 #define EP93XX_MATRIX_ROWS		(8)
-- 
2.20.0

^ permalink raw reply related

* [PATCH 3/4] ARM: ep93xx: move pinctrl interfaces into include/linux/soc
From: Arnd Bergmann @ 2019-04-15 19:25 UTC (permalink / raw)
  To: Hartley Sweeten, Alexander Sverdlin
  Cc: Jens Axboe, linux-pwm, alsa-devel, Arnd Bergmann,
	Bartlomiej Zolnierkiewicz, Linus Walleij, Dmitry Torokhov,
	linux-kernel, linux-ide, Thierry Reding, Mark Brown, linux-input,
	Olof Johansson, linux-arm-kernel
In-Reply-To: <20190415192734.935387-1-arnd@arndb.de>

ep93xx does not have a proper pinctrl driver, but does things
ad-hoc through mach/platform.h, which is also used for setting
up the boards.

To avoid using mach/*.h headers completely, let's move the interfaces
into include/linux/soc/. This is far from great, but gets the job
done here, without the need for a proper pinctrl driver.

Signed-off-by: Arnd Bergmann <arnd@arndb.de>
---
 arch/arm/mach-ep93xx/clock.c                 |  1 +
 arch/arm/mach-ep93xx/core.c                  |  2 ++
 arch/arm/mach-ep93xx/include/mach/platform.h | 16 ---------
 drivers/ata/pata_ep93xx.c                    |  2 +-
 drivers/input/keyboard/ep93xx_keypad.c       |  3 +-
 drivers/pwm/pwm-ep93xx.c                     |  2 +-
 include/linux/soc/cirrus/ep93xx.h            | 37 ++++++++++++++++++++
 sound/soc/cirrus/edb93xx.c                   |  2 +-
 sound/soc/cirrus/ep93xx-ac97.c               |  1 +
 sound/soc/cirrus/ep93xx-i2s.c                |  3 +-
 sound/soc/cirrus/simone.c                    |  2 +-
 sound/soc/cirrus/snappercl15.c               |  2 +-
 12 files changed, 48 insertions(+), 25 deletions(-)
 create mode 100644 include/linux/soc/cirrus/ep93xx.h

diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
index d2eee707d27f..9f43362eb62d 100644
--- a/arch/arm/mach-ep93xx/clock.c
+++ b/arch/arm/mach-ep93xx/clock.c
@@ -20,6 +20,7 @@
 #include <linux/io.h>
 #include <linux/spinlock.h>
 #include <linux/clkdev.h>
+#include <linux/soc/cirrus/ep93xx.h>
 
 #include <mach/hardware.h>
 
diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
index 706515faee06..3d245668846d 100644
--- a/arch/arm/mach-ep93xx/core.c
+++ b/arch/arm/mach-ep93xx/core.c
@@ -43,6 +43,8 @@
 #include <linux/platform_data/video-ep93xx.h>
 #include <linux/platform_data/keypad-ep93xx.h>
 #include <linux/platform_data/spi-ep93xx.h>
+#include <linux/soc/cirrus/ep93xx.h>
+
 #include <mach/gpio-ep93xx.h>
 
 #include <asm/mach/arch.h>
diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
index 43446f33c2be..b4045a186239 100644
--- a/arch/arm/mach-ep93xx/include/mach/platform.h
+++ b/arch/arm/mach-ep93xx/include/mach/platform.h
@@ -19,14 +19,6 @@ struct ep93xx_spi_info;
 void ep93xx_map_io(void);
 void ep93xx_init_irq(void);
 
-#define EP93XX_CHIP_REV_D0	3
-#define EP93XX_CHIP_REV_D1	4
-#define EP93XX_CHIP_REV_E0	5
-#define EP93XX_CHIP_REV_E1	6
-#define EP93XX_CHIP_REV_E2	7
-
-unsigned int ep93xx_chip_revision(void);
-
 void ep93xx_register_flash(unsigned int width,
 			   resource_size_t start, resource_size_t size);
 
@@ -36,19 +28,11 @@ void ep93xx_register_spi(struct ep93xx_spi_info *info,
 			 struct spi_board_info *devices, int num);
 void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
 void ep93xx_register_pwm(int pwm0, int pwm1);
-int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
-void ep93xx_pwm_release_gpio(struct platform_device *pdev);
 void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
-int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
-void ep93xx_keypad_release_gpio(struct platform_device *pdev);
 void ep93xx_register_i2s(void);
-int ep93xx_i2s_acquire(void);
-void ep93xx_i2s_release(void);
 void ep93xx_register_ac97(void);
 void ep93xx_register_ide(void);
 void ep93xx_register_adc(void);
-int ep93xx_ide_acquire_gpio(struct platform_device *pdev);
-void ep93xx_ide_release_gpio(struct platform_device *pdev);
 
 struct device *ep93xx_init_devices(void);
 extern void ep93xx_timer_init(void);
diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
index cc6d06c1b2c7..db271b705529 100644
--- a/drivers/ata/pata_ep93xx.c
+++ b/drivers/ata/pata_ep93xx.c
@@ -44,7 +44,7 @@
 #include <linux/ktime.h>
 
 #include <linux/platform_data/dma-ep93xx.h>
-#include <mach/platform.h>
+#include <linux/soc/cirrus/ep93xx.h>
 
 #define DRV_NAME	"ep93xx-ide"
 #define DRV_VERSION	"1.0"
diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c
index 71472f6257c0..575dac52f7b4 100644
--- a/drivers/input/keyboard/ep93xx_keypad.c
+++ b/drivers/input/keyboard/ep93xx_keypad.c
@@ -27,8 +27,7 @@
 #include <linux/io.h>
 #include <linux/input/matrix_keypad.h>
 #include <linux/slab.h>
-
-#include <mach/hardware.h>
+#include <linux/soc/cirrus/ep93xx.h>
 #include <linux/platform_data/keypad-ep93xx.h>
 
 /*
diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c
index bbf10ae02f0e..fa168581e6b8 100644
--- a/drivers/pwm/pwm-ep93xx.c
+++ b/drivers/pwm/pwm-ep93xx.c
@@ -35,7 +35,7 @@
 
 #include <asm/div64.h>
 
-#include <mach/platform.h>	/* for ep93xx_pwm_{acquire,release}_gpio() */
+#include <linux/soc/cirrus/ep93xx.h>	/* for ep93xx_pwm_{acquire,release}_gpio() */
 
 #define EP93XX_PWMx_TERM_COUNT	0x00
 #define EP93XX_PWMx_DUTY_CYCLE	0x04
diff --git a/include/linux/soc/cirrus/ep93xx.h b/include/linux/soc/cirrus/ep93xx.h
new file mode 100644
index 000000000000..56fbe2dc59b1
--- /dev/null
+++ b/include/linux/soc/cirrus/ep93xx.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _SOC_EP93XX_H
+#define _SOC_EP93XX_H
+
+struct platform_device;
+
+#define EP93XX_CHIP_REV_D0	3
+#define EP93XX_CHIP_REV_D1	4
+#define EP93XX_CHIP_REV_E0	5
+#define EP93XX_CHIP_REV_E1	6
+#define EP93XX_CHIP_REV_E2	7
+
+#ifdef CONFIG_ARCH_EP93XX
+int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
+void ep93xx_pwm_release_gpio(struct platform_device *pdev);
+int ep93xx_ide_acquire_gpio(struct platform_device *pdev);
+void ep93xx_ide_release_gpio(struct platform_device *pdev);
+int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
+void ep93xx_keypad_release_gpio(struct platform_device *pdev);
+int ep93xx_i2s_acquire(void);
+void ep93xx_i2s_release(void);
+unsigned int ep93xx_chip_revision(void);
+
+#else
+static inline int ep93xx_pwm_acquire_gpio(struct platform_device *pdev) { return 0; }
+static inline void ep93xx_pwm_release_gpio(struct platform_device *pdev) {}
+static inline int ep93xx_ide_acquire_gpio(struct platform_device *pdev) { return 0; }
+static inline void ep93xx_ide_release_gpio(struct platform_device *pdev) {}
+static inline int ep93xx_keypad_acquire_gpio(struct platform_device *pdev) { return 0; }
+static inline void ep93xx_keypad_release_gpio(struct platform_device *pdev) {}
+static inline int ep93xx_i2s_acquire(void) { return 0; }
+static inline void ep93xx_i2s_release(void) {}
+static inline unsigned int ep93xx_chip_revision(void) { return 0; }
+
+#endif
+
+#endif
diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
index 3d011abaa266..f678b4c1514a 100644
--- a/sound/soc/cirrus/edb93xx.c
+++ b/sound/soc/cirrus/edb93xx.c
@@ -22,11 +22,11 @@
 #include <linux/platform_device.h>
 #include <linux/gpio.h>
 #include <linux/module.h>
+#include <linux/soc/cirrus/ep93xx.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 #include <asm/mach-types.h>
-#include <mach/hardware.h>
 
 static int edb93xx_hw_params(struct snd_pcm_substream *substream,
 			     struct snd_pcm_hw_params *params)
diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
index cd5a939ad608..c6bc447429af 100644
--- a/sound/soc/cirrus/ep93xx-ac97.c
+++ b/sound/soc/cirrus/ep93xx-ac97.c
@@ -24,6 +24,7 @@
 #include <sound/soc.h>
 
 #include <linux/platform_data/dma-ep93xx.h>
+#include <linux/soc/cirrus/ep93xx.h>
 
 #include "ep93xx-pcm.h"
 
diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
index 0918c5da575a..beab7c516855 100644
--- a/sound/soc/cirrus/ep93xx-i2s.c
+++ b/sound/soc/cirrus/ep93xx-i2s.c
@@ -27,9 +27,8 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include <mach/hardware.h>
-#include <mach/ep93xx-regs.h>
 #include <linux/platform_data/dma-ep93xx.h>
+#include <linux/soc/cirrus/ep93xx.h>
 
 #include "ep93xx-pcm.h"
 
diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c
index 1ec661834e5a..cb850530331b 100644
--- a/sound/soc/cirrus/simone.c
+++ b/sound/soc/cirrus/simone.c
@@ -13,13 +13,13 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/soc/cirrus/ep93xx.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
-#include <mach/hardware.h>
 
 static struct snd_soc_dai_link simone_dai = {
 	.name		= "AC97",
diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
index 11ff7b2672b2..dea4909154c8 100644
--- a/sound/soc/cirrus/snappercl15.c
+++ b/sound/soc/cirrus/snappercl15.c
@@ -13,12 +13,12 @@
 
 #include <linux/platform_device.h>
 #include <linux/module.h>
+#include <linux/soc/cirrus/ep93xx.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
 
 #include <asm/mach-types.h>
-#include <mach/hardware.h>
 
 #include "../codecs/tlv320aic23.h"
 
-- 
2.20.0

^ permalink raw reply related

* RE: [PATCH 2/4] ARM: ep93xx: keypad: stop using mach/platform.h
From: Hartley Sweeten @ 2019-04-15 19:39 UTC (permalink / raw)
  To: Arnd Bergmann, Alexander Sverdlin
  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: <20190415192734.935387-2-arnd@arndb.de>

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.

Hartley

^ permalink raw reply

* Re: [PATCH 3/4] ARM: ep93xx: move pinctrl interfaces into include/linux/soc
From: Alexander Sverdlin @ 2019-04-15 19:41 UTC (permalink / raw)
  To: Arnd Bergmann, Hartley Sweeten
  Cc: Linus Walleij, Bartlomiej Zolnierkiewicz, Jens Axboe,
	Dmitry Torokhov, Thierry Reding, Mark Brown, Olof Johansson,
	linux-arm-kernel, linux-kernel, linux-ide, linux-input, linux-pwm,
	alsa-devel
In-Reply-To: <20190415192734.935387-3-arnd@arndb.de>

On 15/04/2019 21:25, Arnd Bergmann wrote:
> ep93xx does not have a proper pinctrl driver, but does things
> ad-hoc through mach/platform.h, which is also used for setting
> up the boards.
> 
> To avoid using mach/*.h headers completely, let's move the interfaces
> into include/linux/soc/. This is far from great, but gets the job
> done here, without the need for a proper pinctrl driver.

Acked-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>

> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
>  arch/arm/mach-ep93xx/clock.c                 |  1 +
>  arch/arm/mach-ep93xx/core.c                  |  2 ++
>  arch/arm/mach-ep93xx/include/mach/platform.h | 16 ---------
>  drivers/ata/pata_ep93xx.c                    |  2 +-
>  drivers/input/keyboard/ep93xx_keypad.c       |  3 +-
>  drivers/pwm/pwm-ep93xx.c                     |  2 +-
>  include/linux/soc/cirrus/ep93xx.h            | 37 ++++++++++++++++++++
>  sound/soc/cirrus/edb93xx.c                   |  2 +-
>  sound/soc/cirrus/ep93xx-ac97.c               |  1 +
>  sound/soc/cirrus/ep93xx-i2s.c                |  3 +-
>  sound/soc/cirrus/simone.c                    |  2 +-
>  sound/soc/cirrus/snappercl15.c               |  2 +-
>  12 files changed, 48 insertions(+), 25 deletions(-)
>  create mode 100644 include/linux/soc/cirrus/ep93xx.h
> 
> diff --git a/arch/arm/mach-ep93xx/clock.c b/arch/arm/mach-ep93xx/clock.c
> index d2eee707d27f..9f43362eb62d 100644
> --- a/arch/arm/mach-ep93xx/clock.c
> +++ b/arch/arm/mach-ep93xx/clock.c
> @@ -20,6 +20,7 @@
>  #include <linux/io.h>
>  #include <linux/spinlock.h>
>  #include <linux/clkdev.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  
>  #include <mach/hardware.h>
>  
> diff --git a/arch/arm/mach-ep93xx/core.c b/arch/arm/mach-ep93xx/core.c
> index 706515faee06..3d245668846d 100644
> --- a/arch/arm/mach-ep93xx/core.c
> +++ b/arch/arm/mach-ep93xx/core.c
> @@ -43,6 +43,8 @@
>  #include <linux/platform_data/video-ep93xx.h>
>  #include <linux/platform_data/keypad-ep93xx.h>
>  #include <linux/platform_data/spi-ep93xx.h>
> +#include <linux/soc/cirrus/ep93xx.h>
> +
>  #include <mach/gpio-ep93xx.h>
>  
>  #include <asm/mach/arch.h>
> diff --git a/arch/arm/mach-ep93xx/include/mach/platform.h b/arch/arm/mach-ep93xx/include/mach/platform.h
> index 43446f33c2be..b4045a186239 100644
> --- a/arch/arm/mach-ep93xx/include/mach/platform.h
> +++ b/arch/arm/mach-ep93xx/include/mach/platform.h
> @@ -19,14 +19,6 @@ struct ep93xx_spi_info;
>  void ep93xx_map_io(void);
>  void ep93xx_init_irq(void);
>  
> -#define EP93XX_CHIP_REV_D0	3
> -#define EP93XX_CHIP_REV_D1	4
> -#define EP93XX_CHIP_REV_E0	5
> -#define EP93XX_CHIP_REV_E1	6
> -#define EP93XX_CHIP_REV_E2	7
> -
> -unsigned int ep93xx_chip_revision(void);
> -
>  void ep93xx_register_flash(unsigned int width,
>  			   resource_size_t start, resource_size_t size);
>  
> @@ -36,19 +28,11 @@ void ep93xx_register_spi(struct ep93xx_spi_info *info,
>  			 struct spi_board_info *devices, int num);
>  void ep93xx_register_fb(struct ep93xxfb_mach_info *data);
>  void ep93xx_register_pwm(int pwm0, int pwm1);
> -int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
> -void ep93xx_pwm_release_gpio(struct platform_device *pdev);
>  void ep93xx_register_keypad(struct ep93xx_keypad_platform_data *data);
> -int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
> -void ep93xx_keypad_release_gpio(struct platform_device *pdev);
>  void ep93xx_register_i2s(void);
> -int ep93xx_i2s_acquire(void);
> -void ep93xx_i2s_release(void);
>  void ep93xx_register_ac97(void);
>  void ep93xx_register_ide(void);
>  void ep93xx_register_adc(void);
> -int ep93xx_ide_acquire_gpio(struct platform_device *pdev);
> -void ep93xx_ide_release_gpio(struct platform_device *pdev);
>  
>  struct device *ep93xx_init_devices(void);
>  extern void ep93xx_timer_init(void);
> diff --git a/drivers/ata/pata_ep93xx.c b/drivers/ata/pata_ep93xx.c
> index cc6d06c1b2c7..db271b705529 100644
> --- a/drivers/ata/pata_ep93xx.c
> +++ b/drivers/ata/pata_ep93xx.c
> @@ -44,7 +44,7 @@
>  #include <linux/ktime.h>
>  
>  #include <linux/platform_data/dma-ep93xx.h>
> -#include <mach/platform.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  
>  #define DRV_NAME	"ep93xx-ide"
>  #define DRV_VERSION	"1.0"
> diff --git a/drivers/input/keyboard/ep93xx_keypad.c b/drivers/input/keyboard/ep93xx_keypad.c
> index 71472f6257c0..575dac52f7b4 100644
> --- a/drivers/input/keyboard/ep93xx_keypad.c
> +++ b/drivers/input/keyboard/ep93xx_keypad.c
> @@ -27,8 +27,7 @@
>  #include <linux/io.h>
>  #include <linux/input/matrix_keypad.h>
>  #include <linux/slab.h>
> -
> -#include <mach/hardware.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  #include <linux/platform_data/keypad-ep93xx.h>
>  
>  /*
> diff --git a/drivers/pwm/pwm-ep93xx.c b/drivers/pwm/pwm-ep93xx.c
> index bbf10ae02f0e..fa168581e6b8 100644
> --- a/drivers/pwm/pwm-ep93xx.c
> +++ b/drivers/pwm/pwm-ep93xx.c
> @@ -35,7 +35,7 @@
>  
>  #include <asm/div64.h>
>  
> -#include <mach/platform.h>	/* for ep93xx_pwm_{acquire,release}_gpio() */
> +#include <linux/soc/cirrus/ep93xx.h>	/* for ep93xx_pwm_{acquire,release}_gpio() */
>  
>  #define EP93XX_PWMx_TERM_COUNT	0x00
>  #define EP93XX_PWMx_DUTY_CYCLE	0x04
> diff --git a/include/linux/soc/cirrus/ep93xx.h b/include/linux/soc/cirrus/ep93xx.h
> new file mode 100644
> index 000000000000..56fbe2dc59b1
> --- /dev/null
> +++ b/include/linux/soc/cirrus/ep93xx.h
> @@ -0,0 +1,37 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _SOC_EP93XX_H
> +#define _SOC_EP93XX_H
> +
> +struct platform_device;
> +
> +#define EP93XX_CHIP_REV_D0	3
> +#define EP93XX_CHIP_REV_D1	4
> +#define EP93XX_CHIP_REV_E0	5
> +#define EP93XX_CHIP_REV_E1	6
> +#define EP93XX_CHIP_REV_E2	7
> +
> +#ifdef CONFIG_ARCH_EP93XX
> +int ep93xx_pwm_acquire_gpio(struct platform_device *pdev);
> +void ep93xx_pwm_release_gpio(struct platform_device *pdev);
> +int ep93xx_ide_acquire_gpio(struct platform_device *pdev);
> +void ep93xx_ide_release_gpio(struct platform_device *pdev);
> +int ep93xx_keypad_acquire_gpio(struct platform_device *pdev);
> +void ep93xx_keypad_release_gpio(struct platform_device *pdev);
> +int ep93xx_i2s_acquire(void);
> +void ep93xx_i2s_release(void);
> +unsigned int ep93xx_chip_revision(void);
> +
> +#else
> +static inline int ep93xx_pwm_acquire_gpio(struct platform_device *pdev) { return 0; }
> +static inline void ep93xx_pwm_release_gpio(struct platform_device *pdev) {}
> +static inline int ep93xx_ide_acquire_gpio(struct platform_device *pdev) { return 0; }
> +static inline void ep93xx_ide_release_gpio(struct platform_device *pdev) {}
> +static inline int ep93xx_keypad_acquire_gpio(struct platform_device *pdev) { return 0; }
> +static inline void ep93xx_keypad_release_gpio(struct platform_device *pdev) {}
> +static inline int ep93xx_i2s_acquire(void) { return 0; }
> +static inline void ep93xx_i2s_release(void) {}
> +static inline unsigned int ep93xx_chip_revision(void) { return 0; }
> +
> +#endif
> +
> +#endif
> diff --git a/sound/soc/cirrus/edb93xx.c b/sound/soc/cirrus/edb93xx.c
> index 3d011abaa266..f678b4c1514a 100644
> --- a/sound/soc/cirrus/edb93xx.c
> +++ b/sound/soc/cirrus/edb93xx.c
> @@ -22,11 +22,11 @@
>  #include <linux/platform_device.h>
>  #include <linux/gpio.h>
>  #include <linux/module.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  #include <sound/core.h>
>  #include <sound/pcm.h>
>  #include <sound/soc.h>
>  #include <asm/mach-types.h>
> -#include <mach/hardware.h>
>  
>  static int edb93xx_hw_params(struct snd_pcm_substream *substream,
>  			     struct snd_pcm_hw_params *params)
> diff --git a/sound/soc/cirrus/ep93xx-ac97.c b/sound/soc/cirrus/ep93xx-ac97.c
> index cd5a939ad608..c6bc447429af 100644
> --- a/sound/soc/cirrus/ep93xx-ac97.c
> +++ b/sound/soc/cirrus/ep93xx-ac97.c
> @@ -24,6 +24,7 @@
>  #include <sound/soc.h>
>  
>  #include <linux/platform_data/dma-ep93xx.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  
>  #include "ep93xx-pcm.h"
>  
> diff --git a/sound/soc/cirrus/ep93xx-i2s.c b/sound/soc/cirrus/ep93xx-i2s.c
> index 0918c5da575a..beab7c516855 100644
> --- a/sound/soc/cirrus/ep93xx-i2s.c
> +++ b/sound/soc/cirrus/ep93xx-i2s.c
> @@ -27,9 +27,8 @@
>  #include <sound/initval.h>
>  #include <sound/soc.h>
>  
> -#include <mach/hardware.h>
> -#include <mach/ep93xx-regs.h>
>  #include <linux/platform_data/dma-ep93xx.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  
>  #include "ep93xx-pcm.h"
>  
> diff --git a/sound/soc/cirrus/simone.c b/sound/soc/cirrus/simone.c
> index 1ec661834e5a..cb850530331b 100644
> --- a/sound/soc/cirrus/simone.c
> +++ b/sound/soc/cirrus/simone.c
> @@ -13,13 +13,13 @@
>  #include <linux/init.h>
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  
>  #include <sound/core.h>
>  #include <sound/pcm.h>
>  #include <sound/soc.h>
>  
>  #include <asm/mach-types.h>
> -#include <mach/hardware.h>
>  
>  static struct snd_soc_dai_link simone_dai = {
>  	.name		= "AC97",
> diff --git a/sound/soc/cirrus/snappercl15.c b/sound/soc/cirrus/snappercl15.c
> index 11ff7b2672b2..dea4909154c8 100644
> --- a/sound/soc/cirrus/snappercl15.c
> +++ b/sound/soc/cirrus/snappercl15.c
> @@ -13,12 +13,12 @@
>  
>  #include <linux/platform_device.h>
>  #include <linux/module.h>
> +#include <linux/soc/cirrus/ep93xx.h>
>  #include <sound/core.h>
>  #include <sound/pcm.h>
>  #include <sound/soc.h>
>  
>  #include <asm/mach-types.h>
> -#include <mach/hardware.h>
>  
>  #include "../codecs/tlv320aic23.h"
>  
> 

^ 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


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox