* Re: [PATCH 1/5] drivers: input: keyboard: st-keyscan: add keyscan driver
From: Gabriel Fernandez @ 2014-03-14 10:13 UTC (permalink / raw)
To: Lee Jones
Cc: Mark Rutland, devicetree, Russell King, kernel, Pawel Moll,
Ian Campbell, Dmitry Torokhov, linux-doc, linux-kernel,
Rob Herring, Giuseppe Condorelli, Rob Landley, Kumar Gala,
Grant Likely, linux-input, linux-arm-kernel
In-Reply-To: <20140310114819.GO14976@lee--X1>
Hi Lee,
On 03/10/2014 12:48 PM, Lee Jones wrote:
> Hi Gabi,
>
> Sorry for the delay. It was a hectic week last week.
>
> As promised:
>
>> This patch adds ST Keyscan driver to use the keypad hw a subset
>> of ST boards provide. Specific board setup will be put in the
>> given dt.
>>
>> Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
> Are you sure these are in the correct order?
ok i change the order
>> +- linux,keymap: The keymap for keys as described in the binding document
>> + devicetree/bindings/input/matrix-keymap.txt.
>> +
>> +- keypad,num-rows: Number of row lines connected to the keypad controller.
>> +
>> +- keypad,num-columns: Number of column lines connected to the keypad
>> + controller.
>> +
>> +- st,debounce_us: Debouncing interval time in microseconds
> I'm sure there will be a shared binding for de-bounce.
>
> If not, there certainly should be.
you want to refer to "debounce-interval" ?
>
> +Example:
> +
> +keyscan: keyscan@fe4b0000 {
> + compatible = "st,keypad";
> Is there any way we can make this more specific to _this_ IP?
for my knowledge this IP is the same for stixxxx platform.
>
>> + To compile this driver as a module, choose M here: the
>> + module will be called stm-keyscan.
>> +
>> config KEYBOARD_SUNKBD
>> tristate "Sun Type 4 and Type 5 keyboard"
>> select SERIO
>> diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
>> index a699b61..5fd020a 100644
>> --- a/drivers/input/keyboard/Makefile
>> +++ b/drivers/input/keyboard/Makefile
>> @@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
>> obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
>> obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
>> obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
>> +obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
>> obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
>> obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
>> obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
>> diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
>> new file mode 100644
>> index 0000000..606cc19
>> --- /dev/null
>> +++ b/drivers/input/keyboard/st-keyscan.c
>> @@ -0,0 +1,323 @@
>> +/*
>> + * STMicroelectronics Key Scanning driver
>> + *
>> + * Copyright (c) 2014 STMicroelectonics Ltd.
>> + * Author: Stuart Menefy <stuart.menefy@st.com>
>> + *
>> + * Based on sh_keysc.c, copyright 2008 Magnus Damm
> Did sh_keysc.c ever exist in Mainline?
i think no, i 'll suppress "Based on sh_keysc.c, copyright 2008 Magnus Damm"
>
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/clk.h>
>> +#include <linux/io.h>
>> +#include <linux/input/matrix_keypad.h>
>> +
>> +#define ST_KEYSCAN_MAXKEYS 16
>> +
>> +#define KEYSCAN_CONFIG_OFF 0x000
>> +#define KEYSCAN_CONFIG_ENABLE 1
> 0x001?
>
>> +#define KEYSCAN_DEBOUNCE_TIME_OFF 0x004
>> +#define KEYSCAN_MATRIX_STATE_OFF 0x008
>> +#define KEYSCAN_MATRIX_DIM_OFF 0x00c
> Odd that these are 3 digit padded? Is there a reason for that?
no reason for the padded, i will change that.
>> +struct keypad_platform_data {
>> + const struct matrix_keymap_data *keymap_data;
>> + unsigned int num_out_pads;
>> + unsigned int num_in_pads;
>> + unsigned int debounce_us;
>> +};
>> +
>> +struct keyscan_priv {
>> + void __iomem *base;
>> + int irq;
>> + struct clk *clk;
>> + struct input_dev *input_dev;
>> + struct keypad_platform_data *config;
>> + unsigned int last_state;
>> + u32 keycodes[ST_KEYSCAN_MAXKEYS];
> Seems odd to limit this. Can't the information come from DT
> i.e. keypad,num-rows and keypad,num-columns?
>
i 'll rename 'num_out_pads' into 'n_rows' and 'num_in_pads' into 'n_cols'
> Nit: It's pretty unusual to use this for a standard error handling
> variable. Consider 'ret' or 'err' as a replacement.
>
>> + }
>> +
>> + of_property_read_u32(np, "st,debounce_us", &pdata->debounce_us);
> Isn't this a required property? Might be worth checking the return
> value if so.
no mandatory property, i will update the dt binding.
>
>> + st_kp->config = pdata;
>> +
>> + dev_info(dev, "rows=%d col=%d debounce=%d\n",
>> + pdata->num_out_pads,
>> + pdata->num_in_pads,
>> + pdata->debounce_us);
> Might be worth moving this down passed the final point of failure.
>
>> + error = of_property_read_u32_array(np, "linux,keymap",
>> + st_kp->keycodes, ST_KEYSCAN_MAXKEYS);
>> + if (error) {
>> + dev_err(dev, "failed to parse keymap\n");
>> + return error;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int __init keyscan_probe(struct platform_device *pdev)
>> +{
>> + struct keypad_platform_data *pdata =
>> + dev_get_platdata(&pdev->dev);
> Do we really support platform data still?
no, i will remove that.
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + if (!res) {
> + dev_err(dev, "no I/O memory specified\n");
> + return -ENXIO;
> + }
> +
> + len = resource_size(res);
> + if (!devm_request_mem_region(dev, res->start, len, pdev->name)) {
> + dev_err(dev, "failed to reserve I/O memory\n");
> + return -EBUSY;
> + }
> +
> + st_kp->base = devm_ioremap_nocache(dev, res->start, len);
> + if (st_kp->base == NULL) {
> + dev_err(dev, "failed to remap I/O memory\n");
> + return -ENXIO;
> + }
> Replace the two above with devm_ioremap_resource().
>
> Make sure the IORESOURCE_CACHEABLE is set.
ok, we are in no cacheable use case.
>
>> + }
>> +
>> + error = devm_request_irq(dev, st_kp->irq, keyscan_isr, 0,
>> + pdev->name, pdev);
>> + if (error) {
>> + dev_err(dev, "failed to request IRQ\n");
>> + return error;
>> + }
>> +
>> + input_dev = devm_input_allocate_device(&pdev->dev);
>> + if (!input_dev) {
>> + dev_err(&pdev->dev, "failed to allocate the input device\n");
>> + return -ENOMEM;
>> + }
>> +
>> + st_kp->clk = devm_clk_get(dev, NULL);
>> + if (IS_ERR(st_kp->clk)) {
>> + dev_err(dev, "cannot get clock");
>> + return PTR_ERR(st_kp->clk);
>> + }
>> +
>> + input_dev = input_allocate_device();
>> + if (!input_dev) {
>> + dev_err(dev, "failed to allocate input device\n");
>> + return -ENOMEM;
>> + }
> Wasn't this done already?
yes, crap these lines.
>> + st_kp->input_dev = input_dev;
>> +
>> + input_dev->name = pdev->name;
>> + input_dev->phys = "keyscan-keys/input0";
>> + input_dev->dev.parent = dev;
>> +
>> + input_dev->id.bustype = BUS_HOST;
>> + input_dev->id.vendor = 0x0001;
>> + input_dev->id.product = 0x0001;
>> + input_dev->id.version = 0x0100;
> Any chance we can #define these?
i will follow Dmitry remark (omit these information)
>
>> + if (!pdata) {
>> + error = keypad_matrix_key_parse_dt(st_kp);
>> + if (error)
>> + return error;
>> + pdata = st_kp->config;
> Is pdata used after this?
no, i will remove platform data support
Thanks
^ permalink raw reply
* Re: [PATCH 1/5] drivers: input: keyboard: st-keyscan: add keyscan driver
From: Lee Jones @ 2014-03-14 10:42 UTC (permalink / raw)
To: Gabriel Fernandez
Cc: Dmitry Torokhov, Rob Herring, Pawel Moll, Mark Rutland,
Ian Campbell, Kumar Gala, Rob Landley, Russell King, Grant Likely,
devicetree, linux-doc, linux-kernel, linux-arm-kernel,
linux-input, kernel, Giuseppe Condorelli
In-Reply-To: <5322D63D.5030403@st.com>
> >Sorry for the delay. It was a hectic week last week.
> >
> >As promised:
> >
> >>This patch adds ST Keyscan driver to use the keypad hw a subset
> >>of ST boards provide. Specific board setup will be put in the
> >>given dt.
> >>
> >>Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
> >>Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
> >Are you sure these are in the correct order?
> ok i change the order
I'm not saying they are in the wrong order, I'm just asking. Who wrote
the patch? Has it changed since?
> >>+- linux,keymap: The keymap for keys as described in the binding document
> >>+ devicetree/bindings/input/matrix-keymap.txt.
> >>+
> >>+- keypad,num-rows: Number of row lines connected to the keypad controller.
> >>+
> >>+- keypad,num-columns: Number of column lines connected to the keypad
> >>+ controller.
> >>+
> >>+- st,debounce_us: Debouncing interval time in microseconds
> >I'm sure there will be a shared binding for de-bounce.
> >
> >If not, there certainly should be.
>
> you want to refer to "debounce-interval" ?
That sounds more generic, but if it's not documented as such, then
please consider doing so.
> >+Example:
> >+
> >+keyscan: keyscan@fe4b0000 {
> >+ compatible = "st,keypad";
> >Is there any way we can make this more specific to _this_ IP?
> for my knowledge this IP is the same for stixxxx platform.
So st,stix-keypad, or st,sti4x-keypad?
I'm just thinking about future proofing the architecture. What if ST
released stj which has a different keypad IP?
> >>+struct keyscan_priv {
> >>+ void __iomem *base;
> >>+ int irq;
> >>+ struct clk *clk;
> >>+ struct input_dev *input_dev;
> >>+ struct keypad_platform_data *config;
> >>+ unsigned int last_state;
> >>+ u32 keycodes[ST_KEYSCAN_MAXKEYS];
> >Seems odd to limit this. Can't the information come from DT
> >i.e. keypad,num-rows and keypad,num-columns?
> >
> i 'll rename 'num_out_pads' into 'n_rows' and 'num_in_pads' into
> 'n_cols'
That's not quite what I meant, I mean can't ST_KEY_MAXKEYS be more
dynamic and be obtained from (keypad,num-rows * keypad,num-columns)?
--
Lee Jones
Linaro STMicroelectronics Landing Team Lead
Linaro.org │ Open source software for ARM SoCs
Follow Linaro: Facebook | Twitter | Blog
--
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
* Re: [PATCH v3 4/4] mfd: max8997: move regmap handling to function drivers
From: Robert Baldyga @ 2014-03-14 11:21 UTC (permalink / raw)
To: Mark Brown, Lee Jones
Cc: Chanwoo Choi, sameo, myungjoo.ham, dmitry.torokhov, cooloney,
rpurdie, dbaryshkov, dwmw2, lgirdwood, a.zummo, paul.gortmaker,
linux-kernel, linux-input, linux-leds, rtc-linux, m.szyprowski,
k.kozlowski
In-Reply-To: <20140313140901.GW366@sirena.org.uk>
Hi,
On 03/13/2014 03:09 PM, Mark Brown wrote:
> On Thu, Mar 13, 2014 at 12:55:04PM +0000, Lee Jones wrote:
>>> Is it necessary? If previous mfd driver has various i2c line, previous mfd driver
>>> initialize regmap/i2c setting on mfd driver.
>>> I'm not sure that regmap/i2c setting code move from mfd driver to each driver.
>>>
>>> Dear Lee Jones,
>>> I need your opinion about moving regmap/i2c code from mfd driver to each driver.
>
>> I'd rather take advice from Mark on this one.
>
> I don't really case that much; I'm having a hard time seeing it as
> particularly useful to do the refactoring but if it makes people
> happy... Keeping things in the core would help promote reusability I
> guess but I'm not sure that's likely to actually happen with this sort
> of driver/device.
>
If you think it's not needed, you can ignore this patch. I prepared it
due to Dmitry Torokhov suggestion. If you think it's useless it doesn't
make me unhappy :)
Best regards
Robert Baldyga
^ permalink raw reply
* Re: [PATCH v3 0/4] HID: ll transport cleanup: final round
From: Jiri Kosina @ 2014-03-14 14:39 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, David Herrmann, David Barksdale,
Antonio Ospite, linux-input, linux-kernel
In-Reply-To: <1394337163-32478-1-git-send-email-benjamin.tissoires@redhat.com>
On Sat, 8 Mar 2014, Benjamin Tissoires wrote:
> Hop, a new version.
>
> As mentioned in the v2, I did not changed 2/4 but I'll add a patch later
> to remove buf[0] from the call. But I need to do tests first.
>
> Thanks for all the work to everybody who took a part in this cleanup.
Awesome, thanks.
Now queued in for-3.15/hid-core-ll-transport-cleanup.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH v2 0/8] HID: sony: More Sony controller fixes and improvements.
From: Jiri Kosina @ 2014-03-14 14:42 UTC (permalink / raw)
To: Frank Praznik; +Cc: linux-input, dh.herrmann
In-Reply-To: <1394145176-24174-1-git-send-email-frank.praznik@oh.rr.com>
On Thu, 6 Mar 2014, Frank Praznik wrote:
> v2 rebases the previous set of patches against Benjamin Tissoires' latest patch
> set and adds some additional changes:
>
> - Adds a patch setting the new HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP for the
> Sixaxis and DualShock 4 when they are connected via Bluetooth. This adds
> support for sending output reports via hidraw.
>
> - A patch with a new union/struct is introduced for the Sixaxis output report.
> This improves readability, particularly in the later blink support patch.
>
> - Adds an IDA id allocator and uses it to assign the number at the end of the
> battery string and set the default LED values relative to other Sony
> controllers.
>
> - Drops the LED trigger patch as it probably doesn't belong in kernel space.
>
> - Addresses various issues raised during code review of the previous patch set.
Frank,
just to make things clear -- I am waiting for v3 due to baddr changes
planned for 6/8.
Thanks,
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH] HID: hid-lg4ff: Support new version of G27
From: Jiri Kosina @ 2014-03-14 14:44 UTC (permalink / raw)
To: Simon Wood; +Cc: linux-input, linux-kernel, Ivan Baldo, evilcow
In-Reply-To: <1394736123-2102-1-git-send-email-simon@mungewell.org>
On Thu, 13 Mar 2014, Simon Wood wrote:
> It has been reported that there is a new hardware version of the G27
> in the 'wild'. This patch add's this new revision so that it can be
> sent the command to switch to native mode.
>
> Reported-by: "Ivan Baldo" <ibaldo@adinet.com.uy>
> Tested-by: "evilcow" <evilcow93@yahoo.com>
> Signed-off-by: Simon Wood <simon@mungewell.org>
> ---
> drivers/hid/hid-lg4ff.c | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/drivers/hid/hid-lg4ff.c b/drivers/hid/hid-lg4ff.c
> index befe0e3..24883b4 100644
> --- a/drivers/hid/hid-lg4ff.c
> +++ b/drivers/hid/hid-lg4ff.c
> @@ -43,6 +43,7 @@
> #define G25_REV_MIN 0x22
> #define G27_REV_MAJ 0x12
> #define G27_REV_MIN 0x38
> +#define G27_2_REV_MIN 0x39
>
> #define to_hid_device(pdev) container_of(pdev, struct hid_device, dev)
>
> @@ -130,6 +131,7 @@ static const struct lg4ff_usb_revision lg4ff_revs[] = {
> {DFP_REV_MAJ, DFP_REV_MIN, &native_dfp}, /* Driving Force Pro */
> {G25_REV_MAJ, G25_REV_MIN, &native_g25}, /* G25 */
> {G27_REV_MAJ, G27_REV_MIN, &native_g27}, /* G27 */
> + {G27_REV_MAJ, G27_2_REV_MIN, &native_g27}, /* G27 v2 */
> };
Applied, thanks.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply
* Re: [PATCH 1/5] drivers: input: keyboard: st-keyscan: add keyscan driver
From: Dmitry Torokhov @ 2014-03-14 15:26 UTC (permalink / raw)
To: Gabriel Fernandez
Cc: Lee Jones, Rob Herring, Pawel Moll, Mark Rutland, Ian Campbell,
Kumar Gala, Rob Landley, Russell King, Grant Likely, devicetree,
linux-doc, linux-kernel, linux-arm-kernel, linux-input, kernel,
Giuseppe Condorelli
In-Reply-To: <5322D63D.5030403@st.com>
On Fri, Mar 14, 2014 at 11:13:17AM +0100, Gabriel Fernandez wrote:
> Hi Lee,
>
> On 03/10/2014 12:48 PM, Lee Jones wrote:
> >Hi Gabi,
> >
> >Sorry for the delay. It was a hectic week last week.
> >
> >As promised:
> >
> >>This patch adds ST Keyscan driver to use the keypad hw a subset
> >>of ST boards provide. Specific board setup will be put in the
> >>given dt.
> >>
> >>Signed-off-by: Giuseppe Condorelli <giuseppe.condorelli@st.com>
> >>Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
> >Are you sure these are in the correct order?
> ok i change the order
>
> >>+- linux,keymap: The keymap for keys as described in the binding document
> >>+ devicetree/bindings/input/matrix-keymap.txt.
> >>+
> >>+- keypad,num-rows: Number of row lines connected to the keypad controller.
> >>+
> >>+- keypad,num-columns: Number of column lines connected to the keypad
> >>+ controller.
> >>+
> >>+- st,debounce_us: Debouncing interval time in microseconds
> >I'm sure there will be a shared binding for de-bounce.
> >
> >If not, there certainly should be.
>
> you want to refer to "debounce-interval" ?
>
> >
> >+Example:
> >+
> >+keyscan: keyscan@fe4b0000 {
> >+ compatible = "st,keypad";
> >Is there any way we can make this more specific to _this_ IP?
> for my knowledge this IP is the same for stixxxx platform.
>
>
> >
> >>+ To compile this driver as a module, choose M here: the
> >>+ module will be called stm-keyscan.
> >>+
> >> config KEYBOARD_SUNKBD
> >> tristate "Sun Type 4 and Type 5 keyboard"
> >> select SERIO
> >>diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
> >>index a699b61..5fd020a 100644
> >>--- a/drivers/input/keyboard/Makefile
> >>+++ b/drivers/input/keyboard/Makefile
> >>@@ -50,6 +50,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
> >> obj-$(CONFIG_KEYBOARD_SPEAR) += spear-keyboard.o
> >> obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o
> >> obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
> >>+obj-$(CONFIG_KEYBOARD_ST_KEYSCAN) += st-keyscan.o
> >> obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
> >> obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o
> >> obj-$(CONFIG_KEYBOARD_TEGRA) += tegra-kbc.o
> >>diff --git a/drivers/input/keyboard/st-keyscan.c b/drivers/input/keyboard/st-keyscan.c
> >>new file mode 100644
> >>index 0000000..606cc19
> >>--- /dev/null
> >>+++ b/drivers/input/keyboard/st-keyscan.c
> >>@@ -0,0 +1,323 @@
> >>+/*
> >>+ * STMicroelectronics Key Scanning driver
> >>+ *
> >>+ * Copyright (c) 2014 STMicroelectonics Ltd.
> >>+ * Author: Stuart Menefy <stuart.menefy@st.com>
> >>+ *
> >>+ * Based on sh_keysc.c, copyright 2008 Magnus Damm
> >Did sh_keysc.c ever exist in Mainline?
> i think no, i 'll suppress "Based on sh_keysc.c, copyright 2008 Magnus Damm"
It is still in here:
[dtor@dtor-d630 work]$ git log --oneline --
drivers/input/keyboard/sh_keysc.c | wc -l
31
[dtor@dtor-d630 work]$
--
Dmitry
^ permalink raw reply
* Re: [PATCH 1/1] Input: don't modify the id of ioctl-provided ff effect on upload failure
From: Elias Vanderstuyft @ 2014-03-15 12:13 UTC (permalink / raw)
To: Dmitry Torokhov, Anssi Hannula; +Cc: linux-input, Michal Malý
In-Reply-To: <CADbOyBTf4fWhUveA7UgCP4EpLXAyrZCV-xs-wZ7HGyBwxhQFhg@mail.gmail.com>
On Sun, Feb 23, 2014 at 3:18 PM, Elias Vanderstuyft <elias.vds@gmail.com> wrote:
> From: Elias Vanderstuyft <elias.vds@gmail.com>
>
> If a new (id == -1) ff effect was uploaded from userspace,
> ff-core.c::input_ff_upload() will have assigned
> a positive number to the new effect id.
> Currently, evdev.c::evdev_do_ioctl() will save this new id to userspace,
> regardless of whether the upload succeeded or not.
>
> On upload failure, this can be confusing because the dev->ff->effects[] array
> will not contain an element at the index of that new effect id.
>
> Note: Unfortunately applications should still expect changed effect id for
> quite some time.
>
> This has been discussed on:
> http://www.mail-archive.com/linux-input@vger.kernel.org/msg08513.html
> ("ff-core effect id handling in case of a failed effect upload")
>
> Suggested-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
> Signed-off-by: Elias Vanderstuyft <elias.vds@gmail.com>
> ---
> drivers/input/evdev.c | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> --- a/drivers/input/evdev.c 2014-02-23 14:21:19.980606615 +0100
> +++ b/drivers/input/evdev.c 2014-02-23 14:25:04.417118859 +0100
> @@ -954,11 +954,13 @@ static long evdev_do_ioctl(struct file *
> return -EFAULT;
>
> error = input_ff_upload(dev, &effect, file);
> + if (error)
> + return error;
>
> if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
> return -EFAULT;
>
> - return error;
> + return 0;
> }
>
> /* Multi-number variable-length handlers */
This is a reminder that there have been no replies on this subject for
the last 3 weeks.
If there is something wrong with the format of this patch, please tell
me, I tried to follow the instructions on
http://www.kernel.org/doc/Documentation/SubmittingPatches , but it's
the first time I send a patch by myself, so I might have forgotten
something.
Did I forget to CC some people?
The reason I'm insisting on this, is because this change will be
perceivable to userspace developers, and the sooner this gets applied,
the less userland applications will have to circumvent this bug.
Best regards,
Elias
^ permalink raw reply
* [PATCH v3 0/8] HID: sony: More Sony controller fixes and improvements.
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
Just a couple of small changes in v3:
Patch 6 was changed to use the controller Bluetooth MAC address as the unique
value for the battery name string.
The IDA code was moved from patch 6 to 7 since it was no longer needed in
patch 6. It's the same otherwise.
^ permalink raw reply
* [PATCH v3 1/8] HID: sony: Fix Sixaxis cable state detection
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Byte 31 of the Sixaxis report can change depending on whether or not the
controller is rumbling. Using bit 3 is the only reliable way to detect the
state of the cable regardless of rumble activity.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 4884bb5..4c445a0 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -863,7 +863,7 @@ static void sixaxis_parse_report(struct sony_sc *sc, __u8 *rd, int size)
battery_capacity = sixaxis_battery_capacity[index];
battery_charging = 0;
}
- cable_state = !((rd[31] >> 4) & 0x01);
+ cable_state = !(rd[31] & 0x04);
spin_lock_irqsave(&sc->lock, flags);
sc->cable_state = cable_state;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 2/8] HID: sony: Set the quriks flag for Bluetooth controllers
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
The Sixaxis and DualShock 4 want HID output reports sent on the control
endpoint when connected via Bluetooth. Set the
HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP flag for these devices so hidraw write()
works properly.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 4c445a0..908de27 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1632,11 +1632,21 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
sc->worker_initialized = 1;
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
+ /*
+ * The Sixaxis wants output reports sent on the ctrl endpoint
+ * when connected via Bluetooth.
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
sc->worker_initialized = 1;
INIT_WORK(&sc->state_worker, sixaxis_state_worker);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
+ /*
+ * The DualShock 4 wants output reports sent on the ctrl
+ * endpoint when connected via Bluetooth.
+ */
+ hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = dualshock4_set_operational_bt(hdev);
if (ret < 0) {
hid_err(hdev, "failed to set the Dualshock 4 operational mode\n");
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 3/8] HID: sony: Use inliners for work queue initialization and cancellation
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Use inliners to make sure that the work queue initialization flag is always
checked and set correctly when initializing or cancelling the work queue.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
v2 doesn't set worker_initialized to 0 when cancelling work since
cancel_work_sync doesn't deinitialize the work queue.
drivers/hid/hid-sony.c | 29 +++++++++++++++++++----------
1 file changed, 19 insertions(+), 10 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 908de27..3df3306 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1578,6 +1578,20 @@ static int sony_check_add(struct sony_sc *sc)
return sony_check_add_dev_list(sc);
}
+static inline void sony_init_work(struct sony_sc *sc,
+ void(*worker)(struct work_struct *))
+{
+ if (!sc->worker_initialized)
+ INIT_WORK(&sc->state_worker, worker);
+
+ sc->worker_initialized = 1;
+}
+
+static inline void sony_cancel_work_sync(struct sony_sc *sc)
+{
+ if (sc->worker_initialized)
+ cancel_work_sync(&sc->state_worker);
+}
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
@@ -1629,8 +1643,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
hdev->quirks |= HID_QUIRK_SKIP_OUTPUT_REPORT_ID;
ret = sixaxis_set_operational_usb(hdev);
- sc->worker_initialized = 1;
- INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+ sony_init_work(sc, sixaxis_state_worker);
} else if (sc->quirks & SIXAXIS_CONTROLLER_BT) {
/*
* The Sixaxis wants output reports sent on the ctrl endpoint
@@ -1638,8 +1651,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
*/
hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP;
ret = sixaxis_set_operational_bt(hdev);
- sc->worker_initialized = 1;
- INIT_WORK(&sc->state_worker, sixaxis_state_worker);
+ sony_init_work(sc, sixaxis_state_worker);
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
/*
@@ -1661,8 +1673,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0)
goto err_stop;
- sc->worker_initialized = 1;
- INIT_WORK(&sc->state_worker, dualshock4_state_worker);
+ sony_init_work(sc, dualshock4_state_worker);
} else {
ret = 0;
}
@@ -1707,8 +1718,7 @@ err_stop:
sony_leds_remove(hdev);
if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc);
- if (sc->worker_initialized)
- cancel_work_sync(&sc->state_worker);
+ sony_cancel_work_sync(sc);
sony_remove_dev_list(sc);
hid_hw_stop(hdev);
return ret;
@@ -1726,8 +1736,7 @@ static void sony_remove(struct hid_device *hdev)
sony_battery_remove(sc);
}
- if (sc->worker_initialized)
- cancel_work_sync(&sc->state_worker);
+ sony_cancel_work_sync(sc);
sony_remove_dev_list(sc);
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 4/8] HID: sony: Use a struct for the Sixaxis output report.
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Use a struct for the Sixaxis output report that uses named members to set the
report fields.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 66 +++++++++++++++++++++++++++++++++++++-------------
1 file changed, 49 insertions(+), 17 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 3df3306..5e9bb57 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -717,6 +717,36 @@ static enum power_supply_property sony_battery_props[] = {
POWER_SUPPLY_PROP_STATUS,
};
+struct sixaxis_led {
+ __u8 time_enabled; /* the total time the led is active (0xff means forever) */
+ __u8 duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
+ __u8 enabled;
+ __u8 duty_off; /* % of duty_length the led is off (0xff means 100%) */
+ __u8 duty_on; /* % of duty_length the led is on (0xff mean 100%) */
+} __packed;
+
+struct sixaxis_rumble {
+ __u8 padding;
+ __u8 right_duration; /* Right motor duration (0xff means forever) */
+ __u8 right_motor_on; /* Right (small) motor on/off, only supports values of 0 or 1 (off/on) */
+ __u8 left_duration; /* Left motor duration (0xff means forever) */
+ __u8 left_motor_force; /* left (large) motor, supports force values from 0 to 255 */
+} __packed;
+
+struct sixaxis_output_report {
+ __u8 report_id;
+ struct sixaxis_rumble rumble;
+ __u8 padding[4];
+ __u8 leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
+ struct sixaxis_led led[4]; /* LEDx at (4 - x) */
+ struct sixaxis_led _reserved; /* LED5, not actually soldered */
+} __packed;
+
+union sixaxis_output_report_01 {
+ struct sixaxis_output_report data;
+ __u8 buf[36];
+};
+
static spinlock_t sony_dev_list_lock;
static LIST_HEAD(sony_device_list);
@@ -1244,29 +1274,31 @@ error_leds:
static void sixaxis_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
- unsigned char buf[] = {
- 0x01,
- 0x00, 0xff, 0x00, 0xff, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00,
- 0xff, 0x27, 0x10, 0x00, 0x32,
- 0xff, 0x27, 0x10, 0x00, 0x32,
- 0xff, 0x27, 0x10, 0x00, 0x32,
- 0xff, 0x27, 0x10, 0x00, 0x32,
- 0x00, 0x00, 0x00, 0x00, 0x00
+ union sixaxis_output_report_01 report = {
+ .buf = {
+ 0x01,
+ 0x00, 0xff, 0x00, 0xff, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0xff, 0x27, 0x10, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00
+ }
};
#ifdef CONFIG_SONY_FF
- buf[3] = sc->right ? 1 : 0;
- buf[5] = sc->left;
+ report.data.rumble.right_motor_on = sc->right ? 1 : 0;
+ report.data.rumble.left_motor_force = sc->left;
#endif
- buf[10] |= sc->led_state[0] << 1;
- buf[10] |= sc->led_state[1] << 2;
- buf[10] |= sc->led_state[2] << 3;
- buf[10] |= sc->led_state[3] << 4;
+ report.data.leds_bitmap |= sc->led_state[0] << 1;
+ report.data.leds_bitmap |= sc->led_state[1] << 2;
+ report.data.leds_bitmap |= sc->led_state[2] << 3;
+ report.data.leds_bitmap |= sc->led_state[3] << 4;
- hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
- HID_REQ_SET_REPORT);
+ hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
+ sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
static void dualshock4_state_worker(struct work_struct *work)
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 5/8] HID: sony: Convert startup and shutdown functions to use a uniform parameter type
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Convert all of the local initialization and shutdown functions to take a
parameter type of struct sony_sc* instead of using a mix of struct sony_sc* and
struct hid_device*.
Allows for the removal of some calls to hid_get_drvdata().
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 67 ++++++++++++++++++++++++--------------------------
1 file changed, 32 insertions(+), 35 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 5e9bb57..a9bcfbe 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1096,19 +1096,18 @@ static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
hid_hw_request(hdev, report, HID_REQ_SET_REPORT);
}
-static void sony_set_leds(struct hid_device *hdev, const __u8 *leds, int count)
+static void sony_set_leds(struct sony_sc *sc, const __u8 *leds, int count)
{
- struct sony_sc *drv_data = hid_get_drvdata(hdev);
int n;
BUG_ON(count > MAX_LEDS);
- if (drv_data->quirks & BUZZ_CONTROLLER && count == 4) {
- buzz_set_leds(hdev, leds);
+ if (sc->quirks & BUZZ_CONTROLLER && count == 4) {
+ buzz_set_leds(sc->hdev, leds);
} else {
for (n = 0; n < count; n++)
- drv_data->led_state[n] = leds[n];
- schedule_work(&drv_data->state_worker);
+ sc->led_state[n] = leds[n];
+ schedule_work(&sc->state_worker);
}
}
@@ -1131,7 +1130,8 @@ static void sony_led_set_brightness(struct led_classdev *led,
if (led == drv_data->leds[n]) {
if (value != drv_data->led_state[n]) {
drv_data->led_state[n] = value;
- sony_set_leds(hdev, drv_data->led_state, drv_data->led_count);
+ sony_set_leds(drv_data, drv_data->led_state,
+ drv_data->led_count);
}
break;
}
@@ -1160,30 +1160,28 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
return LED_OFF;
}
-static void sony_leds_remove(struct hid_device *hdev)
+static void sony_leds_remove(struct sony_sc *sc)
{
- struct sony_sc *drv_data;
struct led_classdev *led;
int n;
- drv_data = hid_get_drvdata(hdev);
- BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+ BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
- for (n = 0; n < drv_data->led_count; n++) {
- led = drv_data->leds[n];
- drv_data->leds[n] = NULL;
+ for (n = 0; n < sc->led_count; n++) {
+ led = sc->leds[n];
+ sc->leds[n] = NULL;
if (!led)
continue;
led_classdev_unregister(led);
kfree(led);
}
- drv_data->led_count = 0;
+ sc->led_count = 0;
}
-static int sony_leds_init(struct hid_device *hdev)
+static int sony_leds_init(struct sony_sc *sc)
{
- struct sony_sc *drv_data;
+ struct hid_device *hdev = sc->hdev;
int n, ret = 0;
int max_brightness;
int use_colors;
@@ -1195,11 +1193,10 @@ static int sony_leds_init(struct hid_device *hdev)
static const char * const color_str[] = { "red", "green", "blue" };
static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
- drv_data = hid_get_drvdata(hdev);
- BUG_ON(!(drv_data->quirks & SONY_LED_SUPPORT));
+ BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
- if (drv_data->quirks & BUZZ_CONTROLLER) {
- drv_data->led_count = 4;
+ if (sc->quirks & BUZZ_CONTROLLER) {
+ sc->led_count = 4;
max_brightness = 1;
use_colors = 0;
name_len = strlen("::buzz#");
@@ -1207,14 +1204,14 @@ static int sony_leds_init(struct hid_device *hdev)
/* Validate expected report characteristics. */
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
- } else if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
- drv_data->led_count = 3;
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+ sc->led_count = 3;
max_brightness = 255;
use_colors = 1;
name_len = 0;
name_fmt = "%s:%s";
} else {
- drv_data->led_count = 4;
+ sc->led_count = 4;
max_brightness = 1;
use_colors = 0;
name_len = strlen("::sony#");
@@ -1226,11 +1223,11 @@ static int sony_leds_init(struct hid_device *hdev)
* only relevant if the driver is loaded after somebody actively set the
* LEDs to on
*/
- sony_set_leds(hdev, initial_values, drv_data->led_count);
+ sony_set_leds(sc, initial_values, sc->led_count);
name_sz = strlen(dev_name(&hdev->dev)) + name_len + 1;
- for (n = 0; n < drv_data->led_count; n++) {
+ for (n = 0; n < sc->led_count; n++) {
if (use_colors)
name_sz = strlen(dev_name(&hdev->dev)) + strlen(color_str[n]) + 2;
@@ -1260,13 +1257,13 @@ static int sony_leds_init(struct hid_device *hdev)
goto error_leds;
}
- drv_data->leds[n] = led;
+ sc->leds[n] = led;
}
return ret;
error_leds:
- sony_leds_remove(hdev);
+ sony_leds_remove(sc);
return ret;
}
@@ -1355,9 +1352,9 @@ static int sony_play_effect(struct input_dev *dev, void *data,
return 0;
}
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
{
- struct hid_input *hidinput = list_entry(hdev->inputs.next,
+ struct hid_input *hidinput = list_entry(sc->hdev->inputs.next,
struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
@@ -1366,7 +1363,7 @@ static int sony_init_ff(struct hid_device *hdev)
}
#else
-static int sony_init_ff(struct hid_device *hdev)
+static int sony_init_ff(struct sony_sc *sc)
{
return 0;
}
@@ -1718,7 +1715,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
goto err_stop;
if (sc->quirks & SONY_LED_SUPPORT) {
- ret = sony_leds_init(hdev);
+ ret = sony_leds_init(sc);
if (ret < 0)
goto err_stop;
}
@@ -1737,7 +1734,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
if (sc->quirks & SONY_FF_SUPPORT) {
- ret = sony_init_ff(hdev);
+ ret = sony_init_ff(sc);
if (ret < 0)
goto err_close;
}
@@ -1747,7 +1744,7 @@ err_close:
hid_hw_close(hdev);
err_stop:
if (sc->quirks & SONY_LED_SUPPORT)
- sony_leds_remove(hdev);
+ sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT)
sony_battery_remove(sc);
sony_cancel_work_sync(sc);
@@ -1761,7 +1758,7 @@ static void sony_remove(struct hid_device *hdev)
struct sony_sc *sc = hid_get_drvdata(hdev);
if (sc->quirks & SONY_LED_SUPPORT)
- sony_leds_remove(hdev);
+ sony_leds_remove(sc);
if (sc->quirks & SONY_BATTERY_SUPPORT) {
hid_hw_close(hdev);
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 6/8] HID: sony: Use the controller Bluetooth MAC address as the unique value in the battery name string
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Use the controller Bluetooth MAC address as the unique identifier in the
battery name string instead of the atomic integer that was used before.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
v3 moves the IDA code to patch 7 since it isn't needed here anymore.
drivers/hid/hid-sony.c | 8 ++------
1 file changed, 2 insertions(+), 6 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index a9bcfbe..449d08f 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1413,8 +1413,6 @@ static int sony_battery_get_property(struct power_supply *psy,
static int sony_battery_probe(struct sony_sc *sc)
{
- static atomic_t power_id_seq = ATOMIC_INIT(0);
- unsigned long power_id;
struct hid_device *hdev = sc->hdev;
int ret;
@@ -1424,15 +1422,13 @@ static int sony_battery_probe(struct sony_sc *sc)
*/
sc->battery_capacity = 100;
- power_id = (unsigned long)atomic_inc_return(&power_id_seq);
-
sc->battery.properties = sony_battery_props;
sc->battery.num_properties = ARRAY_SIZE(sony_battery_props);
sc->battery.get_property = sony_battery_get_property;
sc->battery.type = POWER_SUPPLY_TYPE_BATTERY;
sc->battery.use_for_apm = 0;
- sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%lu",
- power_id);
+ sc->battery.name = kasprintf(GFP_KERNEL, "sony_controller_battery_%pMR",
+ sc->mac_address);
if (!sc->battery.name)
return -ENOMEM;
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 7/8] HID: sony: Initialize the controller LEDs with a device ID value
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Add an IDA id allocator to assign unique, sequential device ids to Sixaxis and
DualShock 4 controllers.
Use the device ID to initialize the Sixaxis and DualShock 4 controller LEDs to
default values. The number or color of the controller is set relative to other
connected Sony controllers.
Set the LED class brightness values to the initial values and add the new led to
the array before calling led_classdev_register so that the correct brightness
value shows up in the LED sysfs entry.
Use explicit module init and exit functions since the IDA allocator must be
manually destroyed when the module is unloaded.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
v3 moves the IDA code from patch 6 to here since the previous patch no longer
needs it. It's exactly the same otherwise.
drivers/hid/hid-sony.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 114 insertions(+), 3 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 449d08f..d323039 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -33,6 +33,7 @@
#include <linux/power_supply.h>
#include <linux/spinlock.h>
#include <linux/list.h>
+#include <linux/idr.h>
#include <linux/input/mt.h>
#include "hid-ids.h"
@@ -749,6 +750,7 @@ union sixaxis_output_report_01 {
static spinlock_t sony_dev_list_lock;
static LIST_HEAD(sony_device_list);
+static DEFINE_IDA(sony_device_id_allocator);
struct sony_sc {
spinlock_t lock;
@@ -758,6 +760,7 @@ struct sony_sc {
unsigned long quirks;
struct work_struct state_worker;
struct power_supply battery;
+ int device_id;
#ifdef CONFIG_SONY_FF
__u8 left;
@@ -1078,6 +1081,52 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
+static void sixaxis_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+ static const __u8 sixaxis_leds[10][4] = {
+ { 0x01, 0x00, 0x00, 0x00 },
+ { 0x00, 0x01, 0x00, 0x00 },
+ { 0x00, 0x00, 0x01, 0x00 },
+ { 0x00, 0x00, 0x00, 0x01 },
+ { 0x01, 0x00, 0x00, 0x01 },
+ { 0x00, 0x01, 0x00, 0x01 },
+ { 0x00, 0x00, 0x01, 0x01 },
+ { 0x01, 0x00, 0x01, 0x01 },
+ { 0x00, 0x01, 0x01, 0x01 },
+ { 0x01, 0x01, 0x01, 0x01 }
+ };
+
+ BUG_ON(MAX_LEDS < ARRAY_SIZE(sixaxis_leds[0]));
+
+ if (id < 0)
+ return;
+
+ id %= 10;
+ memcpy(values, sixaxis_leds[id], sizeof(sixaxis_leds[id]));
+}
+
+static void dualshock4_set_leds_from_id(int id, __u8 values[MAX_LEDS])
+{
+ /* The first 4 color/index entries match what the PS4 assigns */
+ static const __u8 color_code[7][3] = {
+ /* Blue */ { 0x00, 0x00, 0x01 },
+ /* Red */ { 0x01, 0x00, 0x00 },
+ /* Green */ { 0x00, 0x01, 0x00 },
+ /* Pink */ { 0x02, 0x00, 0x01 },
+ /* Orange */ { 0x02, 0x01, 0x00 },
+ /* Teal */ { 0x00, 0x01, 0x01 },
+ /* White */ { 0x01, 0x01, 0x01 }
+ };
+
+ BUG_ON(MAX_LEDS < ARRAY_SIZE(color_code[0]));
+
+ if (id < 0)
+ return;
+
+ id %= 7;
+ memcpy(values, color_code[id], sizeof(color_code[id]));
+}
+
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
{
struct list_head *report_list =
@@ -1191,7 +1240,7 @@ static int sony_leds_init(struct sony_sc *sc)
size_t name_len;
const char *name_fmt;
static const char * const color_str[] = { "red", "green", "blue" };
- static const __u8 initial_values[MAX_LEDS] = { 0x00, 0x00, 0x00, 0x00 };
+ __u8 initial_values[MAX_LEDS] = { 0 };
BUG_ON(!(sc->quirks & SONY_LED_SUPPORT));
@@ -1205,12 +1254,14 @@ static int sony_leds_init(struct sony_sc *sc)
if (!hid_validate_values(hdev, HID_OUTPUT_REPORT, 0, 0, 7))
return -ENODEV;
} else if (sc->quirks & DUALSHOCK4_CONTROLLER) {
+ dualshock4_set_leds_from_id(sc->device_id, initial_values);
sc->led_count = 3;
max_brightness = 255;
use_colors = 1;
name_len = 0;
name_fmt = "%s:%s";
} else {
+ sixaxis_set_leds_from_id(sc->device_id, initial_values);
sc->led_count = 4;
max_brightness = 1;
use_colors = 0;
@@ -1245,14 +1296,17 @@ static int sony_leds_init(struct sony_sc *sc)
else
snprintf(name, name_sz, name_fmt, dev_name(&hdev->dev), n + 1);
led->name = name;
- led->brightness = 0;
+ led->brightness = initial_values[n];
led->max_brightness = max_brightness;
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
+ sc->leds[n] = led;
+
ret = led_classdev_register(&hdev->dev, led);
if (ret) {
hid_err(hdev, "Failed to register LED %d\n", n);
+ sc->leds[n] = NULL;
kfree(led);
goto error_leds;
}
@@ -1603,6 +1657,38 @@ static int sony_check_add(struct sony_sc *sc)
return sony_check_add_dev_list(sc);
}
+static int sony_set_device_id(struct sony_sc *sc)
+{
+ int ret;
+
+ /*
+ * Only DualShock 4 or Sixaxis controllers get an id.
+ * All others are set to -1.
+ */
+ if ((sc->quirks & SIXAXIS_CONTROLLER) ||
+ (sc->quirks & DUALSHOCK4_CONTROLLER)) {
+ ret = ida_simple_get(&sony_device_id_allocator, 0, 0,
+ GFP_KERNEL);
+ if (ret < 0) {
+ sc->device_id = -1;
+ return ret;
+ }
+ sc->device_id = ret;
+ } else {
+ sc->device_id = -1;
+ }
+
+ return 0;
+}
+
+static void sony_release_device_id(struct sony_sc *sc)
+{
+ if (sc->device_id >= 0) {
+ ida_simple_remove(&sony_device_id_allocator, sc->device_id);
+ sc->device_id = -1;
+ }
+}
+
static inline void sony_init_work(struct sony_sc *sc,
void(*worker)(struct work_struct *))
{
@@ -1654,6 +1740,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
return ret;
}
+ ret = sony_set_device_id(sc);
+ if (ret < 0) {
+ hid_err(hdev, "failed to allocate the device id\n");
+ goto err_stop;
+ }
+
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
/*
* The Sony Sixaxis does not handle HID Output Reports on the
@@ -1745,6 +1837,7 @@ err_stop:
sony_battery_remove(sc);
sony_cancel_work_sync(sc);
sony_remove_dev_list(sc);
+ sony_release_device_id(sc);
hid_hw_stop(hdev);
return ret;
}
@@ -1765,6 +1858,8 @@ static void sony_remove(struct hid_device *hdev)
sony_remove_dev_list(sc);
+ sony_release_device_id(sc);
+
hid_hw_stop(hdev);
}
@@ -1809,6 +1904,22 @@ static struct hid_driver sony_driver = {
.report_fixup = sony_report_fixup,
.raw_event = sony_raw_event
};
-module_hid_driver(sony_driver);
+
+static int __init sony_init(void)
+{
+ dbg_hid("Sony:%s\n", __func__);
+
+ return hid_register_driver(&sony_driver);
+}
+
+static void __exit sony_exit(void)
+{
+ dbg_hid("Sony:%s\n", __func__);
+
+ ida_destroy(&sony_device_id_allocator);
+ hid_unregister_driver(&sony_driver);
+}
+module_init(sony_init);
+module_exit(sony_exit);
MODULE_LICENSE("GPL");
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 8/8] HID: sony: Add blink support to the Sixaxis and DualShock 4 LEDs
From: Frank Praznik @ 2014-03-15 13:41 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, Frank Praznik
In-Reply-To: <1394890882-2039-1-git-send-email-frank.praznik@oh.rr.com>
Add support for setting the blink rate of the LEDs. The Sixaxis allows control
over each individual LED, but the Dualshock 4 only has one global control for
the light bar so changing any individual color changes the global blink rate.
Setting the brightness cancels the blinking as per the LED class specifications.
The Sixaxis and Dualshock 4 controllers accept delays in decisecond increments
from 0 to 255 (2550 milliseconds).
The value at index 1 of the DualShock 4 USB output report must be 0xFF or the
light bar won't blink.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 113 +++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 104 insertions(+), 9 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index d323039..74da922 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -773,6 +773,8 @@ struct sony_sc {
__u8 battery_charging;
__u8 battery_capacity;
__u8 led_state[MAX_LEDS];
+ __u8 led_delay_on[MAX_LEDS];
+ __u8 led_delay_off[MAX_LEDS];
__u8 led_count;
};
@@ -1167,7 +1169,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct sony_sc *drv_data;
- int n;
+ int n, blink_index;
drv_data = hid_get_drvdata(hdev);
if (!drv_data) {
@@ -1176,14 +1178,31 @@ static void sony_led_set_brightness(struct led_classdev *led,
}
for (n = 0; n < drv_data->led_count; n++) {
- if (led == drv_data->leds[n]) {
- if (value != drv_data->led_state[n]) {
- drv_data->led_state[n] = value;
- sony_set_leds(drv_data, drv_data->led_state,
- drv_data->led_count);
- }
+ if (led == drv_data->leds[n])
break;
- }
+ }
+
+ /* This LED is not registered on this device */
+ if (n >= drv_data->led_count)
+ return;
+
+ /* The DualShock 4 has a global blink setting and always uses index 0 */
+ if (drv_data->quirks & DUALSHOCK4_CONTROLLER)
+ blink_index = 0;
+ else
+ blink_index = n;
+
+ if ((value != drv_data->led_state[n]) ||
+ drv_data->led_delay_on[blink_index] ||
+ drv_data->led_delay_off[blink_index]) {
+ drv_data->led_state[n] = value;
+
+ /* Setting the brightness stops the blinking */
+ drv_data->led_delay_on[blink_index] = 0;
+ drv_data->led_delay_off[blink_index] = 0;
+
+ sony_set_leds(drv_data, drv_data->led_state,
+ drv_data->led_count);
}
}
@@ -1209,6 +1228,58 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
return LED_OFF;
}
+static int sony_led_blink_set(struct led_classdev *led, unsigned long *delay_on,
+ unsigned long *delay_off)
+{
+ struct device *dev = led->dev->parent;
+ struct hid_device *hdev = container_of(dev, struct hid_device, dev);
+ struct sony_sc *drv_data = hid_get_drvdata(hdev);
+ int n;
+ __u8 new_on, new_off;
+
+ if (!drv_data) {
+ hid_err(hdev, "No device data\n");
+ return -EINVAL;
+ }
+
+ /* Max delay is 255 deciseconds or 2550 milliseconds */
+ if (*delay_on > 2550)
+ *delay_on = 2550;
+ if (*delay_off > 2550)
+ *delay_off = 2550;
+
+ /* Blink at 1 Hz if both values are zero */
+ if (!*delay_on && !*delay_off)
+ *delay_on = *delay_off = 1000;
+
+ new_on = *delay_on / 10;
+ new_off = *delay_off / 10;
+
+ /* The DualShock 4 has a global blink setting and always uses index 0 */
+ if (drv_data->quirks & DUALSHOCK4_CONTROLLER) {
+ n = 0;
+ } else {
+ for (n = 0; n < drv_data->led_count; n++) {
+ if (led == drv_data->leds[n])
+ break;
+ }
+ }
+
+ /* This LED is not registered on this device */
+ if (n >= drv_data->led_count)
+ return -EINVAL;
+
+ /* Don't schedule work if the values didn't change */
+ if (new_on != drv_data->led_delay_on[n] ||
+ new_off != drv_data->led_delay_off[n]) {
+ drv_data->led_delay_on[n] = new_on;
+ drv_data->led_delay_off[n] = new_off;
+ schedule_work(&drv_data->state_worker);
+ }
+
+ return 0;
+}
+
static void sony_leds_remove(struct sony_sc *sc)
{
struct led_classdev *led;
@@ -1301,6 +1372,9 @@ static int sony_leds_init(struct sony_sc *sc)
led->brightness_get = sony_led_get_brightness;
led->brightness_set = sony_led_set_brightness;
+ if (!(sc->quirks & BUZZ_CONTROLLER))
+ led->blink_set = sony_led_blink_set;
+
sc->leds[n] = led;
ret = led_classdev_register(&hdev->dev, led);
@@ -1325,6 +1399,7 @@ error_leds:
static void sixaxis_state_worker(struct work_struct *work)
{
struct sony_sc *sc = container_of(work, struct sony_sc, state_worker);
+ int n;
union sixaxis_output_report_01 report = {
.buf = {
0x01,
@@ -1348,6 +1423,22 @@ static void sixaxis_state_worker(struct work_struct *work)
report.data.leds_bitmap |= sc->led_state[2] << 3;
report.data.leds_bitmap |= sc->led_state[3] << 4;
+ /*
+ * The LEDs in the report are indexed in reverse order to their
+ * corresponding light on the controller.
+ * Index 0 = LED 4, index 1 = LED 3, etc...
+ *
+ * In the case of both delay values being zero (blinking disabled) the
+ * default report values should be used or the controller LED will be
+ * always off.
+ */
+ for (n = 0; n < 4; n++) {
+ if (sc->led_delay_on[n] || sc->led_delay_off[n]) {
+ report.data.led[3 - n].duty_off = sc->led_delay_off[n];
+ report.data.led[3 - n].duty_on = sc->led_delay_on[n];
+ }
+ }
+
hid_hw_raw_request(sc->hdev, report.data.report_id, report.buf,
sizeof(report), HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
}
@@ -1362,7 +1453,7 @@ static void dualshock4_state_worker(struct work_struct *work)
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
buf[0] = 0x05;
- buf[1] = 0x03;
+ buf[1] = 0xFF;
offset = 4;
} else {
buf[0] = 0x11;
@@ -1382,6 +1473,10 @@ static void dualshock4_state_worker(struct work_struct *work)
buf[offset++] = sc->led_state[1];
buf[offset++] = sc->led_state[2];
+ /* If both delay values are zero the DualShock 4 disables blinking. */
+ buf[offset++] = sc->led_delay_on[0];
+ buf[offset++] = sc->led_delay_off[0];
+
if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
hid_hw_output_report(hdev, buf, 32);
else
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 0/8] mfd: AXP20x: Add support for AXP202 and AXP209
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
AXP209 and AXP202 are the PMUs (Power Management Unit) used by A10, A13
and A20 SoCs and developed by X-Powers, a sister company of Allwinner.
AXP20x comprises an adaptive USB-Compatible PWM charger, 2 BUCK DC-DC
converters, 5 LDOs, multiple 12-bit ADCs of voltage, current and temperature
as well as 4 configurable GPIOs.
This set of patches introduces the core driver and support for two different
subsystems:
- Regulators
- PEK (Power Enable Key)
This patchset depends on patchsets:
irq: sun4i IRQ 0 / ENMI fixes (Hans de Goede)
ARM: sun7i/sun6i: irqchip: Irqchip driver for NMI controller (Carlo Caione)
Changes since v1:
- Added a new standalone patch for defconfig
- MFD core:
* Removed axp,system-power-controller property
- Bindings documentation:
* Corrected description for dcdc-workmode property
* Removed unused axp20x-pek compatible
- Input misc PEK driver:
* Fixed seconds in lower case
- Regulators subsystem:
* Fixed axp20x_set_suspend_voltage()
* Switched to using multi-bit control for regulators
* When "regulators" node is not found driver doesn't quit
* Driver is now using devm_regulator_register()
* Added module_platform_driver() instead of subsys_initcall()
- DT:
* Added new DTSI for AXP209
* Added support for cubietruck and olinuxino-micro
Carlo Caione (8):
mfd: AXP20x: Add mfd driver for AXP20x PMIC
mfd: AXP20x: Add bindings documentation
input: misc: Add driver for AXP20x Power Enable Key
input: misc: Add ABI docs for AXP20x PEK
regulator: AXP20x: Add support for regulators subsystem
ARM: sunxi: dt: Add x-powers-axp209.dtsi file
ARM: sun7i: dt: Add AXP209 support to various boards
ARM: sunxi: Add AXP20x support in defconfig
.../ABI/testing/sysfs-driver-input-axp-pek | 11 +
Documentation/devicetree/bindings/mfd/axp20x.txt | 83 ++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 12 +
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 13 +
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 12 +
arch/arm/boot/dts/x-powers-axp209.dtsi | 60 +++++
arch/arm/configs/sunxi_defconfig | 4 +
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/axp20x-pek.c | 267 +++++++++++++++++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 1 +
drivers/mfd/axp20x.c | 247 +++++++++++++++++
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/axp20x-regulator.c | 293 +++++++++++++++++++++
include/linux/mfd/axp20x.h | 179 +++++++++++++
18 files changed, 1215 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-input-axp-pek
create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
create mode 100644 arch/arm/boot/dts/x-powers-axp209.dtsi
create mode 100644 drivers/input/misc/axp20x-pek.c
create mode 100644 drivers/mfd/axp20x.c
create mode 100644 drivers/regulator/axp20x-regulator.c
create mode 100644 include/linux/mfd/axp20x.h
--
1.8.3.2
^ permalink raw reply
* [PATCH v2 1/8] mfd: AXP20x: Add mfd driver for AXP20x PMIC
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
This patch introduces the preliminary support for PMICs X-Powers AXP202
and AXP209. The AXP209 and AXP202 are the PMUs (Power Management Unit)
used by A10, A13 and A20 SoCs and developed by X-Powers, a sister company
of Allwinner.
The core enables support for two subsystems:
- PEK (Power Enable Key)
- Regulators
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
drivers/mfd/Kconfig | 12 +++
drivers/mfd/Makefile | 1 +
drivers/mfd/axp20x.c | 247 +++++++++++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 179 ++++++++++++++++++++++++++++++++
4 files changed, 439 insertions(+)
create mode 100644 drivers/mfd/axp20x.c
create mode 100644 include/linux/mfd/axp20x.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 49bb445..24ba61a 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -59,6 +59,18 @@ config MFD_AAT2870_CORE
additional drivers must be enabled in order to use the
functionality of the device.
+config MFD_AXP20X
+ bool "X-Powers AXP20X"
+ select MFD_CORE
+ select REGMAP_I2C
+ select REGMAP_IRQ
+ depends on I2C=y
+ help
+ If you say Y here you get support for the AXP20X.
+ This driver provides common support for accessing the device,
+ additional drivers must be enabled in order to use the
+ functionality of the device.
+
config MFD_CROS_EC
tristate "ChromeOS Embedded Controller"
select MFD_CORE
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index 5aea5ef..fb773b5 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -101,6 +101,7 @@ obj-$(CONFIG_PMIC_DA9052) += da9052-irq.o
obj-$(CONFIG_PMIC_DA9052) += da9052-core.o
obj-$(CONFIG_MFD_DA9052_SPI) += da9052-spi.o
obj-$(CONFIG_MFD_DA9052_I2C) += da9052-i2c.o
+obj-$(CONFIG_MFD_AXP20X) += axp20x.o
obj-$(CONFIG_MFD_LP3943) += lp3943.o
obj-$(CONFIG_MFD_LP8788) += lp8788.o lp8788-irq.o
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
new file mode 100644
index 0000000..a790f0f
--- /dev/null
+++ b/drivers/mfd/axp20x.c
@@ -0,0 +1,247 @@
+/*
+ * axp20x.c - mfd core driver for the X-Powers AXP202 and AXP209
+ *
+ * Author: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/mfd/core.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#define AXP20X_OFF 0x80
+
+static const struct regmap_range axp20x_writeable_ranges[] = {
+ regmap_reg_range(AXP20X_DATACACHE(0), AXP20X_IRQ5_STATE),
+ regmap_reg_range(AXP20X_DCDC_MODE, AXP20X_FG_RES),
+};
+
+static const struct regmap_range axp20x_volatile_ranges[] = {
+ regmap_reg_range(AXP20X_IRQ1_EN, AXP20X_IRQ5_STATE),
+};
+
+static const struct regmap_access_table axp20x_writeable_table = {
+ .yes_ranges = axp20x_writeable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp20x_writeable_ranges),
+};
+
+static const struct regmap_access_table axp20x_volatile_table = {
+ .yes_ranges = axp20x_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(axp20x_volatile_ranges),
+};
+
+static struct resource axp20x_pek_resources[] = {
+ {
+ .name = "PEK_DBR",
+ .start = AXP20X_IRQ_PEK_RIS_EDGE,
+ .end = AXP20X_IRQ_PEK_RIS_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+ {
+ .name = "PEK_DBF",
+ .start = AXP20X_IRQ_PEK_FAL_EDGE,
+ .end = AXP20X_IRQ_PEK_FAL_EDGE,
+ .flags = IORESOURCE_IRQ,
+ },
+};
+
+static const struct regmap_config axp20x_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .wr_table = &axp20x_writeable_table,
+ .volatile_table = &axp20x_volatile_table,
+ .max_register = AXP20X_FG_RES,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#define AXP20X_IRQ(_irq, _off, _mask) \
+ [AXP20X_IRQ_##_irq] = { .reg_offset = (_off), .mask = BIT(_mask) }
+
+static const struct regmap_irq axp20x_regmap_irqs[] = {
+ AXP20X_IRQ(ACIN_OVER_V, 0, 7),
+ AXP20X_IRQ(ACIN_PLUGIN, 0, 6),
+ AXP20X_IRQ(ACIN_REMOVAL, 0, 5),
+ AXP20X_IRQ(VBUS_OVER_V, 0, 4),
+ AXP20X_IRQ(VBUS_PLUGIN, 0, 3),
+ AXP20X_IRQ(VBUS_REMOVAL, 0, 2),
+ AXP20X_IRQ(VBUS_V_LOW, 0, 1),
+ AXP20X_IRQ(BATT_PLUGIN, 1, 7),
+ AXP20X_IRQ(BATT_REMOVAL, 1, 6),
+ AXP20X_IRQ(BATT_ENT_ACT_MODE, 1, 5),
+ AXP20X_IRQ(BATT_EXIT_ACT_MODE, 1, 4),
+ AXP20X_IRQ(CHARG, 1, 3),
+ AXP20X_IRQ(CHARG_DONE, 1, 2),
+ AXP20X_IRQ(BATT_TEMP_HIGH, 1, 1),
+ AXP20X_IRQ(BATT_TEMP_LOW, 1, 0),
+ AXP20X_IRQ(DIE_TEMP_HIGH, 2, 7),
+ AXP20X_IRQ(CHARG_I_LOW, 2, 6),
+ AXP20X_IRQ(DCDC1_V_LONG, 2, 5),
+ AXP20X_IRQ(DCDC2_V_LONG, 2, 4),
+ AXP20X_IRQ(DCDC3_V_LONG, 2, 3),
+ AXP20X_IRQ(PEK_SHORT, 2, 1),
+ AXP20X_IRQ(PEK_LONG, 2, 0),
+ AXP20X_IRQ(N_OE_PWR_ON, 3, 7),
+ AXP20X_IRQ(N_OE_PWR_OFF, 3, 6),
+ AXP20X_IRQ(VBUS_VALID, 3, 5),
+ AXP20X_IRQ(VBUS_NOT_VALID, 3, 4),
+ AXP20X_IRQ(VBUS_SESS_VALID, 3, 3),
+ AXP20X_IRQ(VBUS_SESS_END, 3, 2),
+ AXP20X_IRQ(LOW_PWR_LVL1, 3, 1),
+ AXP20X_IRQ(LOW_PWR_LVL2, 3, 0),
+ AXP20X_IRQ(TIMER, 4, 7),
+ AXP20X_IRQ(PEK_RIS_EDGE, 4, 6),
+ AXP20X_IRQ(PEK_FAL_EDGE, 4, 5),
+ AXP20X_IRQ(GPIO3_INPUT, 4, 3),
+ AXP20X_IRQ(GPIO2_INPUT, 4, 2),
+ AXP20X_IRQ(GPIO1_INPUT, 4, 1),
+ AXP20X_IRQ(GPIO0_INPUT, 4, 0),
+};
+
+static const struct regmap_irq_chip axp20x_regmap_irq_chip = {
+ .name = "axp20x_irq_chip",
+ .status_base = AXP20X_IRQ1_STATE,
+ .ack_base = AXP20X_IRQ1_STATE,
+ .mask_base = AXP20X_IRQ1_EN,
+ .num_regs = 5,
+ .irqs = axp20x_regmap_irqs,
+ .num_irqs = ARRAY_SIZE(axp20x_regmap_irqs),
+ .mask_invert = true,
+ .init_ack_masked = true,
+};
+
+static struct mfd_cell axp20x_cells[] = {
+ {
+ .name = "axp20x-pek",
+ .num_resources = ARRAY_SIZE(axp20x_pek_resources),
+ .resources = axp20x_pek_resources,
+ }, {
+ .name = "axp20x-regulator",
+ },
+};
+
+const struct of_device_id axp20x_of_match[] = {
+ { .compatible = "x-powers,axp202", .data = (void *) AXP202_ID },
+ { .compatible = "x-powers,axp209", .data = (void *) AXP209_ID },
+ { },
+};
+
+static struct axp20x_dev *axp20x_pm_power_off;
+static void axp20x_power_off(void)
+{
+ regmap_write(axp20x_pm_power_off->regmap, AXP20X_OFF_CTRL,
+ AXP20X_OFF);
+}
+
+static int axp20x_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct axp20x_dev *axp20x;
+ const struct of_device_id *of_id;
+ int ret;
+
+ axp20x = devm_kzalloc(&i2c->dev, sizeof(*axp20x), GFP_KERNEL);
+ if (!axp20x)
+ return -ENOMEM;
+
+ of_id = of_match_device(axp20x_of_match, &i2c->dev);
+ if (!of_id) {
+ dev_err(&i2c->dev, "Unable to setup AXP20X data\n");
+ return -ENODEV;
+ }
+ axp20x->variant = (int) of_id->data;
+
+ axp20x->i2c_client = i2c;
+ axp20x->dev = &i2c->dev;
+ dev_set_drvdata(axp20x->dev, axp20x);
+
+ axp20x->regmap = devm_regmap_init_i2c(i2c, &axp20x_regmap_config);
+ if (IS_ERR(axp20x->regmap)) {
+ ret = PTR_ERR(axp20x->regmap);
+ dev_err(&i2c->dev, "regmap init failed: %d\n", ret);
+ return ret;
+ }
+
+ axp20x->irq = i2c->irq;
+ ret = regmap_add_irq_chip(axp20x->regmap, axp20x->irq,
+ IRQF_ONESHOT | IRQF_SHARED, -1,
+ &axp20x_regmap_irq_chip,
+ &axp20x->regmap_irqc);
+ if (ret) {
+ dev_err(&i2c->dev, "failed to add irq chip: %d\n", ret);
+ return ret;
+ }
+
+ ret = mfd_add_devices(axp20x->dev, -1, axp20x_cells,
+ ARRAY_SIZE(axp20x_cells), NULL, 0, NULL);
+
+ if (ret) {
+ dev_err(&i2c->dev, "failed to add MFD devices: %d\n", ret);
+ goto mfd_err;
+ }
+
+ if (!pm_power_off) {
+ axp20x_pm_power_off = axp20x;
+ pm_power_off = axp20x_power_off;
+ }
+
+ dev_info(&i2c->dev, "AXP20X driver loaded\n");
+
+ return 0;
+
+mfd_err:
+ regmap_del_irq_chip(axp20x->irq, axp20x->regmap_irqc);
+
+ return ret;
+}
+
+static int axp20x_i2c_remove(struct i2c_client *i2c)
+{
+ struct axp20x_dev *axp20x = i2c_get_clientdata(i2c);
+
+ if (axp20x == axp20x_pm_power_off) {
+ axp20x_pm_power_off = NULL;
+ pm_power_off = NULL;
+ }
+
+ mfd_remove_devices(axp20x->dev);
+ regmap_del_irq_chip(axp20x->i2c_client->irq, axp20x->regmap_irqc);
+
+ return 0;
+}
+
+static const struct i2c_device_id axp20x_i2c_id[] = {
+ { "axp202", AXP202_ID },
+ { "axp209", AXP209_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, axp20x_i2c_id);
+
+static struct i2c_driver axp20x_i2c_driver = {
+ .driver = {
+ .name = "axp20x",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(axp20x_of_match),
+ },
+ .probe = axp20x_i2c_probe,
+ .remove = axp20x_i2c_remove,
+ .id_table = axp20x_i2c_id,
+};
+
+module_i2c_driver(axp20x_i2c_driver);
+
+MODULE_DESCRIPTION("PMIC MFD core driver for AXP20X");
+MODULE_AUTHOR("Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h
new file mode 100644
index 0000000..86d446e
--- /dev/null
+++ b/include/linux/mfd/axp20x.h
@@ -0,0 +1,179 @@
+/*
+ * Functions to access AXP20X power management chip.
+ *
+ * Copyright (C) 2013, Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __LINUX_MFD_AXP20X_H
+#define __LINUX_MFD_AXP20X_H
+
+#define AXP202_ID 0
+#define AXP209_ID 1
+
+#define AXP20X_DATACACHE(m) (0x04 + (m))
+
+/* Power supply */
+#define AXP20X_PWR_INPUT_STATUS 0x00
+#define AXP20X_PWR_OP_MODE 0x01
+#define AXP20X_USB_OTG_STATUS 0x02
+#define AXP20X_PWR_OUT_CTRL 0x12
+#define AXP20X_DCDC2_V_OUT 0x23
+#define AXP20X_DCDC2_LDO3_V_SCAL 0x25
+#define AXP20X_DCDC3_V_OUT 0x27
+#define AXP20X_LDO24_V_OUT 0x28
+#define AXP20X_LDO3_V_OUT 0x29
+#define AXP20X_VBUS_IPSOUT_MGMT 0x30
+#define AXP20X_V_OFF 0x31
+#define AXP20X_OFF_CTRL 0x32
+#define AXP20X_CHRG_CTRL1 0x33
+#define AXP20X_CHRG_CTRL2 0x34
+#define AXP20X_CHRG_BAK_CTRL 0x35
+#define AXP20X_PEK_KEY 0x36
+#define AXP20X_DCDC_FREQ 0x37
+#define AXP20X_V_LTF_CHRG 0x38
+#define AXP20X_V_HTF_CHRG 0x39
+#define AXP20X_APS_WARN_L1 0x3a
+#define AXP20X_APS_WARN_L2 0x3b
+#define AXP20X_V_LTF_DISCHRG 0x3c
+#define AXP20X_V_HTF_DISCHRG 0x3d
+
+/* Interrupt */
+#define AXP20X_IRQ1_EN 0x40
+#define AXP20X_IRQ2_EN 0x41
+#define AXP20X_IRQ3_EN 0x42
+#define AXP20X_IRQ4_EN 0x43
+#define AXP20X_IRQ5_EN 0x44
+#define AXP20X_IRQ1_STATE 0x48
+#define AXP20X_IRQ2_STATE 0x49
+#define AXP20X_IRQ3_STATE 0x4a
+#define AXP20X_IRQ4_STATE 0x4b
+#define AXP20X_IRQ5_STATE 0x4c
+
+/* ADC */
+#define AXP20X_ACIN_V_ADC_H 0x56
+#define AXP20X_ACIN_V_ADC_L 0x57
+#define AXP20X_ACIN_I_ADC_H 0x58
+#define AXP20X_ACIN_I_ADC_L 0x59
+#define AXP20X_VBUS_V_ADC_H 0x5a
+#define AXP20X_VBUS_V_ADC_L 0x5b
+#define AXP20X_VBUS_I_ADC_H 0x5c
+#define AXP20X_VBUS_I_ADC_L 0x5d
+#define AXP20X_TEMP_ADC_H 0x5e
+#define AXP20X_TEMP_ADC_L 0x5f
+#define AXP20X_TS_IN_H 0x62
+#define AXP20X_TS_IN_L 0x63
+#define AXP20X_GPIO0_V_ADC_H 0x64
+#define AXP20X_GPIO0_V_ADC_L 0x65
+#define AXP20X_GPIO1_V_ADC_H 0x66
+#define AXP20X_GPIO1_V_ADC_L 0x67
+#define AXP20X_PWR_BATT_H 0x70
+#define AXP20X_PWR_BATT_M 0x71
+#define AXP20X_PWR_BATT_L 0x72
+#define AXP20X_BATT_V_H 0x78
+#define AXP20X_BATT_V_L 0x79
+#define AXP20X_BATT_CHRG_I_H 0x7a
+#define AXP20X_BATT_CHRG_I_L 0x7b
+#define AXP20X_BATT_DISCHRG_I_H 0x7c
+#define AXP20X_BATT_DISCHRG_I_L 0x7d
+#define AXP20X_IPSOUT_V_HIGH_H 0x7e
+#define AXP20X_IPSOUT_V_HIGH_L 0x7f
+
+/* Power supply */
+#define AXP20X_DCDC_MODE 0x80
+#define AXP20X_ADC_EN1 0x82
+#define AXP20X_ADC_EN2 0x83
+#define AXP20X_ADC_RATE 0x84
+#define AXP20X_GPIO10_IN_RANGE 0x85
+#define AXP20X_GPIO1_ADC_IRQ_RIS 0x86
+#define AXP20X_GPIO1_ADC_IRQ_FAL 0x87
+#define AXP20X_TIMER_CTRL 0x8a
+#define AXP20X_VBUS_MON 0x8b
+#define AXP20X_OVER_TMP 0x8f
+
+/* GPIO */
+#define AXP20X_GPIO0_CTRL 0x90
+#define AXP20X_LDO5_V_OUT 0x91
+#define AXP20X_GPIO1_CTRL 0x92
+#define AXP20X_GPIO2_CTRL 0x93
+#define AXP20X_GPIO20_SS 0x94
+#define AXP20X_GPIO3_CTRL 0x95
+
+/* Battery */
+#define AXP20X_CHRG_CC_31_24 0xb0
+#define AXP20X_CHRG_CC_23_16 0xb1
+#define AXP20X_CHRG_CC_15_8 0xb2
+#define AXP20X_CHRG_CC_7_0 0xb3
+#define AXP20X_DISCHRG_CC_31_24 0xb4
+#define AXP20X_DISCHRG_CC_23_16 0xb5
+#define AXP20X_DISCHRG_CC_15_8 0xb6
+#define AXP20X_DISCHRG_CC_7_0 0xb7
+#define AXP20X_CC_CTRL 0xb8
+#define AXP20X_FG_RES 0xb9
+
+/* Regulators IDs */
+enum {
+ AXP20X_LDO1 = 0,
+ AXP20X_LDO2,
+ AXP20X_LDO3,
+ AXP20X_LDO4,
+ AXP20X_LDO5,
+ AXP20X_DCDC2,
+ AXP20X_DCDC3,
+ AXP20X_REG_ID_MAX,
+};
+
+/* IRQs */
+enum {
+ AXP20X_IRQ_ACIN_OVER_V = 1,
+ AXP20X_IRQ_ACIN_PLUGIN,
+ AXP20X_IRQ_ACIN_REMOVAL,
+ AXP20X_IRQ_VBUS_OVER_V,
+ AXP20X_IRQ_VBUS_PLUGIN,
+ AXP20X_IRQ_VBUS_REMOVAL,
+ AXP20X_IRQ_VBUS_V_LOW,
+ AXP20X_IRQ_BATT_PLUGIN,
+ AXP20X_IRQ_BATT_REMOVAL,
+ AXP20X_IRQ_BATT_ENT_ACT_MODE,
+ AXP20X_IRQ_BATT_EXIT_ACT_MODE,
+ AXP20X_IRQ_CHARG,
+ AXP20X_IRQ_CHARG_DONE,
+ AXP20X_IRQ_BATT_TEMP_HIGH,
+ AXP20X_IRQ_BATT_TEMP_LOW,
+ AXP20X_IRQ_DIE_TEMP_HIGH,
+ AXP20X_IRQ_CHARG_I_LOW,
+ AXP20X_IRQ_DCDC1_V_LONG,
+ AXP20X_IRQ_DCDC2_V_LONG,
+ AXP20X_IRQ_DCDC3_V_LONG,
+ AXP20X_IRQ_PEK_SHORT = 22,
+ AXP20X_IRQ_PEK_LONG,
+ AXP20X_IRQ_N_OE_PWR_ON,
+ AXP20X_IRQ_N_OE_PWR_OFF,
+ AXP20X_IRQ_VBUS_VALID,
+ AXP20X_IRQ_VBUS_NOT_VALID,
+ AXP20X_IRQ_VBUS_SESS_VALID,
+ AXP20X_IRQ_VBUS_SESS_END,
+ AXP20X_IRQ_LOW_PWR_LVL1,
+ AXP20X_IRQ_LOW_PWR_LVL2,
+ AXP20X_IRQ_TIMER,
+ AXP20X_IRQ_PEK_RIS_EDGE,
+ AXP20X_IRQ_PEK_FAL_EDGE,
+ AXP20X_IRQ_GPIO3_INPUT,
+ AXP20X_IRQ_GPIO2_INPUT,
+ AXP20X_IRQ_GPIO1_INPUT,
+ AXP20X_IRQ_GPIO0_INPUT,
+};
+
+struct axp20x_dev {
+ struct device *dev;
+ struct i2c_client *i2c_client;
+ struct regmap *regmap;
+ struct regmap_irq_chip_data *regmap_irqc;
+ int variant;
+ int irq;
+};
+
+#endif /* __LINUX_MFD_AXP20X_H */
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 2/8] mfd: AXP20x: Add bindings documentation
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Bindings documentation for the AXP20x driver. In this file also two
sub-nodes (PEK and regulators) are documented.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
Documentation/devicetree/bindings/mfd/axp20x.txt | 83 ++++++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
2 files changed, 84 insertions(+)
create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
diff --git a/Documentation/devicetree/bindings/mfd/axp20x.txt b/Documentation/devicetree/bindings/mfd/axp20x.txt
new file mode 100644
index 0000000..982aefe
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -0,0 +1,83 @@
+* axp20x device tree bindings
+
+The axp20x family current members :-
+axp202 (X-Powers)
+axp209 (X-Powers)
+
+Required properties:
+- compatible : Should be "x-powers,axp202" or "x-powers,axp209"
+- interrupt-controller : axp20x has its own internal IRQs
+- #interrupt-cells : Should be set to 1
+- interrupt-parent : The parent interrupt controller
+- interrupts : Interrupt specifiers for interrupt sources
+- reg : The I2C slave address for the AXP chip
+
+Sub-nodes:
+* regulators : Contain the regulator nodes. The regulators are bound using
+ their name as listed here: dcdc2, dcdc3, ldo1, ldo2, ldo3,
+ ldo4, ldo5.
+ The bindings details of individual regulator device can be found in:
+ Documentation/devicetree/bindings/regulator/regulator.txt with the
+ exception of:
+
+ - dcdc-freq : defines the work frequency of DC-DC in KHz
+ (range: 750-1875). Default: 1.5MHz
+ - dcdc-workmode : Optional. 1 for PWM mode, 0 for AUTO mode
+ Default: AUTO mode
+
+Example:
+
+axp: axp20x@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ compatible = "x-powers,axp209";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ regulators {
+ dcdc-freq = "1500";
+
+ axp_dcdc2: dcdc2 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <2275000>;
+ dcdc-workmode = <0>;
+ regulator-always-on;
+ };
+
+ axp_dcdc3: dcdc3 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <3500000>;
+ dcdc-workmode = <0>;
+ regulator-always-on;
+ };
+
+ axp_ldo1: ldo1 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ };
+
+ axp_ldo2: ldo2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ axp_ldo3: ldo3 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <3500000>;
+ };
+
+ axp_ldo4: ldo4 {
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ axp_ldo5: ldo5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+};
+
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 40ce2df..d06ba8c 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -95,3 +95,4 @@ winbond Winbond Electronics corp.
wlf Wolfson Microelectronics
wm Wondermedia Technologies, Inc.
xlnx Xilinx
+x-powers X-Powers
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 3/8] input: misc: Add driver for AXP20x Power Enable Key
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
This patch add support for the Power Enable Key found on MFD AXP202 and
AXP209. Besides the basic support for the button, the driver adds two
entries in sysfs to configure the time delay for power on/off.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/axp20x-pek.c | 267 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 279 insertions(+)
create mode 100644 drivers/input/misc/axp20x-pek.c
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 7904ab0..87244fb 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -393,6 +393,17 @@ config INPUT_RETU_PWRBUTTON
To compile this driver as a module, choose M here. The module will
be called retu-pwrbutton.
+config INPUT_AXP20X_PEK
+ tristate "X-Powers AXP20X power button driver"
+ depends on MFD_AXP20X
+ help
+ Say Y here if you want to enable power key reporting via the
+ AXP20X PMIC.
+
+ To compile this driver as a module, choose M here. The module will
+ be called axp20x-pek.
+
+
config INPUT_TWL4030_PWRBUTTON
tristate "TWL4030 Power button Driver"
depends on TWL4030_CORE
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index cda71fc..624abf5 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -50,6 +50,7 @@ obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
+obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
new file mode 100644
index 0000000..060212f
--- /dev/null
+++ b/drivers/input/misc/axp20x-pek.c
@@ -0,0 +1,267 @@
+/*
+ * axp20x power button driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/errno.h>
+#include <linux/irq.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#define AXP20X_PEK_STARTUP_MASK (0xc0)
+#define AXP20X_PEK_SHUTDOWN_MASK (0x03)
+
+static const char const *startup_time[] = { "128ms", "3s" , "1s", "2s" };
+static const char const *shutdown_time[] = { "4s", "6s" , "8s", "10s" };
+
+struct axp20x_pek {
+ struct axp20x_dev *axp20x;
+ struct input_dev *input;
+ int irq_dbr;
+ int irq_dbf;
+};
+
+struct axp20x_pek_ext_attr {
+ const char const **str;
+ unsigned int mask;
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
+ .str = startup_time,
+ .mask = AXP20X_PEK_STARTUP_MASK,
+};
+
+static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
+ .str = shutdown_time,
+ .mask = AXP20X_PEK_SHUTDOWN_MASK,
+};
+
+static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
+{
+ return container_of(attr, struct dev_ext_attribute, attr)->var;
+}
+
+static ssize_t axp20x_show_ext_attr(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+ struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+ unsigned int val;
+ int ret, i;
+ int cnt = 0;
+
+ ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
+ if (ret != 0)
+ return ret;
+
+ val &= axp20x_ea->mask;
+ val >>= ffs(axp20x_ea->mask) - 1;
+
+ for (i = 0; i < 4; i++) {
+ if (val == i)
+ cnt += sprintf(buf + cnt, "[%s] ", axp20x_ea->str[i]);
+ else
+ cnt += sprintf(buf + cnt, "%s ", axp20x_ea->str[i]);
+ }
+
+ cnt += sprintf(buf + cnt, "\n");
+
+ return cnt;
+}
+
+static ssize_t axp20x_store_ext_attr(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
+ struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
+ char val_str[20];
+ int ret, i;
+ size_t len;
+
+ val_str[sizeof(val_str) - 1] = '\0';
+ strncpy(val_str, buf, sizeof(val_str) - 1);
+ len = strlen(val_str);
+
+ if (len && val_str[len - 1] == '\n')
+ val_str[len - 1] = '\0';
+
+ for (i = 0; i < 4; i++) {
+ if (!strcmp(val_str, axp20x_ea->str[i])) {
+
+ i <<= ffs(axp20x_ea->mask) - 1;
+ ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
+ AXP20X_PEK_KEY,
+ axp20x_ea->mask, i);
+ if (ret != 0)
+ return -EINVAL;
+ return count;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static struct dev_ext_attribute axp20x_dev_attr_startup = {
+ .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+ .var = &axp20x_pek_startup_ext_attr
+};
+
+static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
+ __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
+ &axp20x_pek_shutdown_ext_attr
+};
+
+static struct attribute *dev_attrs[] = {
+ &axp20x_dev_attr_startup.attr.attr,
+ &axp20x_dev_attr_shutdown.attr.attr,
+ NULL,
+};
+
+static struct attribute_group dev_attr_group = {
+ .attrs = dev_attrs,
+};
+
+static const struct attribute_group *dev_attr_groups[] = {
+ &dev_attr_group,
+ NULL,
+};
+
+static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
+{
+ struct input_dev *idev = pwr;
+ struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
+
+ if (irq == axp20x_pek->irq_dbr)
+ input_report_key(idev, KEY_POWER, true);
+ else if (irq == axp20x_pek->irq_dbf)
+ input_report_key(idev, KEY_POWER, false);
+
+ input_sync(idev);
+
+ return IRQ_HANDLED;
+}
+
+static int axp20x_pek_probe(struct platform_device *pdev)
+{
+ struct axp20x_pek *axp20x_pek;
+ struct axp20x_dev *axp20x;
+ struct input_dev *idev;
+ int error;
+
+ axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
+ GFP_KERNEL);
+ if (!axp20x_pek)
+ return -ENOMEM;
+
+ axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
+ axp20x = axp20x_pek->axp20x;
+
+ axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
+ if (axp20x_pek->irq_dbr < 0) {
+ dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
+ axp20x_pek->irq_dbr);
+ return axp20x_pek->irq_dbr;
+ }
+ axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
+ axp20x_pek->irq_dbr);
+
+ axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
+ if (axp20x_pek->irq_dbf < 0) {
+ dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
+ axp20x_pek->irq_dbf);
+ return axp20x_pek->irq_dbf;
+ }
+ axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
+ axp20x_pek->irq_dbf);
+
+ axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
+ if (!axp20x_pek->input)
+ return -ENOMEM;
+
+ idev = axp20x_pek->input;
+
+ idev->name = "axp20x-pek";
+ idev->phys = "m1kbd/input2";
+ idev->dev.parent = &pdev->dev;
+
+ input_set_capability(idev, EV_KEY, KEY_POWER);
+
+ input_set_drvdata(idev, axp20x_pek);
+
+ error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbr,
+ NULL, axp20x_pek_irq, 0,
+ "axp20x-pek-dbr", idev);
+ if (error) {
+ dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
+ axp20x_pek->irq_dbr, error);
+
+ return error;
+ }
+
+ error = devm_request_threaded_irq(&pdev->dev, axp20x_pek->irq_dbf,
+ NULL, axp20x_pek_irq, 0,
+ "axp20x-pek-dbf", idev);
+ if (error) {
+ dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
+ axp20x_pek->irq_dbf, error);
+ return error;
+ }
+
+ idev->dev.groups = dev_attr_groups;
+ error = input_register_device(idev);
+ if (error) {
+ dev_err(axp20x->dev, "Can't register input device: %d\n", error);
+ return error;
+ }
+
+ platform_set_drvdata(pdev, axp20x_pek);
+
+ return 0;
+}
+
+static int axp20x_pek_remove(struct platform_device *pdev)
+{
+ struct axp20x_pek *axp20x_pek = platform_get_drvdata(pdev);
+
+ input_unregister_device(axp20x_pek->input);
+
+ return 0;
+}
+
+static const struct of_device_id axp20x_pek_match[] = {
+ { .compatible = "x-powers,axp20x-pek", },
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, axp20x_pek_match);
+
+static struct platform_driver axp20x_pek_driver = {
+ .probe = axp20x_pek_probe,
+ .remove = axp20x_pek_remove,
+ .driver = {
+ .name = "axp20x-pek",
+ .owner = THIS_MODULE,
+ .of_match_table = axp20x_pek_match,
+ },
+};
+module_platform_driver(axp20x_pek_driver);
+
+MODULE_DESCRIPTION("axp20x Power Button");
+MODULE_AUTHOR("Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>");
+MODULE_LICENSE("GPL");
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 4/8] input: misc: Add ABI docs for AXP20x PEK
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Add ABI entries for the PEK found on PMU X-Powers AXP202 and AXP209.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
Documentation/ABI/testing/sysfs-driver-input-axp-pek | 11 +++++++++++
1 file changed, 11 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-input-axp-pek
diff --git a/Documentation/ABI/testing/sysfs-driver-input-axp-pek b/Documentation/ABI/testing/sysfs-driver-input-axp-pek
new file mode 100644
index 0000000..f8cdad2
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-driver-input-axp-pek
@@ -0,0 +1,11 @@
+What: /sys/class/input/input(x)/startup
+Date: March 2014
+Contact: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+Description: Startup time. Board is powered on if the button is pressed
+ for more than <startup_time>
+
+What: /sys/class/input/input(x)/shutdown
+Date: March 2014
+Contact: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+Description: Shutdown time. Board is powered off if the button is pressed
+ for more than <shutdown_time>
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 5/8] regulator: AXP20x: Add support for regulators subsystem
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
AXP202 and AXP209 come with two synchronous step-down DC-DCs and five
LDOs. This patch introduces basic support for those regulators.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/axp20x-regulator.c | 293 +++++++++++++++++++++++++++++++++++
3 files changed, 301 insertions(+)
create mode 100644 drivers/regulator/axp20x-regulator.c
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 6a79328..9f3bc48 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -139,6 +139,13 @@ config REGULATOR_AS3722
AS3722 PMIC. This will enable support for all the software
controllable DCDC/LDO regulators.
+config REGULATOR_AXP20X
+ tristate "X-POWERS AXP20X PMIC Regulators"
+ depends on MFD_AXP20X
+ help
+ This driver provides support for the voltage regulators on the
+ AXP20X PMIC.
+
config REGULATOR_DA903X
tristate "Dialog Semiconductor DA9030/DA9034 regulators"
depends on PMIC_DA903X
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 979f9dd..1dd084a 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o
obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o
+obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o
obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c
new file mode 100644
index 0000000..f09fe35
--- /dev/null
+++ b/drivers/regulator/axp20x-regulator.c
@@ -0,0 +1,293 @@
+/*
+ * AXP20x regulators driver.
+ *
+ * Copyright (C) 2013 Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/err.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define AXP20X_IO_ENABLED (0x03)
+#define AXP20X_IO_DISABLED (0x07)
+
+#define AXP20X_WORKMODE_DCDC2_MASK BIT(2)
+#define AXP20X_WORKMODE_DCDC3_MASK BIT(1)
+
+#define AXP20X_FREQ_DCDC_MASK (0x0f)
+
+struct axp20x_regulators {
+ struct regulator_desc rdesc[AXP20X_REG_ID_MAX];
+ struct regulator_dev *rdev[AXP20X_REG_ID_MAX];
+ struct axp20x_dev *axp20x;
+};
+
+#define AXP20X_DESC_IO(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask, \
+ _enable_val, _disable_val) \
+ [AXP20X_##_id] = { \
+ .name = #_id, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP20X_##_id, \
+ .n_voltages = (((_max) - (_min)) / (_step) + 1), \
+ .owner = THIS_MODULE, \
+ .min_uV = (_min) * 1000, \
+ .uV_step = (_step) * 1000, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .enable_val = (_enable_val), \
+ .disable_val = (_disable_val), \
+ .ops = &axp20x_ops, \
+ }
+
+#define AXP20X_DESC(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask) \
+ AXP20X_DESC_IO(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask, \
+ 0, 0)
+
+#define AXP20X_DESC_FIXED(_id, _volt) \
+ [AXP20X_##_id] = { \
+ .name = #_id, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP20X_##_id, \
+ .n_voltages = 1, \
+ .owner = THIS_MODULE, \
+ .min_uV = (_volt) * 1000, \
+ .ops = &axp20x_ops, \
+ }
+
+#define AXP20X_DESC_TABLE(_id, _table, _vreg, _vmask, _ereg, _emask) \
+ [AXP20X_##_id] = { \
+ .name = #_id, \
+ .type = REGULATOR_VOLTAGE, \
+ .id = AXP20X_##_id, \
+ .n_voltages = ARRAY_SIZE(_table), \
+ .owner = THIS_MODULE, \
+ .vsel_reg = (_vreg), \
+ .vsel_mask = (_vmask), \
+ .enable_reg = (_ereg), \
+ .enable_mask = (_emask), \
+ .volt_table = (_table), \
+ .ops = &axp20x_ops_table, \
+ }
+
+static int axp20x_ldo4_data[] = { 1250000, 1300000, 1400000, 1500000, 1600000, 1700000,
+ 1800000, 1900000, 2000000, 2500000, 2700000, 2800000,
+ 3000000, 3100000, 3200000, 3300000 };
+
+static int axp20x_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+ int sel = regulator_map_voltage_iterate(rdev, uV, uV);
+
+ if (sel < 0)
+ return sel;
+
+ return regulator_set_voltage_sel_regmap(rdev, sel);
+}
+
+static struct regulator_ops axp20x_ops_table = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_table,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = regulator_enable_regmap,
+ .set_suspend_disable = regulator_disable_regmap,
+ .set_suspend_voltage = axp20x_set_suspend_voltage,
+};
+
+
+static struct regulator_ops axp20x_ops = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+ .set_suspend_enable = regulator_enable_regmap,
+ .set_suspend_disable = regulator_disable_regmap,
+ .set_suspend_voltage = axp20x_set_suspend_voltage,
+};
+
+static struct regulator_desc axp20x_regulators[] = {
+ AXP20X_DESC(DCDC2, 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f,
+ AXP20X_PWR_OUT_CTRL, 0x10),
+ AXP20X_DESC(DCDC3, 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f,
+ AXP20X_PWR_OUT_CTRL, 0x02),
+ AXP20X_DESC_FIXED(LDO1, 1300),
+ AXP20X_DESC(LDO2, 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0,
+ AXP20X_PWR_OUT_CTRL, 0x04),
+ AXP20X_DESC(LDO3, 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f,
+ AXP20X_PWR_OUT_CTRL, 0x40),
+ AXP20X_DESC_TABLE(LDO4, axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f,
+ AXP20X_PWR_OUT_CTRL, 0x08),
+ AXP20X_DESC_IO(LDO5, 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0,
+ AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED,
+ AXP20X_IO_DISABLED),
+};
+
+#define AXP_MATCH(_name, _id) \
+ [AXP20X_##_id] = { \
+ .name = #_name, \
+ .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \
+ }
+
+static struct of_regulator_match axp20x_matches[] = {
+ AXP_MATCH(dcdc2, DCDC2),
+ AXP_MATCH(dcdc3, DCDC3),
+ AXP_MATCH(ldo1, LDO1),
+ AXP_MATCH(ldo2, LDO2),
+ AXP_MATCH(ldo3, LDO3),
+ AXP_MATCH(ldo4, LDO4),
+ AXP_MATCH(ldo5, LDO5),
+};
+
+static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
+{
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+
+ if (dcdcfreq < 750)
+ dcdcfreq = 750;
+
+ if (dcdcfreq > 1875)
+ dcdcfreq = 1875;
+
+ dcdcfreq = (dcdcfreq - 750) / 75;
+
+ return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ,
+ AXP20X_FREQ_DCDC_MASK, dcdcfreq);
+}
+
+static int axp20x_regulator_parse_dt(struct platform_device *pdev)
+{
+ struct device_node *np, *regulators;
+ int ret;
+ u32 dcdcfreq;
+
+ np = of_node_get(pdev->dev.parent->of_node);
+ if (!np)
+ return 0;
+
+ regulators = of_find_node_by_name(np, "regulators");
+ if (!regulators) {
+ dev_warn(&pdev->dev, "regulators node not found\n");
+ } else {
+ ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches,
+ ARRAY_SIZE(axp20x_matches));
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret);
+ return ret;
+ }
+
+ dcdcfreq = 1500;
+ of_property_read_u32(regulators, "dcdc-freq", &dcdcfreq);
+ ret = axp20x_set_dcdc_freq(pdev, dcdcfreq);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret);
+ return ret;
+ }
+
+ of_node_put(regulators);
+ }
+
+ return 0;
+}
+
+static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode)
+{
+ unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK;
+
+ if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3))
+ return -EINVAL;
+
+ if (id == AXP20X_DCDC3)
+ mask = AXP20X_WORKMODE_DCDC3_MASK;
+
+ workmode <<= ffs(mask) - 1;
+
+ return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode);
+}
+
+static int axp20x_regulator_probe(struct platform_device *pdev)
+{
+ struct axp20x_regulators *pmic;
+ struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent);
+ struct regulator_config config = { };
+ struct regulator_init_data *init_data;
+ int ret, i;
+ u32 workmode;
+
+ ret = axp20x_regulator_parse_dt(pdev);
+ if (ret)
+ return ret;
+
+ pmic = devm_kzalloc(&pdev->dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic) {
+ dev_err(&pdev->dev, "Failed to alloc pmic\n");
+ return -ENOMEM;
+ }
+
+ pmic->axp20x = axp20x;
+ memcpy(pmic->rdesc, axp20x_regulators, sizeof(pmic->rdesc));
+ platform_set_drvdata(pdev, pmic);
+
+ for (i = 0; i < AXP20X_REG_ID_MAX; i++) {
+ init_data = axp20x_matches[i].init_data;
+
+ config.dev = &pdev->dev;
+ config.init_data = init_data;
+ config.driver_data = pmic;
+ config.regmap = axp20x->regmap;
+ config.of_node = axp20x_matches[i].of_node;
+
+ pmic->rdev[i] = devm_regulator_register(&pdev->dev, &pmic->rdesc[i],
+ &config);
+ if (IS_ERR(pmic->rdev[i])) {
+ dev_err(&pdev->dev, "Failed to register %s\n",
+ pmic->rdesc[i].name);
+
+ return PTR_ERR(pmic->rdev[i]);
+ }
+
+ ret = of_property_read_u32(axp20x_matches[i].of_node, "dcdc-workmode",
+ &workmode);
+ if (!ret) {
+ if (axp20x_set_dcdc_workmode(pmic->rdev[i], i, workmode))
+ dev_err(&pdev->dev, "Failed to set workmode on %s\n",
+ pmic->rdesc[i].name);
+ }
+ }
+
+ return 0;
+}
+
+static struct platform_driver axp20x_regulator_driver = {
+ .probe = axp20x_regulator_probe,
+ .driver = {
+ .name = "axp20x-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+module_platform_driver(axp20x_regulator_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>");
+MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC");
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 6/8] ARM: sunxi: dt: Add x-powers-axp209.dtsi file
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
This dtsi describes the axp209 PMIC, and is to be included from inside
the i2c controller node to which the axp209 is connected.
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
arch/arm/boot/dts/x-powers-axp209.dtsi | 60 ++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
create mode 100644 arch/arm/boot/dts/x-powers-axp209.dtsi
diff --git a/arch/arm/boot/dts/x-powers-axp209.dtsi b/arch/arm/boot/dts/x-powers-axp209.dtsi
new file mode 100644
index 0000000..d272e67
--- /dev/null
+++ b/arch/arm/boot/dts/x-powers-axp209.dtsi
@@ -0,0 +1,60 @@
+/*
+ * x-powers,axp209 common code to be include from inside the axp209 node
+ *
+ * Copyright 2014 - Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
+ *
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+ compatible = "x-powers,axp209";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ regulators {
+ dcdc-freq = "1500";
+
+ axp_dcdc2: dcdc2 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <2275000>;
+ dcdc-workmode = <0>;
+ regulator-always-on;
+ };
+
+ axp_dcdc3: dcdc3 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <3500000>;
+ dcdc-workmode = <0>;
+ regulator-always-on;
+ };
+
+ axp_ldo1: ldo1 {
+ regulator-min-microvolt = <1300000>;
+ regulator-max-microvolt = <1300000>;
+ };
+
+ axp_ldo2: ldo2 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ regulator-always-on;
+ };
+
+ axp_ldo3: ldo3 {
+ regulator-min-microvolt = <700000>;
+ regulator-max-microvolt = <3500000>;
+ };
+
+ axp_ldo4: ldo4 {
+ regulator-min-microvolt = <1250000>;
+ regulator-max-microvolt = <3300000>;
+ };
+
+ axp_ldo5: ldo5 {
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
--
1.8.3.2
^ permalink raw reply related
* [PATCH v2 7/8] ARM: sun7i: dt: Add AXP209 support to various boards
From: Carlo Caione @ 2014-03-15 15:43 UTC (permalink / raw)
To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
hdegoede-H+wXaHxf7aLQT0dZR+AlfA, emilio-0Z03zUJReD5OxF6Tv1QG9Q,
wens-jdAy2FN1RRM, sameo-VuQAYsv1563Yd54FQh9/CA,
lee.jones-QSEj5FYQhm4dnm+yROfE0A,
devicetree-u79uwXL29TY76Z2rM5mHXA,
dmitry.torokhov-Re5JQEeQqe8AvxtiuMwx3w,
linux-input-u79uwXL29TY76Z2rM5mHXA,
linux-doc-u79uwXL29TY76Z2rM5mHXA,
lgirdwood-Re5JQEeQqe8AvxtiuMwx3w, broonie-DgEjT+Ai2ygdnm+yROfE0A
Cc: Carlo Caione
In-Reply-To: <1394898225-28452-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Signed-off-by: Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 12 ++++++++++++
arch/arm/boot/dts/sun7i-a20-cubietruck.dts | 13 +++++++++++++
arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts | 12 ++++++++++++
3 files changed, 37 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 5c51cb8..9ad7da5 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -53,6 +53,18 @@
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ axp: axp20x@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ axp,system-power-controller;
+
+ /include/ "x-powers-axp209.dtsi"
+ };
};
i2c1: i2c@01c2b000 {
diff --git a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
index f9dcb61..aab77f9 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubietruck.dts
@@ -38,6 +38,19 @@
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ axp: axp20x@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ axp,system-power-controller;
+
+ /include/ "x-powers-axp209.dtsi"
+ };
+
};
i2c1: i2c@01c2b000 {
diff --git a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
index ead3013..a5fe5d0 100644
--- a/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
+++ b/arch/arm/boot/dts/sun7i-a20-olinuxino-micro.dts
@@ -65,6 +65,18 @@
pinctrl-names = "default";
pinctrl-0 = <&i2c0_pins_a>;
status = "okay";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ axp: axp20x@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ axp,system-power-controller;
+
+ /include/ "x-powers-axp209.dtsi"
+ };
};
i2c1: i2c@01c2b000 {
--
1.8.3.2
^ 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