diff options
author | Vaibhav Hiremath <vaibhav.hiremath@linaro.org> | 2016-01-11 17:41:24 +0530 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2016-01-11 15:58:10 -0800 |
commit | a463fc1533c9ea2edc99306b31c74f472c62d690 (patch) | |
tree | 58621205bfa11e3fb6aba87ff764cd6b6bf629d3 /drivers/staging/greybus/arche-platform.c | |
parent | f1e9cbd5a097aa6ec62f261bdf44879bbaefb72a (diff) | |
download | op-kernel-dev-a463fc1533c9ea2edc99306b31c74f472c62d690.zip op-kernel-dev-a463fc1533c9ea2edc99306b31c74f472c62d690.tar.gz |
greybus: arche-platform: Add wake/detect support along with handshaking with AP
Add wake_detect support to arche-platform driver which is responsible
for SVC control. This patch also adds code for handshaking between
AP <=> SVC. The sequence is,
1. AP boots
To keep compatibility between DB3 and EVT platform, SVC will be
help in reset and AP driver would release it at appropriate time.
wake/detect pin (WD8A) = Low
reset (SVC/APB1/APB2) = Asserted (as per polarity)
2. AP Driver gets inserted
2.1. AP will deassert reset to SVC (following power on sequence)
2.2. SVC allows 360 milliseconds to elapse after switch boots to work
around bug described in ENG-330.
2.3. AP asserts wake/detect pin (WD8A = HIGH)
3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP
4. AP receives "wake out" signal, takes AP Bridges through their power
on reset sequence as defined in the bridge ASIC reference manuals
5. AP takes USB3613 through its power on reset sequence
6. AP should enumerates AP Bridges
Note: ISR has been deliberately removed (not merged) as we are still
not sure how it will be used, in runtime usage context. Driver as such
doesn't do anything for runtime assert-n-deassert of reset to SVC/APB's,
it just simply offloads it to user by exporting required gpio's.
The exported gpio's are required for FW flashing from user space.
When it comes to usersace manipulated control sequence, user has to manage.
Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@linaro.org>
Tested-by: Michael Scott <michael.scott@linaro.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/arche-platform.c')
-rw-r--r-- | drivers/staging/greybus/arche-platform.c | 75 |
1 files changed, 70 insertions, 5 deletions
diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c index 94e6f5d..d12fa0e 100644 --- a/drivers/staging/greybus/arche-platform.c +++ b/drivers/staging/greybus/arche-platform.c @@ -8,6 +8,7 @@ */ #include <linux/clk.h> +#include <linux/delay.h> #include <linux/gpio.h> #include <linux/init.h> #include <linux/module.h> @@ -23,6 +24,7 @@ struct arche_platform_drvdata { int svc_reset_gpio; bool is_reset_act_hi; int svc_sysboot_gpio; + int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ unsigned int svc_refclk_req; struct clk *svc_ref_clk; @@ -31,6 +33,9 @@ struct arche_platform_drvdata { struct pinctrl_state *pin_default; int num_apbs; + + struct delayed_work delayed_work; + struct device *dev; }; static inline void svc_reset_onoff(unsigned int gpio, bool onoff) @@ -38,6 +43,52 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff) gpio_set_value(gpio, onoff); } +/** + * svc_delayed_work - Time to give SVC to boot. + */ +static void svc_delayed_work(struct work_struct *work) +{ + struct arche_platform_drvdata *arche_pdata = + container_of(work, struct arche_platform_drvdata, delayed_work.work); + struct device *dev = arche_pdata->dev; + struct device_node *np = dev->of_node; + int timeout = 10; + int ret; + + /* + * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin + * deasserted (LOW in this case) + * 2.1. SVC allows 360 milliseconds to elapse after switch boots to work + * around bug described in ENG-330. + * 2.2. AP asserts wake/detect pin (HIGH) (this can proceed in parallel with 2.1) + * 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP + * 4. AP receives "wake out" signal, takes AP Bridges through their power + * on reset sequence as defined in the bridge ASIC reference manuals + * 5. AP takes USB3613 through its power on reset sequence + * 6. AP enumerates AP Bridges + */ + gpio_set_value(arche_pdata->wake_detect_gpio, 1); + gpio_direction_input(arche_pdata->wake_detect_gpio); + do { + /* Read the wake_detect GPIO, for WAKE_OUT event from SVC */ + if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) + break; + + msleep(500); + } while(timeout--); + + if (timeout >= 0) { + ret = of_platform_populate(np, NULL, NULL, dev); + if (!ret) + /* Should we set wake_detect gpio to output again? */ + return; + } + + /* FIXME: We may want to limit retries here */ + gpio_direction_output(arche_pdata->wake_detect_gpio, 0); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); +} + /* Export gpio's to user space */ static void export_gpios(struct arche_platform_drvdata *arche_pdata) { @@ -146,18 +197,32 @@ static int arche_platform_probe(struct platform_device *pdev) arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); - /* probe all childs here */ - ret = of_platform_populate(np, NULL, NULL, dev); + arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); + if (arche_pdata->wake_detect_gpio < 0) { + dev_err(dev, "failed to get wake detect gpio\n"); + ret = arche_pdata->wake_detect_gpio; + goto exit; + } + + ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); if (ret) { - arche_platform_cleanup(arche_pdata); - dev_err(dev, "no child node found\n"); - return ret; + dev_err(dev, "Failed requesting wake_detect gpio %d\n", + arche_pdata->wake_detect_gpio); + goto exit; } + arche_pdata->dev = &pdev->dev; + INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); + schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); + export_gpios(arche_pdata); dev_info(dev, "Device registered successfully\n"); return 0; + +exit: + arche_platform_cleanup(arche_pdata); + return ret; } static int arche_remove_child(struct device *dev, void *unused) |