diff options
| author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-15 13:54:11 +0200 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-15 13:54:11 +0200 |
| commit | c1aa01c96e590714d99c5f17cfae1b14dec8bdee (patch) | |
| tree | 5811422f65efdff3fd89fdaf9a8c6d7d1cc4d06b /greybus_arche.patch | |
| parent | 4758ee8bc89a86c2110b9e85878538ced8045ef5 (diff) | |
| download | patches-c1aa01c96e590714d99c5f17cfae1b14dec8bdee.tar.gz | |
greybus patches
Diffstat (limited to 'greybus_arche.patch')
| -rw-r--r-- | greybus_arche.patch | 1404 |
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 */ |
