summaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/arche-platform.c
diff options
context:
space:
mode:
authorVaibhav Hiremath <vaibhav.hiremath@linaro.org>2016-02-13 02:04:17 +0530
committerGreg Kroah-Hartman <gregkh@google.com>2016-02-15 13:18:40 -0800
commitfd60ac585607979e37b64ecec8afb898f9ad6a85 (patch)
tree0296c0bfb643ad8f123d5fa5318a7c0526ece46d /drivers/staging/greybus/arche-platform.c
parent65fd5a5018c8c5b7ddf14dffa75474b3a9040851 (diff)
downloadlinux-fd60ac585607979e37b64ecec8afb898f9ad6a85.tar.bz2
greybus: arche-platform: Fix boot, poweroff and fw_flashing seq with APBs
Now SVC driver has an access to APBs operational functions (coldboot, standby_boot, fw_flashing and poweroff), SVC driver can control APB's as per below rules, - If SVC goes down (poweroff state), it will also power off APBs and vice a versa for all operational states. - On boot, SVC will probe/populate APB device, but will not coldboot it. APBs will coldboot only after handshaking with SVC over wake/detect line. Note that, both APBs share same wake/detect line. So from user/developer perspective, it is highly recommended that they should use arche-platform interfaces, instead of individual apb interface, # echo [off/active/standby/fw_flashing] > /sys/devices/arche_platform.*/state Note: 'standby' mode is not supported as of now. Testing Done: Testd on EVT1.2 and DB3.5 platform. Signed-off-by: Vaibhav Hiremath <vaibhav.hiremath@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.c77
1 files changed, 62 insertions, 15 deletions
diff --git a/drivers/staging/greybus/arche-platform.c b/drivers/staging/greybus/arche-platform.c
index 3e6432f0f1bc..037e14206059 100644
--- a/drivers/staging/greybus/arche-platform.c
+++ b/drivers/staging/greybus/arche-platform.c
@@ -45,6 +45,37 @@ 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;
+
+ ret = apb_ctrl_fw_flashing(dev);
+ if (ret)
+ dev_warn(dev, "failed to switch to fw flashing state\n");
+
+ /*Child nodes are independent, so do not exit coldboot operation */
+ return 0;
+}
+
+static int apb_poweroff(struct device *dev, void *data)
+{
+ apb_ctrl_poweroff(dev);
+
+ return 0;
+}
+
/**
* svc_delayed_work - Time to give SVC to boot.
*/
@@ -52,10 +83,7 @@ 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 = 50;
- int ret;
/*
* 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin
@@ -79,18 +107,20 @@ static void svc_delayed_work(struct work_struct *work)
msleep(100);
} while(timeout--);
- if (timeout >= 0) {
- ret = of_platform_populate(np, NULL, NULL, dev);
- if (!ret) {
- /* re-assert wake_detect to confirm SVC WAKE_OUT */
- gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
- return;
- }
+ if (timeout < 0) {
+ /* FIXME: We may want to limit retries here */
+ dev_warn(arche_pdata->dev,
+ "Timed out on wake/detect, rescheduling handshake\n");
+ gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
+ schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
+ 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));
+ /* Bring APB out of reset: cold boot sequence */
+ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
+
+ /* re-assert wake_detect to confirm SVC WAKE_OUT */
+ gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
}
/* Export gpio's to user space */
@@ -176,12 +206,17 @@ static ssize_t state_store(struct device *dev,
if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
return count;
+ /* If SVC goes down, bring down APB's as well */
+ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+
arche_platform_poweroff_seq(arche_pdata);
} else if (sysfs_streq(buf, "active")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
return count;
ret = arche_platform_coldboot_seq(arche_pdata);
+ /* Give enough time for SVC to boot and then handshake with SVC */
+ schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
} else if (sysfs_streq(buf, "standby")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
return count;
@@ -193,8 +228,13 @@ static ssize_t state_store(struct device *dev,
/* First we want to make sure we power off everything
* and then enter FW flashing state */
+ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
+
arche_platform_poweroff_seq(arche_pdata);
+
arche_platform_fw_flashing_seq(arche_pdata);
+
+ device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
} else {
dev_err(arche_pdata->dev, "unknown state\n");
ret = -EINVAL;
@@ -321,8 +361,6 @@ static int arche_platform_probe(struct platform_device *pdev)
gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
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));
ret = device_create_file(dev, &dev_attr_state);
if (ret) {
@@ -336,6 +374,15 @@ static int arche_platform_probe(struct platform_device *pdev)
return ret;
}
+ ret = of_platform_populate(np, NULL, NULL, dev);
+ if (ret) {
+ dev_err(dev, "failed to populate child nodes %d\n", ret);
+ return ret;
+ }
+
+ 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");