Commit fd60ac58 authored by Vaibhav Hiremath's avatar Vaibhav Hiremath Committed by Greg Kroah-Hartman

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: default avatarVaibhav Hiremath <vaibhav.hiremath@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@google.com>
parent 65fd5a50
......@@ -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");
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment