greybus: arche-platform: Enable interrupt support on wake/detect line
This patch enabled interrupt support on events received over wake/detect line. The driver follows below state machine, Default: wake/detect line is high (WD_STATE_IDLE) On Falling edge: SVC initiates boot (either cold/standby). On ES3, > 30msec = coldboot, else standby boot. Driver moves to WD_STATE_BOOT_INIT On rising edge (> 30msec): SVC expects APB to coldboot Driver wakes irq thread which kicks off APB coldboot (WD_STATE_COLDBOOT_TRIG) On rising edge (< 30msec): Driver ignores it, do nothing. After coldboot of APB, HUB configuration work is scheduled after 2 sec, allowing enough time for APB<->SVC/Switch to linkup (in multiple iterations) Testing Done: Tested on DB3.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org> Reviewed-by: Michael Scott <michael.scott@linaro.org> Tested-by: Michael Scott <michael.scott@linaro.org> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
parent
685353c12e
commit
f760bbfb5c
@ -17,10 +17,15 @@
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/time.h>
|
||||
#include "arche_platform.h"
|
||||
|
||||
#include <linux/usb/usb3613.h>
|
||||
|
||||
#define WD_COLDBOOT_PULSE_WIDTH_MS 30
|
||||
|
||||
enum svc_wakedetect_state {
|
||||
WD_STATE_IDLE, /* Default state = pulled high/low */
|
||||
WD_STATE_BOOT_INIT, /* WD = falling edge (low) */
|
||||
@ -49,6 +54,9 @@ struct arche_platform_drvdata {
|
||||
|
||||
struct delayed_work delayed_work;
|
||||
enum svc_wakedetect_state wake_detect_state;
|
||||
int wake_detect_irq;
|
||||
spinlock_t lock;
|
||||
unsigned long wake_detect_start;
|
||||
|
||||
struct device *dev;
|
||||
};
|
||||
@ -58,6 +66,18 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
|
||||
gpio_set_value(gpio, onoff);
|
||||
}
|
||||
|
||||
static int apb_cold_boot(struct device *dev, void *data)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = apb_ctrl_coldboot(dev);
|
||||
if (ret)
|
||||
dev_warn(dev, "failed to coldboot\n");
|
||||
|
||||
/*Child nodes are independent, so do not exit coldboot operation */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int apb_fw_flashing_state(struct device *dev, void *data)
|
||||
{
|
||||
int ret;
|
||||
@ -95,6 +115,86 @@ static void hub_conf_delayed_work(struct work_struct *work)
|
||||
dev_warn(arche_pdata->dev, "failed to control hub device\n");
|
||||
}
|
||||
|
||||
static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
|
||||
{
|
||||
struct arche_platform_drvdata *arche_pdata = devid;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&arche_pdata->lock, flags);
|
||||
if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
|
||||
/* Something is wrong */
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START;
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
|
||||
/* Bring APB out of reset: cold boot sequence */
|
||||
device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
|
||||
|
||||
spin_lock_irqsave(&arche_pdata->lock, flags);
|
||||
/* USB HUB configuration */
|
||||
schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
|
||||
arche_pdata->wake_detect_state = WD_STATE_IDLE;
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
|
||||
{
|
||||
struct arche_platform_drvdata *arche_pdata = devid;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&arche_pdata->lock, flags);
|
||||
|
||||
if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
|
||||
/* wake/detect rising */
|
||||
|
||||
/*
|
||||
* If wake/detect line goes high after low, within less than
|
||||
* 30msec, then standby boot sequence is initiated, which is not
|
||||
* supported/implemented as of now. So ignore it.
|
||||
*/
|
||||
if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
|
||||
if (time_before(jiffies,
|
||||
arche_pdata->wake_detect_start +
|
||||
msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
|
||||
/* No harm with cancellation, even if not pending */
|
||||
cancel_delayed_work(&arche_pdata->delayed_work);
|
||||
arche_pdata->wake_detect_state = WD_STATE_IDLE;
|
||||
} else {
|
||||
/* Check we are not in middle of irq thread already */
|
||||
if (arche_pdata->wake_detect_state !=
|
||||
WD_STATE_COLDBOOT_START) {
|
||||
arche_pdata->wake_detect_state =
|
||||
WD_STATE_COLDBOOT_TRIG;
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
return IRQ_WAKE_THREAD;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
/* wake/detect falling */
|
||||
if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
|
||||
arche_pdata->wake_detect_start = jiffies;
|
||||
/* No harm with cancellation even if it is not pending*/
|
||||
cancel_delayed_work(&arche_pdata->delayed_work);
|
||||
/*
|
||||
* In the begining, when wake/detect goes low (first time), we assume
|
||||
* it is meant for coldboot and set the flag. If wake/detect line stays low
|
||||
* beyond 30msec, then it is coldboot else fallback to standby boot.
|
||||
*/
|
||||
arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT;
|
||||
}
|
||||
}
|
||||
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
|
||||
{
|
||||
int ret;
|
||||
@ -148,6 +248,8 @@ static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_
|
||||
|
||||
static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
|
||||
return;
|
||||
|
||||
@ -156,7 +258,9 @@ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pda
|
||||
/* Send disconnect/detach event to SVC */
|
||||
gpio_set_value(arche_pdata->wake_detect_gpio, 0);
|
||||
usleep_range(100, 200);
|
||||
spin_lock_irqsave(&arche_pdata->lock, flags);
|
||||
arche_pdata->wake_detect_state = WD_STATE_IDLE;
|
||||
spin_unlock_irqrestore(&arche_pdata->lock, flags);
|
||||
|
||||
clk_disable_unprepare(arche_pdata->svc_ref_clk);
|
||||
}
|
||||
@ -344,6 +448,22 @@ static int arche_platform_probe(struct platform_device *pdev)
|
||||
|
||||
arche_pdata->dev = &pdev->dev;
|
||||
|
||||
spin_lock_init(&arche_pdata->lock);
|
||||
arche_pdata->wake_detect_irq =
|
||||
gpio_to_irq(arche_pdata->wake_detect_gpio);
|
||||
|
||||
ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
|
||||
arche_platform_wd_irq,
|
||||
arche_platform_wd_irq_thread,
|
||||
IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
|
||||
dev_name(dev), arche_pdata);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
/* Enable it only after sending wake/detect event */
|
||||
disable_irq(arche_pdata->wake_detect_irq);
|
||||
|
||||
ret = device_create_file(dev, &dev_attr_state);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to create state file in sysfs\n");
|
||||
|
Loading…
x
Reference in New Issue
Block a user