* Re: [PATCH 1/1] Add virtio-input driver.
From: David Herrmann @ 2015-03-20 9:55 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b,
virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
mst-H+wXaHxf7aLQT0dZR+AlfA, Rusty Russell, open list,
open list:ABI/API, Dmitry Torokhov
In-Reply-To: <1426844885.32097.36.camel-3OfP5uLMi4C46o+2HkPkLj4oCIwMql/M@public.gmane.org>
Hi
On Fri, Mar 20, 2015 at 10:48 AM, Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>
> Hi,
>
>> > +static int virtinput_send_status(struct virtio_input *vi,
>> > + u16 type, u16 code, s32 value)
>> > +{
>> > + struct virtio_input_event *stsbuf;
>> > + struct scatterlist sg[1];
>> > +
>> > + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
>> > + if (!stsbuf)
>> > + return -ENOMEM;
>> > +
>> > + stsbuf->type = cpu_to_le16(type);
>> > + stsbuf->code = cpu_to_le16(code);
>> > + stsbuf->value = cpu_to_le32(value);
>> > + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
>> > + virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
>> > + virtqueue_kick(vi->sts);
>>
>> GFP_ATOMIC, eww. But everyone does that for input_event() callbacks..
>
> Yea, did it this way because I saw it elsewhere.
>
>> we should fix that for user-space input one day.
>
> Sounds like I have to use GFP_ATOMIC and can't switch to GFP_KERNEL,
> correct?
You cannot use GFP_KERNEL in this context, correct. GFP_ATOMIC is also
what all HID backends do, so it's fine here.
>> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
>> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
>> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
>> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
>> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
>>
>> abs.resolution is missing. Please add it, we really also need to add
>> it to uinput one day.
>
> Ok. How should I handle cases where the resolution is either not known
> or not fixed? Just leave it zero?
Leave it 0, which is what you already do by not assigning it.
>> > + vi->idev->name = vi->name;
>> > + vi->idev->phys = vi->phys;
>>
>> Can you set vi->idev->uniq to the virtio-bus path?
>>
>> > + vi->idev->id.bustype = BUS_VIRTUAL;
>> > + vi->idev->id.vendor = 0x0001;
>> > + vi->idev->id.product = 0x0001;
>> > + vi->idev->id.version = 0x0100;
>>
>> Please don't hardcode those. All user-space based interaction with
>> input-devices relies on those IDs. Can we retrieve it from the host
>> just like the name?
>
> Yes, we can.
>
> There will be emulated devices, i.e. the input coming from
> vnc/gtk/whatever will be sent to the virtio devices (instead of ps/2 or
> usb). For these we should probably have fixed IDs per device. There
> are keyboard/mouse/tablet at the moment. Suggestions how to pick IDs?
>
> There will also be pass-through support, i.e. qemu
> opening /dev/input/event<nr> and forwarding everything to the guest.
> How should that be handled best? Copy all four from the host? Even
> though the bustype is BUS_USB? Not sure this actually improves things
> because the guest can match the device, or whenever this confuses apps
> due to BUS_USB being applied to virtio devices ...
Lemme give an example: We have databases in user-space, that allow
applications to figure out the mouse DPI values of a device. Those
databases match on all four, bus+vid+pid+ver (sometimes even more,
like name and dmi). If one of those is not forwarded, it will not be
detected.
I'd like to see all four forwarded from the host. I'd be fine with
"bus" being set to VIRTUAL, but I'm not sure why that would be a good
thing to do?
Thanks
David
^ permalink raw reply
* Re: [PATCH 1/1] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-20 10:28 UTC (permalink / raw)
To: Michael S. Tsirkin
Cc: virtio-dev, open list, open list:ABI/API, virtualization
In-Reply-To: <20150319123940-mutt-send-email-mst@redhat.com>
Hi,
> > +static int virtinput_send_status(struct virtio_input *vi,
> > + u16 type, u16 code, s32 value)
> > +{
> > + struct virtio_input_event *stsbuf;
> > + struct scatterlist sg[1];
> > +
> > + stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
> > + if (!stsbuf)
> > + return -ENOMEM;
>
> Does this return an error to userspace?
> If so it's not a good idea I think, GFP_ATOMIC failures are
> transient conditions and should not be reported
> to userspace.
> Can use GFP_KERNEL here?
Waiting for an answer from the ioput guys here.
> > +
> > + stsbuf->type = cpu_to_le16(type);
> > + stsbuf->code = cpu_to_le16(code);
> > + stsbuf->value = cpu_to_le32(value);
> > + sg_init_one(sg, stsbuf, sizeof(*stsbuf));
> > + virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
>
> This can fail if queue is full. What prevents this from happening?
Nothing. It's highly unlikely though given the throughput we have for
input devices, not sure it is that useful to put too much effort into
this. Except for freeing stsbuf in the error case.
> > + virtqueue_kick(vi->sts);
>
> Also what prevents multiple virtinput_send_status calls
> from racing with each other? Is there locking at a higher level?
I think input_dev->event_lock does this.
> > +static void virtinput_recv_status(struct virtqueue *vq)
> > +{
> > + struct virtio_input *vi = vq->vdev->priv;
> > + struct virtio_input_event *stsbuf;
> > + unsigned int len;
> > +
> > + while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
>
> looks like this get_buf can race with add above.
Yes, it can.
> Need some locking.
I'll add it.
> Also pls avoid != NULL, removing it makes code less verbose.
>
> > + kfree(stsbuf);
> > + virtqueue_kick(vq);
>
> Why are you kicking here?
Hmm, it is pointless indeed.
> > + if (select == VIRTIO_INPUT_CFG_EV_BITS)
> > + set_bit(subsel, vi->idev->evbit);
> > + for (bit = 0; bit < bitcount; bit++) {
> > + if ((bit % 8) == 0)
> > + virtio_cread(vi->vdev, struct virtio_input_config,
> > + u.bitmap[bit/8], &cfg);
>
> coding style violations above. you need spaces around ops like / and *.
> Please run checkpatch.pl
>
> > + if (cfg & (1 << (bit % 8)))
> > + set_bit(bit, bits);
>
> what if not set? does something clear the mask?
kzalloc?
> > + }
>
> doesn't above just implement bitmap_copy or bitmap_or?
Not fully sure how bitmaps are defined. virtio has a stream of bytes,
first byte carries bits 0-7, second 8-15 etc. linux kernel bitmaps ops
are operating on longs, and native byteorder longs would be something
else ...
> > + size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
> > + virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
>
> you read le field into u32 value.
> Please run sparse on this code. you will get a ton
> of warnings. Same error appears elsewhere.
Indeed. IIRC that wasn't the case a while back. Guess those bitwise
annotations have been added with the virtio 1.0 patches?
In any case I'll fix it up.
> > +static int virtinput_probe(struct virtio_device *vdev)
> > +{
> > + struct virtio_input *vi;
> > + size_t size;
> > + int abs, err;
>
> How about checking VERSION_1 and bailing out of not there?
I don't think this is needed. There isn't a hard dependency on virtio
1.0. It's just that config space is relatively large and because of
that I want it be 1.0 on the host (qemu) side to not allocate large
portions of I/O address space for the legacy virtio pci bar.
> > + vi->idev->name = vi->name;
> > + vi->idev->phys = vi->phys;
> > + vi->idev->id.bustype = BUS_VIRTUAL;
> > + vi->idev->id.vendor = 0x0001;
> > + vi->idev->id.product = 0x0001;
> > + vi->idev->id.version = 0x0100;
>
> Add comments explaining why these #s make sense?
See other subthread, will be changed to be host-provided (like name).
> > + err = input_register_device(vi->idev);
>
> Once you do this, virtinput_status can get called,
> and that will kick, correct?
Correct.
> If so, you must call device_ready before this.
Ok.
> > + if (err)
> > + goto out4;
> > +
> > + return 0;
> > +
> > +out4:
> > + input_free_device(vi->idev);
> > +out3:
> > + vdev->config->del_vqs(vdev);
> > +out2:
> > + kfree(vi);
> > +out1:
> > + return err;
>
> free on error is out of order with initialization.
> Might lead to leaks or other bugs.
> Also - can you name labels something sensible pls?
> out is usually for exiting on success too...
> E.g. out4 -> err_register etc.
Will fix.
> > +static void virtinput_remove(struct virtio_device *vdev)
> > +{
> > + struct virtio_input *vi = vdev->priv;
> > +
> > + input_unregister_device(vi->idev);
> > + vdev->config->reset(vdev);
>
> You don't really need a reset if you just to del_vqs.
> People do this if they want to prevent interrupts
> without deleting vqs.
Ok.
> > + vdev->config->del_vqs(vdev);
> > + kfree(vi);
>
> free_device seems to be missing?
Indeed, good catch.
thanks,
Gerd
^ permalink raw reply
* Re: [PATCH 1/1] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-20 10:36 UTC (permalink / raw)
To: David Herrmann
Cc: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b,
virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
mst-H+wXaHxf7aLQT0dZR+AlfA, Rusty Russell, open list,
open list:ABI/API, Dmitry Torokhov
In-Reply-To: <CANq1E4QLPSK6NVeEx6yihYPdF-XPpXx4rKv0deHwX+s2RzFHCg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi,
> > There will also be pass-through support, i.e. qemu
> > opening /dev/input/event<nr> and forwarding everything to the guest.
> > How should that be handled best? Copy all four from the host? Even
> > though the bustype is BUS_USB? Not sure this actually improves things
> > because the guest can match the device, or whenever this confuses apps
> > due to BUS_USB being applied to virtio devices ...
>
> Lemme give an example: We have databases in user-space, that allow
> applications to figure out the mouse DPI values of a device. Those
> databases match on all four, bus+vid+pid+ver (sometimes even more,
> like name and dmi). If one of those is not forwarded, it will not be
> detected.
Ok, so forward as much as possible.
> I'd like to see all four forwarded from the host. I'd be fine with
> "bus" being set to VIRTUAL, but I'm not sure why that would be a good
> thing to do?
I think for the emulated devices it's fine to use VIRTUAL.
For the passthrough case suspected we could confuse apps because ->phys
points to a virtio device whereas ->type says "I'm usb".
But at least the device database probably doesn't care much about the
physical path I guess, because the mouse is the same no matter which usb
port I plug it in, correct?
cheers,
Gerd
^ permalink raw reply
* Re: [PATCH 1/1] Add virtio-input driver.
From: David Herrmann @ 2015-03-20 10:43 UTC (permalink / raw)
To: Gerd Hoffmann
Cc: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b,
virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
mst-H+wXaHxf7aLQT0dZR+AlfA, Rusty Russell, open list,
open list:ABI/API, Dmitry Torokhov
In-Reply-To: <1426847799.32097.66.camel-3OfP5uLMi4C46o+2HkPkLj4oCIwMql/M@public.gmane.org>
Hi
On Fri, Mar 20, 2015 at 11:36 AM, Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> I'd like to see all four forwarded from the host. I'd be fine with
>> "bus" being set to VIRTUAL, but I'm not sure why that would be a good
>> thing to do?
>
> I think for the emulated devices it's fine to use VIRTUAL.
Yes, on the host side just use BUS_VIRTUAL if you don't have a real bus to set.
> For the passthrough case suspected we could confuse apps because ->phys
> points to a virtio device whereas ->type says "I'm usb".
That's not an issue. The "phys" field hasn't been standardized in any
way that I'm aware of (except on a per-driver basis, maybe).
> But at least the device database probably doesn't care much about the
> physical path I guess, because the mouse is the same no matter which usb
> port I plug it in, correct?
Correct!
Thanks
David
^ permalink raw reply
* [PATCH v4] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 11:20 UTC (permalink / raw)
To: mark.rutland, robh+dt, Pawel.Moll, ijc+devicetree, galak,
dmitry.torokhov, alexander.stein
Cc: hadess, akpm, gregkh, linux-api, devicetree, linux-input,
linux-kernel, Martin Kepplinger, Christoph Muellner
From: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
The MMA8653FC is a low-power, three-axis, capacitive micromachined
accelerometer with 10 bits of resolution with flexible user-programmable
options.
Embedded interrupt functions enable overall power savings, by relieving the
host processor from continuously polling data, for example using the poll()
system call.
The device can be configured to generate wake-up interrupt signals from any
combination of the configurable embedded functions, enabling the MMA8653FC
to monitor events while remaining in a low-power mode during periods of
inactivity.
This driver provides devicetree properties to program the device's behaviour
and a simple, tested and documented sysfs interface. The data sheet and more
information is available on Freescale's website.
Signed-off-by: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
---
patch revision history
......................
v4 changes DT propery names, adds a missing interrupt source and removes
the DT option to set interrupt line active high due to unsuccesful testing
v3 moves the driver from drivers/input/misc to drivers/misc
v2 corrects licensing and commit messages and adds appropriate recipients
.../testing/sysfs-bus-i2c-devices-fsl-mma8653fc | 39 +
.../devicetree/bindings/misc/fsl,mma8653fc.txt | 102 +++
MAINTAINERS | 5 +
drivers/misc/Kconfig | 11 +
drivers/misc/Makefile | 1 +
drivers/misc/mma8653fc.c | 897 +++++++++++++++++++++
6 files changed, 1055 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-bus-i2c-devices-fsl-mma8653fc
create mode 100644 Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt
create mode 100644 drivers/misc/mma8653fc.c
diff --git a/Documentation/ABI/testing/sysfs-bus-i2c-devices-fsl-mma8653fc b/Documentation/ABI/testing/sysfs-bus-i2c-devices-fsl-mma8653fc
new file mode 100644
index 0000000..8172c27
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-i2c-devices-fsl-mma8653fc
@@ -0,0 +1,39 @@
+What: /sys/bus/i2c/drivers/mma8653fc/*/standby
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Write 0 to this in order to turn on the device, and 1 to turn
+ it off. Read to see if it is turned on or off.
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/currentmode
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Reading this provides the current state of the device, read
+ directly from a register. This can be "standby", "wake" or
+ "sleep".
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/position
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Read only. Without interrupts enabled gets current position
+ values by reading. Poll "position" with interrupt conditions
+ set, to get notified; see Documentation/.../fsl,mma8653fc.txt
+
+ position file format:
+ "x y z [landscape/portrait status] [front/back status]"
+
+ x y z values:
+ in mg
+ landscape/portrait status char:
+ r landscape right
+ d portrait down
+ u portrait up
+ l landscape left
+ front/back status char:
+ f front facing
+ b back facing
+
diff --git a/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt b/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt
new file mode 100644
index 0000000..5015813
--- /dev/null
+++ b/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt
@@ -0,0 +1,102 @@
+Freescale MMA8653FC 3-axis Accelerometer
+
+Required properties:
+- compatible
+ "fsl,mma8653fc"
+- reg
+ I2C address
+
+Optional properties:
+
+- interrupt-parent
+ a phandle for the interrupt controller (see
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)
+- interrupts
+ interrupt line to which the chip is connected (active low)
+- int1
+ set to use interrupt line 1, default is line 2
+ the interrupt sources can be routed to one of the two lines
+- ir-freefall-motion-x
+ activate freefall/motion interrupts on x axis
+- ir-freefall-motion-y
+ activate freefall/motion interrupts on y axis
+- ir-freefall-motion-z
+ activate freefall/motion interrupts on z axis
+- irq-threshold
+ 0 < value < 8000: threshold for motion interrupts in mg
+- ir-landscape-portrait
+ activate landscape/portrait interrupts
+- ir-auto-wake
+ activate wake/sleep change interrupts
+- ir-data-ready:
+ activate data-ready interrupts
+ Interrupt events can be activated in any combination.
+- dynamic-range
+ 2, 4, or 8: dynamic measurement range in g, default: 2
+ In ±2 g mode, sensitivity = 256 counts/g.
+ In ±4 g mode, sensitivity = 128 counts/g.
+ In ±8 g mode, sensitivity = 64 counts/g.
+- auto-wake-sleep
+ auto sleep mode (lower frequency)
+- motion-mode
+ use motion mode instead of freefall mode (trigger if >threshold).
+ per default an interrupt occurs if motion values fall below the
+ value set in "threshold" and therefore can detect free fall on the
+ vertical axis (depending on the position of the device).
+ Setting this values inverts the behaviour and an interrupt occurs
+ above the threshold value, so usually activate horizontal axis in
+ this case.
+
+- x-offset
+ 0 < value < 500: calibration offset in mg
+ The offset correction values are used to realign the Zero-g position
+ of the X, Y, and Z-axis after the device is mounted on a board.
+ this value has an offset of 250 itself:
+ 0 is -250mg, 250 is 0 mg, 500 is 250mg
+- y-offset
+ see x-offset
+- z-offset
+ see x-offset
+
+Example 1:
+for a device laying on flat ground to recognize acceleration over 100mg.
+x-axis is calibrated to +10mg. Adapt interrupt line to your device.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ motion-mode;
+ ir-freefall-motion-x;
+ ir-freefall-motion-y;
+ irq-threshold = <100>;
+ x-offset = <160>;
+};
+
+Example 2:
+for a device mounted on a wall with y being the vertical axis. This recognizes
+y-acceleration below 800mg, so free fall or changing the orientation of the
+device (y not being the vertical axis and having ~1000mg anymore).
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ ir-freefall-motion-y;
+ irq-threshold = <800>;
+};
+
+Example 3:
+minimal example. lets users read current acceleration values. No polling
+is available.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ reg = <0x1d>;
+};
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e1abe8..04c080d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4104,6 +4104,11 @@ S: Maintained
F: include/linux/platform_data/video-imxfb.h
F: drivers/video/fbdev/imxfb.c
+FREESCALE MMA8653FC ACCELEROMETER I2C DRIVER
+M: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+S: Maintained
+F: drivers/input/misc/mma8653fc.c
+
FREESCALE QUAD SPI DRIVER
M: Han Xu <han.xu@freescale.com>
L: linux-mtd@lists.infradead.org
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 006242c..05ef1c1 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -324,6 +324,17 @@ config ISL29020
This driver can also be built as a module. If so, the module
will be called isl29020.
+config MMA8653FC
+ tristate "MMA8653FC - Freescale's 3-Axis, 10-bit Digital Accelerometer"
+ depends on I2C
+ default n
+ help
+ Say Y here if you want to support Freescale's MMA8653FC Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8653fc.
+
config SENSORS_TSL2550
tristate "Taos TSL2550 ambient light sensor"
depends on I2C && SYSFS
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 7d5c4cd..044c8fc 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_ICS932S401) += ics932s401.o
obj-$(CONFIG_LKDTM) += lkdtm.o
obj-$(CONFIG_TIFM_CORE) += tifm_core.o
obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o
+obj-$(CONFIG_MMA8653FC) += mma8653fc.o
obj-$(CONFIG_PHANTOM) += phantom.o
obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o
obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o
diff --git a/drivers/misc/mma8653fc.c b/drivers/misc/mma8653fc.c
new file mode 100644
index 0000000..f9b8cf9
--- /dev/null
+++ b/drivers/misc/mma8653fc.c
@@ -0,0 +1,897 @@
+/*
+ * mma8653fc.c - Support for Freescale MMA8653FC 3-axis 10-bit accelerometer
+ *
+ * Copyright (c) 2014 Theobroma Systems Design and Consulting GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define DRV_NAME "mma8653fc"
+#define MMA8653FC_DEVICE_ID 0x5a
+
+#define MMA8653FC_STATUS 0x00
+
+#define ZYXOW_MASK 0x80
+#define ZYXDR 0x08
+
+#define MMA8653FC_WHO_AM_I 0x0d
+
+#define MMA8653FC_SYSMOD 0x0b
+#define STATE_STANDBY 0x00
+#define STATE_WAKE 0x01
+#define STATE_SLEEP 0x02
+
+#define MMA8450_STATUS_ZXYDR 0x08
+
+/*
+ * 10 bit output data registers
+ * MSB: 7:0 bits 9:2 of data word
+ * LSB: 7:6 bits 1:0 of data word
+ */
+#define MMA8653FC_OUT_X_MSB 0x01
+#define MMA8653FC_OUT_X_LSB 0x02
+#define MMA8653FC_OUT_Y_MSB 0x03
+#define MMA8653FC_OUT_Y_LSB 0x04
+#define MMA8653FC_OUT_Z_MSB 0x05
+#define MMA8653FC_OUT_Z_LSB 0x06
+
+/*
+ * Portrait/Landscape Status
+ */
+#define MMA8653FC_PL_STATUS 0x10
+
+#define NEWLP 0x80
+#define LAPO_HIGH 0x04
+#define LAPO_LOW 0x02
+#define BAFRO 0x01
+
+/*
+ * Portrait/Landscape Configuration
+ */
+#define MMA8653FC_PL_CFG 0x11
+
+#define PL_EN (1 << 6)
+
+/*
+ * Data calibration registers
+ */
+#define MMA8653FC_OFF_X 0x2f
+#define MMA8653FC_OFF_Y 0x30
+#define MMA8653FC_OFF_Z 0x31
+
+/* 0 to 500 for dts, but -250 to 250 in mg */
+#define DEFAULT_OFF 250
+
+/*
+ * bits 1:0 dynamic range
+ * 00 +/- 2g
+ * 01 +/- 4g
+ * 10 +/- 8g
+ *
+ * HPF_Out bit 4 - data high pass or low pass filtered
+ */
+#define MMA8653FC_XYZ_DATA_CFG 0x0e
+
+#define RANGE_MASK 0x03
+#define RANGE2G 0x00
+#define RANGE4G 0x01
+#define RANGE8G 0x02
+/* values for calculation */
+#define SHIFT_2G 8
+#define INCR_2G 128
+#define SHIFT_4G 7
+#define INCR_4G 64
+#define SHIFT_8G 6
+#define INCR_8G 32
+#define DYN_RANGE_2G 2
+#define DYN_RANGE_4G 4
+#define DYN_RANGE_8G 8
+
+/*
+ * System Control Reg 1
+ */
+#define MMA8653FC_CTRL_REG1 0x2a
+
+#define ACTIVE_BIT (1 << 0)
+#define ODR_MASK 0x38
+#define ODR_DEFAULT 0x20 /* 50 Hz */
+#define ASLP_RATE_MASK 0xc0
+#define ASLP_RATE_DEFAULT 0x80 /* 6.25 Hz */
+
+/*
+ * Sys Control Reg 2
+ *
+ * auto-sleep enable, software reset
+ */
+#define MMA8653FC_CTRL_REG2 0x2b
+
+#define SLPE (1 << 2)
+#define SELFTEST (1 << 7)
+#define SOFT_RESET (1 << 6)
+
+/*
+ * Interrupt Source
+ */
+#define MMA8653FC_INT_SOURCE 0x0c
+
+#define SRC_ASLP (1 << 7)
+#define SRC_LNDPRT (1 << 4)
+#define SRC_FF_MT (1 << 2)
+#define SRC_DRDY (1 << 0)
+
+/*
+ * Interrupt Control Register
+ *
+ * default: active low
+ * default push-pull, not open-drain
+ */
+#define MMA8653FC_CTRL_REG3 0x2c
+
+#define WAKE_LNDPRT (1 << 5)
+#define WAKE_FF_MT (1 << 3)
+#define IPOL (1 << 1)
+#define PP_OD (1 << 0)
+
+/*
+ * Interrupt Enable Register
+ */
+#define MMA8653FC_CTRL_REG4 0x2d
+
+#define INT_EN_ASLP (1 << 7)
+#define INT_EN_LNDPRT (1 << 4)
+#define INT_EN_FF_MT (1 << 2)
+#define INT_EN_DRDY (1 << 0)
+
+/*
+ * Interrupt Configuration Register
+ * bit value 0 ... INT2 (default)
+ * bit value 1 ... INT1
+ */
+#define MMA8653FC_CTRL_REG5 0x2e
+
+#define INT_CFG_ASLP (1 << 7)
+#define INT_CFG_LNDPRT (1 << 4)
+#define INT_CFG_FF_MT (1 << 2)
+#define INT_CFG_DRDY (1 << 0)
+
+/*
+ * Freefall / Motion Configuration Register
+ *
+ * Event Latch enable/disable, motion or freefall mode
+ * and event flag enable per axis
+ */
+#define MMA8653FC_FF_MT_CFG 0x15
+
+#define FF_MT_CFG_ELE (1 << 7)
+#define FF_MT_CFG_OAE (1 << 6)
+#define FF_MT_CFG_ZEFE (1 << 5)
+#define FF_MT_CFG_YEFE (1 << 4)
+#define FF_MT_CFG_XEFE (1 << 3)
+
+/*
+ * Freefall / Motion Source Register
+ */
+#define MMA8653FC_FF_MT_SRC 0x16
+
+/*
+ * Freefall / Motion Threshold Register
+ *
+ * define motion threshold
+ * 0.063 g/LSB, 127 counts(0x7f) (7 bit from LSB)
+ * range: 0.063g - 8g
+ */
+#define MMA8653FC_FF_MT_THS 0x17
+
+struct axis_triple {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct mma8653fc_pdata {
+ s8 x_axis_offset;
+ s8 y_axis_offset;
+ s8 z_axis_offset;
+ bool auto_wake_sleep;
+ u32 range;
+ bool int1;
+ bool motion_mode;
+ u8 freefall_motion_thr;
+ bool int_src_data_ready;
+ bool int_src_ff_mt_x;
+ bool int_src_ff_mt_y;
+ bool int_src_ff_mt_z;
+ bool int_src_lndprt;
+ bool int_src_aslp;
+};
+
+struct mma8653fc {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct mma8653fc_pdata pdata;
+ struct axis_triple saved;
+ char orientation;
+ char bafro;
+ bool standby;
+ int irq;
+ unsigned int_mask;
+ u8 (*read)(struct mma8653fc *, unsigned char);
+ int (*write)(struct mma8653fc *, unsigned char, unsigned char);
+};
+
+/* defaults */
+static const struct mma8653fc_pdata mma8653fc_default_init = {
+ .range = 2,
+ .x_axis_offset = DEFAULT_OFF,
+ .y_axis_offset = DEFAULT_OFF,
+ .z_axis_offset = DEFAULT_OFF,
+ .auto_wake_sleep = false,
+ .int1 = false,
+ .motion_mode = false,
+ .freefall_motion_thr = 5,
+ .int_src_data_ready = false,
+ .int_src_ff_mt_x = false,
+ .int_src_ff_mt_y = false,
+ .int_src_ff_mt_z = false,
+ .int_src_lndprt = false,
+ .int_src_aslp = false,
+};
+
+static void mma8653fc_get_triple(struct mma8653fc *mma)
+{
+ u8 buf[6];
+ u8 status;
+
+ buf[0] = 0;
+
+ status = mma->read(mma, MMA8653FC_STATUS);
+ if (status & ZYXOW_MASK)
+ dev_dbg(&mma->client->dev, "previous read not completed\n");
+
+ buf[0] = mma->read(mma, MMA8653FC_OUT_X_MSB);
+ buf[1] = mma->read(mma, MMA8653FC_OUT_X_LSB);
+ buf[2] = mma->read(mma, MMA8653FC_OUT_Y_MSB);
+ buf[3] = mma->read(mma, MMA8653FC_OUT_Y_LSB);
+ buf[4] = mma->read(mma, MMA8653FC_OUT_Z_MSB);
+ buf[5] = mma->read(mma, MMA8653FC_OUT_Z_LSB);
+
+ mutex_lock(&mma->mutex);
+ /* move from registers to s16 */
+ mma->saved.x = (buf[1] | (buf[0] << 8)) >> 6;
+ mma->saved.y = (buf[3] | (buf[2] << 8)) >> 6;
+ mma->saved.z = (buf[5] | (buf[4] << 8)) >> 6;
+ mma->saved.x = sign_extend32(mma->saved.x, 9);
+ mma->saved.y = sign_extend32(mma->saved.y, 9);
+ mma->saved.z = sign_extend32(mma->saved.z, 9);
+
+ /* calc g, see data sheet and application note */
+ switch (mma->pdata.range) {
+ case DYN_RANGE_2G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_2G) >> SHIFT_2G);
+ break;
+ case DYN_RANGE_4G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_4G) >> SHIFT_4G);
+ break;
+ case DYN_RANGE_8G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_8G) >> SHIFT_8G);
+ break;
+ default:
+ dev_err(&mma->client->dev, "internal data corrupt\n");
+ }
+ mutex_unlock(&mma->mutex);
+}
+
+static void mma8653fc_get_orientation(struct mma8653fc *mma, u8 byte)
+{
+ if ((byte & LAPO_HIGH) && !(LAPO_LOW))
+ mma->orientation = 'r'; /* landscape right */
+ if (!(byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'd'; /* portrait down */
+ if (!(byte & LAPO_HIGH) && !(byte & LAPO_LOW))
+ mma->orientation = 'u'; /* portrait up */
+ if ((byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'l'; /* landscape left */
+
+ if (byte & BAFRO)
+ mma->bafro = 'b'; /* back facing */
+ else
+ mma->bafro = 'f'; /* front facing */
+}
+
+static irqreturn_t mma8653fc_irq(int irq, void *handle)
+{
+ struct mma8653fc *mma = handle;
+ u8 int_src;
+ u8 byte;
+
+ int_src = mma->read(mma, MMA8653FC_INT_SOURCE);
+ if (int_src & SRC_DRDY)
+ /* data ready handle */
+ if (int_src & SRC_FF_MT) {
+ /* freefall/motion change handle */
+ dev_dbg(&mma->client->dev,
+ "freefall or motion change\n");
+ byte = mma->read(mma, MMA8653FC_FF_MT_SRC);
+ }
+ if (int_src & SRC_LNDPRT) {
+ /* landscape/portrait change handle */
+ dev_dbg(&mma->client->dev,
+ "landscape / portrait change\n");
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ mma8653fc_get_orientation(mma, byte);
+ }
+ if (int_src & SRC_ASLP)
+ /* autosleep change handle */
+ mma8653fc_get_triple(mma);
+
+ sysfs_notify(&mma->client->dev.kobj, NULL, "position");
+
+ return IRQ_HANDLED;
+}
+
+static void __mma8653fc_enable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte | ACTIVE_BIT);
+}
+
+static void __mma8653fc_disable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte & ~ACTIVE_BIT);
+}
+
+static ssize_t mma8653fc_standby_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", mma->standby);
+}
+
+static ssize_t mma8653fc_standby_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&mma->mutex);
+
+ if (val) {
+ if (!mma->standby)
+ __mma8653fc_disable(mma);
+ } else {
+ if (mma->standby)
+ __mma8653fc_enable(mma);
+ }
+
+ mma->standby = !!val;
+
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(standby, 0664, mma8653fc_standby_show,
+ mma8653fc_standby_store);
+
+static ssize_t mma8653fc_currentmode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_SYSMOD);
+ if (byte < 0)
+ return byte;
+
+ switch (byte) {
+ case STATE_STANDBY:
+ count = scnprintf(buf, PAGE_SIZE, "standby\n");
+ break;
+ case STATE_WAKE:
+ count = scnprintf(buf, PAGE_SIZE, "wake\n");
+ break;
+ case STATE_SLEEP:
+ count = scnprintf(buf, PAGE_SIZE, "sleep\n");
+ break;
+ default:
+ count = scnprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ return count;
+}
+static DEVICE_ATTR(currentmode, S_IRUGO, mma8653fc_currentmode_show, NULL);
+
+static ssize_t mma8653fc_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ if (!mma->irq) {
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ if (byte & NEWLP)
+ mma8653fc_get_orientation(mma, byte);
+
+ byte = mma->read(mma, MMA8653FC_STATUS);
+ if (byte & ZYXDR)
+ mma8653fc_get_triple(mma);
+
+ msleep(20);
+ dev_dbg(&client->dev, "data polled\n");
+ }
+ mutex_lock(&mma->mutex);
+ count = scnprintf(buf, PAGE_SIZE, "%d %d %d %c %c\n",
+ mma->saved.x, mma->saved.y, mma->saved.z,
+ mma->orientation, mma->bafro);
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+static DEVICE_ATTR(position, S_IRUGO, mma8653fc_position_show, NULL);
+
+static struct attribute *mma8653fc_attributes[] = {
+ &dev_attr_position.attr,
+ &dev_attr_standby.attr,
+ &dev_attr_currentmode.attr,
+ NULL,
+};
+
+static const struct attribute_group mma8653fc_attr_group = {
+ .attrs = mma8653fc_attributes,
+};
+
+static u8 mma8653fc_read(struct mma8653fc *mma, unsigned char reg)
+{
+ u8 val;
+
+ val = i2c_smbus_read_byte_data(mma->client, reg);
+ if (val < 0) {
+ dev_err(&mma->client->dev,
+ "failed to read %x register\n", reg);
+ }
+ return val;
+}
+
+static int mma8653fc_write(struct mma8653fc *mma, unsigned char reg,
+ unsigned char val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(mma->client, reg, val);
+ if (ret) {
+ dev_err(&mma->client->dev,
+ "failed to write %x register\n", reg);
+ }
+ return ret;
+}
+
+static const struct of_device_id mma8653fc_dt_ids[] = {
+ { .compatible = "fsl,mma8653fc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mma8653fc_dt_ids);
+
+static const struct mma8653fc_pdata *mma8653fc_probe_dt(struct device *dev)
+{
+ struct mma8653fc_pdata *pdata;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
+ u32 testu32;
+ s32 tests32;
+
+ if (!node) {
+ dev_err(dev, "no associated DT data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ match = of_match_device(mma8653fc_dt_ids, dev);
+ if (!match) {
+ dev_err(dev, "unknown device model\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ *pdata = mma8653fc_default_init;
+
+ /* overwrite from dts */
+ testu32 = pdata->x_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "x-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on X axis\n", tests32);
+ /* calc register value, resolution: 1.96mg */
+ pdata->x_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->y_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "y-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Y axis\n", tests32);
+ pdata->y_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->z_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "z-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Z axis\n", tests32);
+ pdata->z_axis_offset = (s8) (tests32 / 2);
+ }
+
+ testu32 = 0;
+ of_property_read_u32(node, "dynamic-range", &testu32);
+ if ((testu32) && (testu32 != 2) && (testu32 != 4) && (testu32 != 8)) {
+ dev_warn(dev, "wrong value for full scale range in dtb\n");
+ } else {
+ if (testu32)
+ pdata->range = testu32;
+ }
+
+ if (of_property_read_bool(node, "auto-wake-sleep"))
+ pdata->auto_wake_sleep = true;
+
+ if (of_property_read_bool(node, "int1"))
+ pdata->int1 = true;
+
+ if (of_property_read_bool(node, "motion-mode"))
+ pdata->motion_mode = true;
+
+ if (of_property_read_bool(node, "ir-data-ready"))
+ pdata->int_src_data_ready = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-x"))
+ pdata->int_src_ff_mt_x = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-y"))
+ pdata->int_src_ff_mt_y = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-z"))
+ pdata->int_src_ff_mt_z = true;
+
+ if (of_property_read_bool(node, "ir-auto-wake"))
+ pdata->int_src_aslp = true;
+
+ if (of_property_read_bool(node, "ir-landscape-portrait"))
+ pdata->int_src_lndprt = true;
+
+ testu32 = 0;
+ of_property_read_u32(node, "irq-threshold", &testu32);
+ /* always uses maximum range +/- 8000g, resolution 63mg */
+ if ((testu32 <= 8000) && (testu32 > 0)) {
+ dev_dbg(dev, "use freefall / motion threshold %dmg\n",
+ testu32);
+ /* calculate register value from mg */
+ pdata->freefall_motion_thr = (testu32 / 63) + 1;
+ }
+
+ return pdata;
+}
+static int mma8653fc_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mma8653fc *mma;
+ const struct mma8653fc_pdata *pdata;
+ int err;
+ u8 byte;
+
+ err = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!err) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ mma = kzalloc(sizeof(*mma), GFP_KERNEL);
+ if (!mma) {
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = mma8653fc_probe_dt(&client->dev);
+ if (IS_ERR(pdata)) {
+ err = PTR_ERR(pdata);
+ pr_err("pdata from DT failed\n");
+ goto err_free_mem;
+ }
+ }
+ mma->pdata = *pdata;
+ pdata = &mma->pdata;
+ mma->client = client;
+ mma->read = &mma8653fc_read;
+ mma->write = &mma8653fc_write;
+
+ mutex_init(&mma->mutex);
+
+ i2c_set_clientdata(client, mma);
+
+ err = sysfs_create_group(&client->dev.kobj, &mma8653fc_attr_group);
+ if (err)
+ goto err_free_mem;
+
+ mma->irq = irq_of_parse_and_map(client->dev.of_node, 0);
+ if (!mma->irq) {
+ dev_err(&client->dev, "Unable to parse or map IRQ\n");
+ goto no_irq;
+ }
+
+ err = irq_set_irq_type(mma->irq, IRQ_TYPE_EDGE_FALLING);
+ if (err) {
+ dev_err(&client->dev, "set_irq_type failed\n");
+ goto err_free_mem;
+ }
+
+ err = request_threaded_irq(mma->irq, NULL, mma8653fc_irq, IRQF_ONESHOT,
+ dev_name(&mma->client->dev), mma);
+ if (err) {
+ dev_err(&client->dev, "irq %d busy?\n", mma->irq);
+ goto err_free_mem;
+ }
+
+ if (mma->write(mma, MMA8653FC_CTRL_REG2, SOFT_RESET))
+ goto err_free_irq;
+
+ __mma8653fc_disable(mma);
+ mma->standby = true;
+
+ /* enable desired interrupts */
+ mma->orientation = '\0';
+ mma->bafro = '\0';
+ byte = 0;
+ if (pdata->int_src_data_ready) {
+ byte |= INT_EN_DRDY;
+ dev_dbg(&client->dev, "DATA READY interrupt source enabled\n");
+ }
+ if (pdata->int_src_ff_mt_x || pdata->int_src_ff_mt_y ||
+ pdata->int_src_ff_mt_z) {
+ byte |= INT_EN_FF_MT;
+ dev_dbg(&client->dev, "FF MT interrupt source enabled\n");
+ }
+ if (pdata->int_src_lndprt) {
+ if (mma->write(mma, MMA8653FC_PL_CFG, PL_EN))
+ goto err_free_irq;
+ byte |= INT_EN_LNDPRT;
+ dev_dbg(&client->dev, "LNDPRT interrupt source enabled\n");
+ }
+ if (pdata->int_src_aslp) {
+ byte |= INT_EN_ASLP;
+ dev_dbg(&client->dev, "ASLP interrupt source enabled\n");
+ }
+ if (mma->write(mma, MMA8653FC_CTRL_REG4, byte))
+ goto err_free_irq;
+
+ /* force everything to line 1 */
+ if (pdata->int1) {
+ if (mma->write(mma, MMA8653FC_CTRL_REG5,
+ (INT_CFG_ASLP | INT_CFG_LNDPRT |
+ INT_CFG_FF_MT | INT_CFG_DRDY)))
+ goto err_free_irq;
+ dev_dbg(&client->dev, "using interrupt line 1\n");
+ }
+no_irq:
+ /* range mode */
+ byte = mma->read(mma, MMA8653FC_XYZ_DATA_CFG);
+ byte &= ~RANGE_MASK;
+ switch (pdata->range) {
+ case DYN_RANGE_2G:
+ byte |= RANGE2G;
+ dev_dbg(&client->dev, "use 2g range\n");
+ break;
+ case DYN_RANGE_4G:
+ byte |= RANGE4G;
+ dev_dbg(&client->dev, "use 4g range\n");
+ break;
+ case DYN_RANGE_8G:
+ byte |= RANGE8G;
+ dev_dbg(&client->dev, "use 8g range\n");
+ break;
+ default:
+ dev_err(&client->dev, "wrong range mode value\n");
+ goto err_free_irq;
+ }
+ if (mma->write(mma, MMA8653FC_XYZ_DATA_CFG, byte))
+ goto err_free_irq;
+
+ /* data calibration offsets */
+ if (pdata->x_axis_offset) {
+ if (mma->write(mma, MMA8653FC_OFF_X, pdata->x_axis_offset))
+ goto err_free_irq;
+ }
+ if (pdata->y_axis_offset) {
+ if (mma->write(mma, MMA8653FC_OFF_Y, pdata->y_axis_offset))
+ goto err_free_irq;
+ }
+ if (pdata->z_axis_offset) {
+ if (mma->write(mma, MMA8653FC_OFF_Z, pdata->z_axis_offset))
+ goto err_free_irq;
+ }
+
+ /* if autosleep, wake on both landscape and motion changes */
+ if (pdata->auto_wake_sleep) {
+ byte = 0;
+ byte |= WAKE_LNDPRT;
+ byte |= WAKE_FF_MT;
+ if (mma->write(mma, MMA8653FC_CTRL_REG3, byte))
+ goto err_free_irq;
+ if (mma->write(mma, MMA8653FC_CTRL_REG2, SLPE))
+ goto err_free_irq;
+ dev_dbg(&client->dev, "auto sleep enabled\n");
+ }
+
+ /* data rates */
+ byte = 0;
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ if (byte < 0)
+ goto err_free_irq;
+ byte &= ~ODR_MASK;
+ byte |= ODR_DEFAULT;
+ byte &= ~ASLP_RATE_MASK;
+ byte |= ASLP_RATE_DEFAULT;
+ if (mma->write(mma, MMA8653FC_CTRL_REG1, byte))
+ goto err_free_irq;
+
+ /* freefall / motion config */
+ byte = 0;
+ if (pdata->motion_mode) {
+ byte |= FF_MT_CFG_OAE;
+ dev_dbg(&client->dev, "detect motion instead of freefall\n");
+ }
+ byte |= FF_MT_CFG_ELE;
+ if (pdata->int_src_ff_mt_x)
+ byte |= FF_MT_CFG_XEFE;
+ if (pdata->int_src_ff_mt_y)
+ byte |= FF_MT_CFG_YEFE;
+ if (pdata->int_src_ff_mt_z)
+ byte |= FF_MT_CFG_ZEFE;
+ if (mma->write(mma, MMA8653FC_FF_MT_CFG, byte))
+ goto err_free_irq;
+
+ if (pdata->freefall_motion_thr) {
+ if (mma->write(mma, MMA8653FC_FF_MT_THS,
+ pdata->freefall_motion_thr))
+ goto err_free_irq;
+ /* calculate back to mg */
+ dev_dbg(&client->dev, "threshold set to %dmg\n",
+ (63 * pdata->freefall_motion_thr) - 1);
+ }
+
+ byte = mma->read(mma, MMA8653FC_WHO_AM_I);
+ if (byte != MMA8653FC_DEVICE_ID) {
+ dev_err(&client->dev, "wrong device for driver\n");
+ goto err_free_irq;
+ }
+ dev_info(&client->dev,
+ "accelerometer driver loaded. device id %x\n", byte);
+
+ return 0;
+
+ err_free_irq:
+ if (mma->irq)
+ free_irq(mma->irq, mma);
+ err_free_mem:
+ kfree(mma);
+ err_out:
+ return err;
+}
+
+static int mma8653fc_remove(struct i2c_client *client)
+{
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ free_irq(mma->irq, mma);
+ dev_dbg(&client->dev, "unregistered accelerometer\n");
+ kfree(mma);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8653fc_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_disable(mma);
+
+ return 0;
+}
+
+static int mma8653fc_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_enable(mma);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mma8653fc_pm_ops, mma8653fc_suspend, mma8653fc_resume);
+#define MMA8653FC_PM_OPS (&mma8653fc_pm_ops)
+#else
+#define MMA8653FC_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id mma8653fc_id[] = {
+ { DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma8653fc_id);
+
+static struct i2c_driver mma8653fc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mma8653fc_dt_ids,
+ .pm = MMA8653FC_PM_OPS,
+ },
+ .probe = mma8653fc_i2c_probe,
+ .remove = mma8653fc_remove,
+ .id_table = mma8653fc_id,
+};
+
+module_i2c_driver(mma8653fc_driver);
+
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@theobroma-systems.com");
+MODULE_DESCRIPTION("Freescale's MMA8653FC Three-Axis Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
--
2.1.4
^ permalink raw reply related
* Re: [PATCH v2] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 11:26 UTC (permalink / raw)
To: Bastien Nocera, Martin Kepplinger
Cc: Alexander Stein, robh+dt, pawel.moll, mark.rutland,
ijc+devicetree, galak, dmitry.torokhov, akpm, gregkh, linux-api,
devicetree, linux-input, linux-kernel, Christoph Muellner
In-Reply-To: <1426760577.6764.14.camel@hadess.net>
Am 2015-03-19 um 11:22 schrieb Bastien Nocera:
> On Wed, 2015-03-18 at 19:28 +0100, Martin Kepplinger wrote:
>> Am 2015-03-18 um 19:05 schrieb Bastien Nocera:
>>> On Wed, 2015-03-18 at 19:02 +0100, Martin Kepplinger wrote:
>>>> Am 2015-03-18 um 17:59 schrieb Bastien Nocera:
>>>>> On Wed, 2015-03-18 at 17:42 +0100, Martin Kepplinger wrote:
>>>>>>
>>>>> <snip>
>>>>>> It could have gone to drivers/iio/accel if it would use an
>>>>>> iio interface, which would make more sense, you are right,
>>>>>> but I
>>>>>> simply don't have the time to merge it in to iio.
>>>>>>
>>>>>> It doesn't use an input interface either but I don't see a
>>>>>> good place for an accelerometer that uses sysfs only.
>>>>>>
>>>>>> It works well, is a relatively recent chip and a clean
>>>>>> dirver. But this is all I can provide.
>>>>>
>>>>> As a person who works on the user-space interaction of those
>>>>> with desktops [1]: Urgh.
>>>>>
>>>>> I already have 3 (probably 4) types of accelerometers to
>>>>> contend with, I'm not fond of adding yet another type.
>>>>>
>>>>> Is there any way to get this hardware working outside the SoCs
>>>>> it's designed for (say, a device with I2C like a Raspberry
>>>>> Pi), so that a kind soul could handle getting this using the
>>>>> right interfaces?
>>>>>
>>>>
>>>> It works on basically any SoC and is in no way limited in this
>>>> regard. Sure, userspace has to expicitely support it and I hear
>>>> you. Using the iio interface would make more sense. I can only
>>>> say I'd love to have the time to move this driver over. I'm
>>>> very sorry.
>>>
>>> How can we get the hardware for somebody to use on their own
>>> laptops/embedded boards to implement this driver?
>>>
>>
>> It's connected over I2C. If the included documentation is not clear
>> please tell me what exacly. Thanks!
>
>
> I'll ask the question a different way: can you please give the address
> of a shop where that hardware is available?
>
there is
http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=RDMMA865x&lang_cd=
and http://linux-sunxi.org/Inet_K970 for example. I think I saw Android
devices with it too, and I would guess it would be used more often if it
were in linux.
Please refer to v4 of the patch for different questions. thanks!
^ permalink raw reply
* Re: [PATCH v4] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Varka Bhadram @ 2015-03-20 11:31 UTC (permalink / raw)
To: Martin Kepplinger, mark.rutland, robh+dt, Pawel.Moll,
ijc+devicetree, galak, dmitry.torokhov, alexander.stein
Cc: hadess, akpm, gregkh, linux-api, devicetree, linux-input,
linux-kernel, Martin Kepplinger, Christoph Muellner
In-Reply-To: <1426850431-31550-1-git-send-email-martink@posteo.de>
On 03/20/2015 04:50 PM, Martin Kepplinger wrote:
> From: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
>
> The MMA8653FC is a low-power, three-axis, capacitive micromachined
> accelerometer with 10 bits of resolution with flexible user-programmable
> options.
>
> Embedded interrupt functions enable overall power savings, by relieving the
> host processor from continuously polling data, for example using the poll()
> system call.
>
> The device can be configured to generate wake-up interrupt signals from any
> combination of the configurable embedded functions, enabling the MMA8653FC
> to monitor events while remaining in a low-power mode during periods of
> inactivity.
>
> This driver provides devicetree properties to program the device's behaviour
> and a simple, tested and documented sysfs interface. The data sheet and more
> information is available on Freescale's website.
>
> Signed-off-by: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
> Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
> ---
>
(...)
> +static int mma8653fc_i2c_probe(struct i2c_client *client,
> + const struct i2c_device_id *id)
> +{
> + struct mma8653fc *mma;
> + const struct mma8653fc_pdata *pdata;
> + int err;
> + u8 byte;
> +
> + err = i2c_check_functionality(client->adapter,
> + I2C_FUNC_SMBUS_BYTE_DATA);
> + if (!err) {
> + dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
> + return -EIO;
> + }
> +
> + mma = kzalloc(sizeof(*mma), GFP_KERNEL);
Why not devm_* API..?
> + if (!mma) {
> + err = -ENOMEM;
> + goto err_out;
> + }
> +
> + pdata = dev_get_platdata(&client->dev);
> + if (!pdata) {
> + pdata = mma8653fc_probe_dt(&client->dev);
> + if (IS_ERR(pdata)) {
> + err = PTR_ERR(pdata);
> + pr_err("pdata from DT failed\n");
Use dev_err() instead of pr_err()..
> + goto err_free_mem;
> + }
> + }
> + mma->pdata = *pdata;
> + pdata = &mma->pdata;
> + mma->client = client;
> + mma->read = &mma8653fc_read;
> + mma->write = &mma8653fc_write;
> +
> + mutex_init(&mma->mutex);
> +
> + i2c_set_clientdata(client, mma);
> +
> + err = sysfs_create_group(&client->dev.kobj, &mma8653fc_attr_group);
> + if (err)
> + goto err_free_mem;
> +
> + mma->irq = irq_of_parse_and_map(client->dev.of_node, 0);
> + if (!mma->irq) {
> + dev_err(&client->dev, "Unable to parse or map IRQ\n");
> + goto no_irq;
> + }
> +
> + err = irq_set_irq_type(mma->irq, IRQ_TYPE_EDGE_FALLING);
> + if (err) {
> + dev_err(&client->dev, "set_irq_type failed\n");
> + goto err_free_mem;
> + }
> +
> + err = request_threaded_irq(mma->irq, NULL, mma8653fc_irq, IRQF_ONESHOT,
> + dev_name(&mma->client->dev), mma);
devm_* API..?
> + if (err) {
> + dev_err(&client->dev, "irq %d busy?\n", mma->irq);
> + goto err_free_mem;
> + }
> +
> + if (mma->write(mma, MMA8653FC_CTRL_REG2, SOFT_RESET))
> + goto err_free_irq;
> +
> + __mma8653fc_disable(mma);
> + mma->standby = true;
> +
> + /* enable desired interrupts */
> + mma->orientation = '\0';
> + mma->bafro = '\0';
> + byte = 0;
> + if (pdata->int_src_data_ready) {
> + byte |= INT_EN_DRDY;
> + dev_dbg(&client->dev, "DATA READY interrupt source enabled\n");
> + }
> + if (pdata->int_src_ff_mt_x || pdata->int_src_ff_mt_y ||
> + pdata->int_src_ff_mt_z) {
> + byte |= INT_EN_FF_MT;
> + dev_dbg(&client->dev, "FF MT interrupt source enabled\n");
> + }
> + if (pdata->int_src_lndprt) {
> + if (mma->write(mma, MMA8653FC_PL_CFG, PL_EN))
> + goto err_free_irq;
> + byte |= INT_EN_LNDPRT;
> + dev_dbg(&client->dev, "LNDPRT interrupt source enabled\n");
> + }
> + if (pdata->int_src_aslp) {
> + byte |= INT_EN_ASLP;
> + dev_dbg(&client->dev, "ASLP interrupt source enabled\n");
> + }
> + if (mma->write(mma, MMA8653FC_CTRL_REG4, byte))
> + goto err_free_irq;
> +
> + /* force everything to line 1 */
> + if (pdata->int1) {
> + if (mma->write(mma, MMA8653FC_CTRL_REG5,
> + (INT_CFG_ASLP | INT_CFG_LNDPRT |
> + INT_CFG_FF_MT | INT_CFG_DRDY)))
> + goto err_free_irq;
> + dev_dbg(&client->dev, "using interrupt line 1\n");
> + }
> +no_irq:
> + /* range mode */
> + byte = mma->read(mma, MMA8653FC_XYZ_DATA_CFG);
> + byte &= ~RANGE_MASK;
> + switch (pdata->range) {
> + case DYN_RANGE_2G:
> + byte |= RANGE2G;
> + dev_dbg(&client->dev, "use 2g range\n");
> + break;
> + case DYN_RANGE_4G:
> + byte |= RANGE4G;
> + dev_dbg(&client->dev, "use 4g range\n");
> + break;
> + case DYN_RANGE_8G:
> + byte |= RANGE8G;
> + dev_dbg(&client->dev, "use 8g range\n");
> + break;
> + default:
> + dev_err(&client->dev, "wrong range mode value\n");
> + goto err_free_irq;
> + }
> + if (mma->write(mma, MMA8653FC_XYZ_DATA_CFG, byte))
> + goto err_free_irq;
> +
> + /* data calibration offsets */
> + if (pdata->x_axis_offset) {
> + if (mma->write(mma, MMA8653FC_OFF_X, pdata->x_axis_offset))
> + goto err_free_irq;
> + }
> + if (pdata->y_axis_offset) {
> + if (mma->write(mma, MMA8653FC_OFF_Y, pdata->y_axis_offset))
> + goto err_free_irq;
> + }
> + if (pdata->z_axis_offset) {
> + if (mma->write(mma, MMA8653FC_OFF_Z, pdata->z_axis_offset))
> + goto err_free_irq;
> + }
> +
> + /* if autosleep, wake on both landscape and motion changes */
> + if (pdata->auto_wake_sleep) {
> + byte = 0;
> + byte |= WAKE_LNDPRT;
> + byte |= WAKE_FF_MT;
> + if (mma->write(mma, MMA8653FC_CTRL_REG3, byte))
> + goto err_free_irq;
> + if (mma->write(mma, MMA8653FC_CTRL_REG2, SLPE))
> + goto err_free_irq;
> + dev_dbg(&client->dev, "auto sleep enabled\n");
> + }
> +
> + /* data rates */
> + byte = 0;
> + byte = mma->read(mma, MMA8653FC_CTRL_REG1);
> + if (byte < 0)
> + goto err_free_irq;
> + byte &= ~ODR_MASK;
> + byte |= ODR_DEFAULT;
> + byte &= ~ASLP_RATE_MASK;
> + byte |= ASLP_RATE_DEFAULT;
> + if (mma->write(mma, MMA8653FC_CTRL_REG1, byte))
> + goto err_free_irq;
> +
> + /* freefall / motion config */
> + byte = 0;
> + if (pdata->motion_mode) {
> + byte |= FF_MT_CFG_OAE;
> + dev_dbg(&client->dev, "detect motion instead of freefall\n");
> + }
> + byte |= FF_MT_CFG_ELE;
> + if (pdata->int_src_ff_mt_x)
> + byte |= FF_MT_CFG_XEFE;
> + if (pdata->int_src_ff_mt_y)
> + byte |= FF_MT_CFG_YEFE;
> + if (pdata->int_src_ff_mt_z)
> + byte |= FF_MT_CFG_ZEFE;
> + if (mma->write(mma, MMA8653FC_FF_MT_CFG, byte))
> + goto err_free_irq;
> +
> + if (pdata->freefall_motion_thr) {
> + if (mma->write(mma, MMA8653FC_FF_MT_THS,
> + pdata->freefall_motion_thr))
> + goto err_free_irq;
> + /* calculate back to mg */
> + dev_dbg(&client->dev, "threshold set to %dmg\n",
> + (63 * pdata->freefall_motion_thr) - 1);
> + }
> +
> + byte = mma->read(mma, MMA8653FC_WHO_AM_I);
> + if (byte != MMA8653FC_DEVICE_ID) {
> + dev_err(&client->dev, "wrong device for driver\n");
> + goto err_free_irq;
> + }
> + dev_info(&client->dev,
> + "accelerometer driver loaded. device id %x\n", byte);
> +
> + return 0;
> +
> + err_free_irq:
> + if (mma->irq)
> + free_irq(mma->irq, mma);
> + err_free_mem:
> + kfree(mma);
If we use devm_* API's above steps are not required
> + err_out:
> + return err;
> +}
> +
> +static int mma8653fc_remove(struct i2c_client *client)
> +{
> + struct mma8653fc *mma = i2c_get_clientdata(client);
> +
> + free_irq(mma->irq, mma);
> + dev_dbg(&client->dev, "unregistered accelerometer\n");
> + kfree(mma);
same as above..
--
Varka Bhadram
^ permalink raw reply
* Re: [PATCH v3] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 11:37 UTC (permalink / raw)
To: Mark Rutland, Martin Kepplinger
Cc: robh+dt@kernel.org, Pawel Moll, ijc+devicetree@hellion.org.uk,
galak@codeaurora.org, dmitry.torokhov@gmail.com,
alexander.stein@systec-electronic.com, hadess@hadess.net,
akpm@linux-foundation.org, gregkh@linuxfoundation.org,
linux-api@vger.kernel.org, devicetree@vger.kernel.org,
linux-input@vger.kernel.org, linux-kernel@vger.kernel.org,
Christoph Muellner
In-Reply-To: <20150319160349.GD25967@leverpostej>
Am 2015-03-19 um 17:03 schrieb Mark Rutland:
>> diff --git a/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt b/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt
>> new file mode 100644
>> index 0000000..3921acb
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/misc/fsl,mma8653fc.txt
>> @@ -0,0 +1,96 @@
>> +Freescale MMA8653FC 3-axis Accelerometer
>> +
>> +Required properties:
>> +- compatible
>> + "fsl,mma8653fc"
>> +- reg
>> + I2C address
>> +
>> +Optional properties:
>> +
>> +- interrupt-parent
>> + a phandle for the interrupt controller (see
>> + Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)
>> +- interrupts
>> + interrupt line to which the chip is connected
>> +- int1
>> + set to use interrupt line 1 instead of 2
>
> If you have two interrupt output lines, you should have two entries in
> interrupts.
>
> You can use interrupt-names to determine which line(s) are wired up.
>
You can't use both interrupt lines. You have to decide on one and all
your selected interrupt sources will be on this one line.
> I don't believe that you need this property.
>
>> +- int_active_high
>> + set interrupt line active high
>
> s/_/-/ in property names please.
>
> What happens if this isn't set? Is it active-low, or edge-triggered?
>
> It feels like we should be able to query when we need to set this from
> the IRQ(s).
It's active low per default. I investigated and couldn't successfully
use active high. In v4 of the patch, this option is gone and documented.
>
>> +- ir_freefall_motion_x
>> + activate freefall/motion interrupts on x axis
>> +- ir_freefall_motion_y
>> + activate freefall/motion interrupts on y axis
>> +- ir_freefall_motion_z
>> + activate freefall/motion interrupts on z axis
>> +- irq_threshold
>> + 0 < value < 8000: threshold for motion interrupts in mg
>> +- ir_landscape_portrait
>> + activate landscape/portrait interrupts
>> +- ir_data_ready:
>> + activate data-ready interrupts
>> + Interrupt events can be activated in any combination.
>
> These all sounds like they would be better as runtime options. I don't
> see why these should necessarily be in the DT.
First, I don't really want to expand the sysfs ABI much. Second, there
are use cases that don't need these at runtime and it would seem to make
it even harder for systems to use this driver.
>
>> +- range
>> + 2, 4, or 8: range in g, default: 2
>
> Likewise.
>
> It would be nice to have a better qualified name than "range".
I use "dynamic-range" in v4 of the patch.
>
>> +- auto_wake_sleep
>> + auto sleep mode (lower frequency)
>> +- motion_mode
>> + use motion mode instead of freefall mode (trigger if >threshold).
>> + per default an interrupt occurs if motion values fall below the
>> + value set in "threshold" and therefore can detect free fall on the
>> + vertical axis (depending on the position of the device).
>> + Setting this values inverts the behaviour and an interrupt occurs
>> + above the threshold value, so usually activate horizontal axis in
>> + this case.
>
> These both sound like they would be better as runtime options.
>
>> +
>> +- x-offset
>> + 0 < value < 500: calibration offset in mg
>> + this value has an offset of 250 itself:
>> + 0 is -250mg, 250 is 0 mg, 500 is 250mg
>> +- y-offset
>> + see x-offset
>> +- z-offset
>> + see x-offset
>
> I'm unsure about these; it really depends on what the calibration is
> for.
this is better documented in v4 of the patch.
>
> Mark.
>
thanks for reviewing!
martin
^ permalink raw reply
* Re: [PATCH v2] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Benjamin Tissoires @ 2015-03-20 12:27 UTC (permalink / raw)
To: Martin Kepplinger
Cc: Bastien Nocera, Martin Kepplinger, Alexander Stein,
robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
mark.rutland-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ, Dmitry Torokhov, Andrew Morton,
Greg Kroah-Hartman, linux-api-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA, linux-input,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Christoph Muellner
In-Reply-To: <550C03FC.1020303-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org>
On Fri, Mar 20, 2015 at 7:26 AM, Martin Kepplinger
<martin.kepplinger-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org> wrote:
> Am 2015-03-19 um 11:22 schrieb Bastien Nocera:
>> On Wed, 2015-03-18 at 19:28 +0100, Martin Kepplinger wrote:
>>> Am 2015-03-18 um 19:05 schrieb Bastien Nocera:
>>>> On Wed, 2015-03-18 at 19:02 +0100, Martin Kepplinger wrote:
>>>>> Am 2015-03-18 um 17:59 schrieb Bastien Nocera:
>>>>>> On Wed, 2015-03-18 at 17:42 +0100, Martin Kepplinger wrote:
>>>>>>>
>>>>>> <snip>
>>>>>>> It could have gone to drivers/iio/accel if it would use an
>>>>>>> iio interface, which would make more sense, you are right,
>>>>>>> but I
>>>>>>> simply don't have the time to merge it in to iio.
>>>>>>>
>>>>>>> It doesn't use an input interface either but I don't see a
>>>>>>> good place for an accelerometer that uses sysfs only.
>>>>>>>
>>>>>>> It works well, is a relatively recent chip and a clean
>>>>>>> dirver. But this is all I can provide.
>>>>>>
>>>>>> As a person who works on the user-space interaction of those
>>>>>> with desktops [1]: Urgh.
>>>>>>
>>>>>> I already have 3 (probably 4) types of accelerometers to
>>>>>> contend with, I'm not fond of adding yet another type.
>>>>>>
>>>>>> Is there any way to get this hardware working outside the SoCs
>>>>>> it's designed for (say, a device with I2C like a Raspberry
>>>>>> Pi), so that a kind soul could handle getting this using the
>>>>>> right interfaces?
>>>>>>
>>>>>
>>>>> It works on basically any SoC and is in no way limited in this
>>>>> regard. Sure, userspace has to expicitely support it and I hear
>>>>> you. Using the iio interface would make more sense. I can only
>>>>> say I'd love to have the time to move this driver over. I'm
>>>>> very sorry.
>>>>
>>>> How can we get the hardware for somebody to use on their own
>>>> laptops/embedded boards to implement this driver?
>>>>
>>>
>>> It's connected over I2C. If the included documentation is not clear
>>> please tell me what exacly. Thanks!
>>
>>
>> I'll ask the question a different way: can you please give the address
>> of a shop where that hardware is available?
>>
>
> there is
> http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=RDMMA865x&lang_cd=
> and http://linux-sunxi.org/Inet_K970 for example. I think I saw Android
> devices with it too, and I would guess it would be used more often if it
> were in linux.
I am going to say again (in a different way) what Bastien said. In its
current form, even in drivers/misc, this is a NACK for me (v1, v2, v3
& v4).
Putting a driver in Linux means we have to support it forever, and
definitively, nobody will use an accelerometer in Android if it is in
drivers/misc.
Android requires drivers to follow the IIO protocol. Period.
So having your own will not help android, it will just be a burden.
The sysfs you are proposing seems simple enough, but we can not afford
having a 3rd custom way of relying the accelerometer information in
the Linux tree (they were first handled in input, then IIO, then a
custom sysfs).
If you really want to have the driver in the tree, I won't be opposed
if you put in under staging. This way, you can break it whenever you
want and people won't rely on it. And then, we can use your driver as
a base to port it to IIO.
Sorry for being rude, but I am starting to get tired of people saying
that they don't have the time to follow what the reviewers said. You
obviously spent some time polishing this driver, why not making it
right from its first inclusion in the tree?
Cheers,
Benjamin
>
> Please refer to v4 of the patch for different questions. thanks!
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [Patch] firmware: dmi_scan: split dmisubsystem from dmi-sysfs
From: Jean Delvare @ 2015-03-20 12:35 UTC (permalink / raw)
To: Matt Fleming
Cc: Ivan.khoronzhuk, linux-kernel, ard.biesheuvel, grant.likely,
linux-api, linux-doc, mikew, dmidecode-devel, leif.lindholm,
msalter
In-Reply-To: <1426852834.2727.25.camel@intel.com>
On Fri, 20 Mar 2015 12:00:34 +0000, Matt Fleming wrote:
> On Fri, 2015-03-20 at 09:16 +0100, Jean Delvare wrote:
> >
> > OK, I understand. Now I see the two patches I was missing, I'll pick
> > them so that I can apply your patch cleanly on top of my tree.
> >
> > I would have appreciated to be Cc'd on these two patches, so I knew
> > they existed, and maybe I could even have commented on them.
>
> Jean, I would suggest you add your name to an entry in MAINTAINERS if
> you want to pickup these patches or be Cc'd on them.
Yeah, I had the same thought when writing the above. Will do.
--
Jean Delvare
SUSE L3 Support
^ permalink raw reply
* [PATCH v2] Add virtio-input driver.
From: Gerd Hoffmann @ 2015-03-20 12:39 UTC (permalink / raw)
To: virtio-dev-sDuHXQ4OtrM4h7I2RyI4rWD2FQJk+8+b,
virtualization-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA
Cc: mst-H+wXaHxf7aLQT0dZR+AlfA, David Herrmann, Dmitry Torokhov,
Gerd Hoffmann, Rusty Russell, open list, open list:ABI/API
virtio-input is basically evdev-events-over-virtio, so this driver isn't
much more than reading configuration from config space and forwarding
incoming events to the linux input layer.
Signed-off-by: Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
---
MAINTAINERS | 6 +
drivers/virtio/Kconfig | 10 ++
drivers/virtio/Makefile | 1 +
drivers/virtio/virtio_input.c | 335 ++++++++++++++++++++++++++++++++++++++
include/uapi/linux/Kbuild | 1 +
include/uapi/linux/virtio_ids.h | 1 +
include/uapi/linux/virtio_input.h | 75 +++++++++
7 files changed, 429 insertions(+)
create mode 100644 drivers/virtio/virtio_input.c
create mode 100644 include/uapi/linux/virtio_input.h
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e1abe8..585e6cd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -10435,6 +10435,12 @@ S: Maintained
F: drivers/vhost/
F: include/uapi/linux/vhost.h
+VIRTIO INPUT DRIVER
+M: Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
+S: Maintained
+F: drivers/virtio/virtio_input.c
+F: include/uapi/linux/virtio_input.h
+
VIA RHINE NETWORK DRIVER
M: Roger Luethi <rl-7uj+XXdSDtwfv37vnLkPlQ@public.gmane.org>
S: Maintained
diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig
index b546da5..cab9f3f 100644
--- a/drivers/virtio/Kconfig
+++ b/drivers/virtio/Kconfig
@@ -48,6 +48,16 @@ config VIRTIO_BALLOON
If unsure, say M.
+config VIRTIO_INPUT
+ tristate "Virtio input driver"
+ depends on VIRTIO
+ depends on INPUT
+ ---help---
+ This driver supports virtio input devices such as
+ keyboards, mice and tablets.
+
+ If unsure, say M.
+
config VIRTIO_MMIO
tristate "Platform bus driver for memory mapped virtio devices"
depends on HAS_IOMEM
diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile
index d85565b..41e30e3 100644
--- a/drivers/virtio/Makefile
+++ b/drivers/virtio/Makefile
@@ -4,3 +4,4 @@ obj-$(CONFIG_VIRTIO_PCI) += virtio_pci.o
virtio_pci-y := virtio_pci_modern.o virtio_pci_common.o
virtio_pci-$(CONFIG_VIRTIO_PCI_LEGACY) += virtio_pci_legacy.o
obj-$(CONFIG_VIRTIO_BALLOON) += virtio_balloon.o
+obj-$(CONFIG_VIRTIO_INPUT) += virtio_input.o
diff --git a/drivers/virtio/virtio_input.c b/drivers/virtio/virtio_input.c
new file mode 100644
index 0000000..dd3215e
--- /dev/null
+++ b/drivers/virtio/virtio_input.c
@@ -0,0 +1,335 @@
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/input.h>
+
+#include <uapi/linux/virtio_ids.h>
+#include <uapi/linux/virtio_input.h>
+
+struct virtio_input {
+ struct virtio_device *vdev;
+ struct input_dev *idev;
+ char name[64];
+ char serial[64];
+ char phys[64];
+ struct virtqueue *evt, *sts;
+ struct virtio_input_event evts[64];
+ struct mutex lock;
+};
+
+static ssize_t serial_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct input_dev *idev = to_input_dev(dev);
+ struct virtio_input *vi = input_get_drvdata(idev);
+
+ return sprintf(buf, "%s\n", vi->serial);
+}
+static DEVICE_ATTR_RO(serial);
+
+static struct attribute *dev_attrs[] = {
+ &dev_attr_serial.attr,
+ NULL
+};
+
+static umode_t dev_attrs_are_visible(struct kobject *kobj,
+ struct attribute *a, int n)
+{
+ struct device *dev = container_of(kobj, struct device, kobj);
+ struct input_dev *idev = to_input_dev(dev);
+ struct virtio_input *vi = input_get_drvdata(idev);
+
+ if (a == &dev_attr_serial.attr && !strlen(vi->serial))
+ return 0;
+
+ return a->mode;
+}
+
+static struct attribute_group dev_attr_grp = {
+ .attrs = dev_attrs,
+ .is_visible = dev_attrs_are_visible,
+};
+
+static const struct attribute_group *dev_attr_groups[] = {
+ &dev_attr_grp,
+ NULL
+};
+
+static void virtinput_queue_evtbuf(struct virtio_input *vi,
+ struct virtio_input_event *evtbuf)
+{
+ struct scatterlist sg[1];
+
+ sg_init_one(sg, evtbuf, sizeof(*evtbuf));
+ virtqueue_add_inbuf(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
+}
+
+static void virtinput_recv_events(struct virtqueue *vq)
+{
+ struct virtio_input *vi = vq->vdev->priv;
+ struct virtio_input_event *event;
+ unsigned int len;
+
+ mutex_lock(&vi->lock);
+ while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
+ input_event(vi->idev,
+ le16_to_cpu(event->type),
+ le16_to_cpu(event->code),
+ le32_to_cpu(event->value));
+ virtinput_queue_evtbuf(vi, event);
+ }
+ virtqueue_kick(vq);
+ mutex_unlock(&vi->lock);
+}
+
+static int virtinput_send_status(struct virtio_input *vi,
+ u16 type, u16 code, s32 value)
+{
+ struct virtio_input_event *stsbuf;
+ struct scatterlist sg[1];
+ int rc;
+
+ stsbuf = kzalloc(sizeof(*stsbuf), GFP_ATOMIC);
+ if (!stsbuf)
+ return -ENOMEM;
+
+ stsbuf->type = cpu_to_le16(type);
+ stsbuf->code = cpu_to_le16(code);
+ stsbuf->value = cpu_to_le32(value);
+ sg_init_one(sg, stsbuf, sizeof(*stsbuf));
+
+ mutex_lock(&vi->lock);
+ rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
+ virtqueue_kick(vi->sts);
+ mutex_unlock(&vi->lock);
+
+ if (rc != 0)
+ kfree(stsbuf);
+ return rc;
+}
+
+static void virtinput_recv_status(struct virtqueue *vq)
+{
+ struct virtio_input *vi = vq->vdev->priv;
+ struct virtio_input_event *stsbuf;
+ unsigned int len;
+
+ mutex_lock(&vi->lock);
+ while ((stsbuf = virtqueue_get_buf(vi->sts, &len)) != NULL)
+ kfree(stsbuf);
+ mutex_unlock(&vi->lock);
+}
+
+static int virtinput_status(struct input_dev *idev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct virtio_input *vi = input_get_drvdata(idev);
+
+ return virtinput_send_status(vi, type, code, value);
+}
+
+static size_t virtinput_cfg_select(struct virtio_input *vi,
+ u8 select, u8 subsel)
+{
+ u8 size;
+
+ virtio_cwrite(vi->vdev, struct virtio_input_config, select, &select);
+ virtio_cwrite(vi->vdev, struct virtio_input_config, subsel, &subsel);
+ virtio_cread(vi->vdev, struct virtio_input_config, size, &size);
+ return size;
+}
+
+static void virtinput_cfg_bits(struct virtio_input *vi, int select, int subsel,
+ unsigned long *bits, unsigned int bitcount)
+{
+ unsigned int bit;
+ size_t bytes;
+ u8 cfg = 0;
+
+ bytes = virtinput_cfg_select(vi, select, subsel);
+ if (!bytes)
+ return;
+ if (bitcount > bytes*8)
+ bitcount = bytes*8;
+ if (select == VIRTIO_INPUT_CFG_EV_BITS)
+ set_bit(subsel, vi->idev->evbit);
+ for (bit = 0; bit < bitcount; bit++) {
+ if ((bit % 8) == 0)
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.bitmap[bit / 8], &cfg);
+ if (cfg & (1 << (bit % 8)))
+ set_bit(bit, bits);
+ }
+}
+
+static void virtinput_cfg_abs(struct virtio_input *vi, int abs)
+{
+ u32 mi, ma, fu, fl;
+
+ virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ABS_INFO, abs);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.min, &mi);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.max, &ma);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.fuzz, &fu);
+ virtio_cread(vi->vdev, struct virtio_input_config, u.abs.flat, &fl);
+ input_set_abs_params(vi->idev, abs, mi, ma, fu, fl);
+}
+
+static int virtinput_init_vqs(struct virtio_input *vi)
+{
+ struct virtqueue *vqs[2];
+ vq_callback_t *cbs[] = { virtinput_recv_events,
+ virtinput_recv_status };
+ static const char * names[] = { "events", "status" };
+ int i, err, size;
+
+ err = vi->vdev->config->find_vqs(vi->vdev, 2, vqs, cbs, names);
+ if (err)
+ return err;
+ vi->evt = vqs[0];
+ vi->sts = vqs[1];
+
+ size = virtqueue_get_vring_size(vi->evt);
+ if (size > ARRAY_SIZE(vi->evts))
+ size = ARRAY_SIZE(vi->evts);
+ for (i = 0; i < size; i++)
+ virtinput_queue_evtbuf(vi, &vi->evts[i]);
+ virtqueue_kick(vi->evt);
+
+ return 0;
+}
+
+static int virtinput_probe(struct virtio_device *vdev)
+{
+ struct virtio_input *vi;
+ size_t size;
+ int abs, err;
+
+ vi = kzalloc(sizeof(*vi), GFP_KERNEL);
+ if (!vi)
+ return -ENOMEM;
+
+ vdev->priv = vi;
+ vi->vdev = vdev;
+ mutex_init(&vi->lock);
+
+ err = virtinput_init_vqs(vi);
+ if (err)
+ goto err_init_vq;
+
+ vi->idev = input_allocate_device();
+ if (!vi->idev) {
+ err = -ENOMEM;
+ goto err_input_alloc;
+ }
+ input_set_drvdata(vi->idev, vi);
+
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_NAME, 0);
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
+ vi->name, min(size, sizeof(vi->name)));
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_SERIAL, 0);
+ virtio_cread_bytes(vi->vdev, offsetof(struct virtio_input_config, u),
+ vi->serial, min(size, sizeof(vi->serial)));
+ snprintf(vi->phys, sizeof(vi->phys),
+ "virtio%d/input0", vdev->index);
+ vi->idev->name = vi->name;
+ vi->idev->phys = vi->phys;
+ vi->idev->uniq = vi->serial;
+
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_ID_DEVIDS, 0);
+ if (size >= 8) {
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.bustype, &vi->idev->id.bustype);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.vendor, &vi->idev->id.vendor);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.product, &vi->idev->id.product);
+ virtio_cread(vi->vdev, struct virtio_input_config,
+ u.ids.version, &vi->idev->id.version);
+ } else {
+ vi->idev->id.bustype = BUS_VIRTUAL;
+ }
+
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_PROP_BITS, 0,
+ vi->idev->propbit, INPUT_PROP_CNT);
+ size = virtinput_cfg_select(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REP);
+ if (size)
+ set_bit(EV_REP, vi->idev->evbit);
+
+ vi->idev->dev.parent = &vdev->dev;
+ vi->idev->dev.groups = dev_attr_groups;
+ vi->idev->event = virtinput_status;
+
+ /* device -> kernel */
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY,
+ vi->idev->keybit, KEY_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_REL,
+ vi->idev->relbit, REL_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS,
+ vi->idev->absbit, ABS_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_MSC,
+ vi->idev->mscbit, MSC_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SW,
+ vi->idev->swbit, SW_CNT);
+
+ /* kernel -> device */
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_LED,
+ vi->idev->ledbit, LED_CNT);
+ virtinput_cfg_bits(vi, VIRTIO_INPUT_CFG_EV_BITS, EV_SND,
+ vi->idev->sndbit, SND_CNT);
+
+ if (test_bit(EV_ABS, vi->idev->evbit)) {
+ for (abs = 0; abs < ABS_CNT; abs++) {
+ if (!test_bit(abs, vi->idev->absbit))
+ continue;
+ virtinput_cfg_abs(vi, abs);
+ }
+ }
+ virtio_device_ready(vdev);
+
+ err = input_register_device(vi->idev);
+ if (err)
+ goto err_input_register;
+
+ return 0;
+
+err_input_register:
+ input_free_device(vi->idev);
+err_input_alloc:
+ vdev->config->del_vqs(vdev);
+err_init_vq:
+ kfree(vi);
+ return err;
+}
+
+static void virtinput_remove(struct virtio_device *vdev)
+{
+ struct virtio_input *vi = vdev->priv;
+
+ input_unregister_device(vi->idev);
+ input_free_device(vi->idev);
+ vdev->config->del_vqs(vdev);
+ kfree(vi);
+}
+
+static unsigned int features[] = {
+};
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_INPUT, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static struct virtio_driver virtio_input_driver = {
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .id_table = id_table,
+ .probe = virtinput_probe,
+ .remove = virtinput_remove,
+};
+
+module_virtio_driver(virtio_input_driver);
+MODULE_DEVICE_TABLE(virtio, id_table);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Virtio input device driver");
+MODULE_AUTHOR("Gerd Hoffmann <kraxel-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>");
diff --git a/include/uapi/linux/Kbuild b/include/uapi/linux/Kbuild
index 68ceb97..04b829e 100644
--- a/include/uapi/linux/Kbuild
+++ b/include/uapi/linux/Kbuild
@@ -430,6 +430,7 @@ header-y += virtio_blk.h
header-y += virtio_config.h
header-y += virtio_console.h
header-y += virtio_ids.h
+header-y += virtio_input.h
header-y += virtio_net.h
header-y += virtio_pci.h
header-y += virtio_ring.h
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index 284fc3a..5f60aa4 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -39,5 +39,6 @@
#define VIRTIO_ID_9P 9 /* 9p virtio console */
#define VIRTIO_ID_RPROC_SERIAL 11 /* virtio remoteproc serial link */
#define VIRTIO_ID_CAIF 12 /* Virtio caif */
+#define VIRTIO_ID_INPUT 18 /* virtio input */
#endif /* _LINUX_VIRTIO_IDS_H */
diff --git a/include/uapi/linux/virtio_input.h b/include/uapi/linux/virtio_input.h
new file mode 100644
index 0000000..9302422
--- /dev/null
+++ b/include/uapi/linux/virtio_input.h
@@ -0,0 +1,75 @@
+#ifndef _LINUX_VIRTIO_INPUT_H
+#define _LINUX_VIRTIO_INPUT_H
+/* This header is BSD licensed so anyone can use the definitions to implement
+ * compatible drivers/servers.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of IBM nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL IBM OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE. */
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+
+enum virtio_input_config_select {
+ VIRTIO_INPUT_CFG_UNSET = 0x00,
+ VIRTIO_INPUT_CFG_ID_NAME = 0x01,
+ VIRTIO_INPUT_CFG_ID_SERIAL = 0x02,
+ VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03,
+ VIRTIO_INPUT_CFG_PROP_BITS = 0x10,
+ VIRTIO_INPUT_CFG_EV_BITS = 0x11,
+ VIRTIO_INPUT_CFG_ABS_INFO = 0x12,
+};
+
+struct virtio_input_absinfo {
+ __u32 min;
+ __u32 max;
+ __u32 fuzz;
+ __u32 flat;
+};
+
+struct virtio_input_devids {
+ __u16 bustype;
+ __u16 vendor;
+ __u16 product;
+ __u16 version;
+};
+
+struct virtio_input_config {
+ __u8 select;
+ __u8 subsel;
+ __u8 size;
+ __u8 reserved;
+ union {
+ char string[128];
+ __u8 bitmap[128];
+ struct virtio_input_absinfo abs;
+ struct virtio_input_devids ids;
+ } u;
+};
+
+struct virtio_input_event {
+ __le16 type;
+ __le16 code;
+ __le32 value;
+};
+
+#endif /* _LINUX_VIRTIO_INPUT_H */
--
1.8.3.1
^ permalink raw reply related
* Re: [v10 3/5] ext4: adds project quota support
From: Li Xi @ 2015-03-20 13:02 UTC (permalink / raw)
To: Andreas Dilger
Cc: linux-fsdevel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Ext4 Developers List,
linux-api-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Theodore Ts'o, Jan Kara,
viro-RmSDqhL/yNMiFSDQTTA3OLVCufUGDwFn@public.gmane.org,
hch-wEGCiKHe2LqWVfeAwA7xHQ@public.gmane.org, Dmitry Monakhov
In-Reply-To: <D6B2436D-287C-4CC2-8646-8FF0A9F74E07-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org>
On Fri, Mar 20, 2015 at 3:23 AM, Andreas Dilger <adilger-m1MBpc4rdrD3fQ9qLvQP4Q@public.gmane.org> wrote:
> On Mar 18, 2015, at 1:04 PM, Li Xi <pkuelelixi-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
>>
>> This patch adds mount options for enabling/disabling project quota
>> accounting and enforcement. A new specific inode is also used for
>> project quota accounting.
>>
>> Signed-off-by: Li Xi <lixi-LfVdkaOWEx8@public.gmane.org>
>> Signed-off-by: Dmitry Monakhov <dmonakhov-GEFAQzZX7r8dnm+yROfE0A@public.gmane.org>
>> Reviewed-by: Jan Kara <jack-AlSwsSmVLrQ@public.gmane.org>
>> ---
>> fs/ext4/ext4.h | 9 +++++-
>> fs/ext4/super.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++------
>> 2 files changed, 74 insertions(+), 11 deletions(-)
>>
>> diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h
>> index 7acb2da..ad650d4 100644
>> --- a/fs/ext4/ext4.h
>> +++ b/fs/ext4/ext4.h
>> @@ -208,6 +208,7 @@ struct ext4_io_submit {
>> #define EXT4_UNDEL_DIR_INO 6 /* Undelete directory inode */
>> #define EXT4_RESIZE_INO 7 /* Reserved group descriptors inode */
>> #define EXT4_JOURNAL_INO 8 /* Journal inode */
>> +#define EXT4_PRJ_QUOTA_INO 11 /* Project quota inode */
>
> To me it doesn't make sense to reserve this inode number if we aren't
> going to use it for project quota. I think it is just confusing for
> developers that incorrectly think this is correct instead of checking
> s_prj_quota_inum.
Yeah, I agree that this definition is confusing and it is not so useful since
it can be replaced by s_prj_quota_inum. I will removing this definition.
In my current patches for E2fsprogs, project quota still uses inode number
11. And if project quota feature is enabled, the s_first_ino will be set to
s_prj_quota_inum + 1. I hope this is a acceptable implementation.
>
>> /* First non-reserved inode for old ext4 filesystems */
>> #define EXT4_GOOD_OLD_FIRST_INO 11
>> @@ -987,6 +988,7 @@ struct ext4_inode_info {
>> #define EXT4_MOUNT_DIOREAD_NOLOCK 0x400000 /* Enable support for dio read nolocking */
>> #define EXT4_MOUNT_JOURNAL_CHECKSUM 0x800000 /* Journal checksums */
>> #define EXT4_MOUNT_JOURNAL_ASYNC_COMMIT 0x1000000 /* Journal Async Commit */
>> +#define EXT4_MOUNT_PRJJQUOTA 0x2000000 /* Journaled Project quota */
>> #define EXT4_MOUNT_DELALLOC 0x8000000 /* Delalloc support */
>> #define EXT4_MOUNT_DATA_ERR_ABORT 0x10000000 /* Abort on file data write */
>> #define EXT4_MOUNT_BLOCK_VALIDITY 0x20000000 /* Block validity checking */
>> @@ -1169,7 +1171,8 @@ struct ext4_super_block {
>> __le32 s_overhead_clusters; /* overhead blocks/clusters in fs */
>> __le32 s_backup_bgs[2]; /* groups with sparse_super2 SBs */
>> __u8 s_encrypt_algos[4]; /* Encryption algorithms in use */
>> - __le32 s_reserved[105]; /* Padding to the end of the block */
>> + __le32 s_prj_quota_inum; /* inode for tracking project quota */
>> + __le32 s_reserved[104]; /* Padding to the end of the block */
>> __le32 s_checksum; /* crc32c(superblock) */
>> };
>>
>> @@ -1184,7 +1187,7 @@ struct ext4_super_block {
>> #define EXT4_MF_FS_ABORTED 0x0002 /* Fatal error detected */
>>
>> /* Number of quota types we support */
>> -#define EXT4_MAXQUOTAS 2
>> +#define EXT4_MAXQUOTAS 3
>>
>> /*
>> * fourth extended-fs super-block data in memory
>> @@ -1376,6 +1379,8 @@ static inline int ext4_valid_inum(struct super_block *sb, unsigned long ino)
>> ino == EXT4_BOOT_LOADER_INO ||
>> ino == EXT4_JOURNAL_INO ||
>> ino == EXT4_RESIZE_INO ||
>> + (EXT4_FIRST_INO(sb) > EXT4_PRJ_QUOTA_INO &&
>> + ino == EXT4_PRJ_QUOTA_INO) ||
>
> This check isn't correct either, if s_prj_quota_inum != EXT4_PRJ_QUOTA_INO.
>
> Cheers, Andreas
>
>> (ino >= EXT4_FIRST_INO(sb) &&
>> ino <= le32_to_cpu(EXT4_SB(sb)->s_es->s_inodes_count));
>> }
>> diff --git a/fs/ext4/super.c b/fs/ext4/super.c
>> index 04c6cc3..4077932 100644
>> --- a/fs/ext4/super.c
>> +++ b/fs/ext4/super.c
>> @@ -1036,8 +1036,8 @@ static int bdev_try_to_free_page(struct super_block *sb, struct page *page,
>> }
>>
>> #ifdef CONFIG_QUOTA
>> -#define QTYPE2NAME(t) ((t) == USRQUOTA ? "user" : "group")
>> -#define QTYPE2MOPT(on, t) ((t) == USRQUOTA?((on)##USRJQUOTA):((on)##GRPJQUOTA))
>> +static char *quotatypes[] = INITQFNAMES;
>> +#define QTYPE2NAME(t) (quotatypes[t])
>>
>> static int ext4_write_dquot(struct dquot *dquot);
>> static int ext4_acquire_dquot(struct dquot *dquot);
>> @@ -1135,7 +1135,8 @@ enum {
>> Opt_journal_path, Opt_journal_checksum, Opt_journal_async_commit,
>> Opt_abort, Opt_data_journal, Opt_data_ordered, Opt_data_writeback,
>> Opt_data_err_abort, Opt_data_err_ignore,
>> - Opt_usrjquota, Opt_grpjquota, Opt_offusrjquota, Opt_offgrpjquota,
>> + Opt_usrjquota, Opt_grpjquota, Opt_prjjquota,
>> + Opt_offusrjquota, Opt_offgrpjquota, Opt_offprjjquota,
>> Opt_jqfmt_vfsold, Opt_jqfmt_vfsv0, Opt_jqfmt_vfsv1, Opt_quota,
>> Opt_noquota, Opt_barrier, Opt_nobarrier, Opt_err,
>> Opt_usrquota, Opt_grpquota, Opt_i_version,
>> @@ -1190,6 +1191,8 @@ static const match_table_t tokens = {
>> {Opt_usrjquota, "usrjquota=%s"},
>> {Opt_offgrpjquota, "grpjquota="},
>> {Opt_grpjquota, "grpjquota=%s"},
>> + {Opt_prjjquota, "prjjquota"},
>> + {Opt_offprjjquota, "offprjjquota"},
>> {Opt_jqfmt_vfsold, "jqfmt=vfsold"},
>> {Opt_jqfmt_vfsv0, "jqfmt=vfsv0"},
>> {Opt_jqfmt_vfsv1, "jqfmt=vfsv1"},
>> @@ -1412,11 +1415,16 @@ static const struct mount_opts {
>> {Opt_grpquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_GRPQUOTA,
>> MOPT_SET | MOPT_Q},
>> {Opt_noquota, (EXT4_MOUNT_QUOTA | EXT4_MOUNT_USRQUOTA |
>> - EXT4_MOUNT_GRPQUOTA), MOPT_CLEAR | MOPT_Q},
>> + EXT4_MOUNT_GRPQUOTA | EXT4_MOUNT_PRJJQUOTA),
>> + MOPT_CLEAR | MOPT_Q},
>> {Opt_usrjquota, 0, MOPT_Q},
>> {Opt_grpjquota, 0, MOPT_Q},
>> + {Opt_prjjquota, EXT4_MOUNT_QUOTA | EXT4_MOUNT_PRJJQUOTA,
>> + MOPT_SET | MOPT_Q},
>> {Opt_offusrjquota, 0, MOPT_Q},
>> {Opt_offgrpjquota, 0, MOPT_Q},
>> + {Opt_offprjjquota, 0, EXT4_MOUNT_PRJJQUOTA,
>> + MOPT_CLEAR | MOPT_Q},
>> {Opt_jqfmt_vfsold, QFMT_VFS_OLD, MOPT_QFMT},
>> {Opt_jqfmt_vfsv0, QFMT_VFS_V0, MOPT_QFMT},
>> {Opt_jqfmt_vfsv1, QFMT_VFS_V1, MOPT_QFMT},
>> @@ -1673,7 +1681,9 @@ static int parse_options(char *options, struct super_block *sb,
>> "feature is enabled");
>> return 0;
>> }
>> - if (sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
>> + if (sbi->s_qf_names[USRQUOTA] ||
>> + sbi->s_qf_names[GRPQUOTA] ||
>> + test_opt(sb, PRJJQUOTA)) {
>> if (test_opt(sb, USRQUOTA) && sbi->s_qf_names[USRQUOTA])
>> clear_opt(sb, USRQUOTA);
>>
>> @@ -3944,7 +3954,7 @@ static int ext4_fill_super(struct super_block *sb, void *data, int silent)
>> sb->s_qcop = &ext4_qctl_sysfile_operations;
>> else
>> sb->s_qcop = &ext4_qctl_operations;
>> - sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP;
>> + sb->s_quota_types = QTYPE_MASK_USR | QTYPE_MASK_GRP | QTYPE_MASK_PRJ;
>> #endif
>> memcpy(sb->s_uuid, es->s_uuid, sizeof(es->s_uuid));
>>
>> @@ -5040,6 +5050,46 @@ restore_opts:
>> return err;
>> }
>>
>> +static int ext4_statfs_project(struct super_block *sb,
>> + kprojid_t projid, struct kstatfs *buf)
>> +{
>> + struct kqid qid;
>> + struct dquot *dquot;
>> + u64 limit;
>> + u64 curblock;
>> +
>> + qid = make_kqid_projid(projid);
>> + dquot = dqget(sb, qid);
>> + if (!dquot)
>> + return -ESRCH;
>> + spin_lock(&dq_data_lock);
>> +
>> + limit = dquot->dq_dqb.dqb_bsoftlimit ?
>> + dquot->dq_dqb.dqb_bsoftlimit :
>> + dquot->dq_dqb.dqb_bhardlimit;
>> + if (limit && buf->f_blocks * buf->f_bsize > limit) {
>> + curblock = dquot->dq_dqb.dqb_curspace / buf->f_bsize;
>> + buf->f_blocks = limit / buf->f_bsize;
>> + buf->f_bfree = buf->f_bavail =
>> + (buf->f_blocks > curblock) ?
>> + (buf->f_blocks - curblock) : 0;
>> + }
>> +
>> + limit = dquot->dq_dqb.dqb_isoftlimit ?
>> + dquot->dq_dqb.dqb_isoftlimit :
>> + dquot->dq_dqb.dqb_ihardlimit;
>> + if (limit && buf->f_files > limit) {
>> + buf->f_files = limit;
>> + buf->f_ffree =
>> + (buf->f_files > dquot->dq_dqb.dqb_curinodes) ?
>> + (buf->f_files - dquot->dq_dqb.dqb_curinodes) : 0;
>> + }
>> +
>> + spin_unlock(&dq_data_lock);
>> + dqput(dquot);
>> + return 0;
>> +}
>> +
>> static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
>> {
>> struct super_block *sb = dentry->d_sb;
>> @@ -5048,6 +5098,7 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
>> ext4_fsblk_t overhead = 0, resv_blocks;
>> u64 fsid;
>> s64 bfree;
>> + struct inode *inode = dentry->d_inode;
>> resv_blocks = EXT4_C2B(sbi, atomic64_read(&sbi->s_resv_clusters));
>>
>> if (!test_opt(sb, MINIX_DF))
>> @@ -5072,6 +5123,9 @@ static int ext4_statfs(struct dentry *dentry, struct kstatfs *buf)
>> buf->f_fsid.val[0] = fsid & 0xFFFFFFFFUL;
>> buf->f_fsid.val[1] = (fsid >> 32) & 0xFFFFFFFFUL;
>>
>> + if (ext4_test_inode_flag(inode, EXT4_INODE_PROJINHERIT) &&
>> + sb_has_quota_limits_enabled(sb, PRJQUOTA))
>> + ext4_statfs_project(sb, EXT4_I(inode)->i_projid, buf);
>> return 0;
>> }
>>
>> @@ -5152,7 +5206,9 @@ static int ext4_mark_dquot_dirty(struct dquot *dquot)
>>
>> /* Are we journaling quotas? */
>> if (EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA) ||
>> - sbi->s_qf_names[USRQUOTA] || sbi->s_qf_names[GRPQUOTA]) {
>> + sbi->s_qf_names[USRQUOTA] ||
>> + sbi->s_qf_names[GRPQUOTA] ||
>> + test_opt(sb, PRJJQUOTA)) {
>> dquot_mark_dquot_dirty(dquot);
>> return ext4_write_dquot(dquot);
>> } else {
>> @@ -5236,7 +5292,8 @@ static int ext4_quota_enable(struct super_block *sb, int type, int format_id,
>> struct inode *qf_inode;
>> unsigned long qf_inums[EXT4_MAXQUOTAS] = {
>> le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
>> - le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
>> + le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
>> + le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
>> };
>>
>> BUG_ON(!EXT4_HAS_RO_COMPAT_FEATURE(sb, EXT4_FEATURE_RO_COMPAT_QUOTA));
>> @@ -5264,7 +5321,8 @@ static int ext4_enable_quotas(struct super_block *sb)
>> int type, err = 0;
>> unsigned long qf_inums[EXT4_MAXQUOTAS] = {
>> le32_to_cpu(EXT4_SB(sb)->s_es->s_usr_quota_inum),
>> - le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum)
>> + le32_to_cpu(EXT4_SB(sb)->s_es->s_grp_quota_inum),
>> + le32_to_cpu(EXT4_SB(sb)->s_es->s_prj_quota_inum)
>> };
>>
>> sb_dqopt(sb)->flags |= DQUOT_QUOTA_SYS_FILE;
>> --
>> 1.7.1
>>
>
>
> Cheers, Andreas
>
>
>
>
>
^ permalink raw reply
* Re: [v10 4/5] ext4: adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR interface support
From: Li Xi @ 2015-03-20 13:24 UTC (permalink / raw)
To: Jan Kara
Cc: linux-fsdevel@vger.kernel.org, Ext4 Developers List,
linux-api@vger.kernel.org, Theodore Ts'o, Andreas Dilger,
viro@zeniv.linux.org.uk, hch@infradead.org, Dmitry Monakhov
In-Reply-To: <20150319095634.GF28368@quack.suse.cz>
On Thu, Mar 19, 2015 at 5:56 PM, Jan Kara <jack@suse.cz> wrote:
> On Thu 19-03-15 04:04:56, Li Xi wrote:
>> This patch adds FS_IOC_FSSETXATTR/FS_IOC_FSGETXATTR ioctl interface
>> support for ext4. The interface is kept consistent with
>> XFS_IOC_FSGETXATTR/XFS_IOC_FSGETXATTR.
> Two more comments below.
>
>>
>> Signed-off-by: Li Xi <lixi@ddn.com>
> ...
>> +static int ext4_ioctl_setproject(struct file *filp, __u32 projid)
>> +{
>> + struct inode *inode = file_inode(filp);
>> + struct super_block *sb = inode->i_sb;
>> + struct ext4_inode_info *ei = EXT4_I(inode);
>> + int err;
>> + handle_t *handle;
>> + kprojid_t kprojid;
>> + struct ext4_iloc iloc;
>> + struct ext4_inode *raw_inode;
>> +
>> + struct dquot *transfer_to[EXT4_MAXQUOTAS] = { };
>> +
>> + /* Make sure caller can change project. */
>> + if (!capable(CAP_SYS_ADMIN))
>> + return -EACCES;
> Why do you test capabilities here when you already test permission in
> EXT4_IOC_FSSETXATTR? Furthermore this test is too restrictive. Just delete
> it.
It seems we need this restrictive permission. Otherwise the owner of the file
can change the project ID to any other project. That means, the owner can
walk around the quota limit whenever he/she wants. But I agree checking
permission twice is not good.
> ...
>> -flags_out:
>> + err = ext4_ioctl_setflags(inode, flags);
>> mutex_unlock(&inode->i_mutex);
>> - mnt_drop_write_file(filp);
>> + mnt_drop_write(filp->f_path.mnt);
> Why did you change this? mnt_drop_write_file() should stay here.
Sorry, my mistake.
Regards,
Li Xi
^ permalink raw reply
* Re: [PATCH] audit.h: remove the macro AUDIT_ARCH_ARMEB definition
From: Paul Moore @ 2015-03-20 13:29 UTC (permalink / raw)
To: roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w
Cc: Eric Paris, linux-audit-H+wXaHxf7aLQT0dZR+AlfA,
linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <1426827329-27976-1-git-send-email-roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
On Fri, Mar 20, 2015 at 12:55 AM, <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:
> From: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>
> After 2f9783669 [ARM: 7412/1: audit: use only AUDIT_ARCH_ARM regardless
> of endianness], no kernel user uses this macro;
>
> Keeping this macro, only makes the compiling old version audit [before
> changeset 931 Improve ARM and AARCH64 support] success, but the audit
> program can not work with the kernel after 2f9783669 still,
> since no syscall entry is enabled for AUDIT_ARCH_ARMEB in kernel.
>
> so remove it to force to use the latest audit program
>
> Signed-off-by: Li RongQing <roy.qing.li-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> ---
> other workaround is to define AUDIT_ARCH_ARMEB as AUDIT_ARCH_ARM,
> but it seems very strange
>
> include/uapi/linux/audit.h | 1 -
> 1 file changed, 1 deletion(-)
Since this #define lives in the user visible headers I don't want to
remove it and risk causing a userspace breakage. Leaving the #define
in the header, even if it is unused by modern userspace, is harmless.
> diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
> index d3475e1..125aa49 100644
> --- a/include/uapi/linux/audit.h
> +++ b/include/uapi/linux/audit.h
> @@ -351,7 +351,6 @@ enum {
> #define AUDIT_ARCH_AARCH64 (EM_AARCH64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
> #define AUDIT_ARCH_ALPHA (EM_ALPHA|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
> #define AUDIT_ARCH_ARM (EM_ARM|__AUDIT_ARCH_LE)
> -#define AUDIT_ARCH_ARMEB (EM_ARM)
> #define AUDIT_ARCH_CRIS (EM_CRIS|__AUDIT_ARCH_LE)
> #define AUDIT_ARCH_FRV (EM_FRV)
> #define AUDIT_ARCH_I386 (EM_386|__AUDIT_ARCH_LE)
> --
> 2.1.0
>
--
paul moore
www.paul-moore.com
^ permalink raw reply
* Re: [PATCH v4] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 13:43 UTC (permalink / raw)
To: Varka Bhadram, mark.rutland, robh+dt, Pawel.Moll, ijc+devicetree,
galak, dmitry.torokhov, alexander.stein
Cc: hadess, akpm, gregkh, linux-api, devicetree, linux-input,
linux-kernel, Martin Kepplinger, Christoph Muellner
In-Reply-To: <550C0502.7090507@gmail.com>
Am 2015-03-20 um 12:31 schrieb Varka Bhadram:
> On 03/20/2015 04:50 PM, Martin Kepplinger wrote:
>> From: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
>>
( ...)
>> + return 0;
>> +
>> + err_free_irq:
>> + if (mma->irq)
>> + free_irq(mma->irq, mma);
>> + err_free_mem:
>> + kfree(mma);
>
> If we use devm_* API's above steps are not required
>
>> + err_out:
>> + return err;
>> +}
>> +
>> +static int mma8653fc_remove(struct i2c_client *client)
>> +{
>> + struct mma8653fc *mma = i2c_get_clientdata(client);
>> +
>> + free_irq(mma->irq, mma);
>> + dev_dbg(&client->dev, "unregistered accelerometer\n");
>> + kfree(mma);
>
> same as above..
>
thanks!
^ permalink raw reply
* [PATCH V7] Allow compaction of unevictable pages
From: Eric B Munson @ 2015-03-20 13:49 UTC (permalink / raw)
To: Andrew Morton
Cc: Eric B Munson, Vlastimil Babka, Thomas Gleixner,
Christoph Lameter, Peter Zijlstra, Mel Gorman, David Rientjes,
Rik van Riel, Michal Hocko, linux-doc, linux-rt-users, linux-mm,
linux-api, linux-kernel
Currently, pages which are marked as unevictable are protected from
compaction, but not from other types of migration. The POSIX real time
extension explicitly states that mlock() will prevent a major page
fault, but the spirit of this is that mlock() should give a process the
ability to control sources of latency, including minor page faults.
However, the mlock manpage only explicitly says that a locked page will
not be written to swap and this can cause some confusion. The
compaction code today does not give a developer who wants to avoid swap
but wants to have large contiguous areas available any method to achieve
this state. This patch introduces a sysctl for controlling compaction
behavior with respect to the unevictable lru. Users that demand no page
faults after a page is present can set compact_unevictable_allowed to 0
and users who need the large contiguous areas can enable compaction on
locked memory by leaving the default value of 1.
To illustrate this problem I wrote a quick test program that mmaps a
large number of 1MB files filled with random data. These maps are
created locked and read only. Then every other mmap is unmapped and I
attempt to allocate huge pages to the static huge page pool. When the
compact_unevictable_allowed sysctl is 0, I cannot allocate hugepages
after fragmenting memory. When the value is set to 1, allocations
succeed.
Signed-off-by: Eric B Munson <emunson@akamai.com>
Acked-by: Michal Hocko <mhocko@suse.cz>
Acked-by: Vlastimil Babka <vbabka@suse.cz>
Acked-by: Christoph Lameter <cl@linux.com>
Acked-by: David Rientjes <rientjes@google.com>
Cc: Vlastimil Babka <vbabka@suse.cz>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Christoph Lameter <cl@linux.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Mel Gorman <mgorman@suse.de>
Cc: David Rientjes <rientjes@google.com>
Cc: Rik van Riel <riel@redhat.com>
Cc: Michal Hocko <mhocko@suse.cz>
Cc: linux-doc@vger.kernel.org
Cc: linux-rt-users@vger.kernel.org
Cc: linux-mm@kvack.org
Cc: linux-api@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
---
Changes from V6:
* Fixed changelog spelling
Documentation/sysctl/vm.txt | 11 +++++++++++
include/linux/compaction.h | 1 +
kernel/sysctl.c | 9 +++++++++
mm/compaction.c | 7 +++++++
4 files changed, 28 insertions(+)
diff --git a/Documentation/sysctl/vm.txt b/Documentation/sysctl/vm.txt
index 902b457..9832ec5 100644
--- a/Documentation/sysctl/vm.txt
+++ b/Documentation/sysctl/vm.txt
@@ -21,6 +21,7 @@ Currently, these files are in /proc/sys/vm:
- admin_reserve_kbytes
- block_dump
- compact_memory
+- compact_unevictable_allowed
- dirty_background_bytes
- dirty_background_ratio
- dirty_bytes
@@ -106,6 +107,16 @@ huge pages although processes will also directly compact memory as required.
==============================================================
+compact_unevictable_allowed
+
+Available only when CONFIG_COMPACTION is set. When set to 1, compaction is
+allowed to examine the unevictable lru (mlocked pages) for pages to compact.
+This should be used on systems where stalls for minor page faults are an
+acceptable trade for large contiguous free memory. Set to 0 to prevent
+compaction from moving pages that are unevictable. Default value is 1.
+
+==============================================================
+
dirty_background_bytes
Contains the amount of dirty memory at which the background kernel
diff --git a/include/linux/compaction.h b/include/linux/compaction.h
index a014559..aa8f61c 100644
--- a/include/linux/compaction.h
+++ b/include/linux/compaction.h
@@ -34,6 +34,7 @@ extern int sysctl_compaction_handler(struct ctl_table *table, int write,
extern int sysctl_extfrag_threshold;
extern int sysctl_extfrag_handler(struct ctl_table *table, int write,
void __user *buffer, size_t *length, loff_t *ppos);
+extern int sysctl_compact_unevictable_allowed;
extern int fragmentation_index(struct zone *zone, unsigned int order);
extern unsigned long try_to_compact_pages(gfp_t gfp_mask, unsigned int order,
diff --git a/kernel/sysctl.c b/kernel/sysctl.c
index 88ea2d6..2f6c880 100644
--- a/kernel/sysctl.c
+++ b/kernel/sysctl.c
@@ -1313,6 +1313,15 @@ static struct ctl_table vm_table[] = {
.extra1 = &min_extfrag_threshold,
.extra2 = &max_extfrag_threshold,
},
+ {
+ .procname = "compact_unevictable_allowed",
+ .data = &sysctl_compact_unevictable_allowed,
+ .maxlen = sizeof(int),
+ .mode = 0644,
+ .proc_handler = proc_dointvec,
+ .extra1 = &zero,
+ .extra2 = &one,
+ },
#endif /* CONFIG_COMPACTION */
{
diff --git a/mm/compaction.c b/mm/compaction.c
index 8c0d945..ad88a8c 100644
--- a/mm/compaction.c
+++ b/mm/compaction.c
@@ -1047,6 +1047,12 @@ typedef enum {
} isolate_migrate_t;
/*
+ * Allow userspace to control policy on scanning the unevictable LRU for
+ * compactable pages.
+ */
+int sysctl_compact_unevictable_allowed __read_mostly = 1;
+
+/*
* Isolate all pages that can be migrated from the first suitable block,
* starting at the block pointed to by the migrate scanner pfn within
* compact_control.
@@ -1057,6 +1063,7 @@ static isolate_migrate_t isolate_migratepages(struct zone *zone,
unsigned long low_pfn, end_pfn;
struct page *page;
const isolate_mode_t isolate_mode =
+ (sysctl_compact_unevictable_allowed ? ISOLATE_UNEVICTABLE : 0) |
(cc->mode == MIGRATE_ASYNC ? ISOLATE_ASYNC_MIGRATE : 0);
/*
--
1.7.9.5
^ permalink raw reply related
* Re: [PATCH v2] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 13:56 UTC (permalink / raw)
To: Benjamin Tissoires, Martin Kepplinger
Cc: Bastien Nocera, Alexander Stein, robh+dt, pawel.moll,
mark.rutland, ijc+devicetree, galak, Dmitry Torokhov,
Andrew Morton, Greg Kroah-Hartman, linux-api, devicetree,
linux-input, linux-kernel@vger.kernel.org, Christoph Muellner
In-Reply-To: <CAN+gG=GGFptRo=FXLp7S_Y1+mwerzYNN+Sf+6ZCwSpmTU-w9ag@mail.gmail.com>
Am 2015-03-20 um 13:27 schrieb Benjamin Tissoires:
> On Fri, Mar 20, 2015 at 7:26 AM, Martin Kepplinger
> <martin.kepplinger@theobroma-systems.com> wrote:
>> Am 2015-03-19 um 11:22 schrieb Bastien Nocera:
>>> On Wed, 2015-03-18 at 19:28 +0100, Martin Kepplinger wrote:
>>>> Am 2015-03-18 um 19:05 schrieb Bastien Nocera:
>>>>> On Wed, 2015-03-18 at 19:02 +0100, Martin Kepplinger wrote:
>>>>>> Am 2015-03-18 um 17:59 schrieb Bastien Nocera:
>>>>>>> On Wed, 2015-03-18 at 17:42 +0100, Martin Kepplinger wrote:
>>>>>>>>
>>>>>>> <snip>
>>>>>>>> It could have gone to drivers/iio/accel if it would use an
>>>>>>>> iio interface, which would make more sense, you are right,
>>>>>>>> but I
>>>>>>>> simply don't have the time to merge it in to iio.
>>>>>>>>
>>>>>>>> It doesn't use an input interface either but I don't see a
>>>>>>>> good place for an accelerometer that uses sysfs only.
>>>>>>>>
>>>>>>>> It works well, is a relatively recent chip and a clean
>>>>>>>> dirver. But this is all I can provide.
>>>>>>>
>>>>>>> As a person who works on the user-space interaction of those
>>>>>>> with desktops [1]: Urgh.
>>>>>>>
>>>>>>> I already have 3 (probably 4) types of accelerometers to
>>>>>>> contend with, I'm not fond of adding yet another type.
>>>>>>>
>>>>>>> Is there any way to get this hardware working outside the SoCs
>>>>>>> it's designed for (say, a device with I2C like a Raspberry
>>>>>>> Pi), so that a kind soul could handle getting this using the
>>>>>>> right interfaces?
>>>>>>>
>>>>>>
>>>>>> It works on basically any SoC and is in no way limited in this
>>>>>> regard. Sure, userspace has to expicitely support it and I hear
>>>>>> you. Using the iio interface would make more sense. I can only
>>>>>> say I'd love to have the time to move this driver over. I'm
>>>>>> very sorry.
>>>>>
>>>>> How can we get the hardware for somebody to use on their own
>>>>> laptops/embedded boards to implement this driver?
>>>>>
>>>>
>>>> It's connected over I2C. If the included documentation is not clear
>>>> please tell me what exacly. Thanks!
>>>
>>>
>>> I'll ask the question a different way: can you please give the address
>>> of a shop where that hardware is available?
>>>
>>
>> there is
>> http://www.freescale.com/webapp/sps/site/prod_summary.jsp?code=RDMMA865x&lang_cd=
>> and http://linux-sunxi.org/Inet_K970 for example. I think I saw Android
>> devices with it too, and I would guess it would be used more often if it
>> were in linux.
>
> I am going to say again (in a different way) what Bastien said. In its
> current form, even in drivers/misc, this is a NACK for me (v1, v2, v3
> & v4).
>
> Putting a driver in Linux means we have to support it forever, and
> definitively, nobody will use an accelerometer in Android if it is in
> drivers/misc.
> Android requires drivers to follow the IIO protocol. Period.
> So having your own will not help android, it will just be a burden.
>
> The sysfs you are proposing seems simple enough, but we can not afford
> having a 3rd custom way of relying the accelerometer information in
> the Linux tree (they were first handled in input, then IIO, then a
> custom sysfs).
>
> If you really want to have the driver in the tree, I won't be opposed
> if you put in under staging. This way, you can break it whenever you
> want and people won't rely on it. And then, we can use your driver as
> a base to port it to IIO.
That seems reasonable. I have prepared a (little more cleaned up) v5 of
the patch and moved it to staging, with a TODO file containing also the
current documentation. I hope to be able to do the iio integration
sometime "soon", but this could speed things up.
>
> Sorry for being rude, but I am starting to get tired of people saying
> that they don't have the time to follow what the reviewers said. You
> obviously spent some time polishing this driver, why not making it
> right from its first inclusion in the tree?
That's totally fine. Of course iio is the way to go. I had the driver
before I really knew iio and this was lazyness (when I found
Documentation/ABI/testing), plus the desire to publish it before the
chip itself is deprecated.
>
> Cheers,
> Benjamin
>
>>
>> Please refer to v4 of the patch for different questions. thanks!
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-input" in
>> the body of a message to majordomo@vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH v5] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 14:06 UTC (permalink / raw)
To: mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
Pawel.Moll-5wv7dgnIgG8, ijc+devicetree-KcIKpvwj1kUDXYZnReoRVg,
galak-sgV2jX0FEOL9JmXXK+q4OQ,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
alexander.stein-93q1YBGzJSMe9JSWTWOYM3xStJ4P+DSV,
benjamin.tissoires-Re5JQEeQqe8AvxtiuMwx3w
Cc: hadess-0MeiytkfxGOsTnJN9+BGXg,
akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
linux-api-u79uwXL29TY76Z2rM5mHXA,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA, Martin Kepplinger,
Christoph Muellner
From: Martin Kepplinger <martin.kepplinger-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org>
The MMA8653FC is a low-power, three-axis, capacitive micromachined
accelerometer with 10 bits of resolution with flexible user-programmable
options.
Embedded interrupt functions enable overall power savings, by relieving the
host processor from continuously polling data, for example using the poll()
system call.
The device can be configured to generate wake-up interrupt signals from any
combination of the configurable embedded functions, enabling the MMA8653FC
to monitor events while remaining in a low-power mode during periods of
inactivity.
This driver provides devicetree properties to program the device's behaviour
and a simple, tested and documented sysfs interface. The data sheet and more
information is available on Freescale's website.
Signed-off-by: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
---
patch revision history
......................
v5 clean up (suggested by Varka Bhadram) and move the driver to staging
v4 changes DT propery names, adds a missing interrupt source and removes
the DT option to set interrupt line active high due to unsuccesful testing
v3 moves the driver from drivers/input/misc to drivers/misc
v2 corrects licensing and commit messages and adds appropriate recipients
drivers/staging/Kconfig | 2 +
drivers/staging/mma8653fc/Kconfig | 10 +
drivers/staging/mma8653fc/Makefile | 1 +
drivers/staging/mma8653fc/TODO | 146 ++++++
drivers/staging/mma8653fc/mma8653fc.c | 864 ++++++++++++++++++++++++++++++++++
5 files changed, 1023 insertions(+)
create mode 100644 drivers/staging/mma8653fc/Kconfig
create mode 100644 drivers/staging/mma8653fc/Makefile
create mode 100644 drivers/staging/mma8653fc/TODO
create mode 100644 drivers/staging/mma8653fc/mma8653fc.c
diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
index 45baa83..661e5f5 100644
--- a/drivers/staging/Kconfig
+++ b/drivers/staging/Kconfig
@@ -108,4 +108,6 @@ source "drivers/staging/fbtft/Kconfig"
source "drivers/staging/i2o/Kconfig"
+source "drivers/staging/mma8653fc/Kconfig"
+
endif # STAGING
diff --git a/drivers/staging/mma8653fc/Kconfig b/drivers/staging/mma8653fc/Kconfig
new file mode 100644
index 0000000..988451b
--- /dev/null
+++ b/drivers/staging/mma8653fc/Kconfig
@@ -0,0 +1,10 @@
+config MMA8653FC
+ tristate "MMA8653FC - Freescale's 3-Axis, 10-bit Digital Accelerometer"
+ depends on I2C
+ default n
+ help
+ Say Y here if you want to support Freescale's MMA8653FC Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8653fc.
diff --git a/drivers/staging/mma8653fc/Makefile b/drivers/staging/mma8653fc/Makefile
new file mode 100644
index 0000000..9a245a3
--- /dev/null
+++ b/drivers/staging/mma8653fc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MMA8653FC) += mma8653fc.o
diff --git a/drivers/staging/mma8653fc/TODO b/drivers/staging/mma8653fc/TODO
new file mode 100644
index 0000000..0a31225
--- /dev/null
+++ b/drivers/staging/mma8653fc/TODO
@@ -0,0 +1,146 @@
+- move to IIO device API. The current DT/sysfs interface is documented below
+
+Documentation/ABI/testing/...
+-----------------------------
+What: /sys/bus/i2c/drivers/mma8653fc/*/standby
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org>
+Description:
+ Write 0 to this in order to turn on the device, and 1 to turn
+ it off. Read to see if it is turned on or off.
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/currentmode
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org>
+Description:
+ Reading this provides the current state of the device, read
+ directly from a register. This can be "standby", "wake" or
+ "sleep".
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/position
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger-SN7IsUiht6C/RdPyistoZJqQE7yCjDx5@public.gmane.org>
+Description:
+ Read only. Without interrupts enabled gets current position
+ values by reading. Poll "position" with interrupt conditions
+ set, to get notified; see Documentation/.../fsl,mma8653fc.txt
+
+ position file format:
+ "x y z [landscape/portrait status] [front/back status]"
+
+ x y z values:
+ in mg
+ landscape/portrait status char:
+ r landscape right
+ d portrait down
+ u portrait up
+ l landscape left
+ front/back status char:
+ f front facing
+ b back facing
+
+
+Documentation/devicetree/bindings/...
+-------------------------------------
+Required properties:
+- compatible
+ "fsl,mma8653fc"
+- reg
+ I2C address
+
+Optional properties:
+
+- interrupt-parent
+ a phandle for the interrupt controller (see
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)
+- interrupts
+ interrupt line to which the chip is connected (active low)
+- int1
+ set to use interrupt line 1, default is line 2
+ the interrupt sources can be routed to one of the two lines
+- ir-freefall-motion-x
+ activate freefall/motion interrupts on x axis
+- ir-freefall-motion-y
+ activate freefall/motion interrupts on y axis
+- ir-freefall-motion-z
+ activate freefall/motion interrupts on z axis
+- irq-threshold
+ 0 < value < 8000: threshold for motion interrupts in mg
+- ir-landscape-portrait
+ activate landscape/portrait interrupts
+- ir-auto-wake
+ activate wake/sleep change interrupts
+- ir-data-ready:
+ activate data-ready interrupts
+ Interrupt events can be activated in any combination.
+- dynamic-range
+ 2, 4, or 8: dynamic measurement range in g, default: 2
+ In ±2 g mode, sensitivity = 256 counts/g.
+ In ±4 g mode, sensitivity = 128 counts/g.
+ In ±8 g mode, sensitivity = 64 counts/g.
+- auto-wake-sleep
+ auto sleep mode (lower frequency)
+- motion-mode
+ use motion mode instead of freefall mode (trigger if >threshold).
+ per default an interrupt occurs if motion values fall below the
+ value set in "threshold" and therefore can detect free fall on the
+ vertical axis (depending on the position of the device).
+ Setting this values inverts the behaviour and an interrupt occurs
+ above the threshold value, so usually activate horizontal axis in
+ this case.
+
+- x-offset
+ 0 < value < 500: calibration offset in mg
+ The offset correction values are used to realign the Zero-g position
+ of the X, Y, and Z-axis after the device is mounted on a board.
+ this value has an offset of 250 itself:
+ 0 is -250mg, 250 is 0 mg, 500 is 250mg
+- y-offset
+ see x-offset
+- z-offset
+ see x-offset
+
+Example 1:
+for a device laying on flat ground to recognize acceleration over 100mg.
+x-axis is calibrated to +10mg. Adapt interrupt line to your device.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ motion-mode;
+ ir-freefall-motion-x;
+ ir-freefall-motion-y;
+ irq-threshold = <100>;
+ x-offset = <160>;
+};
+
+Example 2:
+for a device mounted on a wall with y being the vertical axis. This recognizes
+y-acceleration below 800mg, so free fall or changing the orientation of the
+device (y not being the vertical axis and having ~1000mg anymore).
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ ir-freefall-motion-y;
+ irq-threshold = <800>;
+};
+
+Example 3:
+minimal example. lets users read current acceleration values. No polling
+is available.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ reg = <0x1d>;
+};
diff --git a/drivers/staging/mma8653fc/mma8653fc.c b/drivers/staging/mma8653fc/mma8653fc.c
new file mode 100644
index 0000000..4bd7f99
--- /dev/null
+++ b/drivers/staging/mma8653fc/mma8653fc.c
@@ -0,0 +1,864 @@
+/*
+ * mma8653fc.c - Support for Freescale MMA8653FC 3-axis 10-bit accelerometer
+ *
+ * Copyright (c) 2014 Theobroma Systems Design and Consulting GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define DRV_NAME "mma8653fc"
+#define MMA8653FC_DEVICE_ID 0x5a
+
+#define MMA8653FC_STATUS 0x00
+
+#define ZYXOW_MASK 0x80
+#define ZYXDR 0x08
+
+#define MMA8653FC_WHO_AM_I 0x0d
+
+#define MMA8653FC_SYSMOD 0x0b
+#define STATE_STANDBY 0x00
+#define STATE_WAKE 0x01
+#define STATE_SLEEP 0x02
+
+#define MMA8450_STATUS_ZXYDR 0x08
+
+/*
+ * 10 bit output data registers
+ * MSB: 7:0 bits 9:2 of data word
+ * LSB: 7:6 bits 1:0 of data word
+ */
+#define MMA8653FC_OUT_X_MSB 0x01
+#define MMA8653FC_OUT_X_LSB 0x02
+#define MMA8653FC_OUT_Y_MSB 0x03
+#define MMA8653FC_OUT_Y_LSB 0x04
+#define MMA8653FC_OUT_Z_MSB 0x05
+#define MMA8653FC_OUT_Z_LSB 0x06
+
+/*
+ * Portrait/Landscape Status
+ */
+#define MMA8653FC_PL_STATUS 0x10
+
+#define NEWLP 0x80
+#define LAPO_HIGH 0x04
+#define LAPO_LOW 0x02
+#define BAFRO 0x01
+
+/*
+ * Portrait/Landscape Configuration
+ */
+#define MMA8653FC_PL_CFG 0x11
+
+#define PL_EN (1 << 6)
+
+/*
+ * Data calibration registers
+ */
+#define MMA8653FC_OFF_X 0x2f
+#define MMA8653FC_OFF_Y 0x30
+#define MMA8653FC_OFF_Z 0x31
+
+/* 0 to 500 for dts, but -250 to 250 in mg */
+#define DEFAULT_OFF 250
+
+/*
+ * bits 1:0 dynamic range
+ * 00 +/- 2g
+ * 01 +/- 4g
+ * 10 +/- 8g
+ *
+ * HPF_Out bit 4 - data high pass or low pass filtered
+ */
+#define MMA8653FC_XYZ_DATA_CFG 0x0e
+
+#define RANGE_MASK 0x03
+#define RANGE2G 0x00
+#define RANGE4G 0x01
+#define RANGE8G 0x02
+/* values for calculation */
+#define SHIFT_2G 8
+#define INCR_2G 128
+#define SHIFT_4G 7
+#define INCR_4G 64
+#define SHIFT_8G 6
+#define INCR_8G 32
+#define DYN_RANGE_2G 2
+#define DYN_RANGE_4G 4
+#define DYN_RANGE_8G 8
+
+/*
+ * System Control Reg 1
+ */
+#define MMA8653FC_CTRL_REG1 0x2a
+
+#define ACTIVE_BIT (1 << 0)
+#define ODR_MASK 0x38
+#define ODR_DEFAULT 0x20 /* 50 Hz */
+#define ASLP_RATE_MASK 0xc0
+#define ASLP_RATE_DEFAULT 0x80 /* 6.25 Hz */
+
+/*
+ * Sys Control Reg 2
+ *
+ * auto-sleep enable, software reset
+ */
+#define MMA8653FC_CTRL_REG2 0x2b
+
+#define SLPE (1 << 2)
+#define SELFTEST (1 << 7)
+#define SOFT_RESET (1 << 6)
+
+/*
+ * Interrupt Source
+ */
+#define MMA8653FC_INT_SOURCE 0x0c
+
+#define SRC_ASLP (1 << 7)
+#define SRC_LNDPRT (1 << 4)
+#define SRC_FF_MT (1 << 2)
+#define SRC_DRDY (1 << 0)
+
+/*
+ * Interrupt Control Register
+ *
+ * default: active low
+ * default push-pull, not open-drain
+ */
+#define MMA8653FC_CTRL_REG3 0x2c
+
+#define WAKE_LNDPRT (1 << 5)
+#define WAKE_FF_MT (1 << 3)
+#define IPOL (1 << 1)
+#define PP_OD (1 << 0)
+
+/*
+ * Interrupt Enable Register
+ */
+#define MMA8653FC_CTRL_REG4 0x2d
+
+#define INT_EN_ASLP (1 << 7)
+#define INT_EN_LNDPRT (1 << 4)
+#define INT_EN_FF_MT (1 << 2)
+#define INT_EN_DRDY (1 << 0)
+
+/*
+ * Interrupt Configuration Register
+ * bit value 0 ... INT2 (default)
+ * bit value 1 ... INT1
+ */
+#define MMA8653FC_CTRL_REG5 0x2e
+
+#define INT_CFG_ASLP (1 << 7)
+#define INT_CFG_LNDPRT (1 << 4)
+#define INT_CFG_FF_MT (1 << 2)
+#define INT_CFG_DRDY (1 << 0)
+
+/*
+ * Freefall / Motion Configuration Register
+ *
+ * Event Latch enable/disable, motion or freefall mode
+ * and event flag enable per axis
+ */
+#define MMA8653FC_FF_MT_CFG 0x15
+
+#define FF_MT_CFG_ELE (1 << 7)
+#define FF_MT_CFG_OAE (1 << 6)
+#define FF_MT_CFG_ZEFE (1 << 5)
+#define FF_MT_CFG_YEFE (1 << 4)
+#define FF_MT_CFG_XEFE (1 << 3)
+
+/*
+ * Freefall / Motion Source Register
+ */
+#define MMA8653FC_FF_MT_SRC 0x16
+
+/*
+ * Freefall / Motion Threshold Register
+ *
+ * define motion threshold
+ * 0.063 g/LSB, 127 counts(0x7f) (7 bit from LSB)
+ * range: 0.063g - 8g
+ */
+#define MMA8653FC_FF_MT_THS 0x17
+
+struct axis_triple {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct mma8653fc_pdata {
+ s8 x_axis_offset;
+ s8 y_axis_offset;
+ s8 z_axis_offset;
+ bool auto_wake_sleep;
+ u32 range;
+ bool int1;
+ bool motion_mode;
+ u8 freefall_motion_thr;
+ bool int_src_data_ready;
+ bool int_src_ff_mt_x;
+ bool int_src_ff_mt_y;
+ bool int_src_ff_mt_z;
+ bool int_src_lndprt;
+ bool int_src_aslp;
+};
+
+struct mma8653fc {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct mma8653fc_pdata pdata;
+ struct axis_triple saved;
+ char orientation;
+ char bafro;
+ bool standby;
+ int irq;
+ unsigned int_mask;
+ u8 (*read)(struct mma8653fc *, unsigned char);
+ void (*write)(struct mma8653fc *, unsigned char, unsigned char);
+};
+
+/* defaults */
+static const struct mma8653fc_pdata mma8653fc_default_init = {
+ .range = 2,
+ .x_axis_offset = DEFAULT_OFF,
+ .y_axis_offset = DEFAULT_OFF,
+ .z_axis_offset = DEFAULT_OFF,
+ .auto_wake_sleep = false,
+ .int1 = false,
+ .motion_mode = false,
+ .freefall_motion_thr = 5,
+ .int_src_data_ready = false,
+ .int_src_ff_mt_x = false,
+ .int_src_ff_mt_y = false,
+ .int_src_ff_mt_z = false,
+ .int_src_lndprt = false,
+ .int_src_aslp = false,
+};
+
+static void mma8653fc_get_triple(struct mma8653fc *mma)
+{
+ u8 buf[6];
+ u8 status;
+
+ buf[0] = 0;
+
+ status = mma->read(mma, MMA8653FC_STATUS);
+ if (status & ZYXOW_MASK)
+ dev_dbg(&mma->client->dev, "previous read not completed\n");
+
+ buf[0] = mma->read(mma, MMA8653FC_OUT_X_MSB);
+ buf[1] = mma->read(mma, MMA8653FC_OUT_X_LSB);
+ buf[2] = mma->read(mma, MMA8653FC_OUT_Y_MSB);
+ buf[3] = mma->read(mma, MMA8653FC_OUT_Y_LSB);
+ buf[4] = mma->read(mma, MMA8653FC_OUT_Z_MSB);
+ buf[5] = mma->read(mma, MMA8653FC_OUT_Z_LSB);
+
+ mutex_lock(&mma->mutex);
+ /* move from registers to s16 */
+ mma->saved.x = (buf[1] | (buf[0] << 8)) >> 6;
+ mma->saved.y = (buf[3] | (buf[2] << 8)) >> 6;
+ mma->saved.z = (buf[5] | (buf[4] << 8)) >> 6;
+ mma->saved.x = sign_extend32(mma->saved.x, 9);
+ mma->saved.y = sign_extend32(mma->saved.y, 9);
+ mma->saved.z = sign_extend32(mma->saved.z, 9);
+
+ /* calc g, see data sheet and application note */
+ switch (mma->pdata.range) {
+ case DYN_RANGE_2G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_2G) >> SHIFT_2G);
+ break;
+ case DYN_RANGE_4G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_4G) >> SHIFT_4G);
+ break;
+ case DYN_RANGE_8G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_8G) >> SHIFT_8G);
+ break;
+ default:
+ dev_err(&mma->client->dev, "internal data corrupt\n");
+ }
+ mutex_unlock(&mma->mutex);
+}
+
+static void mma8653fc_get_orientation(struct mma8653fc *mma, u8 byte)
+{
+ if ((byte & LAPO_HIGH) && !(LAPO_LOW))
+ mma->orientation = 'r'; /* landscape right */
+ if (!(byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'd'; /* portrait down */
+ if (!(byte & LAPO_HIGH) && !(byte & LAPO_LOW))
+ mma->orientation = 'u'; /* portrait up */
+ if ((byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'l'; /* landscape left */
+
+ if (byte & BAFRO)
+ mma->bafro = 'b'; /* back facing */
+ else
+ mma->bafro = 'f'; /* front facing */
+}
+
+static irqreturn_t mma8653fc_irq(int irq, void *handle)
+{
+ struct mma8653fc *mma = handle;
+ u8 int_src;
+ u8 byte;
+
+ int_src = mma->read(mma, MMA8653FC_INT_SOURCE);
+ if (int_src & SRC_DRDY)
+ /* data ready handle */
+ if (int_src & SRC_FF_MT) {
+ /* freefall/motion change handle */
+ dev_dbg(&mma->client->dev,
+ "freefall or motion change\n");
+ byte = mma->read(mma, MMA8653FC_FF_MT_SRC);
+ }
+ if (int_src & SRC_LNDPRT) {
+ /* landscape/portrait change handle */
+ dev_dbg(&mma->client->dev,
+ "landscape / portrait change\n");
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ mma8653fc_get_orientation(mma, byte);
+ }
+ if (int_src & SRC_ASLP)
+ /* autosleep change handle */
+ mma8653fc_get_triple(mma);
+
+ sysfs_notify(&mma->client->dev.kobj, NULL, "position");
+
+ return IRQ_HANDLED;
+}
+
+static void __mma8653fc_enable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte | ACTIVE_BIT);
+}
+
+static void __mma8653fc_disable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte & ~ACTIVE_BIT);
+}
+
+static ssize_t mma8653fc_standby_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", mma->standby);
+}
+
+static ssize_t mma8653fc_standby_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&mma->mutex);
+
+ if (val) {
+ if (!mma->standby)
+ __mma8653fc_disable(mma);
+ } else {
+ if (mma->standby)
+ __mma8653fc_enable(mma);
+ }
+
+ mma->standby = !!val;
+
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(standby, 0664, mma8653fc_standby_show,
+ mma8653fc_standby_store);
+
+static ssize_t mma8653fc_currentmode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_SYSMOD);
+ if (byte < 0)
+ return byte;
+
+ switch (byte) {
+ case STATE_STANDBY:
+ count = scnprintf(buf, PAGE_SIZE, "standby\n");
+ break;
+ case STATE_WAKE:
+ count = scnprintf(buf, PAGE_SIZE, "wake\n");
+ break;
+ case STATE_SLEEP:
+ count = scnprintf(buf, PAGE_SIZE, "sleep\n");
+ break;
+ default:
+ count = scnprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ return count;
+}
+static DEVICE_ATTR(currentmode, S_IRUGO, mma8653fc_currentmode_show, NULL);
+
+static ssize_t mma8653fc_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ if (!mma->irq) {
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ if (byte & NEWLP)
+ mma8653fc_get_orientation(mma, byte);
+
+ byte = mma->read(mma, MMA8653FC_STATUS);
+ if (byte & ZYXDR)
+ mma8653fc_get_triple(mma);
+
+ msleep(20);
+ dev_dbg(&client->dev, "data polled\n");
+ }
+ mutex_lock(&mma->mutex);
+ count = scnprintf(buf, PAGE_SIZE, "%d %d %d %c %c\n",
+ mma->saved.x, mma->saved.y, mma->saved.z,
+ mma->orientation, mma->bafro);
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+static DEVICE_ATTR(position, S_IRUGO, mma8653fc_position_show, NULL);
+
+static struct attribute *mma8653fc_attributes[] = {
+ &dev_attr_position.attr,
+ &dev_attr_standby.attr,
+ &dev_attr_currentmode.attr,
+ NULL,
+};
+
+static const struct attribute_group mma8653fc_attr_group = {
+ .attrs = mma8653fc_attributes,
+};
+
+static u8 mma8653fc_read(struct mma8653fc *mma, unsigned char reg)
+{
+ u8 val;
+
+ val = i2c_smbus_read_byte_data(mma->client, reg);
+ if (val < 0) {
+ dev_err(&mma->client->dev,
+ "failed to read %x register\n", reg);
+ }
+ return val;
+}
+
+static void mma8653fc_write(struct mma8653fc *mma, unsigned char reg,
+ unsigned char val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(mma->client, reg, val);
+ if (ret) {
+ dev_err(&mma->client->dev,
+ "failed to write %x register\n", reg);
+ }
+}
+
+static const struct of_device_id mma8653fc_dt_ids[] = {
+ { .compatible = "fsl,mma8653fc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mma8653fc_dt_ids);
+
+static const struct mma8653fc_pdata *mma8653fc_probe_dt(struct device *dev)
+{
+ struct mma8653fc_pdata *pdata;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
+ u32 testu32;
+ s32 tests32;
+
+ if (!node) {
+ dev_err(dev, "no associated DT data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ match = of_match_device(mma8653fc_dt_ids, dev);
+ if (!match) {
+ dev_err(dev, "unknown device model\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ *pdata = mma8653fc_default_init;
+
+ /* overwrite from dts */
+ testu32 = pdata->x_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "x-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on X axis\n", tests32);
+ /* calc register value, resolution: 1.96mg */
+ pdata->x_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->y_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "y-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Y axis\n", tests32);
+ pdata->y_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->z_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "z-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Z axis\n", tests32);
+ pdata->z_axis_offset = (s8) (tests32 / 2);
+ }
+
+ testu32 = 0;
+ of_property_read_u32(node, "dynamic-range", &testu32);
+ if ((testu32) && (testu32 != 2) && (testu32 != 4) && (testu32 != 8)) {
+ dev_warn(dev, "wrong value for full scale range in dtb\n");
+ } else {
+ if (testu32)
+ pdata->range = testu32;
+ }
+
+ if (of_property_read_bool(node, "auto-wake-sleep"))
+ pdata->auto_wake_sleep = true;
+
+ if (of_property_read_bool(node, "int1"))
+ pdata->int1 = true;
+
+ if (of_property_read_bool(node, "motion-mode"))
+ pdata->motion_mode = true;
+
+ if (of_property_read_bool(node, "ir-data-ready"))
+ pdata->int_src_data_ready = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-x"))
+ pdata->int_src_ff_mt_x = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-y"))
+ pdata->int_src_ff_mt_y = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-z"))
+ pdata->int_src_ff_mt_z = true;
+
+ if (of_property_read_bool(node, "ir-auto-wake"))
+ pdata->int_src_aslp = true;
+
+ if (of_property_read_bool(node, "ir-landscape-portrait"))
+ pdata->int_src_lndprt = true;
+
+ testu32 = 0;
+ of_property_read_u32(node, "irq-threshold", &testu32);
+ /* always uses maximum range +/- 8000g, resolution 63mg */
+ if ((testu32 <= 8000) && (testu32 > 0)) {
+ dev_dbg(dev, "use freefall / motion threshold %dmg\n",
+ testu32);
+ /* calculate register value from mg */
+ pdata->freefall_motion_thr = (testu32 / 63) + 1;
+ }
+
+ return pdata;
+}
+static int mma8653fc_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mma8653fc *mma;
+ const struct mma8653fc_pdata *pdata;
+ int err;
+ u8 byte;
+
+ err = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!err) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ mma = devm_kzalloc(&client->dev, sizeof(*mma), GFP_KERNEL);
+ if (!mma)
+ err = -ENOMEM;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = mma8653fc_probe_dt(&client->dev);
+ if (IS_ERR(pdata)) {
+ err = PTR_ERR(pdata);
+ dev_err(&client->dev, "pdata from DT failed\n");
+ return err;
+ }
+ }
+ mma->pdata = *pdata;
+ pdata = &mma->pdata;
+ mma->client = client;
+ mma->read = &mma8653fc_read;
+ mma->write = &mma8653fc_write;
+
+ mutex_init(&mma->mutex);
+
+ i2c_set_clientdata(client, mma);
+
+ err = sysfs_create_group(&client->dev.kobj, &mma8653fc_attr_group);
+ if (err)
+ return err;
+
+ byte = mma->read(mma, MMA8653FC_WHO_AM_I);
+ if (byte != MMA8653FC_DEVICE_ID) {
+ dev_err(&client->dev, "wrong device for driver\n");
+ return -ENODEV;
+ }
+ dev_info(&client->dev, "loading driver for device id %x\n", byte);
+
+ mma->irq = irq_of_parse_and_map(client->dev.of_node, 0);
+ if (!mma->irq) {
+ dev_err(&client->dev, "Unable to parse or map IRQ\n");
+ goto no_irq;
+ }
+
+ err = irq_set_irq_type(mma->irq, IRQ_TYPE_EDGE_FALLING);
+ if (err) {
+ dev_err(&client->dev, "set_irq_type failed\n");
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&client->dev, mma->irq, NULL,
+ mma8653fc_irq, IRQF_ONESHOT,
+ dev_name(&mma->client->dev), mma);
+ if (err) {
+ dev_err(&client->dev, "irq %d busy?\n", mma->irq);
+ return err;
+ }
+
+ mma->write(mma, MMA8653FC_CTRL_REG2, SOFT_RESET);
+
+ __mma8653fc_disable(mma);
+ mma->standby = true;
+
+ /* enable desired interrupts */
+ mma->orientation = '\0';
+ mma->bafro = '\0';
+ byte = 0;
+ if (pdata->int_src_data_ready) {
+ byte |= INT_EN_DRDY;
+ dev_dbg(&client->dev, "DATA READY interrupt source enabled\n");
+ }
+ if (pdata->int_src_ff_mt_x || pdata->int_src_ff_mt_y ||
+ pdata->int_src_ff_mt_z) {
+ byte |= INT_EN_FF_MT;
+ dev_dbg(&client->dev, "FF MT interrupt source enabled\n");
+ }
+ if (pdata->int_src_lndprt) {
+ mma->write(mma, MMA8653FC_PL_CFG, PL_EN);
+ byte |= INT_EN_LNDPRT;
+ dev_dbg(&client->dev, "LNDPRT interrupt source enabled\n");
+ }
+ if (pdata->int_src_aslp) {
+ byte |= INT_EN_ASLP;
+ dev_dbg(&client->dev, "ASLP interrupt source enabled\n");
+ }
+ mma->write(mma, MMA8653FC_CTRL_REG4, byte);
+
+ /* force everything to line 1 */
+ if (pdata->int1) {
+ mma->write(mma, MMA8653FC_CTRL_REG5,
+ (INT_CFG_ASLP | INT_CFG_LNDPRT |
+ INT_CFG_FF_MT | INT_CFG_DRDY));
+ dev_dbg(&client->dev, "using interrupt line 1\n");
+ }
+no_irq:
+ /* range mode */
+ byte = mma->read(mma, MMA8653FC_XYZ_DATA_CFG);
+ byte &= ~RANGE_MASK;
+ switch (pdata->range) {
+ case DYN_RANGE_2G:
+ byte |= RANGE2G;
+ dev_dbg(&client->dev, "use 2g range\n");
+ break;
+ case DYN_RANGE_4G:
+ byte |= RANGE4G;
+ dev_dbg(&client->dev, "use 4g range\n");
+ break;
+ case DYN_RANGE_8G:
+ byte |= RANGE8G;
+ dev_dbg(&client->dev, "use 8g range\n");
+ break;
+ default:
+ dev_err(&client->dev, "wrong range mode value\n");
+ return -EINVAL;
+ }
+ mma->write(mma, MMA8653FC_XYZ_DATA_CFG, byte);
+
+ /* data calibration offsets */
+ if (pdata->x_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_X, pdata->x_axis_offset);
+ if (pdata->y_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_Y, pdata->y_axis_offset);
+ if (pdata->z_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_Z, pdata->z_axis_offset);
+
+ /* if autosleep, wake on both landscape and motion changes */
+ if (pdata->auto_wake_sleep) {
+ byte = 0;
+ byte |= WAKE_LNDPRT;
+ byte |= WAKE_FF_MT;
+ mma->write(mma, MMA8653FC_CTRL_REG3, byte);
+ mma->write(mma, MMA8653FC_CTRL_REG2, SLPE);
+ dev_dbg(&client->dev, "auto sleep enabled\n");
+ }
+
+ /* data rates */
+ byte = 0;
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ byte &= ~ODR_MASK;
+ byte |= ODR_DEFAULT;
+ byte &= ~ASLP_RATE_MASK;
+ byte |= ASLP_RATE_DEFAULT;
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte);
+
+ /* freefall / motion config */
+ byte = 0;
+ if (pdata->motion_mode) {
+ byte |= FF_MT_CFG_OAE;
+ dev_dbg(&client->dev, "detect motion instead of freefall\n");
+ }
+ byte |= FF_MT_CFG_ELE;
+ if (pdata->int_src_ff_mt_x)
+ byte |= FF_MT_CFG_XEFE;
+ if (pdata->int_src_ff_mt_y)
+ byte |= FF_MT_CFG_YEFE;
+ if (pdata->int_src_ff_mt_z)
+ byte |= FF_MT_CFG_ZEFE;
+ mma->write(mma, MMA8653FC_FF_MT_CFG, byte);
+
+ if (pdata->freefall_motion_thr) {
+ mma->write(mma, MMA8653FC_FF_MT_THS,
+ pdata->freefall_motion_thr);
+ /* calculate back to mg */
+ dev_dbg(&client->dev, "threshold set to %dmg\n",
+ (63 * pdata->freefall_motion_thr) - 1);
+ }
+
+ return 0;
+}
+
+static int mma8653fc_remove(struct i2c_client *client)
+{
+ dev_dbg(&client->dev, "unregistered accelerometer\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8653fc_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_disable(mma);
+
+ return 0;
+}
+
+static int mma8653fc_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_enable(mma);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mma8653fc_pm_ops, mma8653fc_suspend, mma8653fc_resume);
+#define MMA8653FC_PM_OPS (&mma8653fc_pm_ops)
+#else
+#define MMA8653FC_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id mma8653fc_id[] = {
+ { DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma8653fc_id);
+
+static struct i2c_driver mma8653fc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mma8653fc_dt_ids,
+ .pm = MMA8653FC_PM_OPS,
+ },
+ .probe = mma8653fc_i2c_probe,
+ .remove = mma8653fc_remove,
+ .id_table = mma8653fc_id,
+};
+
+module_i2c_driver(mma8653fc_driver);
+
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@theobroma-systems.com");
+MODULE_DESCRIPTION("Freescale's MMA8653FC Three-Axis Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
--
2.1.4
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related
* Re: [PATCH V7] Allow compaction of unevictable pages
From: Rik van Riel @ 2015-03-20 14:47 UTC (permalink / raw)
To: Eric B Munson, Andrew Morton
Cc: Vlastimil Babka, Thomas Gleixner, Christoph Lameter,
Peter Zijlstra, Mel Gorman, David Rientjes, Michal Hocko,
linux-doc, linux-rt-users, linux-mm, linux-api, linux-kernel
In-Reply-To: <1426859390-10974-1-git-send-email-emunson@akamai.com>
On 03/20/2015 09:49 AM, Eric B Munson wrote:
> Currently, pages which are marked as unevictable are protected from
> compaction, but not from other types of migration. The POSIX real time
> extension explicitly states that mlock() will prevent a major page
> fault, but the spirit of this is that mlock() should give a process the
> ability to control sources of latency, including minor page faults.
> However, the mlock manpage only explicitly says that a locked page will
> not be written to swap and this can cause some confusion. The
> compaction code today does not give a developer who wants to avoid swap
> but wants to have large contiguous areas available any method to achieve
> this state. This patch introduces a sysctl for controlling compaction
> behavior with respect to the unevictable lru. Users that demand no page
> faults after a page is present can set compact_unevictable_allowed to 0
> and users who need the large contiguous areas can enable compaction on
> locked memory by leaving the default value of 1.
>
> To illustrate this problem I wrote a quick test program that mmaps a
> large number of 1MB files filled with random data. These maps are
> created locked and read only. Then every other mmap is unmapped and I
> attempt to allocate huge pages to the static huge page pool. When the
> compact_unevictable_allowed sysctl is 0, I cannot allocate hugepages
> after fragmenting memory. When the value is set to 1, allocations
> succeed.
>
> Signed-off-by: Eric B Munson <emunson@akamai.com>
> Acked-by: Michal Hocko <mhocko@suse.cz>
> Acked-by: Vlastimil Babka <vbabka@suse.cz>
> Acked-by: Christoph Lameter <cl@linux.com>
> Acked-by: David Rientjes <rientjes@google.com>
Acked-by: Rik van Riel <riel@redhat.com>
--
All rights reversed
^ permalink raw reply
* Re: [PATCH v0 01/11] stm class: Introduce an abstraction for System Trace Module devices
From: Alexander Shishkin @ 2015-03-20 14:53 UTC (permalink / raw)
To: Mathieu Poirier
Cc: Greg Kroah-Hartman, linux-kernel@vger.kernel.org, Pratik Patel,
peter.lachner, norbert.schulz, keven.boell, yann.fouassier,
laurent.fert, linux-api
In-Reply-To: <CANLsYkwithjLgtztf8aBDRv-QL2VGOwc4XZ=1sGBSHg4LiymBg@mail.gmail.com>
Mathieu Poirier <mathieu.poirier@linaro.org> writes:
> As promised I worked on a prototype that connects the coresight-stm
> driver with the generic STM interface you have suggested. Things work
> quite well and aside from the enhancement related to the ioctl() and
> private member as discussed above, we should move ahead with this.
>
> I will send out a new version of the coresight-stm driver as soon as I
> see your patches with those changes.
Actually, instread of a private member I'd simply pass struct stm_data
pointer to the callback (like we do with other callbacks) and the
private data would be in the structure that embeds this struct stm_data,
so that you can get to it using container_of():
struct my_stm {
struct stm_data data;
void *my_stuff;
...
};
...
long my_ioctl(struct stm_data *data, unsigned int cmd, unsigned long
arg)
{
struct my_stm *mine = container_of(data, struct my_stm, data);
...
Would this work for you? I'm otherwise ready to send the second version
of my patchset.
Regards,
--
Alex
^ permalink raw reply
* Re: [PATCH v0 01/11] stm class: Introduce an abstraction for System Trace Module devices
From: Mathieu Poirier @ 2015-03-20 15:19 UTC (permalink / raw)
To: Alexander Shishkin
Cc: Greg Kroah-Hartman,
linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
Pratik Patel, peter.lachner-ral2JQCrhuEAvxtiuMwx3w,
norbert.schulz-ral2JQCrhuEAvxtiuMwx3w,
keven.boell-ral2JQCrhuEAvxtiuMwx3w,
yann.fouassier-ral2JQCrhuEAvxtiuMwx3w,
laurent.fert-ral2JQCrhuEAvxtiuMwx3w,
linux-api-u79uwXL29TY76Z2rM5mHXA
In-Reply-To: <87k2ybd8jl.fsf-qxRn5AmX6ZD9BXuAQUXR0fooFf0ArEBIu+b9c/7xato@public.gmane.org>
On 20 March 2015 at 08:53, Alexander Shishkin
<alexander.shishkin-VuQAYsv1563Yd54FQh9/CA@public.gmane.org> wrote:
> Mathieu Poirier <mathieu.poirier-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> writes:
>
>> As promised I worked on a prototype that connects the coresight-stm
>> driver with the generic STM interface you have suggested. Things work
>> quite well and aside from the enhancement related to the ioctl() and
>> private member as discussed above, we should move ahead with this.
>>
>> I will send out a new version of the coresight-stm driver as soon as I
>> see your patches with those changes.
>
> Actually, instread of a private member I'd simply pass struct stm_data
> pointer to the callback (like we do with other callbacks) and the
> private data would be in the structure that embeds this struct stm_data,
> so that you can get to it using container_of():
>
> struct my_stm {
> struct stm_data data;
> void *my_stuff;
> ...
> };
>
> ...
>
> long my_ioctl(struct stm_data *data, unsigned int cmd, unsigned long
> arg)
> {
> struct my_stm *mine = container_of(data, struct my_stm, data);
>
> ...
>
> Would this work for you?
That should be just fine yes.
> I'm otherwise ready to send the second version
> of my patchset.
Cool
>
> Regards,
> --
> Alex
^ permalink raw reply
* Re: [PATCH] Input: Add MT_TOOL_PALM
From: Dmitry Torokhov @ 2015-03-20 16:44 UTC (permalink / raw)
To: Peter Hutterer
Cc: Charlie Mooney, linux-input, Benjamin Tissoires, Hans de Goede,
Andrew De Los Reyes, Henrik Rydberg, Jonathan Corbet,
Masanari Iida, Jiri Kosina, linux-doc, linux-kernel, linux-api
In-Reply-To: <20150319224155.GA32380@jelly.redhat.com>
On Fri, Mar 20, 2015 at 08:41:55AM +1000, Peter Hutterer wrote:
> On Wed, Mar 18, 2015 at 03:26:35PM -0700, Charlie Mooney wrote:
> > Currently there are only two "tools" that can be specified by a
> > multi-touch driver: MT_TOOL_FINGER and MT_TOOL_PEN. In working with
> > Elan (The touch vendor) and discussing their next-gen devices it
> > seems that it will be useful to have more tools so that their devices
> > can give the upper layers of the stack hints as to what is touching
> > the sensor.
> >
> > In particular they have new experimental firmware that can better
> > differentiate between palms vs fingertips and would like to plumb a
> > patch so that we can use their hints in higher-level gesture soft-
> > ware. The firmware on the device can reasonably do a better job of
> > palm detection because it has access to all of the raw sensor readings
> > as opposed to just the width/pressure/etc that are exposed by the
> > driver. As such, the firmware can characterize what a palm looks like
> > in much finer-grained detail and this change would allow such a
> > device to share its findings with the kernel.
> >
> > Signed-off-by: Charlie Mooney <charliemooney@chromium.org>
>
> Acked-by: Peter Hutterer <peter.hutterer@who-t.net>
Applied, thank you.
>
> Cheers,
> Peter
>
> > ---
> > Documentation/input/multi-touch-protocol.txt | 9 ++++++---
> > include/uapi/linux/input.h | 3 ++-
> > 2 files changed, 8 insertions(+), 4 deletions(-)
> >
> > diff --git a/Documentation/input/multi-touch-protocol.txt b/Documentation/input/multi-touch-protocol.txt
> > index 7b4f59c..b85d000 100644
> > --- a/Documentation/input/multi-touch-protocol.txt
> > +++ b/Documentation/input/multi-touch-protocol.txt
> > @@ -312,9 +312,12 @@ ABS_MT_TOOL_TYPE
> >
> > The type of approaching tool. A lot of kernel drivers cannot distinguish
> > between different tool types, such as a finger or a pen. In such cases, the
> > -event should be omitted. The protocol currently supports MT_TOOL_FINGER and
> > -MT_TOOL_PEN [2]. For type B devices, this event is handled by input core;
> > -drivers should instead use input_mt_report_slot_state().
> > +event should be omitted. The protocol currently supports MT_TOOL_FINGER,
> > +MT_TOOL_PEN, and MT_TOOL_PALM [2]. For type B devices, this event is handled
> > +by input core; drivers should instead use input_mt_report_slot_state().
> > +A contact's ABS_MT_TOOL_TYPE may change over time while still touching the
> > +device, because the firmware may not be able to determine which tool is being
> > +used when it first appears.
> >
> > ABS_MT_BLOB_ID
> >
> > diff --git a/include/uapi/linux/input.h b/include/uapi/linux/input.h
> > index b0a8130..2f62ab2 100644
> > --- a/include/uapi/linux/input.h
> > +++ b/include/uapi/linux/input.h
> > @@ -973,7 +973,8 @@ struct input_keymap_entry {
> > */
> > #define MT_TOOL_FINGER 0
> > #define MT_TOOL_PEN 1
> > -#define MT_TOOL_MAX 1
> > +#define MT_TOOL_PALM 2
> > +#define MT_TOOL_MAX 2
> >
> > /*
> > * Values describing the status of a force-feedback effect
> > --
> > 2.1.2
> >
--
Dmitry
^ permalink raw reply
* [PATCH v5] add support for Freescale's MMA8653FC 10 bit accelerometer
From: Martin Kepplinger @ 2015-03-20 16:58 UTC (permalink / raw)
To: benjamin.tissoires, mark.rutland, robh+dt, Pawel.Moll,
ijc+devicetree, galak, dmitry.torokhov, alexander.stein
Cc: hadess, akpm, gregkh, linux-api, devicetree, linux-input,
linux-kernel, Martin Kepplinger, Christoph Muellner
In-Reply-To: <1426860374-10415-1-git-send-email-martink@posteo.de>
From: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
The MMA8653FC is a low-power, three-axis, capacitive micromachined
accelerometer with 10 bits of resolution with flexible user-programmable
options.
Embedded interrupt functions enable overall power savings, by relieving the
host processor from continuously polling data, for example using the poll()
system call.
The device can be configured to generate wake-up interrupt signals from any
combination of the configurable embedded functions, enabling the MMA8653FC
to monitor events while remaining in a low-power mode during periods of
inactivity.
This driver provides devicetree properties to program the device's behaviour
and a simple, tested and documented sysfs interface. The data sheet and more
information is available on Freescale's website.
Signed-off-by: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
Signed-off-by: Christoph Muellner <christoph.muellner@theobroma-systems.com>
---
This would be v5 aswell, but appies to the current -next tree.
drivers/staging/mma8653fc/Kconfig | 10 +
drivers/staging/mma8653fc/Makefile | 1 +
drivers/staging/mma8653fc/TODO | 146 ++++++
drivers/staging/mma8653fc/mma8653fc.c | 864 ++++++++++++++++++++++++++++++++++
4 files changed, 1021 insertions(+)
create mode 100644 drivers/staging/mma8653fc/Kconfig
create mode 100644 drivers/staging/mma8653fc/Makefile
create mode 100644 drivers/staging/mma8653fc/TODO
create mode 100644 drivers/staging/mma8653fc/mma8653fc.c
diff --git a/drivers/staging/mma8653fc/Kconfig b/drivers/staging/mma8653fc/Kconfig
new file mode 100644
index 0000000..988451b
--- /dev/null
+++ b/drivers/staging/mma8653fc/Kconfig
@@ -0,0 +1,10 @@
+config MMA8653FC
+ tristate "MMA8653FC - Freescale's 3-Axis, 10-bit Digital Accelerometer"
+ depends on I2C
+ default n
+ help
+ Say Y here if you want to support Freescale's MMA8653FC Accelerometer
+ through I2C interface.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mma8653fc.
diff --git a/drivers/staging/mma8653fc/Makefile b/drivers/staging/mma8653fc/Makefile
new file mode 100644
index 0000000..9a245a3
--- /dev/null
+++ b/drivers/staging/mma8653fc/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_MMA8653FC) += mma8653fc.o
diff --git a/drivers/staging/mma8653fc/TODO b/drivers/staging/mma8653fc/TODO
new file mode 100644
index 0000000..0a31225
--- /dev/null
+++ b/drivers/staging/mma8653fc/TODO
@@ -0,0 +1,146 @@
+- move to IIO device API. The current DT/sysfs interface is documented below
+
+Documentation/ABI/testing/...
+-----------------------------
+What: /sys/bus/i2c/drivers/mma8653fc/*/standby
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Write 0 to this in order to turn on the device, and 1 to turn
+ it off. Read to see if it is turned on or off.
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/currentmode
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Reading this provides the current state of the device, read
+ directly from a register. This can be "standby", "wake" or
+ "sleep".
+
+
+What: /sys/bus/i2c/drivers/mma8653fc/*/position
+Date: March 2015
+Contact: Martin Kepplinger <martin.kepplinger@theobroma-systems.com>
+Description:
+ Read only. Without interrupts enabled gets current position
+ values by reading. Poll "position" with interrupt conditions
+ set, to get notified; see Documentation/.../fsl,mma8653fc.txt
+
+ position file format:
+ "x y z [landscape/portrait status] [front/back status]"
+
+ x y z values:
+ in mg
+ landscape/portrait status char:
+ r landscape right
+ d portrait down
+ u portrait up
+ l landscape left
+ front/back status char:
+ f front facing
+ b back facing
+
+
+Documentation/devicetree/bindings/...
+-------------------------------------
+Required properties:
+- compatible
+ "fsl,mma8653fc"
+- reg
+ I2C address
+
+Optional properties:
+
+- interrupt-parent
+ a phandle for the interrupt controller (see
+ Documentation/devicetree/bindings/interrupt-controller/interrupts.txt)
+- interrupts
+ interrupt line to which the chip is connected (active low)
+- int1
+ set to use interrupt line 1, default is line 2
+ the interrupt sources can be routed to one of the two lines
+- ir-freefall-motion-x
+ activate freefall/motion interrupts on x axis
+- ir-freefall-motion-y
+ activate freefall/motion interrupts on y axis
+- ir-freefall-motion-z
+ activate freefall/motion interrupts on z axis
+- irq-threshold
+ 0 < value < 8000: threshold for motion interrupts in mg
+- ir-landscape-portrait
+ activate landscape/portrait interrupts
+- ir-auto-wake
+ activate wake/sleep change interrupts
+- ir-data-ready:
+ activate data-ready interrupts
+ Interrupt events can be activated in any combination.
+- dynamic-range
+ 2, 4, or 8: dynamic measurement range in g, default: 2
+ In ±2 g mode, sensitivity = 256 counts/g.
+ In ±4 g mode, sensitivity = 128 counts/g.
+ In ±8 g mode, sensitivity = 64 counts/g.
+- auto-wake-sleep
+ auto sleep mode (lower frequency)
+- motion-mode
+ use motion mode instead of freefall mode (trigger if >threshold).
+ per default an interrupt occurs if motion values fall below the
+ value set in "threshold" and therefore can detect free fall on the
+ vertical axis (depending on the position of the device).
+ Setting this values inverts the behaviour and an interrupt occurs
+ above the threshold value, so usually activate horizontal axis in
+ this case.
+
+- x-offset
+ 0 < value < 500: calibration offset in mg
+ The offset correction values are used to realign the Zero-g position
+ of the X, Y, and Z-axis after the device is mounted on a board.
+ this value has an offset of 250 itself:
+ 0 is -250mg, 250 is 0 mg, 500 is 250mg
+- y-offset
+ see x-offset
+- z-offset
+ see x-offset
+
+Example 1:
+for a device laying on flat ground to recognize acceleration over 100mg.
+x-axis is calibrated to +10mg. Adapt interrupt line to your device.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ motion-mode;
+ ir-freefall-motion-x;
+ ir-freefall-motion-y;
+ irq-threshold = <100>;
+ x-offset = <160>;
+};
+
+Example 2:
+for a device mounted on a wall with y being the vertical axis. This recognizes
+y-acceleration below 800mg, so free fall or changing the orientation of the
+device (y not being the vertical axis and having ~1000mg anymore).
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ interrupt-parent = <&gpio1>;
+ interrupts = <5 0>;
+ reg = <0x1d>;
+
+ dynamic-range = <2>;
+ ir-freefall-motion-y;
+ irq-threshold = <800>;
+};
+
+Example 3:
+minimal example. lets users read current acceleration values. No polling
+is available.
+
+mma8653fc@1d {
+ compatible = "fsl,mma8653fc";
+ reg = <0x1d>;
+};
diff --git a/drivers/staging/mma8653fc/mma8653fc.c b/drivers/staging/mma8653fc/mma8653fc.c
new file mode 100644
index 0000000..4bd7f99
--- /dev/null
+++ b/drivers/staging/mma8653fc/mma8653fc.c
@@ -0,0 +1,864 @@
+/*
+ * mma8653fc.c - Support for Freescale MMA8653FC 3-axis 10-bit accelerometer
+ *
+ * Copyright (c) 2014 Theobroma Systems Design and Consulting GmbH
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/types.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define DRV_NAME "mma8653fc"
+#define MMA8653FC_DEVICE_ID 0x5a
+
+#define MMA8653FC_STATUS 0x00
+
+#define ZYXOW_MASK 0x80
+#define ZYXDR 0x08
+
+#define MMA8653FC_WHO_AM_I 0x0d
+
+#define MMA8653FC_SYSMOD 0x0b
+#define STATE_STANDBY 0x00
+#define STATE_WAKE 0x01
+#define STATE_SLEEP 0x02
+
+#define MMA8450_STATUS_ZXYDR 0x08
+
+/*
+ * 10 bit output data registers
+ * MSB: 7:0 bits 9:2 of data word
+ * LSB: 7:6 bits 1:0 of data word
+ */
+#define MMA8653FC_OUT_X_MSB 0x01
+#define MMA8653FC_OUT_X_LSB 0x02
+#define MMA8653FC_OUT_Y_MSB 0x03
+#define MMA8653FC_OUT_Y_LSB 0x04
+#define MMA8653FC_OUT_Z_MSB 0x05
+#define MMA8653FC_OUT_Z_LSB 0x06
+
+/*
+ * Portrait/Landscape Status
+ */
+#define MMA8653FC_PL_STATUS 0x10
+
+#define NEWLP 0x80
+#define LAPO_HIGH 0x04
+#define LAPO_LOW 0x02
+#define BAFRO 0x01
+
+/*
+ * Portrait/Landscape Configuration
+ */
+#define MMA8653FC_PL_CFG 0x11
+
+#define PL_EN (1 << 6)
+
+/*
+ * Data calibration registers
+ */
+#define MMA8653FC_OFF_X 0x2f
+#define MMA8653FC_OFF_Y 0x30
+#define MMA8653FC_OFF_Z 0x31
+
+/* 0 to 500 for dts, but -250 to 250 in mg */
+#define DEFAULT_OFF 250
+
+/*
+ * bits 1:0 dynamic range
+ * 00 +/- 2g
+ * 01 +/- 4g
+ * 10 +/- 8g
+ *
+ * HPF_Out bit 4 - data high pass or low pass filtered
+ */
+#define MMA8653FC_XYZ_DATA_CFG 0x0e
+
+#define RANGE_MASK 0x03
+#define RANGE2G 0x00
+#define RANGE4G 0x01
+#define RANGE8G 0x02
+/* values for calculation */
+#define SHIFT_2G 8
+#define INCR_2G 128
+#define SHIFT_4G 7
+#define INCR_4G 64
+#define SHIFT_8G 6
+#define INCR_8G 32
+#define DYN_RANGE_2G 2
+#define DYN_RANGE_4G 4
+#define DYN_RANGE_8G 8
+
+/*
+ * System Control Reg 1
+ */
+#define MMA8653FC_CTRL_REG1 0x2a
+
+#define ACTIVE_BIT (1 << 0)
+#define ODR_MASK 0x38
+#define ODR_DEFAULT 0x20 /* 50 Hz */
+#define ASLP_RATE_MASK 0xc0
+#define ASLP_RATE_DEFAULT 0x80 /* 6.25 Hz */
+
+/*
+ * Sys Control Reg 2
+ *
+ * auto-sleep enable, software reset
+ */
+#define MMA8653FC_CTRL_REG2 0x2b
+
+#define SLPE (1 << 2)
+#define SELFTEST (1 << 7)
+#define SOFT_RESET (1 << 6)
+
+/*
+ * Interrupt Source
+ */
+#define MMA8653FC_INT_SOURCE 0x0c
+
+#define SRC_ASLP (1 << 7)
+#define SRC_LNDPRT (1 << 4)
+#define SRC_FF_MT (1 << 2)
+#define SRC_DRDY (1 << 0)
+
+/*
+ * Interrupt Control Register
+ *
+ * default: active low
+ * default push-pull, not open-drain
+ */
+#define MMA8653FC_CTRL_REG3 0x2c
+
+#define WAKE_LNDPRT (1 << 5)
+#define WAKE_FF_MT (1 << 3)
+#define IPOL (1 << 1)
+#define PP_OD (1 << 0)
+
+/*
+ * Interrupt Enable Register
+ */
+#define MMA8653FC_CTRL_REG4 0x2d
+
+#define INT_EN_ASLP (1 << 7)
+#define INT_EN_LNDPRT (1 << 4)
+#define INT_EN_FF_MT (1 << 2)
+#define INT_EN_DRDY (1 << 0)
+
+/*
+ * Interrupt Configuration Register
+ * bit value 0 ... INT2 (default)
+ * bit value 1 ... INT1
+ */
+#define MMA8653FC_CTRL_REG5 0x2e
+
+#define INT_CFG_ASLP (1 << 7)
+#define INT_CFG_LNDPRT (1 << 4)
+#define INT_CFG_FF_MT (1 << 2)
+#define INT_CFG_DRDY (1 << 0)
+
+/*
+ * Freefall / Motion Configuration Register
+ *
+ * Event Latch enable/disable, motion or freefall mode
+ * and event flag enable per axis
+ */
+#define MMA8653FC_FF_MT_CFG 0x15
+
+#define FF_MT_CFG_ELE (1 << 7)
+#define FF_MT_CFG_OAE (1 << 6)
+#define FF_MT_CFG_ZEFE (1 << 5)
+#define FF_MT_CFG_YEFE (1 << 4)
+#define FF_MT_CFG_XEFE (1 << 3)
+
+/*
+ * Freefall / Motion Source Register
+ */
+#define MMA8653FC_FF_MT_SRC 0x16
+
+/*
+ * Freefall / Motion Threshold Register
+ *
+ * define motion threshold
+ * 0.063 g/LSB, 127 counts(0x7f) (7 bit from LSB)
+ * range: 0.063g - 8g
+ */
+#define MMA8653FC_FF_MT_THS 0x17
+
+struct axis_triple {
+ s16 x;
+ s16 y;
+ s16 z;
+};
+
+struct mma8653fc_pdata {
+ s8 x_axis_offset;
+ s8 y_axis_offset;
+ s8 z_axis_offset;
+ bool auto_wake_sleep;
+ u32 range;
+ bool int1;
+ bool motion_mode;
+ u8 freefall_motion_thr;
+ bool int_src_data_ready;
+ bool int_src_ff_mt_x;
+ bool int_src_ff_mt_y;
+ bool int_src_ff_mt_z;
+ bool int_src_lndprt;
+ bool int_src_aslp;
+};
+
+struct mma8653fc {
+ struct i2c_client *client;
+ struct mutex mutex;
+ struct mma8653fc_pdata pdata;
+ struct axis_triple saved;
+ char orientation;
+ char bafro;
+ bool standby;
+ int irq;
+ unsigned int_mask;
+ u8 (*read)(struct mma8653fc *, unsigned char);
+ void (*write)(struct mma8653fc *, unsigned char, unsigned char);
+};
+
+/* defaults */
+static const struct mma8653fc_pdata mma8653fc_default_init = {
+ .range = 2,
+ .x_axis_offset = DEFAULT_OFF,
+ .y_axis_offset = DEFAULT_OFF,
+ .z_axis_offset = DEFAULT_OFF,
+ .auto_wake_sleep = false,
+ .int1 = false,
+ .motion_mode = false,
+ .freefall_motion_thr = 5,
+ .int_src_data_ready = false,
+ .int_src_ff_mt_x = false,
+ .int_src_ff_mt_y = false,
+ .int_src_ff_mt_z = false,
+ .int_src_lndprt = false,
+ .int_src_aslp = false,
+};
+
+static void mma8653fc_get_triple(struct mma8653fc *mma)
+{
+ u8 buf[6];
+ u8 status;
+
+ buf[0] = 0;
+
+ status = mma->read(mma, MMA8653FC_STATUS);
+ if (status & ZYXOW_MASK)
+ dev_dbg(&mma->client->dev, "previous read not completed\n");
+
+ buf[0] = mma->read(mma, MMA8653FC_OUT_X_MSB);
+ buf[1] = mma->read(mma, MMA8653FC_OUT_X_LSB);
+ buf[2] = mma->read(mma, MMA8653FC_OUT_Y_MSB);
+ buf[3] = mma->read(mma, MMA8653FC_OUT_Y_LSB);
+ buf[4] = mma->read(mma, MMA8653FC_OUT_Z_MSB);
+ buf[5] = mma->read(mma, MMA8653FC_OUT_Z_LSB);
+
+ mutex_lock(&mma->mutex);
+ /* move from registers to s16 */
+ mma->saved.x = (buf[1] | (buf[0] << 8)) >> 6;
+ mma->saved.y = (buf[3] | (buf[2] << 8)) >> 6;
+ mma->saved.z = (buf[5] | (buf[4] << 8)) >> 6;
+ mma->saved.x = sign_extend32(mma->saved.x, 9);
+ mma->saved.y = sign_extend32(mma->saved.y, 9);
+ mma->saved.z = sign_extend32(mma->saved.z, 9);
+
+ /* calc g, see data sheet and application note */
+ switch (mma->pdata.range) {
+ case DYN_RANGE_2G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_2G) >> SHIFT_2G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_2G) >> SHIFT_2G);
+ break;
+ case DYN_RANGE_4G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_4G) >> SHIFT_4G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_4G) >> SHIFT_4G);
+ break;
+ case DYN_RANGE_8G:
+ mma->saved.x = le16_to_cpu((1000 * mma->saved.x +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.y = le16_to_cpu((1000 * mma->saved.y +
+ INCR_8G) >> SHIFT_8G);
+ mma->saved.z = le16_to_cpu((1000 * mma->saved.z +
+ INCR_8G) >> SHIFT_8G);
+ break;
+ default:
+ dev_err(&mma->client->dev, "internal data corrupt\n");
+ }
+ mutex_unlock(&mma->mutex);
+}
+
+static void mma8653fc_get_orientation(struct mma8653fc *mma, u8 byte)
+{
+ if ((byte & LAPO_HIGH) && !(LAPO_LOW))
+ mma->orientation = 'r'; /* landscape right */
+ if (!(byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'd'; /* portrait down */
+ if (!(byte & LAPO_HIGH) && !(byte & LAPO_LOW))
+ mma->orientation = 'u'; /* portrait up */
+ if ((byte & LAPO_HIGH) && (byte & LAPO_LOW))
+ mma->orientation = 'l'; /* landscape left */
+
+ if (byte & BAFRO)
+ mma->bafro = 'b'; /* back facing */
+ else
+ mma->bafro = 'f'; /* front facing */
+}
+
+static irqreturn_t mma8653fc_irq(int irq, void *handle)
+{
+ struct mma8653fc *mma = handle;
+ u8 int_src;
+ u8 byte;
+
+ int_src = mma->read(mma, MMA8653FC_INT_SOURCE);
+ if (int_src & SRC_DRDY)
+ /* data ready handle */
+ if (int_src & SRC_FF_MT) {
+ /* freefall/motion change handle */
+ dev_dbg(&mma->client->dev,
+ "freefall or motion change\n");
+ byte = mma->read(mma, MMA8653FC_FF_MT_SRC);
+ }
+ if (int_src & SRC_LNDPRT) {
+ /* landscape/portrait change handle */
+ dev_dbg(&mma->client->dev,
+ "landscape / portrait change\n");
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ mma8653fc_get_orientation(mma, byte);
+ }
+ if (int_src & SRC_ASLP)
+ /* autosleep change handle */
+ mma8653fc_get_triple(mma);
+
+ sysfs_notify(&mma->client->dev.kobj, NULL, "position");
+
+ return IRQ_HANDLED;
+}
+
+static void __mma8653fc_enable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte | ACTIVE_BIT);
+}
+
+static void __mma8653fc_disable(struct mma8653fc *mma)
+{
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte & ~ACTIVE_BIT);
+}
+
+static ssize_t mma8653fc_standby_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ return scnprintf(buf, PAGE_SIZE, "%u\n", mma->standby);
+}
+
+static ssize_t mma8653fc_standby_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ unsigned int val;
+ int error;
+
+ error = kstrtouint(buf, 10, &val);
+ if (error)
+ return error;
+
+ mutex_lock(&mma->mutex);
+
+ if (val) {
+ if (!mma->standby)
+ __mma8653fc_disable(mma);
+ } else {
+ if (mma->standby)
+ __mma8653fc_enable(mma);
+ }
+
+ mma->standby = !!val;
+
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+
+static DEVICE_ATTR(standby, 0664, mma8653fc_standby_show,
+ mma8653fc_standby_store);
+
+static ssize_t mma8653fc_currentmode_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ byte = mma->read(mma, MMA8653FC_SYSMOD);
+ if (byte < 0)
+ return byte;
+
+ switch (byte) {
+ case STATE_STANDBY:
+ count = scnprintf(buf, PAGE_SIZE, "standby\n");
+ break;
+ case STATE_WAKE:
+ count = scnprintf(buf, PAGE_SIZE, "wake\n");
+ break;
+ case STATE_SLEEP:
+ count = scnprintf(buf, PAGE_SIZE, "sleep\n");
+ break;
+ default:
+ count = scnprintf(buf, PAGE_SIZE, "unknown\n");
+ }
+ return count;
+}
+static DEVICE_ATTR(currentmode, S_IRUGO, mma8653fc_currentmode_show, NULL);
+
+static ssize_t mma8653fc_position_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+ ssize_t count;
+ u8 byte;
+
+ if (!mma->irq) {
+ byte = mma->read(mma, MMA8653FC_PL_STATUS);
+ if (byte & NEWLP)
+ mma8653fc_get_orientation(mma, byte);
+
+ byte = mma->read(mma, MMA8653FC_STATUS);
+ if (byte & ZYXDR)
+ mma8653fc_get_triple(mma);
+
+ msleep(20);
+ dev_dbg(&client->dev, "data polled\n");
+ }
+ mutex_lock(&mma->mutex);
+ count = scnprintf(buf, PAGE_SIZE, "%d %d %d %c %c\n",
+ mma->saved.x, mma->saved.y, mma->saved.z,
+ mma->orientation, mma->bafro);
+ mutex_unlock(&mma->mutex);
+
+ return count;
+}
+static DEVICE_ATTR(position, S_IRUGO, mma8653fc_position_show, NULL);
+
+static struct attribute *mma8653fc_attributes[] = {
+ &dev_attr_position.attr,
+ &dev_attr_standby.attr,
+ &dev_attr_currentmode.attr,
+ NULL,
+};
+
+static const struct attribute_group mma8653fc_attr_group = {
+ .attrs = mma8653fc_attributes,
+};
+
+static u8 mma8653fc_read(struct mma8653fc *mma, unsigned char reg)
+{
+ u8 val;
+
+ val = i2c_smbus_read_byte_data(mma->client, reg);
+ if (val < 0) {
+ dev_err(&mma->client->dev,
+ "failed to read %x register\n", reg);
+ }
+ return val;
+}
+
+static void mma8653fc_write(struct mma8653fc *mma, unsigned char reg,
+ unsigned char val)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(mma->client, reg, val);
+ if (ret) {
+ dev_err(&mma->client->dev,
+ "failed to write %x register\n", reg);
+ }
+}
+
+static const struct of_device_id mma8653fc_dt_ids[] = {
+ { .compatible = "fsl,mma8653fc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mma8653fc_dt_ids);
+
+static const struct mma8653fc_pdata *mma8653fc_probe_dt(struct device *dev)
+{
+ struct mma8653fc_pdata *pdata;
+ struct device_node *node = dev->of_node;
+ const struct of_device_id *match;
+ u32 testu32;
+ s32 tests32;
+
+ if (!node) {
+ dev_err(dev, "no associated DT data\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ match = of_match_device(mma8653fc_dt_ids, dev);
+ if (!match) {
+ dev_err(dev, "unknown device model\n");
+ return ERR_PTR(-EINVAL);
+ }
+
+ pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+ if (!pdata)
+ return ERR_PTR(-ENOMEM);
+
+ *pdata = mma8653fc_default_init;
+
+ /* overwrite from dts */
+ testu32 = pdata->x_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "x-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on X axis\n", tests32);
+ /* calc register value, resolution: 1.96mg */
+ pdata->x_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->y_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "y-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Y axis\n", tests32);
+ pdata->y_axis_offset = (s8) (tests32 / 2);
+ }
+ testu32 = pdata->z_axis_offset;
+ tests32 = 0;
+ of_property_read_u32(node, "z-offset", &testu32);
+ tests32 = testu32 - DEFAULT_OFF;
+ if ((tests32) && (tests32 <= DEFAULT_OFF) &&
+ (tests32 >= -DEFAULT_OFF)) {
+ dev_info(dev, "use %dmg offset on Z axis\n", tests32);
+ pdata->z_axis_offset = (s8) (tests32 / 2);
+ }
+
+ testu32 = 0;
+ of_property_read_u32(node, "dynamic-range", &testu32);
+ if ((testu32) && (testu32 != 2) && (testu32 != 4) && (testu32 != 8)) {
+ dev_warn(dev, "wrong value for full scale range in dtb\n");
+ } else {
+ if (testu32)
+ pdata->range = testu32;
+ }
+
+ if (of_property_read_bool(node, "auto-wake-sleep"))
+ pdata->auto_wake_sleep = true;
+
+ if (of_property_read_bool(node, "int1"))
+ pdata->int1 = true;
+
+ if (of_property_read_bool(node, "motion-mode"))
+ pdata->motion_mode = true;
+
+ if (of_property_read_bool(node, "ir-data-ready"))
+ pdata->int_src_data_ready = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-x"))
+ pdata->int_src_ff_mt_x = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-y"))
+ pdata->int_src_ff_mt_y = true;
+
+ if (of_property_read_bool(node, "ir-freefall-motion-z"))
+ pdata->int_src_ff_mt_z = true;
+
+ if (of_property_read_bool(node, "ir-auto-wake"))
+ pdata->int_src_aslp = true;
+
+ if (of_property_read_bool(node, "ir-landscape-portrait"))
+ pdata->int_src_lndprt = true;
+
+ testu32 = 0;
+ of_property_read_u32(node, "irq-threshold", &testu32);
+ /* always uses maximum range +/- 8000g, resolution 63mg */
+ if ((testu32 <= 8000) && (testu32 > 0)) {
+ dev_dbg(dev, "use freefall / motion threshold %dmg\n",
+ testu32);
+ /* calculate register value from mg */
+ pdata->freefall_motion_thr = (testu32 / 63) + 1;
+ }
+
+ return pdata;
+}
+static int mma8653fc_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mma8653fc *mma;
+ const struct mma8653fc_pdata *pdata;
+ int err;
+ u8 byte;
+
+ err = i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE_DATA);
+ if (!err) {
+ dev_err(&client->dev, "SMBUS Byte Data not Supported\n");
+ return -EIO;
+ }
+
+ mma = devm_kzalloc(&client->dev, sizeof(*mma), GFP_KERNEL);
+ if (!mma)
+ err = -ENOMEM;
+
+ pdata = dev_get_platdata(&client->dev);
+ if (!pdata) {
+ pdata = mma8653fc_probe_dt(&client->dev);
+ if (IS_ERR(pdata)) {
+ err = PTR_ERR(pdata);
+ dev_err(&client->dev, "pdata from DT failed\n");
+ return err;
+ }
+ }
+ mma->pdata = *pdata;
+ pdata = &mma->pdata;
+ mma->client = client;
+ mma->read = &mma8653fc_read;
+ mma->write = &mma8653fc_write;
+
+ mutex_init(&mma->mutex);
+
+ i2c_set_clientdata(client, mma);
+
+ err = sysfs_create_group(&client->dev.kobj, &mma8653fc_attr_group);
+ if (err)
+ return err;
+
+ byte = mma->read(mma, MMA8653FC_WHO_AM_I);
+ if (byte != MMA8653FC_DEVICE_ID) {
+ dev_err(&client->dev, "wrong device for driver\n");
+ return -ENODEV;
+ }
+ dev_info(&client->dev, "loading driver for device id %x\n", byte);
+
+ mma->irq = irq_of_parse_and_map(client->dev.of_node, 0);
+ if (!mma->irq) {
+ dev_err(&client->dev, "Unable to parse or map IRQ\n");
+ goto no_irq;
+ }
+
+ err = irq_set_irq_type(mma->irq, IRQ_TYPE_EDGE_FALLING);
+ if (err) {
+ dev_err(&client->dev, "set_irq_type failed\n");
+ return err;
+ }
+
+ err = devm_request_threaded_irq(&client->dev, mma->irq, NULL,
+ mma8653fc_irq, IRQF_ONESHOT,
+ dev_name(&mma->client->dev), mma);
+ if (err) {
+ dev_err(&client->dev, "irq %d busy?\n", mma->irq);
+ return err;
+ }
+
+ mma->write(mma, MMA8653FC_CTRL_REG2, SOFT_RESET);
+
+ __mma8653fc_disable(mma);
+ mma->standby = true;
+
+ /* enable desired interrupts */
+ mma->orientation = '\0';
+ mma->bafro = '\0';
+ byte = 0;
+ if (pdata->int_src_data_ready) {
+ byte |= INT_EN_DRDY;
+ dev_dbg(&client->dev, "DATA READY interrupt source enabled\n");
+ }
+ if (pdata->int_src_ff_mt_x || pdata->int_src_ff_mt_y ||
+ pdata->int_src_ff_mt_z) {
+ byte |= INT_EN_FF_MT;
+ dev_dbg(&client->dev, "FF MT interrupt source enabled\n");
+ }
+ if (pdata->int_src_lndprt) {
+ mma->write(mma, MMA8653FC_PL_CFG, PL_EN);
+ byte |= INT_EN_LNDPRT;
+ dev_dbg(&client->dev, "LNDPRT interrupt source enabled\n");
+ }
+ if (pdata->int_src_aslp) {
+ byte |= INT_EN_ASLP;
+ dev_dbg(&client->dev, "ASLP interrupt source enabled\n");
+ }
+ mma->write(mma, MMA8653FC_CTRL_REG4, byte);
+
+ /* force everything to line 1 */
+ if (pdata->int1) {
+ mma->write(mma, MMA8653FC_CTRL_REG5,
+ (INT_CFG_ASLP | INT_CFG_LNDPRT |
+ INT_CFG_FF_MT | INT_CFG_DRDY));
+ dev_dbg(&client->dev, "using interrupt line 1\n");
+ }
+no_irq:
+ /* range mode */
+ byte = mma->read(mma, MMA8653FC_XYZ_DATA_CFG);
+ byte &= ~RANGE_MASK;
+ switch (pdata->range) {
+ case DYN_RANGE_2G:
+ byte |= RANGE2G;
+ dev_dbg(&client->dev, "use 2g range\n");
+ break;
+ case DYN_RANGE_4G:
+ byte |= RANGE4G;
+ dev_dbg(&client->dev, "use 4g range\n");
+ break;
+ case DYN_RANGE_8G:
+ byte |= RANGE8G;
+ dev_dbg(&client->dev, "use 8g range\n");
+ break;
+ default:
+ dev_err(&client->dev, "wrong range mode value\n");
+ return -EINVAL;
+ }
+ mma->write(mma, MMA8653FC_XYZ_DATA_CFG, byte);
+
+ /* data calibration offsets */
+ if (pdata->x_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_X, pdata->x_axis_offset);
+ if (pdata->y_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_Y, pdata->y_axis_offset);
+ if (pdata->z_axis_offset)
+ mma->write(mma, MMA8653FC_OFF_Z, pdata->z_axis_offset);
+
+ /* if autosleep, wake on both landscape and motion changes */
+ if (pdata->auto_wake_sleep) {
+ byte = 0;
+ byte |= WAKE_LNDPRT;
+ byte |= WAKE_FF_MT;
+ mma->write(mma, MMA8653FC_CTRL_REG3, byte);
+ mma->write(mma, MMA8653FC_CTRL_REG2, SLPE);
+ dev_dbg(&client->dev, "auto sleep enabled\n");
+ }
+
+ /* data rates */
+ byte = 0;
+ byte = mma->read(mma, MMA8653FC_CTRL_REG1);
+ byte &= ~ODR_MASK;
+ byte |= ODR_DEFAULT;
+ byte &= ~ASLP_RATE_MASK;
+ byte |= ASLP_RATE_DEFAULT;
+ mma->write(mma, MMA8653FC_CTRL_REG1, byte);
+
+ /* freefall / motion config */
+ byte = 0;
+ if (pdata->motion_mode) {
+ byte |= FF_MT_CFG_OAE;
+ dev_dbg(&client->dev, "detect motion instead of freefall\n");
+ }
+ byte |= FF_MT_CFG_ELE;
+ if (pdata->int_src_ff_mt_x)
+ byte |= FF_MT_CFG_XEFE;
+ if (pdata->int_src_ff_mt_y)
+ byte |= FF_MT_CFG_YEFE;
+ if (pdata->int_src_ff_mt_z)
+ byte |= FF_MT_CFG_ZEFE;
+ mma->write(mma, MMA8653FC_FF_MT_CFG, byte);
+
+ if (pdata->freefall_motion_thr) {
+ mma->write(mma, MMA8653FC_FF_MT_THS,
+ pdata->freefall_motion_thr);
+ /* calculate back to mg */
+ dev_dbg(&client->dev, "threshold set to %dmg\n",
+ (63 * pdata->freefall_motion_thr) - 1);
+ }
+
+ return 0;
+}
+
+static int mma8653fc_remove(struct i2c_client *client)
+{
+ dev_dbg(&client->dev, "unregistered accelerometer\n");
+
+ return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mma8653fc_suspend(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_disable(mma);
+
+ return 0;
+}
+
+static int mma8653fc_resume(struct device *dev)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct mma8653fc *mma = i2c_get_clientdata(client);
+
+ __mma8653fc_enable(mma);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(mma8653fc_pm_ops, mma8653fc_suspend, mma8653fc_resume);
+#define MMA8653FC_PM_OPS (&mma8653fc_pm_ops)
+#else
+#define MMA8653FC_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id mma8653fc_id[] = {
+ { DRV_NAME, 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mma8653fc_id);
+
+static struct i2c_driver mma8653fc_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ .of_match_table = mma8653fc_dt_ids,
+ .pm = MMA8653FC_PM_OPS,
+ },
+ .probe = mma8653fc_i2c_probe,
+ .remove = mma8653fc_remove,
+ .id_table = mma8653fc_id,
+};
+
+module_i2c_driver(mma8653fc_driver);
+
+MODULE_AUTHOR("Martin Kepplinger <martin.kepplinger@theobroma-systems.com");
+MODULE_DESCRIPTION("Freescale's MMA8653FC Three-Axis Accelerometer I2C Driver");
+MODULE_LICENSE("GPL");
--
2.1.4
^ permalink raw reply related
* [PATCH v2] coresight: moving to new "hwtracing" directory
From: Mathieu Poirier @ 2015-03-20 17:01 UTC (permalink / raw)
To: linux, catalin.marinas
Cc: will.deacon, gregkh, alexander.shishkin, linux-kernel,
peter.lachner, norbert.schulz, keven.boell, yann.fouassier,
laurent.fert, linux-api, linux-arm-kernel, kaixu.xia,
zhang.chunyan, mathieu.poirier
Keeping drivers related to HW tracing on ARM, i.e coresight,
under "drivers/coresight" doesn't make sense when other
architectures start rolling out technologies of the same
nature.
As such creating a new "drivers/hwtracing" directory where all
drivers of the same kind can reside, reducing namespace
pollution under "drivers/".
Signed-off-by: Mathieu Poirier <mathieu.poirier@linaro.org>
---
Change for v2:
- generated patch with -M option
---
MAINTAINERS | 2 +-
arch/arm/Kconfig.debug | 2 +-
arch/arm64/Kconfig.debug | 2 +-
drivers/Makefile | 2 +-
drivers/{ => hwtracing}/coresight/Kconfig | 0
drivers/{ => hwtracing}/coresight/Makefile | 0
drivers/{ => hwtracing}/coresight/coresight-etb10.c | 0
drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c | 0
drivers/{ => hwtracing}/coresight/coresight-etm.h | 0
drivers/{ => hwtracing}/coresight/coresight-etm3x.c | 0
drivers/{ => hwtracing}/coresight/coresight-funnel.c | 0
drivers/{ => hwtracing}/coresight/coresight-priv.h | 0
drivers/{ => hwtracing}/coresight/coresight-replicator.c | 0
drivers/{ => hwtracing}/coresight/coresight-tmc.c | 0
drivers/{ => hwtracing}/coresight/coresight-tpiu.c | 0
drivers/{ => hwtracing}/coresight/coresight.c | 0
drivers/{ => hwtracing}/coresight/of_coresight.c | 0
17 files changed, 4 insertions(+), 4 deletions(-)
rename drivers/{ => hwtracing}/coresight/Kconfig (100%)
rename drivers/{ => hwtracing}/coresight/Makefile (100%)
rename drivers/{ => hwtracing}/coresight/coresight-etb10.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-etm-cp14.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-etm.h (100%)
rename drivers/{ => hwtracing}/coresight/coresight-etm3x.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-funnel.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-priv.h (100%)
rename drivers/{ => hwtracing}/coresight/coresight-replicator.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-tmc.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight-tpiu.c (100%)
rename drivers/{ => hwtracing}/coresight/coresight.c (100%)
rename drivers/{ => hwtracing}/coresight/of_coresight.c (100%)
diff --git a/MAINTAINERS b/MAINTAINERS
index 0e1abe8cc684..bc5d53ba1197 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -953,7 +953,7 @@ ARM/CORESIGHT FRAMEWORK AND DRIVERS
M: Mathieu Poirier <mathieu.poirier@linaro.org>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
-F: drivers/coresight/*
+F: drivers/hwtracing/coresight/*
F: Documentation/trace/coresight.txt
F: Documentation/devicetree/bindings/arm/coresight.txt
F: Documentation/ABI/testing/sysfs-bus-coresight-devices-*
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index 8d14ad4e1db0..8b0183a9a300 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1610,6 +1610,6 @@ config DEBUG_SET_MODULE_RONX
against certain classes of kernel exploits.
If in doubt, say "N".
-source "drivers/coresight/Kconfig"
+source "drivers/hwtracing/coresight/Kconfig"
endmenu
diff --git a/arch/arm64/Kconfig.debug b/arch/arm64/Kconfig.debug
index 5b2ffd8e6cdb..d6285ef9b5f9 100644
--- a/arch/arm64/Kconfig.debug
+++ b/arch/arm64/Kconfig.debug
@@ -89,6 +89,6 @@ config DEBUG_ALIGN_RODATA
If in doubt, say N
-source "drivers/coresight/Kconfig"
+source "drivers/hwtracing/coresight/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da8d539..46d2554be404 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -163,5 +163,5 @@ obj-$(CONFIG_POWERCAP) += powercap/
obj-$(CONFIG_MCB) += mcb/
obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
-obj-$(CONFIG_CORESIGHT) += coresight/
+obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
obj-$(CONFIG_ANDROID) += android/
diff --git a/drivers/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
similarity index 100%
rename from drivers/coresight/Kconfig
rename to drivers/hwtracing/coresight/Kconfig
diff --git a/drivers/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
similarity index 100%
rename from drivers/coresight/Makefile
rename to drivers/hwtracing/coresight/Makefile
diff --git a/drivers/coresight/coresight-etb10.c b/drivers/hwtracing/coresight/coresight-etb10.c
similarity index 100%
rename from drivers/coresight/coresight-etb10.c
rename to drivers/hwtracing/coresight/coresight-etb10.c
diff --git a/drivers/coresight/coresight-etm-cp14.c b/drivers/hwtracing/coresight/coresight-etm-cp14.c
similarity index 100%
rename from drivers/coresight/coresight-etm-cp14.c
rename to drivers/hwtracing/coresight/coresight-etm-cp14.c
diff --git a/drivers/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h
similarity index 100%
rename from drivers/coresight/coresight-etm.h
rename to drivers/hwtracing/coresight/coresight-etm.h
diff --git a/drivers/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c
similarity index 100%
rename from drivers/coresight/coresight-etm3x.c
rename to drivers/hwtracing/coresight/coresight-etm3x.c
diff --git a/drivers/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c
similarity index 100%
rename from drivers/coresight/coresight-funnel.c
rename to drivers/hwtracing/coresight/coresight-funnel.c
diff --git a/drivers/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h
similarity index 100%
rename from drivers/coresight/coresight-priv.h
rename to drivers/hwtracing/coresight/coresight-priv.h
diff --git a/drivers/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c
similarity index 100%
rename from drivers/coresight/coresight-replicator.c
rename to drivers/hwtracing/coresight/coresight-replicator.c
diff --git a/drivers/coresight/coresight-tmc.c b/drivers/hwtracing/coresight/coresight-tmc.c
similarity index 100%
rename from drivers/coresight/coresight-tmc.c
rename to drivers/hwtracing/coresight/coresight-tmc.c
diff --git a/drivers/coresight/coresight-tpiu.c b/drivers/hwtracing/coresight/coresight-tpiu.c
similarity index 100%
rename from drivers/coresight/coresight-tpiu.c
rename to drivers/hwtracing/coresight/coresight-tpiu.c
diff --git a/drivers/coresight/coresight.c b/drivers/hwtracing/coresight/coresight.c
similarity index 100%
rename from drivers/coresight/coresight.c
rename to drivers/hwtracing/coresight/coresight.c
diff --git a/drivers/coresight/of_coresight.c b/drivers/hwtracing/coresight/of_coresight.c
similarity index 100%
rename from drivers/coresight/of_coresight.c
rename to drivers/hwtracing/coresight/of_coresight.c
--
1.9.1
^ permalink raw reply related
* [PATCH v1 01/11] stm class: Introduce an abstraction for System Trace Module devices
From: Alexander Shishkin @ 2015-03-20 17:29 UTC (permalink / raw)
To: Greg Kroah-Hartman
Cc: linux-kernel, mathieu.poirier, pebolle, peter.lachner,
norbert.schulz, keven.boell, yann.fouassier, laurent.fert,
Alexander Shishkin, linux-api, Pratik Patel
In-Reply-To: <1426872598-68807-1-git-send-email-alexander.shishkin@linux.intel.com>
A System Trace Module (STM) is a device exporting data in System Trace
Protocol (STP) format as defined by MIPI STP standards. Examples of such
devices are Intel Trace Hub and Coresight STM.
This abstraction provides a unified interface for software trace sources
to send their data over an STM device to a debug host. In order to do
that, such a trace source needs to be assigned a pair of master/channel
identifiers that all the data from this source will be tagged with. The
STP decoder on the debug host side will use these master/channel tags to
distinguish different trace streams from one another inside one STP
stream.
This abstraction provides a configfs-based policy management mechanism
for dynamic allocation of these master/channel pairs based on trace
source-supplied string identifier. It has the flexibility of being
defined at runtime and at the same time (provided that the policy
definition is aligned with the decoding end) consistency.
For userspace trace sources, this abstraction provides write()-based and
mmap()-based (if the underlying stm device allows this) output mechanism.
For kernel-side trace sources, we provide "stm_source" device class that
can be connected to an stm device at run time.
Cc: linux-api@vger.kernel.org
Cc: Pratik Patel <pratikp@codeaurora.org>
Cc: Mathieu Poirier <mathieu.poirier@linaro.org>
Signed-off-by: Alexander Shishkin <alexander.shishkin@linux.intel.com>
---
Documentation/ABI/testing/configfs-stp-policy | 44 ++
Documentation/ABI/testing/sysfs-class-stm | 14 +
Documentation/ABI/testing/sysfs-class-stm_source | 11 +
Documentation/trace/stm.txt | 77 ++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/hwtracing/stm/Kconfig | 8 +
drivers/hwtracing/stm/Makefile | 3 +
drivers/hwtracing/stm/core.c | 897 +++++++++++++++++++++++
drivers/hwtracing/stm/policy.c | 467 ++++++++++++
drivers/hwtracing/stm/stm.h | 79 ++
include/linux/stm.h | 102 +++
include/uapi/linux/stm.h | 47 ++
13 files changed, 1752 insertions(+)
create mode 100644 Documentation/ABI/testing/configfs-stp-policy
create mode 100644 Documentation/ABI/testing/sysfs-class-stm
create mode 100644 Documentation/ABI/testing/sysfs-class-stm_source
create mode 100644 Documentation/trace/stm.txt
create mode 100644 drivers/hwtracing/stm/Kconfig
create mode 100644 drivers/hwtracing/stm/Makefile
create mode 100644 drivers/hwtracing/stm/core.c
create mode 100644 drivers/hwtracing/stm/policy.c
create mode 100644 drivers/hwtracing/stm/stm.h
create mode 100644 include/linux/stm.h
create mode 100644 include/uapi/linux/stm.h
diff --git a/Documentation/ABI/testing/configfs-stp-policy b/Documentation/ABI/testing/configfs-stp-policy
new file mode 100644
index 0000000000..1c7ab3dbcd
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-stp-policy
@@ -0,0 +1,44 @@
+What: /config/stp-policy
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ This group contains policies mandating Master/Channel allocation
+ for software sources wishing to send trace data over an STM
+ device.
+
+What: /config/stp-policy/<policy>
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ Root of a policy. This name is an arbitrary string.
+
+What: /config/stp-policy/<policy>/device
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ STM device to which this policy applies. Write a valid stm class
+ device name here to assign this policy to that device.
+
+What: /config/stp-policy/<policy>/<node>
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ Policy node is a string identifier that software clients will
+ use to request a master/channel to be allocated and assigned to
+ them.
+
+What: /config/stp-policy/<policy>/<node>/masters
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ Range of masters from which to allocate for users of this node.
+ Write two numbers: the first master and the last master number.
+
+What: /config/stp-policy/<policy>/<node>/channels
+Date: Jan 2015
+KernelVersion: 3.20
+Description:
+ Range of channels from which to allocate for users of this node.
+ Write two numbers: the first channel and the last channel
+ number.
+
diff --git a/Documentation/ABI/testing/sysfs-class-stm b/Documentation/ABI/testing/sysfs-class-stm
new file mode 100644
index 0000000000..186f8e66e1
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-stm
@@ -0,0 +1,14 @@
+What: /sys/class/stm/<stm>/masters
+Date: Jan 2015
+KernelVersion: 3.20
+Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+ Shows first and last available to software master numbers on
+ this STM device.
+
+What: /sys/class/stm/<stm>/channels
+Date: Jan 2015
+KernelVersion: 3.20
+Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+ Shows the number of channels per master on this STM device.
diff --git a/Documentation/ABI/testing/sysfs-class-stm_source b/Documentation/ABI/testing/sysfs-class-stm_source
new file mode 100644
index 0000000000..735b0d657f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-class-stm_source
@@ -0,0 +1,11 @@
+What: /sys/class/stm_source/<stm_source>/stm_source_link
+Date: Jan 2015
+KernelVersion: 3.20
+Contact: Alexander Shishkin <alexander.shishkin@linux.intel.com>
+Description:
+ stm_source device linkage to stm device, where its tracing data
+ is directed. Reads return an existing connection or "<none>" if
+ this stm_source is not connected to any stm device yet.
+ Write an existing (registered) stm device's name here to
+ connect that device. If a device is already connected to this
+ stm_source, it will first be disconnected.
diff --git a/Documentation/trace/stm.txt b/Documentation/trace/stm.txt
new file mode 100644
index 0000000000..0ba5c9115c
--- /dev/null
+++ b/Documentation/trace/stm.txt
@@ -0,0 +1,77 @@
+System Trace Module
+===================
+
+System Trace Module (STM) is a device described in MIPI STP specs as
+STP trace stream generator. STP (System Trace Protocol) is a trace
+protocol multiplexing data from multiple trace sources, each one of
+which is assigned a unique pair of master and channel. While some of
+these masters and channels are statically allocated to certain
+hardware trace sources, others are available to software. Software
+trace sources are usually free to pick for themselves any
+master/channel combination from this pool.
+
+On the receiving end of this STP stream (the decoder side), trace
+sources can only be identified by master/channel combination, so in
+order for the decoder to be able to make sense of the trace that
+involves multiple trace sources, it needs to be able to map those
+master/channel pairs to the trace sources that it understands.
+
+For instance, it is helpful to know that syslog messages come on
+master 7 channel 15, while arbitrary user applications can use masters
+48 to 63 and channels 0 to 127.
+
+To solve this mapping problem, stm class provides a policy management
+mechanism via configfs, that allows defining rules that map string
+identifiers to ranges of masters and channels. If these rules (policy)
+are consistent with what decoder expects, it will be able to properly
+process the trace data.
+
+This policy is a tree structure containing rules (policy_node) that
+have a name (string identifier) and a range of masters and channels
+associated with it, located in "stp-policy" subsystem directory in
+configfs. From the examle above, a rule may look like this:
+
+$ ls /config/stp-policy/my-policy/user
+channels masters
+$ cat /config/stp-policy/my-policy/user/masters
+48 63
+$ cat /config/stp-policy/my-policy/user/channels
+0 127
+
+which means that the master allocation pool for this rule consists of
+masters 48 through 63 and channel allocation pool has channels 0
+through 127 in it. Now, any producer (trace source) identifying itself
+with "user" identification string will be allocated a master and
+channel from within these ranges.
+
+These rules can be nested, for example, one can define a rule "dummy"
+under "user" directory from the example above and this new rule will
+be used for trace sources with the id string of "user/dummy".
+
+Trace sources have to open the stm class device's node and write their
+trace data into its file descriptor. In order to identify themselves
+to the policy, they need to do a STP_POLICY_ID_SET ioctl on this file
+descriptor providing their id string. Otherwise, they will be
+automatically allocated a master/channel pair upon first write to this
+file descriptor according to the "default" rule of the policy, if such
+exists.
+
+Some STM devices may allow direct mapping of the channel mmio regions
+to userspace for zero-copy writing. One mappable page (in terms of
+mmu) will usually contain multiple channels' mmios, so the user will
+need to allocate that many channels to themselves (via the
+aforementioned ioctl() call) to be able to do this. That is, if your
+stm device's channel mmio region is 64 bytes and hardware page size is
+4096 bytes, after a successful STP_POLICY_ID_SET ioctl() call with
+width==64, you should be able to mmap() one page on this file
+descriptor and obtain direct access to an mmio region for 64 channels.
+
+For kernel-based trace sources, there is "stm_source" device
+class. Devices of this class can be connected and disconnected to/from
+stm devices at runtime via a sysfs attribute.
+
+Examples of STM devices are Intel Trace Hub [1] and Coresight STM
+[2].
+
+[1] https://software.intel.com/sites/default/files/managed/d3/3c/intel-th-developer-manual.pdf
+[2] http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.ddi0444b/index.html
diff --git a/drivers/Kconfig b/drivers/Kconfig
index c0cc96bab9..9850ab81cc 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -182,4 +182,6 @@ source "drivers/thunderbolt/Kconfig"
source "drivers/android/Kconfig"
+source "drivers/hwtracing/stm/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 527a6da8d5..87d7c74e39 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -165,3 +165,4 @@ obj-$(CONFIG_RAS) += ras/
obj-$(CONFIG_THUNDERBOLT) += thunderbolt/
obj-$(CONFIG_CORESIGHT) += coresight/
obj-$(CONFIG_ANDROID) += android/
+obj-$(CONFIG_STM) += hwtracing/stm/
diff --git a/drivers/hwtracing/stm/Kconfig b/drivers/hwtracing/stm/Kconfig
new file mode 100644
index 0000000000..90ed327461
--- /dev/null
+++ b/drivers/hwtracing/stm/Kconfig
@@ -0,0 +1,8 @@
+config STM
+ tristate "System Trace Module devices"
+ help
+ A System Trace Module (STM) is a device exporting data in System
+ Trace Protocol (STP) format as defined by MIPI STP standards.
+ Examples of such devices are Intel Trace Hub and Coresight STM.
+
+ Say Y here to enable System Trace Module device support.
diff --git a/drivers/hwtracing/stm/Makefile b/drivers/hwtracing/stm/Makefile
new file mode 100644
index 0000000000..adec701649
--- /dev/null
+++ b/drivers/hwtracing/stm/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_STM) += stm_core.o
+
+stm_core-y := core.o policy.o
diff --git a/drivers/hwtracing/stm/core.c b/drivers/hwtracing/stm/core.c
new file mode 100644
index 0000000000..9e82634590
--- /dev/null
+++ b/drivers/hwtracing/stm/core.c
@@ -0,0 +1,897 @@
+/*
+ * System Trace Module (STM) infrastructure
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * STM class implements generic infrastructure for System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/compat.h>
+#include <linux/kdev_t.h>
+#include <linux/srcu.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include "stm.h"
+
+#include <uapi/linux/stm.h>
+
+static unsigned int stm_core_up;
+
+/*
+ * The SRCU here makes sure that STM device doesn't disappear from under a
+ * stm_source_write() caller, which may want to have as little overhead as
+ * possible.
+ */
+static struct srcu_struct stm_source_srcu;
+
+static ssize_t masters_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct stm_device *stm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sprintf(buf, "%u %u\n", stm->data->sw_start, stm->data->sw_end);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(masters);
+
+static ssize_t channels_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct stm_device *stm = dev_get_drvdata(dev);
+ int ret;
+
+ ret = sprintf(buf, "%u\n", stm->data->sw_nchannels);
+
+ return ret;
+}
+
+static DEVICE_ATTR_RO(channels);
+
+static struct attribute *stm_attrs[] = {
+ &dev_attr_masters.attr,
+ &dev_attr_channels.attr,
+ NULL,
+};
+
+static const struct attribute_group stm_group = {
+ .attrs = stm_attrs,
+};
+
+static const struct attribute_group *stm_groups[] = {
+ &stm_group,
+ NULL,
+};
+
+static struct class stm_class = {
+ .name = "stm",
+ .dev_groups = stm_groups,
+};
+
+static int stm_dev_match(struct device *dev, const void *data)
+{
+ const char *name = data;
+
+ return sysfs_streq(name, dev_name(dev));
+}
+
+/**
+ * stm_find_device() - find stm device by name
+ * @buf: character buffer containing the name
+ * @len: length of the name in @buf
+ *
+ * This is called from attributes' store methods, so it will
+ * also trim the trailing newline if necessary.
+ *
+ * Return: device pointer or null if lookup failed.
+ */
+struct device *stm_find_device(const char *buf, size_t len)
+{
+ if (!stm_core_up)
+ return NULL;
+
+ return class_find_device(&stm_class, NULL, buf, stm_dev_match);
+}
+
+/*
+ * Internally we only care about software-writable masters here, that is the
+ * ones in the range [stm_data->sw_start..stm_data..sw_end], however we need
+ * original master numbers to be visible externally, since they are the ones
+ * that will appear in the STP stream. Thus, the internal bookkeeping uses
+ * $master - stm_data->sw_start to reference master descriptors and such.
+ */
+
+#define __stm_master(_s, _m) \
+ ((_s)->masters[(_m) - (_s)->data->sw_start])
+
+static inline struct stp_master *
+stm_master(struct stm_device *stm, unsigned int idx)
+{
+ if (idx < stm->data->sw_start || idx > stm->data->sw_end)
+ return NULL;
+
+ return __stm_master(stm, idx);
+}
+
+static int stp_master_alloc(struct stm_device *stm, unsigned int idx)
+{
+ struct stp_master *master;
+ size_t size;
+
+ size = ALIGN(stm->data->sw_nchannels, 8) / 8;
+ size += sizeof(struct stp_master);
+ master = kzalloc(size, GFP_ATOMIC);
+ if (!master)
+ return -ENOMEM;
+
+ master->nr_free = stm->data->sw_nchannels;
+ __stm_master(stm, idx) = master;
+
+ return 0;
+}
+
+static void stp_master_free(struct stm_device *stm, unsigned int idx)
+{
+ struct stp_master *master = stm_master(stm, idx);
+
+ if (!master)
+ return;
+
+ __stm_master(stm, idx) = NULL;
+ kfree(master);
+}
+
+static void stm_output_claim(struct stm_device *stm, struct stm_output *output)
+{
+ struct stp_master *master = stm_master(stm, output->master);
+
+ if (WARN_ON_ONCE(master->nr_free < output->nr_chans))
+ return;
+
+ bitmap_allocate_region(&master->chan_map[0], output->channel,
+ ilog2(output->nr_chans));
+
+ master->nr_free -= output->nr_chans;
+}
+
+static void
+stm_output_disclaim(struct stm_device *stm, struct stm_output *output)
+{
+ struct stp_master *master = stm_master(stm, output->master);
+
+ bitmap_release_region(&master->chan_map[0], output->channel,
+ ilog2(output->nr_chans));
+
+ master->nr_free += output->nr_chans;
+}
+
+/*
+ * This is like bitmap_find_free_region(), except it can ignore @start bits
+ * at the beginning.
+ */
+static int find_free_channels(unsigned long *bitmap, unsigned int start,
+ unsigned int end, unsigned int width)
+{
+ unsigned int pos;
+ int i;
+
+ for (pos = start; pos < end + 1; pos = ALIGN(pos, width)) {
+ pos = find_next_zero_bit(bitmap, end + 1, pos);
+ if (pos + width > end + 1)
+ break;
+
+ if (pos & (width - 1))
+ continue;
+
+ for (i = 1; i < width && !test_bit(pos + i, bitmap); i++)
+ ;
+ if (i == width)
+ return pos;
+ }
+
+ return -1;
+}
+
+static unsigned int
+stm_find_master_chan(struct stm_device *stm, unsigned int width,
+ unsigned int *mstart, unsigned int mend,
+ unsigned int *cstart, unsigned int cend)
+{
+ struct stp_master *master;
+ unsigned int midx;
+ int pos, err;
+
+ for (midx = *mstart; midx <= mend; midx++) {
+ if (!stm_master(stm, midx)) {
+ err = stp_master_alloc(stm, midx);
+ if (err)
+ return err;
+ }
+
+ master = stm_master(stm, midx);
+
+ if (!master->nr_free)
+ continue;
+
+ pos = find_free_channels(master->chan_map, *cstart, cend,
+ width);
+ if (pos < 0)
+ continue;
+
+ *mstart = midx;
+ *cstart = pos;
+ return 0;
+ }
+
+ return -ENOSPC;
+}
+
+static int stm_output_assign(struct stm_device *stm, unsigned int width,
+ struct stp_policy_node *policy_node,
+ struct stm_output *output)
+{
+ unsigned int midx, cidx, mend, cend;
+ int ret = -EBUSY;
+
+ if (width > stm->data->sw_nchannels)
+ return -EINVAL;
+
+ if (policy_node) {
+ stp_policy_node_get_ranges(policy_node,
+ &midx, &mend, &cidx, &cend);
+ } else {
+ midx = stm->data->sw_start;
+ cidx = 0;
+ mend = stm->data->sw_end;
+ cend = stm->data->sw_nchannels - 1;
+ }
+
+ spin_lock(&stm->mc_lock);
+ if (output->nr_chans)
+ goto unlock;
+
+ ret = stm_find_master_chan(stm, width, &midx, mend, &cidx, cend);
+ if (ret)
+ goto unlock;
+
+ output->master = midx;
+ output->channel = cidx;
+ output->nr_chans = width;
+ stm_output_claim(stm, output);
+ dev_dbg(stm->dev, "assigned %u:%u (+%u)\n", midx, cidx, width);
+
+ ret = 0;
+unlock:
+ spin_unlock(&stm->mc_lock);
+
+ return ret;
+}
+
+static void stm_output_free(struct stm_device *stm, struct stm_output *output)
+{
+ spin_lock(&stm->mc_lock);
+ if (output->nr_chans)
+ stm_output_disclaim(stm, output);
+ spin_unlock(&stm->mc_lock);
+}
+
+static int major_match(struct device *dev, const void *data)
+{
+ unsigned int major = *(unsigned int *)data;
+
+ return MAJOR(dev->devt) == major;
+}
+
+static int stm_char_open(struct inode *inode, struct file *file)
+{
+ struct stm_file *stmf;
+ struct device *dev;
+ unsigned int major = imajor(inode);
+ int err = -ENODEV;
+
+ dev = class_find_device(&stm_class, NULL, &major, major_match);
+ if (!dev)
+ return -ENODEV;
+
+ stmf = kzalloc(sizeof(*stmf), GFP_KERNEL);
+ if (!stmf)
+ return -ENOMEM;
+
+ stmf->stm = dev_get_drvdata(dev);
+
+ if (!try_module_get(stmf->stm->owner))
+ goto err_free;
+
+ file->private_data = stmf;
+
+ return nonseekable_open(inode, file);
+
+err_free:
+ kfree(stmf);
+
+ return err;
+}
+
+static int stm_char_release(struct inode *inode, struct file *file)
+{
+ struct stm_file *stmf = file->private_data;
+
+ stm_output_free(stmf->stm, &stmf->output);
+ module_put(stmf->stm->owner);
+ kfree(stmf);
+
+ return 0;
+}
+
+static int stm_file_assign(struct stm_file *stmf, char *id, unsigned int width)
+{
+ struct stm_device *stm = stmf->stm;
+ int ret;
+
+ mutex_lock(&stm->policy_mutex);
+ if (stm->policy)
+ stmf->policy_node = stp_policy_node_lookup(stm->policy, id);
+
+ ret = stm_output_assign(stm, width, stmf->policy_node, &stmf->output);
+ mutex_unlock(&stm->policy_mutex);
+
+ return ret;
+}
+
+static ssize_t stm_char_write(struct file *file, const char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ struct stm_file *stmf = file->private_data;
+ struct stm_device *stm = stmf->stm;
+ char *kbuf;
+ int err;
+
+ /*
+ * if no m/c have been assigned to this writer up to this
+ * point, use "default" policy entry
+ */
+ if (!stmf->output.nr_chans) {
+ err = stm_file_assign(stmf, "default", 1);
+ /*
+ * EBUSY means that somebody else just assigned this
+ * output, which is just fine for write()
+ */
+ if (err && err != -EBUSY)
+ return err;
+ }
+
+ kbuf = kmalloc(count + 1, GFP_KERNEL);
+ if (!kbuf)
+ return -ENOMEM;
+
+ err = copy_from_user(kbuf, buf, count);
+ if (err) {
+ kfree(kbuf);
+ return -EFAULT;
+ }
+
+ stm->data->write(stm->data, stmf->output.master,
+ stmf->output.channel, kbuf, count);
+
+
+ kfree(kbuf);
+
+ return count;
+}
+
+static int stm_char_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ struct stm_file *stmf = file->private_data;
+ struct stm_device *stm = stmf->stm;
+ unsigned long size, phys;
+
+ if (!stm->data->mmio_addr)
+ return -EOPNOTSUPP;
+
+ if (vma->vm_pgoff)
+ return -EINVAL;
+
+ size = vma->vm_end - vma->vm_start;
+
+ if (stmf->output.nr_chans * stm->data->sw_mmiosz != size)
+ return -EINVAL;
+
+ phys = stm->data->mmio_addr(stm->data, stmf->output.master,
+ stmf->output.channel,
+ stmf->output.nr_chans);
+
+ if (!phys)
+ return -EINVAL;
+
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_flags |= VM_IO | VM_DONTEXPAND | VM_DONTDUMP;
+ vm_iomap_memory(vma, phys, size);
+
+ return 0;
+}
+
+static int stm_char_policy_set_ioctl(struct stm_file *stmf, void __user *arg)
+{
+ struct stm_device *stm = stmf->stm;
+ struct stp_policy_id *id;
+ int ret = -EINVAL;
+ u32 size;
+
+ if (stmf->output.nr_chans)
+ return -EBUSY;
+
+ if (copy_from_user(&size, arg, sizeof(size)))
+ return -EFAULT;
+
+ if (size >= PATH_MAX + sizeof(*id))
+ return -EINVAL;
+
+ /* size + 1 to make sure the .id string at the bottom is terminated */
+ id = kzalloc(size + 1, GFP_KERNEL);
+ if (!id)
+ return -ENOMEM;
+
+ if (copy_from_user(id, arg, size)) {
+ ret = -EFAULT;
+ goto err_free;
+ }
+
+ if (id->__reserved_0 || id->__reserved_1)
+ goto err_free;
+
+ if (id->width < 1 ||
+ id->width > PAGE_SIZE / stm->data->sw_mmiosz)
+ goto err_free;
+
+ ret = stm_file_assign(stmf, id->id, id->width);
+ if (ret)
+ goto err_free;
+
+ if (stm->data->link)
+ stm->data->link(stm->data, stmf->output.master,
+ stmf->output.channel);
+
+ ret = 0;
+
+err_free:
+ kfree(id);
+
+ return ret;
+}
+
+static int stm_char_policy_get_ioctl(struct stm_file *stmf, void __user *arg)
+{
+ struct stp_policy_id id = {
+ .size = sizeof(id),
+ .master = stmf->output.master,
+ .channel = stmf->output.channel,
+ .width = stmf->output.nr_chans,
+ .__reserved_0 = 0,
+ .__reserved_1 = 0,
+ };
+
+ return copy_to_user(arg, &id, id.size) ? -EFAULT : 0;
+}
+
+static long
+stm_char_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct stm_file *stmf = file->private_data;
+ struct stm_data *stm_data = stmf->stm->data;
+ int err = -ENOTTY;
+
+ switch (cmd) {
+ case STP_POLICY_ID_SET:
+ err = stm_char_policy_set_ioctl(stmf, (void __user *)arg);
+ if (err)
+ return err;
+
+ return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
+
+ case STP_POLICY_ID_GET:
+ return stm_char_policy_get_ioctl(stmf, (void __user *)arg);
+
+ default:
+ if (stm_data->ioctl)
+ err = stm_data->ioctl(stm_data, cmd, arg);
+
+ break;
+ }
+
+ return err;
+}
+
+#ifdef CONFIG_COMPAT
+static long
+stm_char_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ return stm_char_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
+}
+#else
+#define stm_char_compat_ioctl NULL
+#endif
+
+static const struct file_operations stm_fops = {
+ .open = stm_char_open,
+ .release = stm_char_release,
+ .write = stm_char_write,
+ .mmap = stm_char_mmap,
+ .unlocked_ioctl = stm_char_ioctl,
+ .compat_ioctl = stm_char_compat_ioctl,
+ .llseek = no_llseek,
+};
+
+int stm_register_device(struct device *parent, struct stm_data *stm_data,
+ struct module *owner)
+{
+ struct stm_device *stm;
+ struct device *dev;
+ unsigned int nmasters;
+ int err = -ENOMEM;
+
+ if (!stm_core_up)
+ return -EPROBE_DEFER;
+
+ if (!stm_data->write || !stm_data->sw_nchannels)
+ return -EINVAL;
+
+ nmasters = stm_data->sw_end - stm_data->sw_start;
+ stm = kzalloc(sizeof(*stm) + nmasters * sizeof(void *), GFP_KERNEL);
+ if (!stm)
+ return -ENOMEM;
+
+ stm->major = register_chrdev(0, stm_data->name, &stm_fops);
+ if (stm->major < 0)
+ goto err_free;
+
+ dev = device_create(&stm_class, parent, MKDEV(stm->major, 0), NULL,
+ "%s", stm_data->name);
+ if (IS_ERR(dev)) {
+ err = PTR_ERR(dev);
+ goto err_device;
+ }
+
+ spin_lock_init(&stm->link_lock);
+ INIT_LIST_HEAD(&stm->link_list);
+
+ spin_lock_init(&stm->mc_lock);
+ mutex_init(&stm->policy_mutex);
+ stm->sw_nmasters = nmasters;
+ stm->owner = owner;
+ stm->data = stm_data;
+ stm->dev = dev;
+ stm_data->stm = stm;
+
+ dev_set_drvdata(dev, stm);
+
+ return 0;
+
+err_device:
+ device_unregister(dev);
+err_free:
+ kfree(stm);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(stm_register_device);
+
+static void stm_source_link_drop(struct stm_source_device *src);
+
+void stm_unregister_device(struct stm_data *stm_data)
+{
+ struct stm_device *stm = stm_data->stm;
+ struct stm_source_device *src, *iter;
+ int i;
+
+ spin_lock(&stm->link_lock);
+ list_for_each_entry_safe(src, iter, &stm->link_list, link_entry) {
+ stm_source_link_drop(src);
+ }
+ spin_unlock(&stm->link_lock);
+
+ synchronize_srcu(&stm_source_srcu);
+
+ unregister_chrdev(stm->major, stm_data->name);
+
+ if (stm->policy)
+ stp_policy_unbind(stm->policy);
+
+ for (i = 0; i < stm->sw_nmasters; i++)
+ stp_master_free(stm, i);
+
+ device_unregister(stm->dev);
+ kfree(stm);
+ stm_data->stm = NULL;
+}
+EXPORT_SYMBOL_GPL(stm_unregister_device);
+
+/**
+ * stm_source_link_add() - connect an stm_source device to an stm device
+ * @src: stm_source device
+ * @stm: stm device
+ *
+ * This function establishes a link from stm_source to an stm device so that
+ * the former can send out trace data to the latter.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+static int stm_source_link_add(struct stm_source_device *src,
+ struct stm_device *stm)
+{
+ int err;
+
+ spin_lock(&stm->link_lock);
+ spin_lock(&src->link_lock);
+
+ /* src->link is dereferenced under stm_source_srcu but not the list */
+ rcu_assign_pointer(src->link, stm);
+ list_add_tail(&src->link_entry, &stm->link_list);
+
+ spin_unlock(&src->link_lock);
+ spin_unlock(&stm->link_lock);
+
+ if (stm->policy) {
+ char *id = kstrdup(src->data->name, GFP_KERNEL);
+
+ if (id) {
+ src->policy_node =
+ stp_policy_node_lookup(stm->policy, id);
+
+ kfree(id);
+ }
+ }
+
+ err = stm_output_assign(stm, src->data->nr_chans,
+ src->policy_node, &src->output);
+ if (err)
+ return err;
+
+ /* this is to notify the STM device that a new link has been made */
+ if (stm->data->link)
+ stm->data->link(stm->data, src->output.master,
+ src->output.channel);
+
+ /* this is to let the source carry out all necessary preparations */
+ if (src->data->link)
+ src->data->link(src->data);
+
+ return 0;
+}
+
+/**
+ * stm_source_link_drop() - detach stm_source from its stm device
+ * @src: stm_source device
+ *
+ * Unlinking means disconnecting from source's STM device; after this
+ * writes will be unsuccessful until it is linked to a new STM device.
+ *
+ * This will happen on "stm_source_link" sysfs attribute write to undo
+ * the existing link (if any), or on linked STM device's de-registration.
+ */
+static void stm_source_link_drop(struct stm_source_device *src)
+{
+ int idx = srcu_read_lock(&stm_source_srcu);
+
+ if (src->link && src->data->unlink)
+ src->data->unlink(src->data);
+
+ srcu_read_unlock(&stm_source_srcu, idx);
+
+ spin_lock(&src->link_lock);
+ if (src->link) {
+ stm_output_free(src->link, &src->output);
+ list_del_init(&src->link_entry);
+ rcu_assign_pointer(src->link, NULL);
+ }
+ spin_unlock(&src->link_lock);
+}
+
+static ssize_t stm_source_link_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct stm_source_device *src = dev_get_drvdata(dev);
+ int idx, ret;
+
+ idx = srcu_read_lock(&stm_source_srcu);
+ ret = sprintf(buf, "%s\n",
+ src->link ? dev_name(src->link->dev) : "<none>");
+ srcu_read_unlock(&stm_source_srcu, idx);
+
+ return ret;
+}
+
+static ssize_t stm_source_link_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct stm_source_device *src = dev_get_drvdata(dev);
+ struct stm_device *link;
+ struct device *linkdev;
+ int err;
+
+ stm_source_link_drop(src);
+
+ linkdev = stm_find_device(buf, count);
+ if (!linkdev)
+ return -EINVAL;
+
+ link = dev_get_drvdata(linkdev);
+
+ err = stm_source_link_add(src, link);
+
+ return err ? : count;
+}
+
+static DEVICE_ATTR_RW(stm_source_link);
+
+static struct attribute *stm_source_attrs[] = {
+ &dev_attr_stm_source_link.attr,
+ NULL,
+};
+
+static const struct attribute_group stm_source_group = {
+ .attrs = stm_source_attrs,
+};
+
+static const struct attribute_group *stm_source_groups[] = {
+ &stm_source_group,
+ NULL,
+};
+
+static struct class stm_source_class = {
+ .name = "stm_source",
+ .dev_groups = stm_source_groups,
+};
+
+/**
+ * stm_source_register_device() - register an stm_source device
+ * @parent: parent device
+ * @data: device description structure
+ *
+ * This will create a device of stm_source class that can write
+ * data to an stm device once linked.
+ *
+ * Return: 0 on success, -errno otherwise.
+ */
+int stm_source_register_device(struct device *parent,
+ struct stm_source_data *data)
+{
+ struct stm_source_device *src;
+ struct device *dev;
+
+ if (!stm_core_up)
+ return -EPROBE_DEFER;
+
+ src = kzalloc(sizeof(*src), GFP_KERNEL);
+ if (!src)
+ return -ENOMEM;
+
+ dev = device_create(&stm_source_class, parent, MKDEV(0, 0), NULL, "%s",
+ data->name);
+ if (IS_ERR(dev)) {
+ kfree(src);
+ return PTR_ERR(dev);
+ }
+
+ spin_lock_init(&src->link_lock);
+ INIT_LIST_HEAD(&src->link_entry);
+ src->dev = dev;
+ src->data = data;
+ data->src = src;
+ dev_set_drvdata(dev, src);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(stm_source_register_device);
+
+/**
+ * stm_source_unregister_device() - unregister an stm_source device
+ * @data: device description that was used to register the device
+ *
+ * This will remove a previously created stm_source device from the system.
+ */
+void stm_source_unregister_device(struct stm_source_data *data)
+{
+ struct stm_source_device *src = data->src;
+
+ stm_source_link_drop(src);
+
+ device_destroy(&stm_source_class, src->dev->devt);
+
+ kfree(src);
+}
+EXPORT_SYMBOL_GPL(stm_source_unregister_device);
+
+int stm_source_write(struct stm_source_data *data, unsigned int chan,
+ const char *buf, size_t count)
+{
+ struct stm_source_device *src = data->src;
+ struct stm_device *stm;
+ int idx;
+
+ if (!src->output.nr_chans)
+ return -ENODEV;
+
+ if (chan >= src->output.nr_chans)
+ return -EINVAL;
+
+ idx = srcu_read_lock(&stm_source_srcu);
+
+ stm = srcu_dereference(src->link, &stm_source_srcu);
+ if (stm)
+ count = stm->data->write(stm->data, src->output.master,
+ src->output.channel + chan, buf,
+ count);
+ else
+ count = -ENODEV;
+
+ srcu_read_unlock(&stm_source_srcu, idx);
+
+ return count;
+}
+EXPORT_SYMBOL_GPL(stm_source_write);
+
+static int __init stm_core_init(void)
+{
+ int err;
+
+ err = class_register(&stm_class);
+ if (err)
+ return err;
+
+ err = class_register(&stm_source_class);
+ if (err)
+ goto err_stm;
+
+ err = stp_configfs_init();
+ if (err)
+ goto err_src;
+
+ init_srcu_struct(&stm_source_srcu);
+
+ stm_core_up++;
+
+ return 0;
+
+err_src:
+ class_unregister(&stm_source_class);
+err_stm:
+ class_unregister(&stm_class);
+
+ return err;
+}
+
+postcore_initcall(stm_core_init);
+
+static void __exit stm_core_exit(void)
+{
+ class_unregister(&stm_source_class);
+ class_unregister(&stm_class);
+ stp_configfs_exit();
+}
+
+module_exit(stm_core_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("System Trace Module device class");
+MODULE_AUTHOR("Alexander Shishkin <alexander.shishkin@linux.intel.com>");
diff --git a/drivers/hwtracing/stm/policy.c b/drivers/hwtracing/stm/policy.c
new file mode 100644
index 0000000000..b5c59a0e0c
--- /dev/null
+++ b/drivers/hwtracing/stm/policy.c
@@ -0,0 +1,467 @@
+/*
+ * System Trace Module (STM) master/channel allocation policy management
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * A master/channel allocation policy allows mapping string identifiers to
+ * master and channel ranges, where allocation can be done.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/configfs.h>
+#include <linux/slab.h>
+#include <linux/stm.h>
+#include "stm.h"
+
+/*
+ * STP Master/Channel allocation policy configfs layout.
+ */
+
+struct stp_policy {
+ struct config_group group;
+ struct stm_device *stm;
+};
+
+struct stp_policy_node {
+ struct config_group group;
+ struct stm_device *stm;
+ struct stp_policy *policy;
+ unsigned int first_master;
+ unsigned int last_master;
+ unsigned int first_channel;
+ unsigned int last_channel;
+};
+
+void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
+ unsigned int *mstart, unsigned int *mend,
+ unsigned int *cstart, unsigned int *cend)
+{
+ *mstart = policy_node->first_master;
+ *mend = policy_node->last_master;
+ *cstart = policy_node->first_channel;
+ *cend = policy_node->last_channel;
+}
+
+static inline char *stp_policy_node_name(struct stp_policy_node *policy_node)
+{
+ return policy_node->group.cg_item.ci_name ? : "<none>";
+}
+
+static inline struct stp_policy *to_stp_policy(struct config_item *item)
+{
+ return item ?
+ container_of(to_config_group(item), struct stp_policy, group) :
+ NULL;
+}
+
+static inline struct stp_policy_node *
+to_stp_policy_node(struct config_item *item)
+{
+ return item ?
+ container_of(to_config_group(item), struct stp_policy_node,
+ group) :
+ NULL;
+}
+
+static ssize_t stp_policy_node_masters_show(struct stp_policy_node *policy_node,
+ char *page)
+{
+ ssize_t count;
+
+ count = sprintf(page, "%u %u\n", policy_node->first_master,
+ policy_node->last_master);
+
+ return count;
+}
+
+static ssize_t
+stp_policy_node_masters_store(struct stp_policy_node *policy_node,
+ const char *page, size_t count)
+{
+ struct stm_device *stm = policy_node->stm;
+ unsigned int first, last;
+ char *p = (char *) page;
+
+ if (sscanf(p, "%u %u", &first, &last) != 2)
+ return -EINVAL;
+
+ /* must be within [sw_start..sw_end], which is an inclusive range */
+ if (first > INT_MAX || last > INT_MAX || first > last ||
+ first < stm->data->sw_start ||
+ last > stm->data->sw_end)
+ return -ERANGE;
+
+ policy_node->first_master = first;
+ policy_node->last_master = last;
+
+ return count;
+}
+
+static ssize_t
+stp_policy_node_channels_show(struct stp_policy_node *policy_node, char *page)
+{
+ ssize_t count;
+
+ count = sprintf(page, "%u %u\n", policy_node->first_channel,
+ policy_node->last_channel);
+
+ return count;
+}
+
+static ssize_t
+stp_policy_node_channels_store(struct stp_policy_node *policy_node,
+ const char *page, size_t count)
+{
+ unsigned int first, last;
+ char *p = (char *) page;
+
+ if (sscanf(p, "%u %u", &first, &last) != 2)
+ return -EINVAL;
+
+ if (first > INT_MAX || last > INT_MAX || first > last ||
+ last >= policy_node->stm->data->sw_nchannels)
+ return -ERANGE;
+
+ policy_node->first_channel = first;
+ policy_node->last_channel = last;
+
+ return count;
+}
+
+static void stp_policy_node_release(struct config_item *item)
+{
+ kfree(to_stp_policy_node(item));
+}
+
+struct stp_policy_node_attribute {
+ struct configfs_attribute attr;
+ ssize_t (*show)(struct stp_policy_node *, char *);
+ ssize_t (*store)(struct stp_policy_node *, const char *, size_t);
+};
+
+static ssize_t stp_policy_node_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct stp_policy_node *policy_node = to_stp_policy_node(item);
+ struct stp_policy_node_attribute *pn_attr =
+ container_of(attr, struct stp_policy_node_attribute, attr);
+ ssize_t count = 0;
+
+ if (pn_attr->show)
+ count = pn_attr->show(policy_node, page);
+
+ return count;
+}
+
+static ssize_t stp_policy_node_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *page, size_t len)
+{
+ struct stp_policy_node *policy_node = to_stp_policy_node(item);
+ struct stp_policy_node_attribute *pn_attr =
+ container_of(attr, struct stp_policy_node_attribute, attr);
+ ssize_t count = -EINVAL;
+
+ if (pn_attr->store)
+ count = pn_attr->store(policy_node, page, len);
+
+ return count;
+}
+
+static struct configfs_item_operations stp_policy_node_item_ops = {
+ .release = stp_policy_node_release,
+ .show_attribute = stp_policy_node_attr_show,
+ .store_attribute = stp_policy_node_attr_store,
+};
+
+static struct stp_policy_node_attribute stp_policy_node_attr_range = {
+ .attr = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "masters",
+ .ca_mode = S_IRUGO | S_IWUSR,
+ },
+ .show = stp_policy_node_masters_show,
+ .store = stp_policy_node_masters_store,
+};
+
+static struct stp_policy_node_attribute stp_policy_node_attr_channels = {
+ .attr = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "channels",
+ .ca_mode = S_IRUGO | S_IWUSR,
+ },
+ .show = stp_policy_node_channels_show,
+ .store = stp_policy_node_channels_store,
+};
+
+static struct configfs_attribute *stp_policy_node_attrs[] = {
+ &stp_policy_node_attr_range.attr,
+ &stp_policy_node_attr_channels.attr,
+ NULL,
+};
+
+static struct config_item_type stp_policy_type;
+static struct config_item_type stp_policy_node_type;
+
+static struct config_group *
+stp_policy_node_make(struct config_group *group, const char *name)
+{
+ struct stp_policy_node *policy_node, *parent_node;
+ struct stp_policy *policy;
+
+ if (group->cg_item.ci_type == &stp_policy_type) {
+ policy = container_of(group, struct stp_policy, group);
+ } else {
+ parent_node = container_of(group, struct stp_policy_node,
+ group);
+ policy = parent_node->policy;
+ }
+
+ if (!policy->stm)
+ return ERR_PTR(-ENODEV);
+
+ policy_node = kzalloc(sizeof(struct stp_policy_node), GFP_KERNEL);
+ if (!policy_node)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&policy_node->group, name,
+ &stp_policy_node_type);
+
+ policy_node->policy = policy;
+ policy_node->stm = policy->stm;
+
+ /* default values for the attributes */
+ policy_node->first_master = policy->stm->data->sw_start;
+ policy_node->last_master = policy->stm->data->sw_end;
+ policy_node->first_channel = 0;
+ policy_node->last_channel = policy->stm->data->sw_nchannels - 1;
+
+ return &policy_node->group;
+}
+
+static void
+stp_policy_node_drop(struct config_group *group, struct config_item *item)
+{
+ config_item_put(item);
+}
+
+static struct configfs_group_operations stp_policy_node_group_ops = {
+ .make_group = stp_policy_node_make,
+ .drop_item = stp_policy_node_drop,
+};
+
+static struct config_item_type stp_policy_node_type = {
+ .ct_item_ops = &stp_policy_node_item_ops,
+ .ct_group_ops = &stp_policy_node_group_ops,
+ .ct_attrs = stp_policy_node_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+/*
+ * Root group: policies.
+ */
+static struct configfs_attribute stp_policy_attr_device = {
+ .ca_owner = THIS_MODULE,
+ .ca_name = "device",
+ .ca_mode = S_IRUGO | S_IWUSR,
+};
+
+static struct configfs_attribute *stp_policy_attrs[] = {
+ &stp_policy_attr_device,
+ NULL,
+};
+
+static ssize_t stp_policy_attr_show(struct config_item *item,
+ struct configfs_attribute *attr,
+ char *page)
+{
+ struct stp_policy *policy = to_stp_policy(item);
+
+ return sprintf(page, "%s\n",
+ (policy && policy->stm) ?
+ policy->stm->data->name :
+ "<none>");
+}
+
+static ssize_t stp_policy_attr_store(struct config_item *item,
+ struct configfs_attribute *attr,
+ const char *page, size_t len)
+{
+ struct stp_policy *policy = to_stp_policy(item);
+ ssize_t count = -EINVAL;
+ struct device *dev;
+
+ dev = stm_find_device(page, len);
+ if (dev) {
+ count = len;
+ if (policy->stm)
+ put_device(policy->stm->dev);
+
+ policy->stm = dev_get_drvdata(dev);
+
+ mutex_lock(&policy->stm->policy_mutex);
+ policy->stm->policy = policy;
+ mutex_unlock(&policy->stm->policy_mutex);
+ }
+
+ return count;
+}
+
+void stp_policy_unbind(struct stp_policy *policy)
+{
+ put_device(policy->stm->dev);
+
+ mutex_lock(&policy->stm->policy_mutex);
+ policy->stm->policy = NULL;
+ mutex_unlock(&policy->stm->policy_mutex);
+
+ policy->stm = NULL;
+}
+
+static void stp_policy_release(struct config_item *item)
+{
+ struct stp_policy *policy = to_stp_policy(item);
+
+ stp_policy_unbind(policy);
+ kfree(policy);
+}
+
+static struct configfs_item_operations stp_policy_item_ops = {
+ .release = stp_policy_release,
+ .show_attribute = stp_policy_attr_show,
+ .store_attribute = stp_policy_attr_store,
+};
+
+static struct configfs_group_operations stp_policy_group_ops = {
+ .make_group = stp_policy_node_make,
+};
+
+static struct config_item_type stp_policy_type = {
+ .ct_item_ops = &stp_policy_item_ops,
+ .ct_group_ops = &stp_policy_group_ops,
+ .ct_attrs = stp_policy_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct config_group *
+stp_policies_make(struct config_group *group, const char *name)
+{
+ struct stp_policy *policy;
+
+ policy = kzalloc(sizeof(*policy), GFP_KERNEL);
+ if (!policy)
+ return ERR_PTR(-ENOMEM);
+
+ config_group_init_type_name(&policy->group, name,
+ &stp_policy_type);
+ policy->stm = NULL;
+
+ return &policy->group;
+}
+
+static struct configfs_group_operations stp_policies_group_ops = {
+ .make_group = stp_policies_make,
+};
+
+static struct config_item_type stp_policies_type = {
+ .ct_group_ops = &stp_policies_group_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+static struct configfs_subsystem stp_policy_subsys = {
+ .su_group = {
+ .cg_item = {
+ .ci_namebuf = "stp-policy",
+ .ci_type = &stp_policies_type,
+ },
+ },
+};
+
+/*
+ * Lock the policy mutex from the outside
+ */
+static struct stp_policy_node *
+__stp_policy_node_lookup(struct stp_policy *policy, char *s)
+{
+ struct stp_policy_node *policy_node, *ret;
+ struct list_head *head = &policy->group.cg_children;
+ struct config_item *item;
+ char *start, *end = s;
+
+ if (list_empty(head))
+ return NULL;
+
+ /* return the first entry if everything else fails */
+ item = list_entry(head->next, struct config_item, ci_entry);
+ ret = to_stp_policy_node(item);
+
+next:
+ for (;;) {
+ start = strsep(&end, "/");
+ if (!start)
+ break;
+
+ if (!*start)
+ continue;
+
+ list_for_each_entry(item, head, ci_entry) {
+ policy_node = to_stp_policy_node(item);
+
+ if (!strcmp(start,
+ policy_node->group.cg_item.ci_name)) {
+ ret = policy_node;
+
+ if (!end)
+ goto out;
+
+ head = &policy_node->group.cg_children;
+ goto next;
+ }
+ }
+ break;
+ }
+
+out:
+ return ret;
+}
+
+struct stp_policy_node *
+stp_policy_node_lookup(struct stp_policy *policy, char *s)
+{
+ struct stp_policy_node *policy_node;
+
+ mutex_lock(&stp_policy_subsys.su_mutex);
+ policy_node = __stp_policy_node_lookup(policy, s);
+ mutex_unlock(&stp_policy_subsys.su_mutex);
+
+ return policy_node;
+}
+
+int __init stp_configfs_init(void)
+{
+ int err;
+
+ config_group_init(&stp_policy_subsys.su_group);
+ mutex_init(&stp_policy_subsys.su_mutex);
+ err = configfs_register_subsystem(&stp_policy_subsys);
+
+ return err;
+}
+
+void __exit stp_configfs_exit(void)
+{
+ configfs_unregister_subsystem(&stp_policy_subsys);
+}
diff --git a/drivers/hwtracing/stm/stm.h b/drivers/hwtracing/stm/stm.h
new file mode 100644
index 0000000000..4d088a1400
--- /dev/null
+++ b/drivers/hwtracing/stm/stm.h
@@ -0,0 +1,79 @@
+/*
+ * System Trace Module (STM) infrastructure
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * STM class implements generic infrastructure for System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#ifndef _CLASS_STM_H_
+#define _CLASS_STM_H_
+
+struct stp_policy;
+struct stp_policy_node;
+
+struct stp_policy_node *
+stp_policy_node_lookup(struct stp_policy *policy, char *s);
+void stp_policy_unbind(struct stp_policy *policy);
+
+void stp_policy_node_get_ranges(struct stp_policy_node *policy_node,
+ unsigned int *mstart, unsigned int *mend,
+ unsigned int *cstart, unsigned int *cend);
+int stp_configfs_init(void);
+void stp_configfs_exit(void);
+
+struct stp_master {
+ unsigned int nr_free;
+ unsigned long chan_map[0];
+};
+
+struct stm_device {
+ struct device *dev;
+ struct module *owner;
+ struct stp_policy *policy;
+ struct mutex policy_mutex;
+ int major;
+ unsigned int sw_nmasters;
+ struct stm_data *data;
+ spinlock_t link_lock;
+ struct list_head link_list;
+ /* master allocation */
+ spinlock_t mc_lock;
+ struct stp_master *masters[0];
+};
+
+struct stm_output {
+ unsigned int master;
+ unsigned int channel;
+ unsigned int nr_chans;
+};
+
+struct stm_file {
+ struct stm_device *stm;
+ struct stp_policy_node *policy_node;
+ struct stm_output output;
+};
+
+struct device *stm_find_device(const char *name, size_t len);
+
+struct stm_source_device {
+ struct device *dev;
+ struct stm_source_data *data;
+ spinlock_t link_lock;
+ struct stm_device *link;
+ struct list_head link_entry;
+ /* one output per stm_source device */
+ struct stp_policy_node *policy_node;
+ struct stm_output output;
+};
+
+#endif /* _CLASS_STM_H_ */
diff --git a/include/linux/stm.h b/include/linux/stm.h
new file mode 100644
index 0000000000..976c94d8f1
--- /dev/null
+++ b/include/linux/stm.h
@@ -0,0 +1,102 @@
+/*
+ * System Trace Module (STM) infrastructure apis
+ * Copyright (C) 2014 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _STM_H_
+#define _STM_H_
+
+#include <linux/device.h>
+
+struct stp_policy;
+
+struct stm_device;
+
+/**
+ * struct stm_data - STM device description and callbacks
+ * @name: device name
+ * @stm: internal structure, only used by stm class code
+ * @sw_start: first STP master available to software
+ * @sw_end: last STP master available to software
+ * @sw_nchannels: number of STP channels per master
+ * @sw_mmiosz: size of one channel's IO space, for mmap, optional
+ * @write: write callback
+ * @mmio_addr: mmap callback, optional
+ * @link: called when a new stm_source gets linked to us, optional
+ * @unlink: likewise for unlinking, again optional
+ * @ioctl: ioctl callback for device-specific commands
+ *
+ * Fill out this structure before calling stm_register_device() to create
+ * an STM device and stm_unregister_device() to destroy it. It will also be
+ * passed back to @write(), @mmio_addr(), @link(), @unlink() and @ioctl()
+ * callbacks.
+ *
+ * Normally, an STM device will have a range of masters available to software
+ * and the rest being statically assigned to various hardware trace sources.
+ * The former is defined by the the range [@sw_start..@sw_end] of the device
+ * description. That is, the lowest master that can be allocated to software
+ * writers is @sw_start and data from this writer will appear is @sw_start
+ * master in the STP stream.
+ */
+struct stm_data {
+ const char *name;
+ struct stm_device *stm;
+ unsigned int sw_start;
+ unsigned int sw_end;
+ unsigned int sw_nchannels;
+ unsigned int sw_mmiosz;
+ ssize_t (*write)(struct stm_data *, unsigned int,
+ unsigned int, const char *, size_t);
+ phys_addr_t (*mmio_addr)(struct stm_data *, unsigned int,
+ unsigned int, unsigned int);
+ void (*link)(struct stm_data *, unsigned int,
+ unsigned int);
+ void (*unlink)(struct stm_data *, unsigned int,
+ unsigned int);
+ long (*ioctl)(struct stm_data *, unsigned int,
+ unsigned long);
+};
+
+int stm_register_device(struct device *parent, struct stm_data *stm_data,
+ struct module *owner);
+void stm_unregister_device(struct stm_data *stm_data);
+
+struct stm_source_device;
+
+/**
+ * struct stm_source_data - STM source device description and callbacks
+ * @name: device name, will be used for policy lookup
+ * @src: internal structure, only used by stm class code
+ * @nr_chans: number of channels to allocate
+ * @link: called when this source gets linked to an STM device
+ * @unlink: called when this source is about to get unlinked from its STM
+ *
+ * Fill in this structure before calling stm_source_register_device() to
+ * register a source device. Also pass it to unregister and write calls.
+ */
+struct stm_source_data {
+ const char *name;
+ struct stm_source_device *src;
+ unsigned int percpu;
+ unsigned int nr_chans;
+ int (*link)(struct stm_source_data *data);
+ void (*unlink)(struct stm_source_data *data);
+};
+
+int stm_source_register_device(struct device *parent,
+ struct stm_source_data *data);
+void stm_source_unregister_device(struct stm_source_data *data);
+
+int stm_source_write(struct stm_source_data *data, unsigned int chan,
+ const char *buf, size_t count);
+
+#endif /* _STM_H_ */
diff --git a/include/uapi/linux/stm.h b/include/uapi/linux/stm.h
new file mode 100644
index 0000000000..042b58b53b
--- /dev/null
+++ b/include/uapi/linux/stm.h
@@ -0,0 +1,47 @@
+/*
+ * System Trace Module (STM) userspace interfaces
+ * Copyright (c) 2014, Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * STM class implements generic infrastructure for System Trace Module devices
+ * as defined in MIPI STPv2 specification.
+ */
+
+#ifndef _UAPI_LINUX_STM_H
+#define _UAPI_LINUX_STM_H
+
+/**
+ * struct stp_policy_id - identification for the STP policy
+ * @size: size of the structure including real id[] length
+ * @master: assigned master
+ * @channel: first assigned channel
+ * @width: number of requested channels
+ * @id: identification string
+ *
+ * User must calculate the total size of the structure and put it into
+ * @size field, fill out the @id and desired @width. In return, kernel
+ * fills out @master, @channel and @width.
+ */
+struct stp_policy_id {
+ __u32 size;
+ __u16 master;
+ __u16 channel;
+ __u16 width;
+ /* padding */
+ __u16 __reserved_0;
+ __u32 __reserved_1;
+ char id[0];
+};
+
+#define STP_POLICY_ID_SET _IOWR('%', 0, struct stp_policy_id)
+#define STP_POLICY_ID_GET _IOR('%', 1, struct stp_policy_id)
+
+#endif /* _UAPI_LINUX_STM_H */
--
2.1.4
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox