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) ...@@ -45,6 +45,37 @@ static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
gpio_set_value(gpio, 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. * svc_delayed_work - Time to give SVC to boot.
*/ */
...@@ -52,10 +83,7 @@ static void svc_delayed_work(struct work_struct *work) ...@@ -52,10 +83,7 @@ static void svc_delayed_work(struct work_struct *work)
{ {
struct arche_platform_drvdata *arche_pdata = struct arche_platform_drvdata *arche_pdata =
container_of(work, struct arche_platform_drvdata, delayed_work.work); 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 timeout = 50;
int ret;
/* /*
* 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin * 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) ...@@ -79,18 +107,20 @@ static void svc_delayed_work(struct work_struct *work)
msleep(100); msleep(100);
} while(timeout--); } while(timeout--);
if (timeout >= 0) { if (timeout < 0) {
ret = of_platform_populate(np, NULL, NULL, dev); /* FIXME: We may want to limit retries here */
if (!ret) { dev_warn(arche_pdata->dev,
/* re-assert wake_detect to confirm SVC WAKE_OUT */ "Timed out on wake/detect, rescheduling handshake\n");
gpio_direction_output(arche_pdata->wake_detect_gpio, 1); gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
return; schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
} return;
} }
/* FIXME: We may want to limit retries here */ /* Bring APB out of reset: cold boot sequence */
gpio_direction_output(arche_pdata->wake_detect_gpio, 0); device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
/* re-assert wake_detect to confirm SVC WAKE_OUT */
gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
} }
/* Export gpio's to user space */ /* Export gpio's to user space */
...@@ -176,12 +206,17 @@ static ssize_t state_store(struct device *dev, ...@@ -176,12 +206,17 @@ static ssize_t state_store(struct device *dev,
if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
return count; 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); arche_platform_poweroff_seq(arche_pdata);
} else if (sysfs_streq(buf, "active")) { } else if (sysfs_streq(buf, "active")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
return count; return count;
ret = arche_platform_coldboot_seq(arche_pdata); 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")) { } else if (sysfs_streq(buf, "standby")) {
if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
return count; return count;
...@@ -193,8 +228,13 @@ static ssize_t state_store(struct device *dev, ...@@ -193,8 +228,13 @@ static ssize_t state_store(struct device *dev,
/* First we want to make sure we power off everything /* First we want to make sure we power off everything
* and then enter FW flashing state */ * and then enter FW flashing state */
device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
arche_platform_poweroff_seq(arche_pdata); arche_platform_poweroff_seq(arche_pdata);
arche_platform_fw_flashing_seq(arche_pdata); arche_platform_fw_flashing_seq(arche_pdata);
device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
} else { } else {
dev_err(arche_pdata->dev, "unknown state\n"); dev_err(arche_pdata->dev, "unknown state\n");
ret = -EINVAL; ret = -EINVAL;
...@@ -321,8 +361,6 @@ static int arche_platform_probe(struct platform_device *pdev) ...@@ -321,8 +361,6 @@ static int arche_platform_probe(struct platform_device *pdev)
gpio_direction_output(arche_pdata->wake_detect_gpio, 0); gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
arche_pdata->dev = &pdev->dev; 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); ret = device_create_file(dev, &dev_attr_state);
if (ret) { if (ret) {
...@@ -336,6 +374,15 @@ static int arche_platform_probe(struct platform_device *pdev) ...@@ -336,6 +374,15 @@ static int arche_platform_probe(struct platform_device *pdev)
return ret; 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); export_gpios(arche_pdata);
dev_info(dev, "Device registered successfully\n"); 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