aboutsummaryrefslogtreecommitdiffstats
path: root/greybus_arche.patch
diff options
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-15 13:54:11 +0200
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2016-09-15 13:54:11 +0200
commitc1aa01c96e590714d99c5f17cfae1b14dec8bdee (patch)
tree5811422f65efdff3fd89fdaf9a8c6d7d1cc4d06b /greybus_arche.patch
parent4758ee8bc89a86c2110b9e85878538ced8045ef5 (diff)
downloadpatches-c1aa01c96e590714d99c5f17cfae1b14dec8bdee.tar.gz
greybus patches
Diffstat (limited to 'greybus_arche.patch')
-rw-r--r--greybus_arche.patch1404
1 files changed, 1404 insertions, 0 deletions
diff --git a/greybus_arche.patch b/greybus_arche.patch
new file mode 100644
index 00000000000000..e4222119051d8a
--- /dev/null
+++ b/greybus_arche.patch
@@ -0,0 +1,1404 @@
+---
+ drivers/greybus/arche-apb-ctrl.c | 522 ++++++++++++++++++++++++
+ drivers/greybus/arche-platform.c | 828 +++++++++++++++++++++++++++++++++++++++
+ drivers/greybus/arche_platform.h | 39 +
+ 3 files changed, 1389 insertions(+)
+
+--- /dev/null
++++ b/drivers/greybus/arche-apb-ctrl.c
+@@ -0,0 +1,522 @@
++/*
++ * Arche Platform driver to control APB.
++ *
++ * Copyright 2014-2015 Google Inc.
++ * Copyright 2014-2015 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/interrupt.h>
++#include <linux/of_gpio.h>
++#include <linux/of_irq.h>
++#include <linux/module.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/regulator/consumer.h>
++#include <linux/spinlock.h>
++#include "arche_platform.h"
++
++
++struct arche_apb_ctrl_drvdata {
++ /* Control GPIO signals to and from AP <=> AP Bridges */
++ int resetn_gpio;
++ int boot_ret_gpio;
++ int pwroff_gpio;
++ int wake_in_gpio;
++ int wake_out_gpio;
++ int pwrdn_gpio;
++
++ enum arche_platform_state state;
++ bool init_disabled;
++
++ struct regulator *vcore;
++ struct regulator *vio;
++
++ int clk_en_gpio;
++ struct clk *clk;
++
++ struct pinctrl *pinctrl;
++ struct pinctrl_state *pin_default;
++
++ /* V2: SPI Bus control */
++ int spi_en_gpio;
++ bool spi_en_polarity_high;
++};
++
++/*
++ * Note that these low level api's are active high
++ */
++static inline void deassert_reset(unsigned int gpio)
++{
++ gpio_set_value(gpio, 1);
++}
++
++static inline void assert_reset(unsigned int gpio)
++{
++ gpio_set_value(gpio, 0);
++}
++
++/*
++ * Note: Please do not modify the below sequence, as it is as per the spec
++ */
++static int coldboot_seq(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
++ int ret;
++
++ if (apb->init_disabled ||
++ apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
++ return 0;
++
++ /* Hold APB in reset state */
++ assert_reset(apb->resetn_gpio);
++
++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
++ gpio_is_valid(apb->spi_en_gpio))
++ devm_gpio_free(dev, apb->spi_en_gpio);
++
++ /* Enable power to APB */
++ if (!IS_ERR(apb->vcore)) {
++ ret = regulator_enable(apb->vcore);
++ if (ret) {
++ dev_err(dev, "failed to enable core regulator\n");
++ return ret;
++ }
++ }
++
++ if (!IS_ERR(apb->vio)) {
++ ret = regulator_enable(apb->vio);
++ if (ret) {
++ dev_err(dev, "failed to enable IO regulator\n");
++ return ret;
++ }
++ }
++
++ apb_bootret_deassert(dev);
++
++ /* On DB3 clock was not mandatory */
++ if (gpio_is_valid(apb->clk_en_gpio))
++ gpio_set_value(apb->clk_en_gpio, 1);
++
++ usleep_range(100, 200);
++
++ /* deassert reset to APB : Active-low signal */
++ deassert_reset(apb->resetn_gpio);
++
++ apb->state = ARCHE_PLATFORM_STATE_ACTIVE;
++
++ return 0;
++}
++
++static int fw_flashing_seq(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
++ int ret;
++
++ if (apb->init_disabled ||
++ apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
++ return 0;
++
++ ret = regulator_enable(apb->vcore);
++ if (ret) {
++ dev_err(dev, "failed to enable core regulator\n");
++ return ret;
++ }
++
++ ret = regulator_enable(apb->vio);
++ if (ret) {
++ dev_err(dev, "failed to enable IO regulator\n");
++ return ret;
++ }
++
++ if (gpio_is_valid(apb->spi_en_gpio)) {
++ unsigned long flags;
++
++ if (apb->spi_en_polarity_high)
++ flags = GPIOF_OUT_INIT_HIGH;
++ else
++ flags = GPIOF_OUT_INIT_LOW;
++
++ ret = devm_gpio_request_one(dev, apb->spi_en_gpio,
++ flags, "apb_spi_en");
++ if (ret) {
++ dev_err(dev, "Failed requesting SPI bus en gpio %d\n",
++ apb->spi_en_gpio);
++ return ret;
++ }
++ }
++
++ /* for flashing device should be in reset state */
++ assert_reset(apb->resetn_gpio);
++ apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
++
++ return 0;
++}
++
++static int standby_boot_seq(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
++
++ if (apb->init_disabled)
++ return 0;
++
++ /* Even if it is in OFF state, then we do not want to change the state */
++ if (apb->state == ARCHE_PLATFORM_STATE_STANDBY ||
++ apb->state == ARCHE_PLATFORM_STATE_OFF)
++ return 0;
++
++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
++ gpio_is_valid(apb->spi_en_gpio))
++ devm_gpio_free(dev, apb->spi_en_gpio);
++
++ /*
++ * As per WDM spec, do nothing
++ *
++ * Pasted from WDM spec,
++ * - A falling edge on POWEROFF_L is detected (a)
++ * - WDM enters standby mode, but no output signals are changed
++ * */
++
++ /* TODO: POWEROFF_L is input to WDM module */
++ apb->state = ARCHE_PLATFORM_STATE_STANDBY;
++ return 0;
++}
++
++static void poweroff_seq(struct platform_device *pdev)
++{
++ struct device *dev = &pdev->dev;
++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
++
++ if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF)
++ return;
++
++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING &&
++ gpio_is_valid(apb->spi_en_gpio))
++ devm_gpio_free(dev, apb->spi_en_gpio);
++
++ /* disable the clock */
++ if (gpio_is_valid(apb->clk_en_gpio))
++ gpio_set_value(apb->clk_en_gpio, 0);
++
++ if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0)
++ regulator_disable(apb->vcore);
++
++ if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0)
++ regulator_disable(apb->vio);
++
++ /* As part of exit, put APB back in reset state */
++ assert_reset(apb->resetn_gpio);
++ apb->state = ARCHE_PLATFORM_STATE_OFF;
++
++ /* TODO: May have to send an event to SVC about this exit */
++}
++
++void apb_bootret_assert(struct device *dev)
++{
++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
++
++ gpio_set_value(apb->boot_ret_gpio, 1);
++}
++
++void apb_bootret_deassert(struct device *dev)
++{
++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
++
++ gpio_set_value(apb->boot_ret_gpio, 0);
++}
++
++int apb_ctrl_coldboot(struct device *dev)
++{
++ return coldboot_seq(to_platform_device(dev));
++}
++
++int apb_ctrl_fw_flashing(struct device *dev)
++{
++ return fw_flashing_seq(to_platform_device(dev));
++}
++
++int apb_ctrl_standby_boot(struct device *dev)
++{
++ return standby_boot_seq(to_platform_device(dev));
++}
++
++void apb_ctrl_poweroff(struct device *dev)
++{
++ poweroff_seq(to_platform_device(dev));
++}
++
++static ssize_t state_store(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev);
++ int ret = 0;
++ bool is_disabled;
++
++ if (sysfs_streq(buf, "off")) {
++ if (apb->state == ARCHE_PLATFORM_STATE_OFF)
++ return count;
++
++ poweroff_seq(pdev);
++ } else if (sysfs_streq(buf, "active")) {
++ if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE)
++ return count;
++
++ poweroff_seq(pdev);
++ is_disabled = apb->init_disabled;
++ apb->init_disabled = false;
++ ret = coldboot_seq(pdev);
++ if (ret)
++ apb->init_disabled = is_disabled;
++ } else if (sysfs_streq(buf, "standby")) {
++ if (apb->state == ARCHE_PLATFORM_STATE_STANDBY)
++ return count;
++
++ ret = standby_boot_seq(pdev);
++ } else if (sysfs_streq(buf, "fw_flashing")) {
++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
++ return count;
++
++ /* First we want to make sure we power off everything
++ * and then enter FW flashing state */
++ poweroff_seq(pdev);
++ ret = fw_flashing_seq(pdev);
++ } else {
++ dev_err(dev, "unknown state\n");
++ ret = -EINVAL;
++ }
++
++ return ret ? ret : count;
++}
++
++static ssize_t state_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev);
++
++ switch (apb->state) {
++ case ARCHE_PLATFORM_STATE_OFF:
++ return sprintf(buf, "off%s\n",
++ apb->init_disabled ? ",disabled" : "");
++ case ARCHE_PLATFORM_STATE_ACTIVE:
++ return sprintf(buf, "active\n");
++ case ARCHE_PLATFORM_STATE_STANDBY:
++ return sprintf(buf, "standby\n");
++ case ARCHE_PLATFORM_STATE_FW_FLASHING:
++ return sprintf(buf, "fw_flashing\n");
++ default:
++ return sprintf(buf, "unknown state\n");
++ }
++}
++
++static DEVICE_ATTR_RW(state);
++
++static int apb_ctrl_get_devtree_data(struct platform_device *pdev,
++ struct arche_apb_ctrl_drvdata *apb)
++{
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ int ret;
++
++ apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0);
++ if (apb->resetn_gpio < 0) {
++ dev_err(dev, "failed to get reset gpio\n");
++ return apb->resetn_gpio;
++ }
++ ret = devm_gpio_request_one(dev, apb->resetn_gpio,
++ GPIOF_OUT_INIT_LOW, "apb-reset");
++ if (ret) {
++ dev_err(dev, "Failed requesting reset gpio %d\n",
++ apb->resetn_gpio);
++ return ret;
++ }
++
++ apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0);
++ if (apb->boot_ret_gpio < 0) {
++ dev_err(dev, "failed to get boot retention gpio\n");
++ return apb->boot_ret_gpio;
++ }
++ ret = devm_gpio_request_one(dev, apb->boot_ret_gpio,
++ GPIOF_OUT_INIT_LOW, "boot retention");
++ if (ret) {
++ dev_err(dev, "Failed requesting bootret gpio %d\n",
++ apb->boot_ret_gpio);
++ return ret;
++ }
++
++ /* It's not mandatory to support power management interface */
++ apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0);
++ if (apb->pwroff_gpio < 0) {
++ dev_err(dev, "failed to get power off gpio\n");
++ return apb->pwroff_gpio;
++ }
++ ret = devm_gpio_request_one(dev, apb->pwroff_gpio,
++ GPIOF_IN, "pwroff_n");
++ if (ret) {
++ dev_err(dev, "Failed requesting pwroff_n gpio %d\n",
++ apb->pwroff_gpio);
++ return ret;
++ }
++
++ /* Do not make clock mandatory as of now (for DB3) */
++ apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0);
++ if (apb->clk_en_gpio < 0) {
++ dev_warn(dev, "failed to get clock en gpio\n");
++ } else if (gpio_is_valid(apb->clk_en_gpio)) {
++ ret = devm_gpio_request_one(dev, apb->clk_en_gpio,
++ GPIOF_OUT_INIT_LOW, "apb_clk_en");
++ if (ret) {
++ dev_warn(dev, "Failed requesting APB clock en gpio %d\n",
++ apb->clk_en_gpio);
++ return ret;
++ }
++ }
++
++ apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0);
++ if (apb->pwrdn_gpio < 0)
++ dev_warn(dev, "failed to get power down gpio\n");
++
++ /* Regulators are optional, as we may have fixed supply coming in */
++ apb->vcore = devm_regulator_get(dev, "vcore");
++ if (IS_ERR(apb->vcore))
++ dev_warn(dev, "no core regulator found\n");
++
++ apb->vio = devm_regulator_get(dev, "vio");
++ if (IS_ERR(apb->vio))
++ dev_warn(dev, "no IO regulator found\n");
++
++ apb->pinctrl = devm_pinctrl_get(&pdev->dev);
++ if (IS_ERR(apb->pinctrl)) {
++ dev_err(&pdev->dev, "could not get pinctrl handle\n");
++ return PTR_ERR(apb->pinctrl);
++ }
++ apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default");
++ if (IS_ERR(apb->pin_default)) {
++ dev_err(&pdev->dev, "could not get default pin state\n");
++ return PTR_ERR(apb->pin_default);
++ }
++
++ /* Only applicable for platform >= V2 */
++ apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0);
++ if (apb->spi_en_gpio >= 0) {
++ if (of_property_read_bool(pdev->dev.of_node,
++ "spi-en-active-high"))
++ apb->spi_en_polarity_high = true;
++ }
++
++ return 0;
++}
++
++static int arche_apb_ctrl_probe(struct platform_device *pdev)
++{
++ int ret;
++ struct arche_apb_ctrl_drvdata *apb;
++ struct device *dev = &pdev->dev;
++
++ apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL);
++ if (!apb)
++ return -ENOMEM;
++
++ ret = apb_ctrl_get_devtree_data(pdev, apb);
++ if (ret) {
++ dev_err(dev, "failed to get apb devicetree data %d\n", ret);
++ return ret;
++ }
++
++ /* Initially set APB to OFF state */
++ apb->state = ARCHE_PLATFORM_STATE_OFF;
++ /* Check whether device needs to be enabled on boot */
++ if (of_property_read_bool(pdev->dev.of_node, "arche,init-disable"))
++ apb->init_disabled = true;
++
++ platform_set_drvdata(pdev, apb);
++
++ /* Create sysfs interface to allow user to change state dynamically */
++ ret = device_create_file(dev, &dev_attr_state);
++ if (ret) {
++ dev_err(dev, "failed to create state file in sysfs\n");
++ return ret;
++ }
++
++ dev_info(&pdev->dev, "Device registered successfully\n");
++ return 0;
++}
++
++static int arche_apb_ctrl_remove(struct platform_device *pdev)
++{
++ device_remove_file(&pdev->dev, &dev_attr_state);
++ poweroff_seq(pdev);
++ platform_set_drvdata(pdev, NULL);
++
++ return 0;
++}
++
++static int arche_apb_ctrl_suspend(struct device *dev)
++{
++ /*
++ * If timing profile permits, we may shutdown bridge
++ * completely
++ *
++ * TODO: sequence ??
++ *
++ * Also, need to make sure we meet precondition for unipro suspend
++ * Precondition: Definition ???
++ */
++ return 0;
++}
++
++static int arche_apb_ctrl_resume(struct device *dev)
++{
++ /*
++ * Atleast for ES2 we have to meet the delay requirement between
++ * unipro switch and AP bridge init, depending on whether bridge is in
++ * OFF state or standby state.
++ *
++ * Based on whether bridge is in standby or OFF state we may have to
++ * assert multiple signals. Please refer to WDM spec, for more info.
++ *
++ */
++ return 0;
++}
++
++static void arche_apb_ctrl_shutdown(struct platform_device *pdev)
++{
++ apb_ctrl_poweroff(&pdev->dev);
++}
++
++static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend,
++ arche_apb_ctrl_resume);
++
++static struct of_device_id arche_apb_ctrl_of_match[] = {
++ { .compatible = "usbffff,2", },
++ { },
++};
++
++static struct platform_driver arche_apb_ctrl_device_driver = {
++ .probe = arche_apb_ctrl_probe,
++ .remove = arche_apb_ctrl_remove,
++ .shutdown = arche_apb_ctrl_shutdown,
++ .driver = {
++ .name = "arche-apb-ctrl",
++ .pm = &arche_apb_ctrl_pm_ops,
++ .of_match_table = arche_apb_ctrl_of_match,
++ }
++};
++
++int __init arche_apb_init(void)
++{
++ return platform_driver_register(&arche_apb_ctrl_device_driver);
++}
++
++void __exit arche_apb_exit(void)
++{
++ platform_driver_unregister(&arche_apb_ctrl_device_driver);
++}
+--- /dev/null
++++ b/drivers/greybus/arche-platform.c
+@@ -0,0 +1,828 @@
++/*
++ * Arche Platform driver to enable Unipro link.
++ *
++ * Copyright 2014-2015 Google Inc.
++ * Copyright 2014-2015 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/gpio.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/of_gpio.h>
++#include <linux/of_platform.h>
++#include <linux/pinctrl/consumer.h>
++#include <linux/platform_device.h>
++#include <linux/pm.h>
++#include <linux/interrupt.h>
++#include <linux/irq.h>
++#include <linux/suspend.h>
++#include <linux/time.h>
++#include "arche_platform.h"
++#include "greybus.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) */
++ WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */
++ WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */
++ WD_STATE_COLDBOOT_START, /* Cold boot process started */
++ WD_STATE_STANDBYBOOT_START, /* Not used */
++ WD_STATE_TIMESYNC,
++};
++
++struct arche_platform_drvdata {
++ /* Control GPIO signals to and from AP <=> SVC */
++ 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 */
++
++ enum arche_platform_state state;
++
++ int svc_refclk_req;
++ struct clk *svc_ref_clk;
++
++ struct pinctrl *pinctrl;
++ struct pinctrl_state *pin_default;
++
++ int num_apbs;
++
++ enum svc_wakedetect_state wake_detect_state;
++ int wake_detect_irq;
++ spinlock_t wake_lock; /* Protect wake_detect_state */
++ struct mutex platform_state_mutex; /* Protect state */
++ wait_queue_head_t wq; /* WQ for arche_pdata->state */
++ unsigned long wake_detect_start;
++ struct notifier_block pm_notifier;
++
++ struct device *dev;
++ struct gb_timesync_svc *timesync_svc_pdata;
++};
++
++static int arche_apb_bootret_assert(struct device *dev, void *data)
++{
++ apb_bootret_assert(dev);
++ return 0;
++}
++
++static int arche_apb_bootret_deassert(struct device *dev, void *data)
++{
++ apb_bootret_deassert(dev);
++ return 0;
++}
++
++/* Requires calling context to hold arche_pdata->platform_state_mutex */
++static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
++ enum arche_platform_state state)
++{
++ arche_pdata->state = state;
++}
++
++/*
++ * arche_platform_change_state: Change the operational state
++ *
++ * This exported function allows external drivers to change the state
++ * of the arche-platform driver.
++ * Note that this function only supports transitions between two states
++ * with limited functionality.
++ *
++ * - ARCHE_PLATFORM_STATE_TIME_SYNC:
++ * Once set, allows timesync operations between SVC <=> AP and makes
++ * sure that arche-platform driver ignores any subsequent events/pulses
++ * from SVC over wake/detect.
++ *
++ * - ARCHE_PLATFORM_STATE_ACTIVE:
++ * Puts back driver to active state, where any pulse from SVC on wake/detect
++ * line would trigger either cold/standby boot.
++ * Note: Transition request from this function does not trigger cold/standby
++ * boot. It just puts back driver book keeping variable back to ACTIVE
++ * state and restores the interrupt.
++ *
++ * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
++ * satisfy the requested state-transition or -EINVAL for all other
++ * state-transition requests.
++ */
++int arche_platform_change_state(enum arche_platform_state state,
++ struct gb_timesync_svc *timesync_svc_pdata)
++{
++ struct arche_platform_drvdata *arche_pdata;
++ struct platform_device *pdev;
++ struct device_node *np;
++ int ret = -EAGAIN;
++ unsigned long flags;
++
++ np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
++ if (!np) {
++ pr_err("google,arche-platform device node not found\n");
++ return -ENODEV;
++ }
++
++ pdev = of_find_device_by_node(np);
++ if (!pdev) {
++ pr_err("arche-platform device not found\n");
++ return -ENODEV;
++ }
++
++ arche_pdata = platform_get_drvdata(pdev);
++
++ mutex_lock(&arche_pdata->platform_state_mutex);
++ spin_lock_irqsave(&arche_pdata->wake_lock, flags);
++
++ if (arche_pdata->state == state) {
++ ret = 0;
++ goto exit;
++ }
++
++ switch (state) {
++ case ARCHE_PLATFORM_STATE_TIME_SYNC:
++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
++ ret = -EINVAL;
++ goto exit;
++ }
++ if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
++ dev_err(arche_pdata->dev,
++ "driver busy with wake/detect line ops\n");
++ goto exit;
++ }
++ device_for_each_child(arche_pdata->dev, NULL,
++ arche_apb_bootret_assert);
++ arche_pdata->wake_detect_state = WD_STATE_TIMESYNC;
++ break;
++ case ARCHE_PLATFORM_STATE_ACTIVE:
++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) {
++ ret = -EINVAL;
++ goto exit;
++ }
++ device_for_each_child(arche_pdata->dev, NULL,
++ arche_apb_bootret_deassert);
++ arche_pdata->wake_detect_state = WD_STATE_IDLE;
++ break;
++ case ARCHE_PLATFORM_STATE_OFF:
++ case ARCHE_PLATFORM_STATE_STANDBY:
++ case ARCHE_PLATFORM_STATE_FW_FLASHING:
++ dev_err(arche_pdata->dev, "busy, request to retry later\n");
++ goto exit;
++ default:
++ ret = -EINVAL;
++ dev_err(arche_pdata->dev,
++ "invalid state transition request\n");
++ goto exit;
++ }
++ arche_pdata->timesync_svc_pdata = timesync_svc_pdata;
++ arche_platform_set_state(arche_pdata, state);
++ if (state == ARCHE_PLATFORM_STATE_ACTIVE)
++ wake_up(&arche_pdata->wq);
++
++ ret = 0;
++exit:
++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++ of_node_put(np);
++ return ret;
++}
++EXPORT_SYMBOL_GPL(arche_platform_change_state);
++
++/* Requires arche_pdata->wake_lock is held by calling context */
++static void arche_platform_set_wake_detect_state(
++ struct arche_platform_drvdata *arche_pdata,
++ enum svc_wakedetect_state state)
++{
++ arche_pdata->wake_detect_state = state;
++}
++
++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_poweroff(struct device *dev, void *data)
++{
++ apb_ctrl_poweroff(dev);
++
++ /* Enable HUB3613 into HUB mode. */
++ if (usb3613_hub_mode_ctrl(false))
++ dev_warn(dev, "failed to control hub device\n");
++
++ return 0;
++}
++
++static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
++{
++ /* Enable interrupt here, to read event back from SVC */
++ gpio_direction_input(arche_pdata->wake_detect_gpio);
++ enable_irq(arche_pdata->wake_detect_irq);
++}
++
++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->wake_lock, flags);
++ if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
++ /* Something is wrong */
++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
++ return IRQ_HANDLED;
++ }
++
++ arche_platform_set_wake_detect_state(arche_pdata,
++ WD_STATE_COLDBOOT_START);
++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
++
++ /* It should complete power cycle, so first make sure it is poweroff */
++ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
++
++ /* Bring APB out of reset: cold boot sequence */
++ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
++
++ /* Enable HUB3613 into HUB mode. */
++ if (usb3613_hub_mode_ctrl(true))
++ dev_warn(arche_pdata->dev, "failed to control hub device\n");
++
++ spin_lock_irqsave(&arche_pdata->wake_lock, flags);
++ arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
++ spin_unlock_irqrestore(&arche_pdata->wake_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->wake_lock, flags);
++
++ if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) {
++ gb_timesync_irq(arche_pdata->timesync_svc_pdata);
++ goto exit;
++ }
++
++ 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))) {
++ arche_platform_set_wake_detect_state(arche_pdata,
++ 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_platform_set_wake_detect_state(arche_pdata,
++ WD_STATE_COLDBOOT_TRIG);
++ spin_unlock_irqrestore(
++ &arche_pdata->wake_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;
++ /*
++ * 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_platform_set_wake_detect_state(arche_pdata,
++ WD_STATE_BOOT_INIT);
++ }
++ }
++
++exit:
++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
++
++ return IRQ_HANDLED;
++}
++
++/*
++ * Requires arche_pdata->platform_state_mutex to be held
++ */
++static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
++{
++ int ret;
++
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
++ return 0;
++
++ dev_info(arche_pdata->dev, "Booting from cold boot state\n");
++
++ svc_reset_onoff(arche_pdata->svc_reset_gpio,
++ arche_pdata->is_reset_act_hi);
++
++ gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
++ usleep_range(100, 200);
++
++ ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
++ if (ret) {
++ dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
++ ret);
++ return ret;
++ }
++
++ /* bring SVC out of reset */
++ svc_reset_onoff(arche_pdata->svc_reset_gpio,
++ !arche_pdata->is_reset_act_hi);
++
++ arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
++
++ return 0;
++}
++
++/*
++ * Requires arche_pdata->platform_state_mutex to be held
++ */
++static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
++{
++ int ret;
++
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
++ return 0;
++
++ dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
++
++ svc_reset_onoff(arche_pdata->svc_reset_gpio,
++ arche_pdata->is_reset_act_hi);
++
++ gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
++
++ usleep_range(100, 200);
++
++ ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
++ if (ret) {
++ dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
++ ret);
++ return ret;
++ }
++
++ svc_reset_onoff(arche_pdata->svc_reset_gpio,
++ !arche_pdata->is_reset_act_hi);
++
++ arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
++
++ return 0;
++}
++
++/*
++ * Requires arche_pdata->platform_state_mutex to be held
++ */
++static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
++{
++ unsigned long flags;
++
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
++ return;
++
++ /* If in fw_flashing mode, then no need to repeate things again */
++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
++ disable_irq(arche_pdata->wake_detect_irq);
++
++ spin_lock_irqsave(&arche_pdata->wake_lock, flags);
++ arche_platform_set_wake_detect_state(arche_pdata,
++ WD_STATE_IDLE);
++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
++ }
++
++ clk_disable_unprepare(arche_pdata->svc_ref_clk);
++
++ /* As part of exit, put APB back in reset state */
++ svc_reset_onoff(arche_pdata->svc_reset_gpio,
++ arche_pdata->is_reset_act_hi);
++
++ arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
++}
++
++static ssize_t state_store(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++ struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
++ int ret = 0;
++
++retry:
++ mutex_lock(&arche_pdata->platform_state_mutex);
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++ ret = wait_event_interruptible(
++ arche_pdata->wq,
++ arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC);
++ if (ret)
++ return ret;
++ goto retry;
++ }
++
++ if (sysfs_streq(buf, "off")) {
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
++ goto exit;
++
++ /* 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)
++ goto exit;
++
++ /* First we want to make sure we power off everything
++ * and then activate back again */
++ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
++ arche_platform_poweroff_seq(arche_pdata);
++
++ arche_platform_wd_irq_en(arche_pdata);
++ ret = arche_platform_coldboot_seq(arche_pdata);
++ if (ret)
++ goto exit;
++
++ } else if (sysfs_streq(buf, "standby")) {
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
++ goto exit;
++
++ dev_warn(arche_pdata->dev, "standby state not supported\n");
++ } else if (sysfs_streq(buf, "fw_flashing")) {
++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
++ goto exit;
++
++ /*
++ * Here we only control SVC.
++ *
++ * In case of FW_FLASHING mode we do not want to control
++ * APBs, as in case of V2, SPI bus is shared between both
++ * the APBs. So let user chose which APB he wants to flash.
++ */
++ arche_platform_poweroff_seq(arche_pdata);
++
++ ret = arche_platform_fw_flashing_seq(arche_pdata);
++ if (ret)
++ goto exit;
++ } else {
++ dev_err(arche_pdata->dev, "unknown state\n");
++ ret = -EINVAL;
++ }
++
++exit:
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++ return ret ? ret : count;
++}
++
++static ssize_t state_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
++
++ switch (arche_pdata->state) {
++ case ARCHE_PLATFORM_STATE_OFF:
++ return sprintf(buf, "off\n");
++ case ARCHE_PLATFORM_STATE_ACTIVE:
++ return sprintf(buf, "active\n");
++ case ARCHE_PLATFORM_STATE_STANDBY:
++ return sprintf(buf, "standby\n");
++ case ARCHE_PLATFORM_STATE_FW_FLASHING:
++ return sprintf(buf, "fw_flashing\n");
++ case ARCHE_PLATFORM_STATE_TIME_SYNC:
++ return sprintf(buf, "time_sync\n");
++ default:
++ return sprintf(buf, "unknown state\n");
++ }
++}
++
++static DEVICE_ATTR_RW(state);
++
++static int arche_platform_pm_notifier(struct notifier_block *notifier,
++ unsigned long pm_event, void *unused)
++{
++ struct arche_platform_drvdata *arche_pdata =
++ container_of(notifier, struct arche_platform_drvdata,
++ pm_notifier);
++ int ret = NOTIFY_DONE;
++
++ mutex_lock(&arche_pdata->platform_state_mutex);
++ switch (pm_event) {
++ case PM_SUSPEND_PREPARE:
++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
++ ret = NOTIFY_STOP;
++ break;
++ }
++ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
++ arche_platform_poweroff_seq(arche_pdata);
++ break;
++ case PM_POST_SUSPEND:
++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
++ break;
++
++ arche_platform_wd_irq_en(arche_pdata);
++ arche_platform_coldboot_seq(arche_pdata);
++ break;
++ default:
++ break;
++ }
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++
++ return ret;
++}
++
++static int arche_platform_probe(struct platform_device *pdev)
++{
++ struct arche_platform_drvdata *arche_pdata;
++ struct device *dev = &pdev->dev;
++ struct device_node *np = dev->of_node;
++ int ret;
++
++ arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
++ if (!arche_pdata)
++ return -ENOMEM;
++
++ /* setup svc reset gpio */
++ arche_pdata->is_reset_act_hi = of_property_read_bool(np,
++ "svc,reset-active-high");
++ arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
++ if (arche_pdata->svc_reset_gpio < 0) {
++ dev_err(dev, "failed to get reset-gpio\n");
++ return arche_pdata->svc_reset_gpio;
++ }
++ ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
++ if (ret) {
++ dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
++ return ret;
++ }
++ ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
++ arche_pdata->is_reset_act_hi);
++ if (ret) {
++ dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
++ return ret;
++ }
++ arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
++
++ arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
++ "svc,sysboot-gpio", 0);
++ if (arche_pdata->svc_sysboot_gpio < 0) {
++ dev_err(dev, "failed to get sysboot gpio\n");
++ return arche_pdata->svc_sysboot_gpio;
++ }
++ ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
++ if (ret) {
++ dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
++ return ret;
++ }
++ ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
++ if (ret) {
++ dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
++ return ret;
++ }
++
++ /* setup the clock request gpio first */
++ arche_pdata->svc_refclk_req = of_get_named_gpio(np,
++ "svc,refclk-req-gpio", 0);
++ if (arche_pdata->svc_refclk_req < 0) {
++ dev_err(dev, "failed to get svc clock-req gpio\n");
++ return arche_pdata->svc_refclk_req;
++ }
++ ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
++ if (ret) {
++ dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
++ return ret;
++ }
++ ret = gpio_direction_input(arche_pdata->svc_refclk_req);
++ if (ret) {
++ dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
++ return ret;
++ }
++
++ /* setup refclk2 to follow the pin */
++ arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
++ if (IS_ERR(arche_pdata->svc_ref_clk)) {
++ ret = PTR_ERR(arche_pdata->svc_ref_clk);
++ dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
++ return ret;
++ }
++
++ platform_set_drvdata(pdev, arche_pdata);
++
++ arche_pdata->num_apbs = of_get_child_count(np);
++ dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
++
++ 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;
++ return ret;
++ }
++
++ ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
++ if (ret) {
++ dev_err(dev, "Failed requesting wake_detect gpio %d\n",
++ arche_pdata->wake_detect_gpio);
++ return ret;
++ }
++
++ arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
++
++ arche_pdata->dev = &pdev->dev;
++
++ spin_lock_init(&arche_pdata->wake_lock);
++ mutex_init(&arche_pdata->platform_state_mutex);
++ init_waitqueue_head(&arche_pdata->wq);
++ 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;
++ }
++ 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");
++ return ret;
++ }
++
++ ret = of_platform_populate(np, NULL, NULL, dev);
++ if (ret) {
++ dev_err(dev, "failed to populate child nodes %d\n", ret);
++ goto err_device_remove;
++ }
++
++ arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
++ ret = register_pm_notifier(&arche_pdata->pm_notifier);
++
++ if (ret) {
++ dev_err(dev, "failed to register pm notifier %d\n", ret);
++ goto err_device_remove;
++ }
++
++ /* Register callback pointer */
++ arche_platform_change_state_cb = arche_platform_change_state;
++
++ /* Explicitly power off if requested */
++ if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
++ mutex_lock(&arche_pdata->platform_state_mutex);
++ ret = arche_platform_coldboot_seq(arche_pdata);
++ if (ret) {
++ dev_err(dev, "Failed to cold boot svc %d\n", ret);
++ goto err_coldboot;
++ }
++ arche_platform_wd_irq_en(arche_pdata);
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++ }
++
++ dev_info(dev, "Device registered successfully\n");
++ return 0;
++
++err_coldboot:
++ mutex_unlock(&arche_pdata->platform_state_mutex);
++err_device_remove:
++ device_remove_file(&pdev->dev, &dev_attr_state);
++ return ret;
++}
++
++static int arche_remove_child(struct device *dev, void *unused)
++{
++ struct platform_device *pdev = to_platform_device(dev);
++
++ platform_device_unregister(pdev);
++
++ return 0;
++}
++
++static int arche_platform_remove(struct platform_device *pdev)
++{
++ struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
++
++ unregister_pm_notifier(&arche_pdata->pm_notifier);
++ device_remove_file(&pdev->dev, &dev_attr_state);
++ device_for_each_child(&pdev->dev, NULL, arche_remove_child);
++ arche_platform_poweroff_seq(arche_pdata);
++ platform_set_drvdata(pdev, NULL);
++
++ if (usb3613_hub_mode_ctrl(false))
++ dev_warn(arche_pdata->dev, "failed to control hub device\n");
++ /* TODO: Should we do anything more here ?? */
++ return 0;
++}
++
++static int arche_platform_suspend(struct device *dev)
++{
++ /*
++ * If timing profile premits, we may shutdown bridge
++ * completely
++ *
++ * TODO: sequence ??
++ *
++ * Also, need to make sure we meet precondition for unipro suspend
++ * Precondition: Definition ???
++ */
++ return 0;
++}
++
++static int arche_platform_resume(struct device *dev)
++{
++ /*
++ * Atleast for ES2 we have to meet the delay requirement between
++ * unipro switch and AP bridge init, depending on whether bridge is in
++ * OFF state or standby state.
++ *
++ * Based on whether bridge is in standby or OFF state we may have to
++ * assert multiple signals. Please refer to WDM spec, for more info.
++ *
++ */
++ return 0;
++}
++
++static void arche_platform_shutdown(struct platform_device *pdev)
++{
++ struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
++
++ arche_platform_poweroff_seq(arche_pdata);
++
++ usb3613_hub_mode_ctrl(false);
++}
++
++static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
++ arche_platform_suspend,
++ arche_platform_resume);
++
++static struct of_device_id arche_platform_of_match[] = {
++ { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
++ { },
++};
++
++static struct of_device_id arche_combined_id[] = {
++ { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
++ { .compatible = "usbffff,2", },
++ { },
++};
++MODULE_DEVICE_TABLE(of, arche_combined_id);
++
++static struct platform_driver arche_platform_device_driver = {
++ .probe = arche_platform_probe,
++ .remove = arche_platform_remove,
++ .shutdown = arche_platform_shutdown,
++ .driver = {
++ .name = "arche-platform-ctrl",
++ .pm = &arche_platform_pm_ops,
++ .of_match_table = arche_platform_of_match,
++ }
++};
++
++static int __init arche_init(void)
++{
++ int retval;
++
++ retval = platform_driver_register(&arche_platform_device_driver);
++ if (retval)
++ return retval;
++
++ retval = arche_apb_init();
++ if (retval)
++ platform_driver_unregister(&arche_platform_device_driver);
++
++ return retval;
++}
++module_init(arche_init);
++
++static void __exit arche_exit(void)
++{
++ arche_apb_exit();
++ platform_driver_unregister(&arche_platform_device_driver);
++}
++module_exit(arche_exit);
++
++MODULE_LICENSE("GPL v2");
++MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
++MODULE_DESCRIPTION("Arche Platform Driver");
+--- /dev/null
++++ b/drivers/greybus/arche_platform.h
+@@ -0,0 +1,39 @@
++/*
++ * Arche Platform driver to enable Unipro link.
++ *
++ * Copyright 2015-2016 Google Inc.
++ * Copyright 2015-2016 Linaro Ltd.
++ *
++ * Released under the GPLv2 only.
++ */
++
++#ifndef __ARCHE_PLATFORM_H
++#define __ARCHE_PLATFORM_H
++
++#include "timesync.h"
++
++enum arche_platform_state {
++ ARCHE_PLATFORM_STATE_OFF,
++ ARCHE_PLATFORM_STATE_ACTIVE,
++ ARCHE_PLATFORM_STATE_STANDBY,
++ ARCHE_PLATFORM_STATE_FW_FLASHING,
++ ARCHE_PLATFORM_STATE_TIME_SYNC,
++};
++
++int arche_platform_change_state(enum arche_platform_state state,
++ struct gb_timesync_svc *pdata);
++
++extern int (*arche_platform_change_state_cb)(enum arche_platform_state state,
++ struct gb_timesync_svc *pdata);
++int __init arche_apb_init(void);
++void __exit arche_apb_exit(void);
++
++/* Operational states for the APB device */
++int apb_ctrl_coldboot(struct device *dev);
++int apb_ctrl_fw_flashing(struct device *dev);
++int apb_ctrl_standby_boot(struct device *dev);
++void apb_ctrl_poweroff(struct device *dev);
++void apb_bootret_assert(struct device *dev);
++void apb_bootret_deassert(struct device *dev);
++
++#endif /* __ARCHE_PLATFORM_H */