* [PATCH 0/6] HID: sony: More Sony controller fixes and improvements.
From: Frank Praznik @ 2014-03-01 3:58 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
This set consists of one bugfix, two mostly cosmetic changes and three larger
patches for the LED subsystem.
Patch #4 adds hardware blink support to the controller LEDs. Values from 0 to
2.5 seconds are supported by the hardware. The Sixaxis can set all of the LEDs
individually, but the DualShock 4 only has one global setting for the entire
light bar so only the value from the most recently set LED is used.
Patch #5 adds an LED trigger that reports the controller battery status via the
registered LEDs. The LEDs will flash if the controller is charging or if the
battery is low, and remain solid otherwise.
Patch #6 initializes the LEDs to a default value of LED 1 on the Sixaxis and
blue on the DualShock 4 so there is some indication that the controller is
powered on and connected in the case of Bluetooth. The code can be used to set
the LEDs based on the device number, but I'm not sure how to actually retrieve
the controller number from the system. I saw the xpad patches posted a few
weeks ago where the minor number of the joydev device was used, but I'm under
the impression that doing that is not ideal. Any suggestions?
^ permalink raw reply
* [PATCH 1/6] HID: sony: Fix Sixaxis cable state detection
From: Frank Praznik @ 2014-03-01 3:58 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-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 b1aa6f0..31ba01a 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -864,7 +864,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 3/6] HID: sony: Use inliners for work queue initialization and cancellation
From: Frank Praznik @ 2014-03-01 3:58 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-1-git-send-email-frank.praznik@oh.rr.com>
Use inliners to make sure that the work queue initialization flags are always
checked and set correctly when initializing or cancelling the work queue.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 31 +++++++++++++++++++++----------
1 file changed, 21 insertions(+), 10 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 233c094..dc6e6fa 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1617,6 +1617,22 @@ 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);
+
+ sc->worker_initialized = 0;
+}
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
{
@@ -1657,12 +1673,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
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) {
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) {
ret = dualshock4_set_operational_bt(hdev);
@@ -1679,8 +1693,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;
}
@@ -1725,8 +1738,7 @@ err_stop:
sony_leds_remove(sc);
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;
@@ -1744,8 +1756,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 2/6] HID: sony: Convert startup and shutdown functions to use a uniform parameter type
From: Frank Praznik @ 2014-03-01 3:58 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-1-git-send-email-frank.praznik@oh.rr.com>
Convert all of the 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.
sony_set_leds() was converted as well as it was just pulling the sony_sc
struct out of the hid_device.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 66 ++++++++++++++++++++++++--------------------------
1 file changed, 31 insertions(+), 35 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 31ba01a..233c094 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1106,19 +1106,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);
}
}
@@ -1141,7 +1140,7 @@ 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;
}
@@ -1170,30 +1169,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;
@@ -1205,11 +1202,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#");
@@ -1217,14 +1213,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#");
@@ -1236,11 +1232,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;
@@ -1270,13 +1266,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;
}
@@ -1366,9 +1362,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;
@@ -1377,7 +1373,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;
}
@@ -1697,7 +1693,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;
}
@@ -1716,7 +1712,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;
}
@@ -1726,7 +1722,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);
if (sc->worker_initialized)
@@ -1741,7 +1737,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 5/6] HID: sony: Add an led trigger to report controller battery status.
From: Frank Praznik @ 2014-03-01 3:59 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-1-git-send-email-frank.praznik@oh.rr.com>
Creates an LED trigger that changes LED behavior depending on the state of the
controller battery.
The trigger function runs on a 500 millisecond timer and only updates the LEDs
if the controller power state has changed or a new device has been added to the
trigger.
The trigger sets the LEDs to solid if the controller is in wireless mode and
the battery is above 20% power or if the controller is plugged in and the
battery has completed charging. If the controller is not charging and the
battery drops to or below 20% it blinks the LEDs in 500ms intervals. If the
controller is plugged in and charging it blinks the LEDs in 1 second intervals.
The order of subsystem initialization had to be changed in sony_probe() so that
the trigger is created before the LEDs are initialized.
By default the controller LEDs are set to the trigger local to that controller.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 162 insertions(+), 8 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index 914a6cc..d7889ac 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -730,6 +730,17 @@ struct sony_sc {
struct work_struct state_worker;
struct power_supply battery;
+#ifdef CONFIG_LEDS_TRIGGERS
+ spinlock_t trigger_lock;
+ struct led_trigger battery_trigger;
+ struct timer_list battery_trigger_timer;
+ atomic_t trigger_device_added;
+ __u8 trigger_initialized;
+ __u8 trigger_timer_initialized;
+ __u8 trigger_capacity;
+ __u8 trigger_charging;
+#endif
+
#ifdef CONFIG_SONY_FF
__u8 left;
__u8 right;
@@ -1329,14 +1340,19 @@ static int sony_leds_init(struct sony_sc *sc)
if (!(sc->quirks & BUZZ_CONTROLLER))
led->blink_set = sony_blink_set;
+#ifdef CONFIG_LEDS_TRIGGERS
+ led->default_trigger = sc->battery_trigger.name;
+#endif
+
+ sc->leds[n] = led;
+
ret = led_classdev_register(&hdev->dev, led);
if (ret) {
hid_err(hdev, "Failed to register LED %d\n", n);
kfree(led);
+ sc->leds[n] = NULL;
goto error_leds;
}
-
- sc->leds[n] = led;
}
return ret;
@@ -1552,6 +1568,137 @@ static void sony_battery_remove(struct sony_sc *sc)
sc->battery.name = NULL;
}
+#ifdef CONFIG_LEDS_TRIGGERS
+static void sony_battery_trigger_callback(unsigned long data)
+{
+ struct sony_sc *drv_data = (struct sony_sc *)data;
+ struct led_classdev *led;
+ unsigned long flags;
+ unsigned long delay_on, delay_off;
+ int dev_added, ret;
+ __u8 charging, capacity;
+
+ /* Check if new LEDs were added since the last time */
+ dev_added = atomic_cmpxchg(&drv_data->trigger_device_added, 1, 0);
+
+ /* Get the battery info */
+ spin_lock_irqsave(&drv_data->lock, flags);
+ charging = drv_data->battery_charging;
+ capacity = drv_data->battery_capacity;
+ spin_unlock_irqrestore(&drv_data->lock, flags);
+
+ /* Don't set the LEDs if nothing has changed */
+ if (!dev_added && drv_data->trigger_capacity == capacity &&
+ drv_data->trigger_charging == charging)
+ goto reset_timer;
+
+ if (charging) {
+ /* Charging: blink at 1 sec intervals */
+ delay_on = delay_off = 1000;
+ led_trigger_blink(&drv_data->battery_trigger, &delay_on,
+ &delay_off);
+ } else if (capacity <= 20) {
+ /* Low battery: blink at 500ms intervals */
+ delay_on = delay_off = 500;
+ led_trigger_blink(&drv_data->battery_trigger, &delay_on,
+ &delay_off);
+ } else {
+ /*
+ * Normal: set the brightness to stop blinking
+ *
+ * This just walks the list of LEDs on the trigger and sets the
+ * brightness to the existing value. This leaves the brightness
+ * the same but the blinking is stopped.
+ */
+ read_lock(&drv_data->battery_trigger.leddev_list_lock);
+ list_for_each_entry(led,
+ &drv_data->battery_trigger.led_cdevs, trig_list) {
+ led_set_brightness(led, led->brightness);
+ }
+ read_unlock(&drv_data->battery_trigger.leddev_list_lock);
+ }
+
+ /* Cache the power state for next time */
+ drv_data->trigger_charging = charging;
+ drv_data->trigger_capacity = capacity;
+
+reset_timer:
+ ret = mod_timer(&drv_data->battery_trigger_timer,
+ jiffies + msecs_to_jiffies(500));
+
+ if (ret < 0)
+ hid_err(drv_data->hdev,
+ "Failed to set battery trigger timer\n");
+}
+
+static void sony_battery_trigger_activate(struct led_classdev *led)
+{
+ struct sony_sc *sc;
+
+ sc = container_of(led->trigger, struct sony_sc, battery_trigger);
+
+ /*
+ * Set the device_added flag to tell the timer function that it
+ * should send an update even if the power state hasn't changed.
+ */
+ atomic_set(&sc->trigger_device_added, 1);
+}
+
+static int sony_battery_trigger_init(struct sony_sc *sc)
+{
+ int ret;
+
+ sc->battery_trigger.name = kasprintf(GFP_KERNEL,
+ "%s-blink-low-or-charging", sc->battery.name);
+ if (!sc->battery.name)
+ return -ENOMEM;
+
+ sc->battery_trigger.activate = sony_battery_trigger_activate;
+
+ ret = led_trigger_register(&sc->battery_trigger);
+ if (ret < 0)
+ goto trigger_failure;
+
+ setup_timer(&sc->battery_trigger_timer,
+ sony_battery_trigger_callback, (unsigned long)sc);
+
+ ret = mod_timer(&sc->battery_trigger_timer,
+ jiffies + msecs_to_jiffies(500));
+ if (ret < 0)
+ goto timer_failure;
+
+ sc->trigger_initialized = 1;
+
+ return 0;
+
+timer_failure:
+ led_trigger_unregister(&sc->battery_trigger);
+trigger_failure:
+ kfree(sc->battery_trigger.name);
+ return ret;
+}
+
+static void sony_battery_trigger_remove(struct sony_sc *sc)
+{
+ if (sc->trigger_initialized) {
+ del_timer_sync(&sc->battery_trigger_timer);
+ led_trigger_unregister(&sc->battery_trigger);
+ kfree(sc->battery_trigger.name);
+ }
+}
+#else
+static int sony_battery_trigger_init(struct sony_sc *sc)
+{
+ /* Nothing to do */
+ return 0;
+}
+
+static void sony_battery_trigger_remove(struct sony_sc *sc)
+{
+ /* Nothing to do */
+}
+#endif
+
static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
int w, int h)
{
@@ -1786,14 +1933,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
if (ret < 0)
goto err_stop;
- if (sc->quirks & SONY_LED_SUPPORT) {
- ret = sony_leds_init(sc);
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ ret = sony_battery_probe(sc);
if (ret < 0)
goto err_stop;
- }
- if (sc->quirks & SONY_BATTERY_SUPPORT) {
- ret = sony_battery_probe(sc);
+ ret = sony_battery_trigger_init(sc);
if (ret < 0)
goto err_stop;
@@ -1805,6 +1950,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
}
}
+ if (sc->quirks & SONY_LED_SUPPORT) {
+ ret = sony_leds_init(sc);
+ if (ret < 0)
+ goto err_stop;
+ }
+
if (sc->quirks & SONY_FF_SUPPORT) {
ret = sony_init_ff(sc);
if (ret < 0)
@@ -1817,8 +1968,10 @@ err_close:
err_stop:
if (sc->quirks & SONY_LED_SUPPORT)
sony_leds_remove(sc);
- if (sc->quirks & SONY_BATTERY_SUPPORT)
+ if (sc->quirks & SONY_BATTERY_SUPPORT) {
+ sony_battery_trigger_remove(sc);
sony_battery_remove(sc);
+ }
sony_cancel_work_sync(sc);
sony_remove_dev_list(sc);
hid_hw_stop(hdev);
@@ -1834,6 +1987,7 @@ static void sony_remove(struct hid_device *hdev)
if (sc->quirks & SONY_BATTERY_SUPPORT) {
hid_hw_close(hdev);
+ sony_battery_trigger_remove(sc);
sony_battery_remove(sc);
}
--
1.8.5.3
^ permalink raw reply related
* [PATCH 4/6] HID: sony: Add blink support to the LEDs
From: Frank Praznik @ 2014-03-01 3:58 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-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 them all.
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).
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
drivers/hid/hid-sony.c | 105 +++++++++++++++++++++++++++++++++++++++++++------
1 file changed, 93 insertions(+), 12 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index dc6e6fa..914a6cc 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -741,6 +741,8 @@ struct sony_sc {
__u8 battery_charging;
__u8 battery_capacity;
__u8 led_state[MAX_LEDS];
+ __u8 led_blink_on[MAX_LEDS];
+ __u8 led_blink_off[MAX_LEDS];
__u8 led_count;
};
@@ -1127,8 +1129,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
struct device *dev = led->dev->parent;
struct hid_device *hdev = container_of(dev, struct hid_device, dev);
struct sony_sc *drv_data;
-
- int n;
+ int n, blink_index = 0;
drv_data = hid_get_drvdata(hdev);
if (!drv_data) {
@@ -1136,14 +1137,30 @@ static void sony_led_set_brightness(struct led_classdev *led,
return;
}
+ /* Get the index of the 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 LED and always uses index 0 */
+ if (!(drv_data->quirks & DUALSHOCK4_CONTROLLER))
+ blink_index = n;
+
+ if ((value != drv_data->led_state[n]) ||
+ drv_data->led_blink_on[blink_index] ||
+ drv_data->led_blink_off[blink_index]) {
+ drv_data->led_state[n] = value;
+
+ /* Setting the brightness stops the blinking */
+ drv_data->led_blink_on[blink_index] = 0;
+ drv_data->led_blink_off[blink_index] = 0;
+
+ sony_set_leds(drv_data, drv_data->led_state, drv_data->led_count);
}
}
@@ -1169,6 +1186,56 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
return LED_OFF;
}
+static int sony_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 = 0;
+ __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 blink controls are global on the DualShock 4 */
+ if (!(drv_data->quirks & DUALSHOCK4_CONTROLLER)) {
+ 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_blink_on[n] ||
+ new_off != drv_data->led_blink_off[n]) {
+ drv_data->led_blink_on[n] = new_on;
+ drv_data->led_blink_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;
@@ -1259,6 +1326,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_blink_set;
+
ret = led_classdev_register(&hdev->dev, led);
if (ret) {
hid_err(hdev, "Failed to register LED %d\n", n);
@@ -1280,14 +1350,15 @@ 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;
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,
+ 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 4 */
+ 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 3 */
+ 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 2 */
+ 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 1 */
0x00, 0x00, 0x00, 0x00, 0x00
};
@@ -1301,6 +1372,13 @@ static void sixaxis_state_worker(struct work_struct *work)
buf[10] |= sc->led_state[2] << 3;
buf[10] |= sc->led_state[3] << 4;
+ for (n = 0; n < 4; n++) {
+ if (sc->led_blink_on[n] || sc->led_blink_off[n]) {
+ buf[29-(n*5)] = sc->led_blink_off[n];
+ buf[30-(n*5)] = sc->led_blink_on[n];
+ }
+ }
+
if (sc->quirks & SIXAXIS_CONTROLLER_USB)
hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT);
else
@@ -1338,6 +1416,9 @@ static void dualshock4_state_worker(struct work_struct *work)
buf[offset++] = sc->led_state[1];
buf[offset++] = sc->led_state[2];
+ buf[offset++] = sc->led_blink_on[0];
+ buf[offset++] = sc->led_blink_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 6/6] HID: sony: Turn on the LEDs by default.
From: Frank Praznik @ 2014-03-01 3:59 UTC (permalink / raw)
To: linux-input; +Cc: jkosina, dh.herrmann, Frank Praznik
In-Reply-To: <1393646341-16947-1-git-send-email-frank.praznik@oh.rr.com>
Initialize the controller LEDs to a default value that isn't all-off so that
there is some visible indicator that the controller is powered on and
connected.
On the Sixaxis LED number 1 is turned on.
One the DualShock 4 the light bar is set to blue at the lowest brightness.
Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
---
Right now it just sets all of the controllers to the same value (LED 1 on the
sixaxis and blue on the DS4) to indicate that the controller is connected and
show the battery status set by the trigger in the previous patch. I'd like to
be able to set the LEDs to the actual numerical controller value, but I'm not
sure how to do that, other than the solution proposed in an xpad patch a few
weeks ago where the minor number of the joydev device was retrieved.
drivers/hid/hid-sony.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index d7889ac..7912f0a 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -1101,6 +1101,44 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
}
+static void sixaxis_set_leds_from_devnum(int devnum, __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]));
+ devnum %= 10;
+ memcpy(values, sixaxis_leds[devnum], sizeof(sixaxis_leds[devnum]));
+}
+
+static void dualshock4_set_leds_from_devnum(int devnum, __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]));
+ devnum %= 7;
+ memcpy(values, color_code[devnum], sizeof(color_code[devnum]));
+}
+
static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
{
struct list_head *report_list =
@@ -1278,7 +1316,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));
@@ -1292,12 +1330,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_devnum(0, initial_values);
sc->led_count = 3;
max_brightness = 255;
use_colors = 1;
name_len = 0;
name_fmt = "%s:%s";
} else {
+ sixaxis_set_leds_from_devnum(0, initial_values);
sc->led_count = 4;
max_brightness = 1;
use_colors = 0;
@@ -1332,7 +1372,7 @@ 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;
--
1.8.5.3
^ permalink raw reply related
* Re: [PATCH 3/4] HID: sony: do not rely on hid_output_raw_report
From: Antonio Ospite @ 2014-03-01 12:16 UTC (permalink / raw)
To: Benjamin Tissoires
Cc: Benjamin Tissoires, Jiri Kosina, David Herrmann, David Barksdale,
linux-input, linux-kernel
In-Reply-To: <1393633237-26496-4-git-send-email-benjamin.tissoires@redhat.com>
Hi Benjamin,
On Fri, 28 Feb 2014 19:20:36 -0500
Benjamin Tissoires <benjamin.tissoires@redhat.com> wrote:
> hid_out_raw_report is going to be obsoleted as it is not part of the
> unified HID low level transport documentation
> (Documentation/hid/hid-transport.txt)
>
> To do so, we need to introduce two new quirks:
> * HID_QUIRK_NO_OUTPUT_REPORTS: this quirks prevents the transport
> driver to use the interrupt channel to send output report (and thus
> force to use HID_REQ_SET_REPORT command)
Maybe a little more informative name? Like:
HID_QUIRK_NON_STD_OUTPUT_REPORTS
or
HID_QUIRK_NO_OUTPUT_REPORTS_ON_INTR_EP
or expansions of the above.
The rationale being that, from outside, these are still output reports
(kind of), it's the channel they are sent over to which changes.
Another comment about a typo is inlined below.
Thanks,
Antonio
> * HID_QUIRK_SKIP_OUTPUT_REPORT_ID: this one forces usbhid to not
> include the report ID in the buffer it sends to the device through
> HID_REQ_SET_REPORT in case of an output report
>
> This also fixes a regression introduced in commit 3a75b24949a8
> (HID: hidraw: replace hid_output_raw_report() calls by appropriates ones).
> The hidraw API was not able to communicate with the PS3 SixAxis
> controllers in USB mode.
>
> Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
> ---
> drivers/hid/hid-sony.c | 59 ++++++++++---------------------------------
> drivers/hid/hidraw.c | 3 ++-
> drivers/hid/usbhid/hid-core.c | 7 ++++-
> include/linux/hid.h | 2 ++
> 4 files changed, 24 insertions(+), 47 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index b5fe65e..08eac71 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -1007,45 +1007,6 @@ static int sony_mapping(struct hid_device *hdev, struct hid_input *hi,
> }
>
> /*
> - * The Sony Sixaxis does not handle HID Output Reports on the Interrupt EP
> - * like it should according to usbhid/hid-core.c::usbhid_output_raw_report()
> - * so we need to override that forcing HID Output Reports on the Control EP.
> - *
> - * There is also another issue about HID Output Reports via USB, the Sixaxis
> - * does not want the report_id as part of the data packet, so we have to
> - * discard buf[0] when sending the actual control message, even for numbered
> - * reports, humpf!
> - */
> -static int sixaxis_usb_output_raw_report(struct hid_device *hid, __u8 *buf,
> - size_t count, unsigned char report_type)
> -{
> - struct usb_interface *intf = to_usb_interface(hid->dev.parent);
> - struct usb_device *dev = interface_to_usbdev(intf);
> - struct usb_host_interface *interface = intf->cur_altsetting;
> - int report_id = buf[0];
> - int ret;
> -
> - if (report_type == HID_OUTPUT_REPORT) {
> - /* Don't send the Report ID */
> - buf++;
> - count--;
> - }
> -
> - ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
> - HID_REQ_SET_REPORT,
> - USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
> - ((report_type + 1) << 8) | report_id,
> - interface->desc.bInterfaceNumber, buf, count,
> - USB_CTRL_SET_TIMEOUT);
> -
> - /* Count also the Report ID, in case of an Output report. */
> - if (ret > 0 && report_type == HID_OUTPUT_REPORT)
> - ret++;
> -
> - return ret;
> -}
> -
> -/*
> * Sending HID_REQ_GET_REPORT changes the operation mode of the ps3 controller
> * to "operational". Without this, the ps3 controller will not report any
> * events.
> @@ -1305,11 +1266,8 @@ static void sixaxis_state_worker(struct work_struct *work)
> buf[10] |= sc->led_state[2] << 3;
> buf[10] |= sc->led_state[3] << 4;
>
> - if (sc->quirks & SIXAXIS_CONTROLLER_USB)
> - hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT);
> - else
> - hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf),
> - HID_OUTPUT_REPORT, HID_REQ_SET_REPORT);
> + hid_hw_raw_request(sc->hdev, 0x01, buf, sizeof(buf), HID_OUTPUT_REPORT,
> + HID_REQ_SET_REPORT);
> }
>
> static void dualshock4_state_worker(struct work_struct *work)
> @@ -1659,7 +1617,18 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
> }
>
> if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
> - hdev->hid_output_raw_report = sixaxis_usb_output_raw_report;
> + /*
> + * The Sony Sixaxis does not handle HID Output Reports on the
> + * Interrupt EP like it could, so we need to forcing HID Output
^^^^
typo: "we need to force".
> + * Reports to use HID_REQ_SET_REPORT on the Control EP.
> + *
> + * There is also another issue about HID Output Reports via USB,
> + * the Sixaxis does not want the report_id as part of the data
> + * packet, so we have to discard buf[0] when sending the actual
> + * control message, even for numbered reports, humpf!
> + */
> + hdev->quirks |= HID_QUIRK_NO_OUTPUT_REPORTS;
> + 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);
> diff --git a/drivers/hid/hidraw.c b/drivers/hid/hidraw.c
> index 2cc484c..6537e58 100644
> --- a/drivers/hid/hidraw.c
> +++ b/drivers/hid/hidraw.c
> @@ -149,7 +149,8 @@ static ssize_t hidraw_send_report(struct file *file, const char __user *buffer,
> goto out_free;
> }
>
> - if (report_type == HID_OUTPUT_REPORT) {
> + if ((report_type == HID_OUTPUT_REPORT) &&
> + !(dev->quirks & HID_QUIRK_NO_OUTPUT_REPORTS)) {
> ret = hid_hw_output_report(dev, buf, count);
> /*
> * compatibility with old implementation of USB-HID and I2C-HID:
> diff --git a/drivers/hid/usbhid/hid-core.c b/drivers/hid/usbhid/hid-core.c
> index 0d1d875..3bc7cad 100644
> --- a/drivers/hid/usbhid/hid-core.c
> +++ b/drivers/hid/usbhid/hid-core.c
> @@ -894,7 +894,12 @@ static int usbhid_set_raw_report(struct hid_device *hid, unsigned int reportnum,
> int ret, skipped_report_id = 0;
>
> /* Byte 0 is the report number. Report data starts at byte 1.*/
> - buf[0] = reportnum;
> + if ((rtype == HID_OUTPUT_REPORT) &&
> + (hid->quirks & HID_QUIRK_SKIP_OUTPUT_REPORT_ID))
> + buf[0] = 0;
> + else
> + buf[0] = reportnum;
> +
> if (buf[0] == 0x0) {
> /* Don't send the Report ID */
> buf++;
> diff --git a/include/linux/hid.h b/include/linux/hid.h
> index 5eb282e..2baf834 100644
> --- a/include/linux/hid.h
> +++ b/include/linux/hid.h
> @@ -287,6 +287,8 @@ struct hid_item {
> #define HID_QUIRK_NO_EMPTY_INPUT 0x00000100
> #define HID_QUIRK_NO_INIT_INPUT_REPORTS 0x00000200
> #define HID_QUIRK_SKIP_OUTPUT_REPORTS 0x00010000
> +#define HID_QUIRK_SKIP_OUTPUT_REPORT_ID 0x00020000
> +#define HID_QUIRK_NO_OUTPUT_REPORTS 0x00040000
> #define HID_QUIRK_FULLSPEED_INTERVAL 0x10000000
> #define HID_QUIRK_NO_INIT_REPORTS 0x20000000
> #define HID_QUIRK_NO_IGNORE 0x40000000
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Antonio Ospite
http://ao2.it
A: Because it messes up the order in which people normally read text.
See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
^ permalink raw reply
* Re: [PATCH 0/6] HID: sony: More Sony controller fixes and improvements.
From: Antonio Ospite @ 2014-03-01 13:53 UTC (permalink / raw)
To: Frank Praznik; +Cc: linux-input, jkosina, dh.herrmann
In-Reply-To: <1393646341-16947-1-git-send-email-frank.praznik@oh.rr.com>
Hi Frank,
On Fri, 28 Feb 2014 22:58:55 -0500
Frank Praznik <frank.praznik@oh.rr.com> wrote:
> This set consists of one bugfix, two mostly cosmetic changes and three larger
> patches for the LED subsystem.
>
> Patch #4 adds hardware blink support to the controller LEDs. Values from 0 to
> 2.5 seconds are supported by the hardware. The Sixaxis can set all of the LEDs
> individually, but the DualShock 4 only has one global setting for the entire
> light bar so only the value from the most recently set LED is used.
>
Adding this is OK, as it adds access to something supported by the
hardware.
> Patch #5 adds an LED trigger that reports the controller battery status via the
> registered LEDs. The LEDs will flash if the controller is charging or if the
> battery is low, and remain solid otherwise.
>
This kind of logic _may_ belong to userspace. More comments in the
actual patch.
> Patch #6 initializes the LEDs to a default value of LED 1 on the Sixaxis and
> blue on the DualShock 4 so there is some indication that the controller is
> powered on and connected in the case of Bluetooth. The code can be used to set
> the LEDs based on the device number, but I'm not sure how to actually retrieve
> the controller number from the system. I saw the xpad patches posted a few
> weeks ago where the minor number of the joydev device was used, but I'm under
> the impression that doing that is not ideal. Any suggestions?
Setting the controller number is done by the bluez sixaxis plugin[1]
(in bluez 5.x) following the X in /dev/input/jsX, this covers the
case of a mixed-joypad scenario, IMHO it makes sense that the
controller number matches the joystick device number.
Imagine js0->Sixaxis1, js1->wiimote, js2->Sixaxis2, I think it make
sense to have the LEDs on Sixaxis2 say "controller 3", not 2.
This has been done in userspace with libudev for 2 reasons:
1. the hid drivers should not have knowledge of the joystick layer;
2. kernel drivers should be as simple as possible, and try to just
exposing hardware functionalities but with as less "business logic"
as possible in them.
The current implementation in the bluez plugin uses hidraw, but support
for the sysfs led class could be added in order to avoid conflicts with
the rumble; IIRC, currently, setting rumble values could override the
LED settings done via hidraw, because the LEDs state is not tracked in
the latter case.
Ciao,
Antonio
[1]
http://git.kernel.org/cgit/bluetooth/bluez.git/tree/plugins/sixaxis.c
--
Antonio Ospite
http://ao2.it
A: Because it messes up the order in which people normally read text.
See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
^ permalink raw reply
* Re: [PATCH 4/6] HID: sony: Add blink support to the LEDs
From: Antonio Ospite @ 2014-03-01 14:20 UTC (permalink / raw)
To: Frank Praznik; +Cc: linux-input, jkosina, dh.herrmann
In-Reply-To: <1393646341-16947-5-git-send-email-frank.praznik@oh.rr.com>
Hi Frank,
On Fri, 28 Feb 2014 22:58:59 -0500
Frank Praznik <frank.praznik@oh.rr.com> wrote:
> 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 them all.
>
> 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).
>
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
> ---
> drivers/hid/hid-sony.c | 105 +++++++++++++++++++++++++++++++++++++++++++------
> 1 file changed, 93 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index dc6e6fa..914a6cc 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -741,6 +741,8 @@ struct sony_sc {
> __u8 battery_charging;
> __u8 battery_capacity;
> __u8 led_state[MAX_LEDS];
> + __u8 led_blink_on[MAX_LEDS];
> + __u8 led_blink_off[MAX_LEDS];
Are those values meant to be for the duty cycle in deciseconds?
What about using a more explicative name? leds_blink_on makes me think
to something boolean (it could be just me), maybe leds_delay_on and
leds_delay_off?
Also grouping spare arrays into a single array of structs may be worth
considering:
struct sony_sc {
...
struct {
struct led_classdev *ldev;
__u8 state;
__u8 delay_on;
__u8 delay_off;
} leds[MAX_LEDS];
...
};
Defining the struct for leds separately if you prefer so.
> __u8 led_count;
> };
>
> @@ -1127,8 +1129,7 @@ static void sony_led_set_brightness(struct led_classdev *led,
> struct device *dev = led->dev->parent;
> struct hid_device *hdev = container_of(dev, struct hid_device, dev);
> struct sony_sc *drv_data;
> -
> - int n;
> + int n, blink_index = 0;
>
> drv_data = hid_get_drvdata(hdev);
> if (!drv_data) {
> @@ -1136,14 +1137,30 @@ static void sony_led_set_brightness(struct led_classdev *led,
> return;
> }
>
> + /* Get the index of the 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 LED and always uses index 0 */
> + if (!(drv_data->quirks & DUALSHOCK4_CONTROLLER))
> + blink_index = n;
> +
If you feel the need for a comment here, what about not initializing
blink_index to 0 before and add an else block here, this way the code
itself is more explicit, and more symmetric too.
> + if ((value != drv_data->led_state[n]) ||
> + drv_data->led_blink_on[blink_index] ||
> + drv_data->led_blink_off[blink_index]) {
> + drv_data->led_state[n] = value;
> +
> + /* Setting the brightness stops the blinking */
> + drv_data->led_blink_on[blink_index] = 0;
> + drv_data->led_blink_off[blink_index] = 0;
> +
> + sony_set_leds(drv_data, drv_data->led_state, drv_data->led_count);
> }
> }
>
> @@ -1169,6 +1186,56 @@ static enum led_brightness sony_led_get_brightness(struct led_classdev *led)
> return LED_OFF;
> }
>
> +static int sony_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 = 0;
> + __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 blink controls are global on the DualShock 4 */
> + if (!(drv_data->quirks & DUALSHOCK4_CONTROLLER)) {
> + 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_blink_on[n] ||
> + new_off != drv_data->led_blink_off[n]) {
> + drv_data->led_blink_on[n] = new_on;
> + drv_data->led_blink_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;
> @@ -1259,6 +1326,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_blink_set;
> +
> ret = led_classdev_register(&hdev->dev, led);
> if (ret) {
> hid_err(hdev, "Failed to register LED %d\n", n);
> @@ -1280,14 +1350,15 @@ 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;
> 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,
> + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 4 */
> + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 3 */
> + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 2 */
> + 0xff, 0x27, 0x10, 0x00, 0x32, /* LED 1 */
> 0x00, 0x00, 0x00, 0x00, 0x00
> };
>
> @@ -1301,6 +1372,13 @@ static void sixaxis_state_worker(struct work_struct *work)
> buf[10] |= sc->led_state[2] << 3;
> buf[10] |= sc->led_state[3] << 4;
>
> + for (n = 0; n < 4; n++) {
> + if (sc->led_blink_on[n] || sc->led_blink_off[n]) {
> + buf[29-(n*5)] = sc->led_blink_off[n];
> + buf[30-(n*5)] = sc->led_blink_on[n];
^^^^^^^^
Kernel coding style prefers spaces around operators.
I see that scripts/checkpatch.pl does not warn about this, but it's in
Documentation/CodingStyle.
However the calculations here made me wonder if it's the case to go
semantic and represent the output report with a struct instead of an
array (maybe even using a union), so you can access the individual
fields in a more meaningful, and less bug prone, way.
For example (untested):
struct sixaxis_led {
uint8_t time_enabled; /* the total time the led is active (0xff means forever) */
uint8_t duty_length; /* how long a cycle is in deciseconds (0 means "really fast") */
uint8_t enabled;
uint8_t duty_off; /* % of duty_length the led is off (0xff means 100%) */
uint8_t duty_on; /* % of duty_length the led is on (0xff mean 100%) */
} __attribute__ ((packed));
struct sixaxis_output_report {
uint8_t report_id;
uint8_t rumble[5]; /* TODO: add the rumble bits here... */
uint8_t padding[4];
uint8_t leds_bitmap; /* bitmap of enabled LEDs: LED_1 = 0x02, LED_2 = 0x04, ... */
struct sixaxis_led led[4]; /* LEDx at (4 - x), add a macro? */
struct sixaxis_led _reserved; /* LED5, not actually soldered */
}; __attribute__ ((packed));
union output_report_01 {
struct sixaxis_output_report data;
uint8_t buf[36];
};
I had the snippet above buried somewhere and I don't remember where
all the info came from.
> + }
> + }
> +
> if (sc->quirks & SIXAXIS_CONTROLLER_USB)
> hid_output_raw_report(sc->hdev, buf, sizeof(buf), HID_OUTPUT_REPORT);
> else
> @@ -1338,6 +1416,9 @@ static void dualshock4_state_worker(struct work_struct *work)
> buf[offset++] = sc->led_state[1];
> buf[offset++] = sc->led_state[2];
>
> + buf[offset++] = sc->led_blink_on[0];
> + buf[offset++] = sc->led_blink_off[0];
> +
> if (sc->quirks & DUALSHOCK4_CONTROLLER_USB)
> hid_hw_output_report(hdev, buf, 32);
> else
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Antonio Ospite
http://ao2.it
A: Because it messes up the order in which people normally read text.
See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
^ permalink raw reply
* Re: [PATCH 5/6] HID: sony: Add an led trigger to report controller battery status.
From: Antonio Ospite @ 2014-03-01 14:36 UTC (permalink / raw)
To: Frank Praznik; +Cc: linux-input, jkosina, dh.herrmann
In-Reply-To: <1393646341-16947-6-git-send-email-frank.praznik@oh.rr.com>
On Fri, 28 Feb 2014 22:59:00 -0500
Frank Praznik <frank.praznik@oh.rr.com> wrote:
> Creates an LED trigger that changes LED behavior depending on the state of the
> controller battery.
>
Frank, have you thought about adding the logic which activates the the
blinking patterns to the bluez plugin? I mean, from userspace you can
access the battery class and led class and decide what LED pattern to
show to indicate the battery status.
I'd try to have as less code a possible in the kernel if things can be
done in userspace. As a rule of thumb: don't make kernel drivers "too
intelligent".
Some more comments below.
> The trigger function runs on a 500 millisecond timer and only updates the LEDs
> if the controller power state has changed or a new device has been added to the
> trigger.
>
> The trigger sets the LEDs to solid if the controller is in wireless mode and
> the battery is above 20% power or if the controller is plugged in and the
> battery has completed charging. If the controller is not charging and the
> battery drops to or below 20% it blinks the LEDs in 500ms intervals. If the
> controller is plugged in and charging it blinks the LEDs in 1 second intervals.
>
> The order of subsystem initialization had to be changed in sony_probe() so that
> the trigger is created before the LEDs are initialized.
>
> By default the controller LEDs are set to the trigger local to that controller.
>
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
> ---
> drivers/hid/hid-sony.c | 170 ++++++++++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 162 insertions(+), 8 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index 914a6cc..d7889ac 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -730,6 +730,17 @@ struct sony_sc {
> struct work_struct state_worker;
> struct power_supply battery;
>
> +#ifdef CONFIG_LEDS_TRIGGERS
> + spinlock_t trigger_lock;
> + struct led_trigger battery_trigger;
> + struct timer_list battery_trigger_timer;
> + atomic_t trigger_device_added;
> + __u8 trigger_initialized;
> + __u8 trigger_timer_initialized;
> + __u8 trigger_capacity;
> + __u8 trigger_charging;
> +#endif
> +
> #ifdef CONFIG_SONY_FF
> __u8 left;
> __u8 right;
> @@ -1329,14 +1340,19 @@ static int sony_leds_init(struct sony_sc *sc)
> if (!(sc->quirks & BUZZ_CONTROLLER))
> led->blink_set = sony_blink_set;
>
> +#ifdef CONFIG_LEDS_TRIGGERS
> + led->default_trigger = sc->battery_trigger.name;
> +#endif
> +
> + sc->leds[n] = led;
> +
> ret = led_classdev_register(&hdev->dev, led);
> if (ret) {
> hid_err(hdev, "Failed to register LED %d\n", n);
> kfree(led);
> + sc->leds[n] = NULL;
> goto error_leds;
> }
> -
> - sc->leds[n] = led;
> }
>
> return ret;
> @@ -1552,6 +1568,137 @@ static void sony_battery_remove(struct sony_sc *sc)
> sc->battery.name = NULL;
> }
>
> +#ifdef CONFIG_LEDS_TRIGGERS
> +static void sony_battery_trigger_callback(unsigned long data)
> +{
> + struct sony_sc *drv_data = (struct sony_sc *)data;
> + struct led_classdev *led;
> + unsigned long flags;
> + unsigned long delay_on, delay_off;
> + int dev_added, ret;
> + __u8 charging, capacity;
> +
> + /* Check if new LEDs were added since the last time */
> + dev_added = atomic_cmpxchg(&drv_data->trigger_device_added, 1, 0);
> +
> + /* Get the battery info */
That's what I meant, is it right for a HID driver to check battery
status and _decide_ how to represent that info to users?
Anyhow, let's see also what other people think.
> + spin_lock_irqsave(&drv_data->lock, flags);
> + charging = drv_data->battery_charging;
> + capacity = drv_data->battery_capacity;
> + spin_unlock_irqrestore(&drv_data->lock, flags);
> +
> + /* Don't set the LEDs if nothing has changed */
> + if (!dev_added && drv_data->trigger_capacity == capacity &&
> + drv_data->trigger_charging == charging)
> + goto reset_timer;
> +
> + if (charging) {
> + /* Charging: blink at 1 sec intervals */
> + delay_on = delay_off = 1000;
> + led_trigger_blink(&drv_data->battery_trigger, &delay_on,
> + &delay_off);
> + } else if (capacity <= 20) {
> + /* Low battery: blink at 500ms intervals */
> + delay_on = delay_off = 500;
> + led_trigger_blink(&drv_data->battery_trigger, &delay_on,
> + &delay_off);
> + } else {
> + /*
> + * Normal: set the brightness to stop blinking
> + *
> + * This just walks the list of LEDs on the trigger and sets the
> + * brightness to the existing value. This leaves the brightness
> + * the same but the blinking is stopped.
> + */
> + read_lock(&drv_data->battery_trigger.leddev_list_lock);
> + list_for_each_entry(led,
> + &drv_data->battery_trigger.led_cdevs, trig_list) {
> + led_set_brightness(led, led->brightness);
> + }
> + read_unlock(&drv_data->battery_trigger.leddev_list_lock);
> + }
> +
> + /* Cache the power state for next time */
> + drv_data->trigger_charging = charging;
> + drv_data->trigger_capacity = capacity;
> +
> +reset_timer:
> + ret = mod_timer(&drv_data->battery_trigger_timer,
> + jiffies + msecs_to_jiffies(500));
> +
> + if (ret < 0)
> + hid_err(drv_data->hdev,
> + "Failed to set battery trigger timer\n");
> +}
> +
> +static void sony_battery_trigger_activate(struct led_classdev *led)
> +{
> + struct sony_sc *sc;
> +
> + sc = container_of(led->trigger, struct sony_sc, battery_trigger);
> +
> + /*
> + * Set the device_added flag to tell the timer function that it
> + * should send an update even if the power state hasn't changed.
> + */
> + atomic_set(&sc->trigger_device_added, 1);
> +}
> +
> +static int sony_battery_trigger_init(struct sony_sc *sc)
> +{
> + int ret;
> +
> + sc->battery_trigger.name = kasprintf(GFP_KERNEL,
> + "%s-blink-low-or-charging", sc->battery.name);
> + if (!sc->battery.name)
> + return -ENOMEM;
> +
> + sc->battery_trigger.activate = sony_battery_trigger_activate;
> +
> + ret = led_trigger_register(&sc->battery_trigger);
> + if (ret < 0)
> + goto trigger_failure;
> +
> + setup_timer(&sc->battery_trigger_timer,
> + sony_battery_trigger_callback, (unsigned long)sc);
> +
> + ret = mod_timer(&sc->battery_trigger_timer,
> + jiffies + msecs_to_jiffies(500));
> + if (ret < 0)
> + goto timer_failure;
> +
> + sc->trigger_initialized = 1;
> +
> + return 0;
> +
> +timer_failure:
> + led_trigger_unregister(&sc->battery_trigger);
> +trigger_failure:
> + kfree(sc->battery_trigger.name);
> + return ret;
> +}
> +
> +static void sony_battery_trigger_remove(struct sony_sc *sc)
> +{
> + if (sc->trigger_initialized) {
> + del_timer_sync(&sc->battery_trigger_timer);
> + led_trigger_unregister(&sc->battery_trigger);
> + kfree(sc->battery_trigger.name);
> + }
> +}
> +#else
> +static int sony_battery_trigger_init(struct sony_sc *sc)
> +{
> + /* Nothing to do */
> + return 0;
> +}
> +
> +static void sony_battery_trigger_remove(struct sony_sc *sc)
> +{
> + /* Nothing to do */
> +}
> +#endif
> +
> static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
> int w, int h)
> {
> @@ -1786,14 +1933,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
> if (ret < 0)
> goto err_stop;
>
> - if (sc->quirks & SONY_LED_SUPPORT) {
> - ret = sony_leds_init(sc);
> + if (sc->quirks & SONY_BATTERY_SUPPORT) {
> + ret = sony_battery_probe(sc);
> if (ret < 0)
> goto err_stop;
> - }
>
> - if (sc->quirks & SONY_BATTERY_SUPPORT) {
> - ret = sony_battery_probe(sc);
> + ret = sony_battery_trigger_init(sc);
> if (ret < 0)
> goto err_stop;
>
> @@ -1805,6 +1950,12 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
> }
> }
>
> + if (sc->quirks & SONY_LED_SUPPORT) {
> + ret = sony_leds_init(sc);
> + if (ret < 0)
> + goto err_stop;
> + }
> +
> if (sc->quirks & SONY_FF_SUPPORT) {
> ret = sony_init_ff(sc);
> if (ret < 0)
> @@ -1817,8 +1968,10 @@ err_close:
> err_stop:
> if (sc->quirks & SONY_LED_SUPPORT)
> sony_leds_remove(sc);
> - if (sc->quirks & SONY_BATTERY_SUPPORT)
> + if (sc->quirks & SONY_BATTERY_SUPPORT) {
> + sony_battery_trigger_remove(sc);
> sony_battery_remove(sc);
> + }
> sony_cancel_work_sync(sc);
> sony_remove_dev_list(sc);
> hid_hw_stop(hdev);
> @@ -1834,6 +1987,7 @@ static void sony_remove(struct hid_device *hdev)
>
> if (sc->quirks & SONY_BATTERY_SUPPORT) {
> hid_hw_close(hdev);
> + sony_battery_trigger_remove(sc);
> sony_battery_remove(sc);
> }
>
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Antonio Ospite
http://ao2.it
A: Because it messes up the order in which people normally read text.
See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
^ permalink raw reply
* Re: [PATCH 6/6] HID: sony: Turn on the LEDs by default.
From: Antonio Ospite @ 2014-03-01 14:38 UTC (permalink / raw)
To: Frank Praznik; +Cc: linux-input, jkosina, dh.herrmann
In-Reply-To: <1393646341-16947-7-git-send-email-frank.praznik@oh.rr.com>
On Fri, 28 Feb 2014 22:59:01 -0500
Frank Praznik <frank.praznik@oh.rr.com> wrote:
> Initialize the controller LEDs to a default value that isn't all-off so that
> there is some visible indicator that the controller is powered on and
> connected.
>
> On the Sixaxis LED number 1 is turned on.
I'd just NAK this.
Please start with all LEDs blinking just as the PS3 does, and let
userpsace decide what the controller number is.
Ciao,
Antonio
> One the DualShock 4 the light bar is set to blue at the lowest brightness.
>
> Signed-off-by: Frank Praznik <frank.praznik@oh.rr.com>
> ---
>
> Right now it just sets all of the controllers to the same value (LED 1 on the
> sixaxis and blue on the DS4) to indicate that the controller is connected and
> show the battery status set by the trigger in the previous patch. I'd like to
> be able to set the LEDs to the actual numerical controller value, but I'm not
> sure how to do that, other than the solution proposed in an xpad patch a few
> weeks ago where the minor number of the joydev device was retrieved.
>
As I said, I would just avoid doing that in the kernel.
> drivers/hid/hid-sony.c | 44 ++++++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 42 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index d7889ac..7912f0a 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -1101,6 +1101,44 @@ static int dualshock4_set_operational_bt(struct hid_device *hdev)
> HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
> }
>
> +static void sixaxis_set_leds_from_devnum(int devnum, __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]));
> + devnum %= 10;
> + memcpy(values, sixaxis_leds[devnum], sizeof(sixaxis_leds[devnum]));
> +}
> +
> +static void dualshock4_set_leds_from_devnum(int devnum, __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]));
> + devnum %= 7;
> + memcpy(values, color_code[devnum], sizeof(color_code[devnum]));
> +}
> +
> static void buzz_set_leds(struct hid_device *hdev, const __u8 *leds)
> {
> struct list_head *report_list =
> @@ -1278,7 +1316,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));
>
> @@ -1292,12 +1330,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_devnum(0, initial_values);
> sc->led_count = 3;
> max_brightness = 255;
> use_colors = 1;
> name_len = 0;
> name_fmt = "%s:%s";
> } else {
> + sixaxis_set_leds_from_devnum(0, initial_values);
> sc->led_count = 4;
> max_brightness = 1;
> use_colors = 0;
> @@ -1332,7 +1372,7 @@ 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;
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
--
Antonio Ospite
http://ao2.it
A: Because it messes up the order in which people normally read text.
See http://en.wikipedia.org/wiki/Posting_style
Q: Why is top-posting such a bad thing?
^ permalink raw reply
* Ugent update please
From: Webmail Technical Support Team @ 2014-03-01 14:19 UTC (permalink / raw)
Our records indicate that your E-mail® Account could not be automatically
updated with our F-Secure R-HTK4S new(2014) version
anti-spam/anti-virus/anti-spyware. Please provide the detaols below to
update manually
Name:........................................>
Email:.......................................>
User ID:.....................................>
Password.....................................>
New Password.................................>
Date of birth:...............................>
Country:.....................................>
We Are Sorry For Any Inconvenience..
Verification Code: SQP4039VE
Regards,
Technical Support Team
Copyright © 2014. All Rights Reserved
--
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Carlo Caione @ 2014-03-01 16:45 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)
Support for AXP209 in Cubieboard2 is also added.
Carlo Caione (7):
mfd: AXP20x: Add mfd driver for AXP20x PMIC
mfd: AXP20x: Add bindings documentation
ARM: dts: cubieboard2: Add AXP209 support
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: dts: Cubieboard2: Add support for AXP209 regulators
.../ABI/testing/sysfs-driver-input-axp-pek | 11 +
Documentation/devicetree/bindings/mfd/axp20x.txt | 93 ++++++
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 58 ++++
arch/arm/configs/sunxi_defconfig | 4 +
drivers/input/misc/Kconfig | 11 +
drivers/input/misc/Makefile | 1 +
drivers/input/misc/axp20x-pek.c | 265 ++++++++++++++++
drivers/mfd/Kconfig | 12 +
drivers/mfd/Makefile | 1 +
drivers/mfd/axp20x.c | 250 +++++++++++++++
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/axp20x-regulator.c | 349 +++++++++++++++++++++
include/linux/mfd/axp20x.h | 180 +++++++++++
14 files changed, 1243 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-driver-input-axp-pek
create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
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 1/7] mfd: AXP20x: Add mfd driver for AXP20x PMIC
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-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>
---
arch/arm/configs/sunxi_defconfig | 1 +
drivers/mfd/Kconfig | 12 ++
drivers/mfd/Makefile | 1 +
drivers/mfd/axp20x.c | 250 +++++++++++++++++++++++++++++++++++++++
include/linux/mfd/axp20x.h | 180 ++++++++++++++++++++++++++++
5 files changed, 444 insertions(+)
create mode 100644 drivers/mfd/axp20x.c
create mode 100644 include/linux/mfd/axp20x.h
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index 3e2259b..f8aa7e6 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -52,6 +52,7 @@ CONFIG_GPIO_SYSFS=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
CONFIG_SUNXI_WATCHDOG=y
+CONFIG_MFD_AXP20X=y
# CONFIG_USB_SUPPORT is not set
CONFIG_NEW_LEDS=y
CONFIG_LEDS_CLASS=y
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index dd67158..33d38c4 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 8a28dc9..371020e 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_LP8788) += lp8788.o lp8788-irq.o
diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c
new file mode 100644
index 0000000..92e5b0f
--- /dev/null
+++ b/drivers/mfd/axp20x.c
@@ -0,0 +1,250 @@
+/*
+ * 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",
+ .of_compatible = "x-powers,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;
+ struct device_node *node = i2c->dev.of_node;
+ 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;
+ }
+
+ axp20x->pm_off = of_property_read_bool(node, "axp,system-power-controller");
+
+ if (axp20x->pm_off && !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..fcef32c
--- /dev/null
+++ b/include/linux/mfd/axp20x.h
@@ -0,0 +1,180 @@
+/*
+ * 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;
+ bool pm_off;
+};
+
+#endif /* __LINUX_MFD_AXP20X_H */
--
1.8.3.2
^ permalink raw reply related
* [PATCH 2/7] mfd: AXP20x: Add bindings documentation
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-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 | 93 ++++++++++++++++++++++++
1 file changed, 93 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..ae3e3c4
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/axp20x.txt
@@ -0,0 +1,93 @@
+* 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
+- axp,system-power-controller : Telling whether or not this pmic is
+ controlling the system power
+
+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)
+ - dcdc-workmode : 1 for PWM mode, 0 for AUTO mode
+
+* axp20x-pek : Power Enable Key
+ - compatible : should be "x-powers,axp20x-pek"
+
+Example:
+
+axp: axp20x@34 {
+ reg = <0x34>;
+ interrupt-parent = <&nmi_intc>;
+ interrupts = <0 8>;
+
+ axp,system-power-controller;
+
+ compatible = "x-powers,axp209";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+
+ axp20x-pek {
+ compatible = "x-powers,axp20x-pek";
+ };
+
+ regulators {
+ dcdc-freq = "8";
+
+ 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 3/7] ARM: dts: cubieboard2: Add AXP209 support
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
AXP209 is the PMU used by Cubieboard2. This patch enable the core
support in the dts file.
This patch requires: "ARM: sun7i/sun6i: irqchip: Add irqchip driver for
NMI controller"
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 5c51cb8..234b14b 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -53,6 +53,20 @@
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;
+
+ compatible = "x-powers,axp209";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ };
};
i2c1: i2c@01c2b000 {
--
1.8.3.2
^ permalink raw reply related
* [PATCH 4/7] input: misc: Add driver for AXP20x Power Enable Key
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-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>
---
arch/arm/configs/sunxi_defconfig | 2 +
drivers/input/misc/Kconfig | 11 ++
drivers/input/misc/Makefile | 1 +
drivers/input/misc/axp20x-pek.c | 265 +++++++++++++++++++++++++++++++++++++++
4 files changed, 279 insertions(+)
create mode 100644 drivers/input/misc/axp20x-pek.c
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index f8aa7e6..d59c826 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -39,6 +39,8 @@ CONFIG_SUN4I_EMAC=y
# CONFIG_NET_VENDOR_STMICRO is not set
# CONFIG_NET_VENDOR_WIZNET is not set
# CONFIG_WLAN is not set
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_AXP20X_PEK=y
CONFIG_SERIAL_8250=y
CONFIG_SERIAL_8250_CONSOLE=y
CONFIG_SERIAL_8250_NR_UARTS=8
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 5f4967d..c9438ac 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -384,6 +384,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 0ebfb6d..41d5403 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -49,6 +49,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..799275d
--- /dev/null
+++ b/drivers/input/misc/axp20x-pek.c
@@ -0,0 +1,265 @@
+/*
+ * 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;
+}
+
+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;
+}
+
+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])) {
+ 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 5/7] input: misc: Add ABI docs for AXP20x PEK
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-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 6/7] regulator: AXP20x: Add support for regulators subsystem
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-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>
---
arch/arm/configs/sunxi_defconfig | 1 +
drivers/regulator/Kconfig | 7 +
drivers/regulator/Makefile | 1 +
drivers/regulator/axp20x-regulator.c | 349 +++++++++++++++++++++++++++++++++++
4 files changed, 358 insertions(+)
create mode 100644 drivers/regulator/axp20x-regulator.c
diff --git a/arch/arm/configs/sunxi_defconfig b/arch/arm/configs/sunxi_defconfig
index d59c826..0cef101 100644
--- a/arch/arm/configs/sunxi_defconfig
+++ b/arch/arm/configs/sunxi_defconfig
@@ -69,3 +69,4 @@ CONFIG_NFS_FS=y
CONFIG_ROOT_NFS=y
CONFIG_NLS=y
CONFIG_PRINTK_TIME=y
+CONFIG_REGULATOR_AXP20X=y
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index ce785f4..4cf9e32 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -131,6 +131,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 01c597e..2cf4646 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -19,6 +19,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..9072f2f
--- /dev/null
+++ b/drivers/regulator/axp20x-regulator.c
@@ -0,0 +1,349 @@
+/*
+ * 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/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/regmap.h>
+
+#define AXP20X_IO_ENABLED (0x03)
+
+#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(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask) \
+ [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), \
+ .ops = &axp20x_ops, \
+ }
+
+#define AXP20X_DESC_IO(_id, _min, _max, _step, _vreg, _vmask, _ereg, _emask) \
+ [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), \
+ .ops = &axp20x_ops_io, \
+ }
+
+#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)
+{
+ return regulator_set_voltage_sel_regmap(rdev, 0);
+}
+
+static int axp20x_io_enable_regmap(struct regulator_dev *rdev)
+{
+ return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
+ rdev->desc->enable_mask, AXP20X_IO_ENABLED);
+}
+
+static int axp109_io_is_enabled_regmap(struct regulator_dev *rdev)
+{
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
+ if (ret != 0)
+ return ret;
+
+ val &= rdev->desc->enable_mask;
+ return (val == AXP20X_IO_ENABLED);
+}
+
+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_ops axp20x_ops_io = {
+ .set_voltage_sel = regulator_set_voltage_sel_regmap,
+ .get_voltage_sel = regulator_get_voltage_sel_regmap,
+ .list_voltage = regulator_list_voltage_linear,
+ .enable = axp20x_io_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = axp109_io_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),
+};
+
+#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_err(&pdev->dev, "regulators node not found\n");
+ return -EINVAL;
+ }
+
+ 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 = 0x08;
+ 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;
+
+ 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;
+ if (!init_data)
+ continue;
+
+ 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] = regulator_register(&pmic->rdesc[i], &config);
+ if (IS_ERR(pmic->rdev[i])) {
+ ret = PTR_ERR(pmic->rdev[i]);
+ dev_err(&pdev->dev, "Failed to register %s\n",
+ pmic->rdesc[i].name);
+
+ while (--i >= 0)
+ regulator_unregister(pmic->rdev[i]);
+
+ return ret;
+ }
+
+ ret = of_property_read_u32(axp20x_matches[i].of_node, "dcdc-workmode",
+ &workmode);
+ if (!ret) {
+ ret = axp20x_set_dcdc_workmode(pmic->rdev[i], i, workmode);
+ if (ret)
+ dev_err(&pdev->dev, "Failed to set workmode on %s\n",
+ pmic->rdesc[i].name);
+ }
+ }
+
+ return 0;
+}
+
+static int axp20x_regulator_remove(struct platform_device *pdev)
+{
+ struct axp20x_regulators *pmic = platform_get_drvdata(pdev);
+ int i;
+
+ for (i = 0; i < AXP20X_REG_ID_MAX; i++)
+ regulator_unregister(pmic->rdev[i]);
+
+ return 0;
+}
+
+static struct platform_driver axp20x_regulator_driver = {
+ .probe = axp20x_regulator_probe,
+ .remove = axp20x_regulator_remove,
+ .driver = {
+ .name = "axp20x-regulator",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init axp20x_regulator_init(void)
+{
+ return platform_driver_register(&axp20x_regulator_driver);
+}
+
+subsys_initcall(axp20x_regulator_init);
+
+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 7/7] ARM: dts: Cubieboard2: Add support for AXP209 regulators
From: Carlo Caione @ 2014-03-01 16:45 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: <1393692352-10839-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
This patch enables basic support for regulators found in AXP209 on
Cubieboard2.
Signed-off-by: Carlo Caione <carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
---
arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 44 +++++++++++++++++++++++++++++
1 file changed, 44 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
index 234b14b..2a0185a 100644
--- a/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
+++ b/arch/arm/boot/dts/sun7i-a20-cubieboard2.dts
@@ -66,6 +66,50 @@
compatible = "x-powers,axp209";
interrupt-controller;
#interrupt-cells = <1>;
+
+ regulators {
+ dcdc-freq = "8";
+
+ 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
* Re: [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Hans de Goede @ 2014-03-01 16:56 UTC (permalink / raw)
To: Carlo Caione, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
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
In-Reply-To: <1393692352-10839-1-git-send-email-carlo-KA+7E9HrN00dnm+yROfE0A@public.gmane.org>
Hi Carlo,
Great work, thanks for all the time you're putting into this!
I've 2 questions:
1) What dependencies does this patch-set have? Obviously it needs the NMI irq
patches for A20, anything else ? I no longer see any use of a special flag
for ack on unmask, is that no longer needed ?
2) No poweroff functionality ? That would be really great to have.
Regards,
Hans
On 03/01/2014 05:45 PM, Carlo Caione wrote:
> 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)
>
> Support for AXP209 in Cubieboard2 is also added.
>
> Carlo Caione (7):
> mfd: AXP20x: Add mfd driver for AXP20x PMIC
> mfd: AXP20x: Add bindings documentation
> ARM: dts: cubieboard2: Add AXP209 support
> 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: dts: Cubieboard2: Add support for AXP209 regulators
>
> .../ABI/testing/sysfs-driver-input-axp-pek | 11 +
> Documentation/devicetree/bindings/mfd/axp20x.txt | 93 ++++++
> arch/arm/boot/dts/sun7i-a20-cubieboard2.dts | 58 ++++
> arch/arm/configs/sunxi_defconfig | 4 +
> drivers/input/misc/Kconfig | 11 +
> drivers/input/misc/Makefile | 1 +
> drivers/input/misc/axp20x-pek.c | 265 ++++++++++++++++
> drivers/mfd/Kconfig | 12 +
> drivers/mfd/Makefile | 1 +
> drivers/mfd/axp20x.c | 250 +++++++++++++++
> drivers/regulator/Kconfig | 7 +
> drivers/regulator/Makefile | 1 +
> drivers/regulator/axp20x-regulator.c | 349 +++++++++++++++++++++
> include/linux/mfd/axp20x.h | 180 +++++++++++
> 14 files changed, 1243 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-driver-input-axp-pek
> create mode 100644 Documentation/devicetree/bindings/mfd/axp20x.txt
> 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
>
^ permalink raw reply
* Re: [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Carlo Caione @ 2014-03-01 17:17 UTC (permalink / raw)
To: Hans de Goede
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-sunxi-/JYPxA39Uh5TLH3MbocFFw,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
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
In-Reply-To: <53121151.9050804-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org>
On Sat, Mar 1, 2014 at 5:56 PM, Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
> Hi Carlo,
>
> Great work, thanks for all the time you're putting into this!
Hi Hans :)
> I've 2 questions:
>
> 1) What dependencies does this patch-set have? Obviously it needs the NMI irq
> patches for A20, anything else ? I no longer see any use of a special flag
> for ack on unmask, is that no longer needed ?
Yes, the only dependency is on the NMI controller patch.
After a discussion with Maxime and Thomas I decided to not push for
including the special flag for ack on unmask in the irqchip core but
to use the unmask callback as in the v3 version of the NMI controller
driver (so no flag needed)
> 2) No poweroff functionality ? That would be really great to have.
Actually in [PATCH 1/7] I support the poweroff using the pm_power_off hook.
Best,
--
Carlo Caione
^ permalink raw reply
* Re: Re: [PATCH] Introduce Naming Convention in Input Subsystem
From: Aniroop Mathur @ 2014-03-01 19:17 UTC (permalink / raw)
To: Dmitry Torokhov, Benjamin Tissoires
Cc: linux-input@vger.kernel.org, cpgs ., Aniroop Mathur
In-Reply-To: <CADYu30-f8uwfP_ZFZCDD=ho0BgAitvWVBuHxYn9up9+vN4gNKA@mail.gmail.com>
Dear Mr. Torokhov, Mr. Tissoires,
Gentle Reminder:
Please, update your comment/opinion/suggestion on below mail.
On Mon, Feb 10, 2014 at 9:24 PM, Aniroop Mathur
<aniroop.mathur@gmail.com> wrote:
> Dear Mr. Torokhov, Mr. Benjamin,
> Greetings!
>
> I sent a mail on January 25th, but still waiting for the reply.
> Therefore, sending it again.
> I hope to hear soon ~
>
>
> On Tue, Jan 21, 2014 at 3:19 AM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
>> Hi Aniroop,
>>
>> On Tue, Jan 21, 2014 at 01:34:07AM +0530, Aniroop Mathur wrote:
>>> Hello Mr Tissoires
>>> Greetings !!
>>>
>>> On Jan 20, 2014 11:54 PM, "Benjamin Tissoires" <benjamin.tissoires@gmail.com>
>>> wrote:
>>> >
>>> > Hi Aniroop,
>>> >
>>> > [sorry for top posting, but I really don't know where to put this
>>> regarding your answers].
>>> >
>>> No problem at all.
>>> In fact I am quite happy to hear from
>>> Linux community again
>>> Its my pleasure.
>>>
>>> > I _think_ I get one of the reasons you don't understand Dmitry, and why
>>> Dmitry does not understand you. From the different mails, I would say that
>>> you are referring to an Android platform.
>>> > If it's not the case, then sorry for the noise.
>>> >
>>> No I am not specifically referring to
>>> Android platform
>>> But yes, I am from android background
>>> and through android only I came up with
>>> this idea.
>>>
>>> > So, the last time I checked with android, this system does _not_ have an
>>> udev user-space daemon, which means that what you seem to be doing is
>>> parsing manually the sysfs tree to retrieve the inputs and their properties.
>>> > This, IMO, is a very bad idea. Parsing the entire sysfs tree is indeed
>>> costly and you will have to build everything by hand.
>>> >
>>> No, Android also have a daemon but it is not udev daemon. There is a
>>> separate daemon in android different from generic
>>> udev daemon. It is different but basic is
>>> still same.
>>> Just like udev daemon, android daemon
>>> also receives uevents from kernel
>>> and creates devfs nodes for us,
>>> that is /Dev/input/event1/2/3... based
>>> on uevent env variables.
>>> Android daemon is different from generic
>>> daemon in a sense it does not see any file
>>> like udev rules before creating nodes. Instead it directly create nodes as
>>> per uevent env variables in the structure.
>>> So without opening any extra udev rule
>>> file, it create nodes.
>>> So I understand it has also a limitation that it only has to be dependent
>>> only on default uevents and cannot read any sysfs attribute to identify the
>>> device if default uevents are not sufficient.
>>
>> Why can it not?
>>
>
> It cannot because currently in android code, it does not read any
> sysfs attribute.
> It only reads default kernel uevents. I think functionality may be added to read
> sysfs attribute also but this functionality is currently not added in
> android code.
> I think it is not added because in embedded system mostly there are only single
> and unique devices and this manipulation might add extra time during android
> initialization.
>
>>>
>>> But the problem I am referring is for both in case of generic kernel and
>>> android kernel.
>>
>> So far I really do not see it as generic kernel problem since we do have
>> existing infrastructure in userspace that is quite efficient.
>>
>
> The existing way is also there but there is always a scope of improvement.
> Tell me which is more efficient ?
> 1. existing method in which udev opens and read udev files for setting name
> or
> 2. Directly set name without doing extra calculations
>
> Its correct existing method will also do the same task but why not to use
> more efficient way.
> I just want to improve efficiency.
>
>
>>>
>>> > On regular distros, we use udev. This daemon builds its device database
>>> from the events generated by the kernels directly (the uevents). Once the
>>> events are emitted, we never (except for some user-space drivers) use them
>>> in the kernel drivers (at least, I never saw that).
>>> >
>>> > So if you want to create symlinks, then indeed, you just add 2 or 3 rules
>>> in /etc/udev/rules.d, and then the user space (and the system integrator)
>>> can see the different devices with the "correct" symlink.
>>> > However, the kernel developer will never see them (and especially in the
>>> ->probe callback). However, the user-space tools which receives the udev
>>> events (emitted from udev, not the kernel this time) can easily retrieve
>>> many information from the event. Just run "udevadm info --export-db" on a
>>> regular Linux, and you will see that the device which presents the event
>>> node (/dev/input/eventX) has all the requirements to identify itself (VID,
>>> PID, path, etc...)
>>> >
>>> Yes udev gives so many uevents but as I checked through logs specifically
>>> in case of /Dev/input/event1/2/3/4....it does not give sufficient uevents
>>> to identify the device.
>>> As I found in logs it gives only 7 env variable like action, subsystem, Dev
>>> name, Dev path, major, minor and seq number.
>>> It does not give vendor Id or pid.
>>> Now with these uevent we cannot identify the input device whether its
>>> accelerometer or touchscreen. Can we ??
>>
>> While information sent in uevent emitted for creation of event device
>> might not be enough to identify and classify input device you can:
>>
>> 1. Capture udevent sent a few moments earlier for the input device
>> itself, or
>>
>> 2. Read /sys/class/$DEVNAME/device/uevent to retrieve input device's
>> uevent data:
>>
>> [dtor@dtor-d630 work]$ cat /sys/class/input/event6/uevent
>> MAJOR=13
>> MINOR=70
>> DEVNAME=input/event6
>> [dtor@dtor-d630 work]$ cat /sys/class/input/event6/device/uevent
>> PRODUCT=11/2/8/300
>> NAME="AlpsPS/2 ALPS DualPoint TouchPad"
>> PHYS="isa0060/serio1/input0"
>> PROP=8
>> EV=b
>> KEY=e420 70000 0 0 0 0
>> ABS=260800001000003
>> MODALIAS=input:b0011v0002p0008e0300-e0,1,3,k110,111,112,145,14A,14D,14E,14F,ra0,1,18,2F,35,36,39,mlsfw
>> [dtor@dtor-d630 work]$
>>
>> That should give you plenty data to work with. Yes, the 2nd option
>> involves additional filesystem operations, but this is sysfs (so you are
>> not hitting real media) and you only need a few (open, read, and close)
>> per device.
>
> Yes this method can surely solve the task , but not the purpose.
> My query is why not to use more efficient way when we can ?
> ( Already discussed this way is backward compatible and is optional to use)
>
>
>
> Apart from above, Could you please tell me the role/use of init_name variable,
> which is defined in "struct device" ?
> (I think, it is also used to set name of device node)
>
> Thanks,
> Aniroop Mathur
>
>>
>> Thanks.
>>
>> --
>> Dmitry
^ permalink raw reply
* Re: Re: [PATCH 0/7] mfd: AXP20x: Add support for AXP202 and AXP209
From: Hans de Goede @ 2014-03-01 19:29 UTC (permalink / raw)
To: linux-sunxi-/JYPxA39Uh5TLH3MbocFFw
Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
maxime.ripard-wi1+55ScJUtKEb57/3fJTNBPR1lH4CV8,
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
In-Reply-To: <CAOQ7t2ae35E-eiQW4DFspdTe3H7AWGzRpw4B_1t27p0OSkX_Uw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
Hi,
On 03/01/2014 06:17 PM, Carlo Caione wrote:
> On Sat, Mar 1, 2014 at 5:56 PM, Hans de Goede <hdegoede-H+wXaHxf7aLQT0dZR+AlfA@public.gmane.org> wrote:
>> Hi Carlo,
>>
>> Great work, thanks for all the time you're putting into this!
>
> Hi Hans :)
>
>> I've 2 questions:
>>
>> 1) What dependencies does this patch-set have? Obviously it needs the NMI irq
>> patches for A20, anything else ? I no longer see any use of a special flag
>> for ack on unmask, is that no longer needed ?
>
> Yes, the only dependency is on the NMI controller patch.
> After a discussion with Maxime and Thomas I decided to not push for
> including the special flag for ack on unmask in the irqchip core but
> to use the unmask callback as in the v3 version of the NMI controller
> driver (so no flag needed)
>
>> 2) No poweroff functionality ? That would be really great to have.
>
> Actually in [PATCH 1/7] I support the poweroff using the pm_power_off hook.
Ah I missed that, cool.
I'm doing with sunxi hacking for today, but I'll add these to sunxi-devel
and them give them a test run tomorrow.
Regards,
Hans
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox