Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f760bbfb authored by Vaibhav Hiremath's avatar Vaibhav Hiremath Committed by Greg Kroah-Hartman
Browse files

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: default avatarVaibhav Hiremath <vaibhav.hiremath@linaro.org>
Reviewed-by: default avatarMichael Scott <michael.scott@linaro.org>
Tested-by: default avatarMichael Scott <michael.scott@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 685353c1
Loading
Loading
Loading
Loading
+120 −0
Original line number Diff line number Diff line
@@ -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");