17fa60654SVaibhav Hiremath /* 27fa60654SVaibhav Hiremath * Arche Platform driver to enable Unipro link. 37fa60654SVaibhav Hiremath * 47fa60654SVaibhav Hiremath * Copyright 2014-2015 Google Inc. 57fa60654SVaibhav Hiremath * Copyright 2014-2015 Linaro Ltd. 67fa60654SVaibhav Hiremath * 77fa60654SVaibhav Hiremath * Released under the GPLv2 only. 87fa60654SVaibhav Hiremath */ 97fa60654SVaibhav Hiremath 107fa60654SVaibhav Hiremath #include <linux/clk.h> 11a463fc15SVaibhav Hiremath #include <linux/delay.h> 123b858df0SViresh Kumar #include <linux/gpio.h> 133b858df0SViresh Kumar #include <linux/init.h> 143b858df0SViresh Kumar #include <linux/module.h> 157fa60654SVaibhav Hiremath #include <linux/of_gpio.h> 163b858df0SViresh Kumar #include <linux/of_platform.h> 177fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h> 183b858df0SViresh Kumar #include <linux/platform_device.h> 193b858df0SViresh Kumar #include <linux/pm.h> 20f760bbfbSVaibhav Hiremath #include <linux/interrupt.h> 21f760bbfbSVaibhav Hiremath #include <linux/irq.h> 229160b7c7SDavid Lin #include <linux/suspend.h> 23f760bbfbSVaibhav Hiremath #include <linux/time.h> 241e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 257fa60654SVaibhav Hiremath 26ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h> 27ad4d3f95SVaibhav Hiremath 28f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS 30 29f760bbfbSVaibhav Hiremath 30685353c1SVaibhav Hiremath enum svc_wakedetect_state { 31685353c1SVaibhav Hiremath WD_STATE_IDLE, /* Default state = pulled high/low */ 32685353c1SVaibhav Hiremath WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 33685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 34685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 35685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_START, /* Cold boot process started */ 36685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_START, /* Not used */ 37685353c1SVaibhav Hiremath }; 38685353c1SVaibhav Hiremath 397fa60654SVaibhav Hiremath struct arche_platform_drvdata { 407fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 417fa60654SVaibhav Hiremath int svc_reset_gpio; 427fa60654SVaibhav Hiremath bool is_reset_act_hi; 437fa60654SVaibhav Hiremath int svc_sysboot_gpio; 44a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 457fa60654SVaibhav Hiremath 46e74d04a5SVaibhav Hiremath enum arche_platform_state state; 47e74d04a5SVaibhav Hiremath 487fe93014SDavid Lin int svc_refclk_req; 497fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 507fa60654SVaibhav Hiremath 517fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 527fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 537fa60654SVaibhav Hiremath 547fa60654SVaibhav Hiremath int num_apbs; 55a463fc15SVaibhav Hiremath 56685353c1SVaibhav Hiremath enum svc_wakedetect_state wake_detect_state; 57f760bbfbSVaibhav Hiremath int wake_detect_irq; 586c1ca56dSVaibhav Hiremath spinlock_t wake_lock; /* Protect wake_detect_state */ 59886aba55SVaibhav Hiremath struct mutex platform_state_mutex; /* Protect state */ 60f760bbfbSVaibhav Hiremath unsigned long wake_detect_start; 619160b7c7SDavid Lin struct notifier_block pm_notifier; 62685353c1SVaibhav Hiremath 63a463fc15SVaibhav Hiremath struct device *dev; 647fa60654SVaibhav Hiremath }; 657fa60654SVaibhav Hiremath 66c4058b79SBryan O'Donoghue /* Requires calling context to hold arche_pdata->spinlock */ 67c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, 68c4058b79SBryan O'Donoghue enum arche_platform_state state) 69c4058b79SBryan O'Donoghue { 70c4058b79SBryan O'Donoghue arche_pdata->state = state; 71c4058b79SBryan O'Donoghue } 72c4058b79SBryan O'Donoghue 73886aba55SVaibhav Hiremath /* 74886aba55SVaibhav Hiremath * arche_platform_change_state: Change the operational state 75886aba55SVaibhav Hiremath * 76886aba55SVaibhav Hiremath * This exported function allows external drivers to change the state 77886aba55SVaibhav Hiremath * of the arche-platform driver. 78886aba55SVaibhav Hiremath * Note that this function only supports transitions between two states 79886aba55SVaibhav Hiremath * with limited functionality. 80886aba55SVaibhav Hiremath * 81886aba55SVaibhav Hiremath * - ARCHE_PLATFORM_STATE_TIME_SYNC: 82886aba55SVaibhav Hiremath * Once set, allows timesync operations between SVC <=> AP and makes 83886aba55SVaibhav Hiremath * sure that arche-platform driver ignores any subsequent events/pulses 84886aba55SVaibhav Hiremath * from SVC over wake/detect. 85886aba55SVaibhav Hiremath * 86886aba55SVaibhav Hiremath * - ARCHE_PLATFORM_STATE_ACTIVE: 87886aba55SVaibhav Hiremath * Puts back driver to active state, where any pulse from SVC on wake/detect 88886aba55SVaibhav Hiremath * line would trigger either cold/standby boot. 89886aba55SVaibhav Hiremath * Note: Transition request from this function does not trigger cold/standby 90886aba55SVaibhav Hiremath * boot. It just puts back driver book keeping variable back to ACTIVE 91886aba55SVaibhav Hiremath * state and restores the interrupt. 92886aba55SVaibhav Hiremath * 93886aba55SVaibhav Hiremath * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently 94886aba55SVaibhav Hiremath * satisfy the requested state-transition or -EINVAL for all other 95886aba55SVaibhav Hiremath * state-transition requests. 96886aba55SVaibhav Hiremath */ 97886aba55SVaibhav Hiremath int arche_platform_change_state(enum arche_platform_state state) 98886aba55SVaibhav Hiremath { 99886aba55SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 100886aba55SVaibhav Hiremath struct platform_device *pdev; 101886aba55SVaibhav Hiremath struct device_node *np; 102886aba55SVaibhav Hiremath int ret = -EAGAIN; 103886aba55SVaibhav Hiremath unsigned long flags; 104886aba55SVaibhav Hiremath 105886aba55SVaibhav Hiremath np = of_find_compatible_node(NULL, NULL, "google,arche-platform"); 106886aba55SVaibhav Hiremath if (!np) { 107886aba55SVaibhav Hiremath pr_err("google,arche-platform device node not found\n"); 108886aba55SVaibhav Hiremath return -ENODEV; 109886aba55SVaibhav Hiremath } 110886aba55SVaibhav Hiremath 111886aba55SVaibhav Hiremath pdev = of_find_device_by_node(np); 112886aba55SVaibhav Hiremath if (!pdev) { 113886aba55SVaibhav Hiremath pr_err("arche-platform device not found\n"); 114886aba55SVaibhav Hiremath return -ENODEV; 115886aba55SVaibhav Hiremath } 116886aba55SVaibhav Hiremath 117886aba55SVaibhav Hiremath arche_pdata = platform_get_drvdata(pdev); 118886aba55SVaibhav Hiremath 119886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 120886aba55SVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 121886aba55SVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { 122886aba55SVaibhav Hiremath dev_err(arche_pdata->dev, 123886aba55SVaibhav Hiremath "driver busy with wake/detect line ops\n"); 124886aba55SVaibhav Hiremath goto exit; 125886aba55SVaibhav Hiremath } 126886aba55SVaibhav Hiremath 127886aba55SVaibhav Hiremath if (arche_pdata->state == state) { 128886aba55SVaibhav Hiremath ret = 0; 129886aba55SVaibhav Hiremath goto exit; 130886aba55SVaibhav Hiremath } 131886aba55SVaibhav Hiremath 132c4058b79SBryan O'Donoghue switch (state) { 133c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_TIME_SYNC: 134c4058b79SBryan O'Donoghue disable_irq(arche_pdata->wake_detect_irq); 135c4058b79SBryan O'Donoghue break; 136c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_ACTIVE: 137c4058b79SBryan O'Donoghue enable_irq(arche_pdata->wake_detect_irq); 138c4058b79SBryan O'Donoghue break; 139c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_OFF: 140c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_STANDBY: 141c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_FW_FLASHING: 142886aba55SVaibhav Hiremath dev_err(arche_pdata->dev, "busy, request to retry later\n"); 143886aba55SVaibhav Hiremath goto exit; 144c4058b79SBryan O'Donoghue default: 145c4058b79SBryan O'Donoghue ret = -EINVAL; 146c4058b79SBryan O'Donoghue dev_err(arche_pdata->dev, 147c4058b79SBryan O'Donoghue "invalid state transition request\n"); 148c4058b79SBryan O'Donoghue goto exit; 149886aba55SVaibhav Hiremath } 150c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, state); 151c4058b79SBryan O'Donoghue ret = 0; 152886aba55SVaibhav Hiremath exit: 153886aba55SVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 154886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 155886aba55SVaibhav Hiremath of_node_put(np); 156886aba55SVaibhav Hiremath return ret; 157886aba55SVaibhav Hiremath } 158886aba55SVaibhav Hiremath EXPORT_SYMBOL_GPL(arche_platform_change_state); 159886aba55SVaibhav Hiremath 160c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */ 161c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state( 162c4058b79SBryan O'Donoghue struct arche_platform_drvdata *arche_pdata, 163c4058b79SBryan O'Donoghue enum svc_wakedetect_state state) 164c4058b79SBryan O'Donoghue { 165c4058b79SBryan O'Donoghue arche_pdata->wake_detect_state = state; 166c4058b79SBryan O'Donoghue } 167c4058b79SBryan O'Donoghue 1687fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 1697fa60654SVaibhav Hiremath { 1707fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 1717fa60654SVaibhav Hiremath } 1727fa60654SVaibhav Hiremath 173f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data) 174f760bbfbSVaibhav Hiremath { 175f760bbfbSVaibhav Hiremath int ret; 176f760bbfbSVaibhav Hiremath 177f760bbfbSVaibhav Hiremath ret = apb_ctrl_coldboot(dev); 178f760bbfbSVaibhav Hiremath if (ret) 179f760bbfbSVaibhav Hiremath dev_warn(dev, "failed to coldboot\n"); 180f760bbfbSVaibhav Hiremath 181f760bbfbSVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 182f760bbfbSVaibhav Hiremath return 0; 183f760bbfbSVaibhav Hiremath } 184f760bbfbSVaibhav Hiremath 185fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data) 186fd60ac58SVaibhav Hiremath { 187fd60ac58SVaibhav Hiremath int ret; 188fd60ac58SVaibhav Hiremath 189fd60ac58SVaibhav Hiremath ret = apb_ctrl_fw_flashing(dev); 190fd60ac58SVaibhav Hiremath if (ret) 191fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to switch to fw flashing state\n"); 192fd60ac58SVaibhav Hiremath 193fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 194fd60ac58SVaibhav Hiremath return 0; 195fd60ac58SVaibhav Hiremath } 196fd60ac58SVaibhav Hiremath 197fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 198fd60ac58SVaibhav Hiremath { 199fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 200fd60ac58SVaibhav Hiremath 201e915ce48SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 202e915ce48SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 203e915ce48SVaibhav Hiremath dev_warn(dev, "failed to control hub device\n"); 204e915ce48SVaibhav Hiremath 205fd60ac58SVaibhav Hiremath return 0; 206fd60ac58SVaibhav Hiremath } 207fd60ac58SVaibhav Hiremath 2087ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) 20916fe18caSVaibhav Hiremath { 21016fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 21116fe18caSVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 21216fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 21316fe18caSVaibhav Hiremath } 21416fe18caSVaibhav Hiremath 215f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 216f760bbfbSVaibhav Hiremath { 217f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 218f760bbfbSVaibhav Hiremath unsigned long flags; 219f760bbfbSVaibhav Hiremath 2206c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 221f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 222f760bbfbSVaibhav Hiremath /* Something is wrong */ 2236c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 224f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 225f760bbfbSVaibhav Hiremath } 226f760bbfbSVaibhav Hiremath 227c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 228c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_START); 2296c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 230f760bbfbSVaibhav Hiremath 231ff788de0SVaibhav Hiremath /* It should complete power cycle, so first make sure it is poweroff */ 232ff788de0SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 233e915ce48SVaibhav Hiremath 234f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 235f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 236f760bbfbSVaibhav Hiremath 237a7ddda1fSVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 238a7ddda1fSVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 239a7ddda1fSVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 240a7ddda1fSVaibhav Hiremath 2416c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 242c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 2436c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 244f760bbfbSVaibhav Hiremath 245f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 246f760bbfbSVaibhav Hiremath } 247f760bbfbSVaibhav Hiremath 248f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 249f760bbfbSVaibhav Hiremath { 250f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 251f760bbfbSVaibhav Hiremath unsigned long flags; 252f760bbfbSVaibhav Hiremath 2536c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 254f760bbfbSVaibhav Hiremath 255f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 256f760bbfbSVaibhav Hiremath /* wake/detect rising */ 257f760bbfbSVaibhav Hiremath 258f760bbfbSVaibhav Hiremath /* 259f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 260f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 261f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 262f760bbfbSVaibhav Hiremath */ 263f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 264f760bbfbSVaibhav Hiremath if (time_before(jiffies, 265f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 266f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 267c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 268c4058b79SBryan O'Donoghue WD_STATE_IDLE); 269f760bbfbSVaibhav Hiremath } else { 270f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 271f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 272f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 273c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 274c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_TRIG); 2756c1ca56dSVaibhav Hiremath spin_unlock_irqrestore( 2766c1ca56dSVaibhav Hiremath &arche_pdata->wake_lock, 2776c1ca56dSVaibhav Hiremath flags); 278f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 279f760bbfbSVaibhav Hiremath } 280f760bbfbSVaibhav Hiremath } 281f760bbfbSVaibhav Hiremath } 282f760bbfbSVaibhav Hiremath } else { 283f760bbfbSVaibhav Hiremath /* wake/detect falling */ 284f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 285f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 286f760bbfbSVaibhav Hiremath /* 287f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 288f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 289f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 290f760bbfbSVaibhav Hiremath */ 291c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 292c4058b79SBryan O'Donoghue WD_STATE_BOOT_INIT); 293f760bbfbSVaibhav Hiremath } 294f760bbfbSVaibhav Hiremath } 295f760bbfbSVaibhav Hiremath 2966c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 297f760bbfbSVaibhav Hiremath 298f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 299f760bbfbSVaibhav Hiremath } 300f760bbfbSVaibhav Hiremath 301886aba55SVaibhav Hiremath /* 302886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 303886aba55SVaibhav Hiremath */ 304758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 305758ca99dSVaibhav Hiremath { 306758ca99dSVaibhav Hiremath int ret; 307758ca99dSVaibhav Hiremath 308599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 309599159b6SVaibhav Hiremath return 0; 310599159b6SVaibhav Hiremath 311758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 312758ca99dSVaibhav Hiremath 313758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 314758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 315758ca99dSVaibhav Hiremath 316758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 317758ca99dSVaibhav Hiremath usleep_range(100, 200); 318758ca99dSVaibhav Hiremath 319758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 320758ca99dSVaibhav Hiremath if (ret) { 321758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 322758ca99dSVaibhav Hiremath ret); 323758ca99dSVaibhav Hiremath return ret; 324758ca99dSVaibhav Hiremath } 325758ca99dSVaibhav Hiremath 326758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 327758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 328758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 329758ca99dSVaibhav Hiremath 330c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); 331e74d04a5SVaibhav Hiremath 332758ca99dSVaibhav Hiremath return 0; 333758ca99dSVaibhav Hiremath } 334758ca99dSVaibhav Hiremath 335886aba55SVaibhav Hiremath /* 336886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 337886aba55SVaibhav Hiremath */ 3387691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 3397691fed2SVaibhav Hiremath { 340599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 341599159b6SVaibhav Hiremath return; 342599159b6SVaibhav Hiremath 3437691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 3447691fed2SVaibhav Hiremath 3457691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3467691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3477691fed2SVaibhav Hiremath 3487691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 3497691fed2SVaibhav Hiremath 3507691fed2SVaibhav Hiremath usleep_range(100, 200); 3517691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3527691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 3537691fed2SVaibhav Hiremath 354c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); 3557691fed2SVaibhav Hiremath 3567691fed2SVaibhav Hiremath } 3577691fed2SVaibhav Hiremath 358886aba55SVaibhav Hiremath /* 359886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 360886aba55SVaibhav Hiremath */ 3615993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 3627fa60654SVaibhav Hiremath { 363f760bbfbSVaibhav Hiremath unsigned long flags; 364f760bbfbSVaibhav Hiremath 365599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 366599159b6SVaibhav Hiremath return; 367599159b6SVaibhav Hiremath 36825847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 36925847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 370d2320b2dSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 37116fd976cSVaibhav Hiremath 3726c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 373c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 374c4058b79SBryan O'Donoghue WD_STATE_IDLE); 3756c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 376b4c95fcaSVaibhav Hiremath 377d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 37825847ee7SVaibhav Hiremath } 37925847ee7SVaibhav Hiremath 3807fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 3817fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3827fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 383e74d04a5SVaibhav Hiremath 384c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 3857fa60654SVaibhav Hiremath } 3867fa60654SVaibhav Hiremath 3872923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 3882923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 3892923c58eSVaibhav Hiremath { 3902923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 3912923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 3922923c58eSVaibhav Hiremath int ret = 0; 3932923c58eSVaibhav Hiremath 394886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 395886aba55SVaibhav Hiremath 3962923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 3972923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 398886aba55SVaibhav Hiremath goto exit; 3992923c58eSVaibhav Hiremath 400fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 401fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 402fd60ac58SVaibhav Hiremath 4032923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 404ad4d3f95SVaibhav Hiremath 4052923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 4062923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 407886aba55SVaibhav Hiremath goto exit; 4082923c58eSVaibhav Hiremath 4097ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 4102923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 41116fe18caSVaibhav Hiremath 4122923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 4132923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 414886aba55SVaibhav Hiremath goto exit; 4152923c58eSVaibhav Hiremath 4162923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 4177691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 4187691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 419886aba55SVaibhav Hiremath goto exit; 4207691fed2SVaibhav Hiremath 4217691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 4227691fed2SVaibhav Hiremath * and then enter FW flashing state */ 423fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 424fd60ac58SVaibhav Hiremath 4257691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 426fd60ac58SVaibhav Hiremath 4277691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 428fd60ac58SVaibhav Hiremath 429fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 4302923c58eSVaibhav Hiremath } else { 4312923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 4322923c58eSVaibhav Hiremath ret = -EINVAL; 4332923c58eSVaibhav Hiremath } 4342923c58eSVaibhav Hiremath 435886aba55SVaibhav Hiremath exit: 436886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 4372923c58eSVaibhav Hiremath return ret ? ret : count; 4382923c58eSVaibhav Hiremath } 4392923c58eSVaibhav Hiremath 4402923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 4412923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 4422923c58eSVaibhav Hiremath { 4432923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 4442923c58eSVaibhav Hiremath 4452923c58eSVaibhav Hiremath switch (arche_pdata->state) { 4462923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 4472923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 4482923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 4492923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 4502923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 4512923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 4527691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 4537691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 454886aba55SVaibhav Hiremath case ARCHE_PLATFORM_STATE_TIME_SYNC: 455886aba55SVaibhav Hiremath return sprintf(buf, "time_sync\n"); 4562923c58eSVaibhav Hiremath default: 4572923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 4582923c58eSVaibhav Hiremath } 4592923c58eSVaibhav Hiremath } 4602923c58eSVaibhav Hiremath 4612923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 4622923c58eSVaibhav Hiremath 4639160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier, 4649160b7c7SDavid Lin unsigned long pm_event, void *unused) 4659160b7c7SDavid Lin { 4669160b7c7SDavid Lin struct arche_platform_drvdata *arche_pdata = 4679160b7c7SDavid Lin container_of(notifier, struct arche_platform_drvdata, 4689160b7c7SDavid Lin pm_notifier); 469886aba55SVaibhav Hiremath int ret = NOTIFY_DONE; 4709160b7c7SDavid Lin 471886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 4729160b7c7SDavid Lin switch (pm_event) { 4739160b7c7SDavid Lin case PM_SUSPEND_PREPARE: 474886aba55SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 475886aba55SVaibhav Hiremath ret = NOTIFY_STOP; 476886aba55SVaibhav Hiremath break; 477886aba55SVaibhav Hiremath } 4789160b7c7SDavid Lin device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 4799160b7c7SDavid Lin arche_platform_poweroff_seq(arche_pdata); 4809160b7c7SDavid Lin break; 4819160b7c7SDavid Lin case PM_POST_SUSPEND: 4827ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 48316fd976cSVaibhav Hiremath arche_platform_coldboot_seq(arche_pdata); 4849160b7c7SDavid Lin break; 4859160b7c7SDavid Lin default: 4869160b7c7SDavid Lin break; 4879160b7c7SDavid Lin } 488886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 4899160b7c7SDavid Lin 490886aba55SVaibhav Hiremath return ret; 4919160b7c7SDavid Lin } 4929160b7c7SDavid Lin 4937fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 4947fa60654SVaibhav Hiremath { 4957fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 4967fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 4977fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 4987fa60654SVaibhav Hiremath int ret; 4997fa60654SVaibhav Hiremath 5007fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 5017fa60654SVaibhav Hiremath if (!arche_pdata) 5027fa60654SVaibhav Hiremath return -ENOMEM; 5037fa60654SVaibhav Hiremath 5047fa60654SVaibhav Hiremath /* setup svc reset gpio */ 5057fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 5067fa60654SVaibhav Hiremath "svc,reset-active-high"); 5077fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 5087fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 5097fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 510f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 5117fa60654SVaibhav Hiremath } 5127fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 5137fa60654SVaibhav Hiremath if (ret) { 5147fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 5157fa60654SVaibhav Hiremath return ret; 5167fa60654SVaibhav Hiremath } 5177fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 5187fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 5197fa60654SVaibhav Hiremath if (ret) { 5207fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 5217fa60654SVaibhav Hiremath return ret; 5227fa60654SVaibhav Hiremath } 523c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 5247fa60654SVaibhav Hiremath 5257fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 5267fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 5277fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 5287fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 529f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 5307fa60654SVaibhav Hiremath } 5317fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 5327fa60654SVaibhav Hiremath if (ret) { 5337fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 5347fa60654SVaibhav Hiremath return ret; 5357fa60654SVaibhav Hiremath } 5367fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 5377fa60654SVaibhav Hiremath if (ret) { 5387fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 5397fa60654SVaibhav Hiremath return ret; 5407fa60654SVaibhav Hiremath } 5417fa60654SVaibhav Hiremath 5427fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 5437fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 5447fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 5457fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 5467fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 547f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 5487fa60654SVaibhav Hiremath } 5497fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 5507fa60654SVaibhav Hiremath if (ret) { 5517fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 5527fa60654SVaibhav Hiremath return ret; 5537fa60654SVaibhav Hiremath } 5547fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 5557fa60654SVaibhav Hiremath if (ret) { 5567fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 5577fa60654SVaibhav Hiremath return ret; 5587fa60654SVaibhav Hiremath } 5597fa60654SVaibhav Hiremath 5607fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 5617fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 5627fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 5637fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 5647fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 5657fa60654SVaibhav Hiremath return ret; 5667fa60654SVaibhav Hiremath } 5677fa60654SVaibhav Hiremath 5687fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 5697fa60654SVaibhav Hiremath 5707fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 5717fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 5727fa60654SVaibhav Hiremath 573a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 574a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 575a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 576a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 577758ca99dSVaibhav Hiremath return ret; 57872a8c24bSViresh Kumar } 5797fa60654SVaibhav Hiremath 580a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 581a463fc15SVaibhav Hiremath if (ret) { 582a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 583a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 584758ca99dSVaibhav Hiremath return ret; 585a463fc15SVaibhav Hiremath } 58616fd976cSVaibhav Hiremath 587c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 588a463fc15SVaibhav Hiremath 589a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 590a463fc15SVaibhav Hiremath 5916c1ca56dSVaibhav Hiremath spin_lock_init(&arche_pdata->wake_lock); 592886aba55SVaibhav Hiremath mutex_init(&arche_pdata->platform_state_mutex); 593f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 594f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 595f760bbfbSVaibhav Hiremath 596f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 597f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 598f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 599f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 600f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 601f760bbfbSVaibhav Hiremath if (ret) { 602f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 603f760bbfbSVaibhav Hiremath return ret; 604f760bbfbSVaibhav Hiremath } 60516fd976cSVaibhav Hiremath 606d7be800fSBryan O'Donoghue gpio_direction_input(arche_pdata->wake_detect_gpio); 607f760bbfbSVaibhav Hiremath 6082923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 6092923c58eSVaibhav Hiremath if (ret) { 6102923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 6112923c58eSVaibhav Hiremath return ret; 6122923c58eSVaibhav Hiremath } 6132923c58eSVaibhav Hiremath 614886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 615758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 616758ca99dSVaibhav Hiremath if (ret) { 617758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 6186743a6fdSVaibhav Hiremath goto err_coldboot; 619758ca99dSVaibhav Hiremath } 620758ca99dSVaibhav Hiremath 621fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 622fd60ac58SVaibhav Hiremath if (ret) { 623fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 6246743a6fdSVaibhav Hiremath goto err_populate; 625fd60ac58SVaibhav Hiremath } 626fd60ac58SVaibhav Hiremath 6279160b7c7SDavid Lin arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; 6289160b7c7SDavid Lin ret = register_pm_notifier(&arche_pdata->pm_notifier); 629886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 630886aba55SVaibhav Hiremath 6319160b7c7SDavid Lin if (ret) { 6329160b7c7SDavid Lin dev_err(dev, "failed to register pm notifier %d\n", ret); 6339160b7c7SDavid Lin goto err_populate; 6349160b7c7SDavid Lin } 6359160b7c7SDavid Lin 6367fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 63772a8c24bSViresh Kumar return 0; 6386743a6fdSVaibhav Hiremath 6396743a6fdSVaibhav Hiremath err_populate: 6406743a6fdSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 6416743a6fdSVaibhav Hiremath err_coldboot: 642886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 6436743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 6446743a6fdSVaibhav Hiremath return ret; 6457fa60654SVaibhav Hiremath } 6467fa60654SVaibhav Hiremath 647bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 648bc142bbbSVaibhav Hiremath { 649bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 650bc142bbbSVaibhav Hiremath 651bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 652bc142bbbSVaibhav Hiremath 653bc142bbbSVaibhav Hiremath return 0; 654bc142bbbSVaibhav Hiremath } 655bc142bbbSVaibhav Hiremath 6567fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 6577fa60654SVaibhav Hiremath { 6587fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 6597fa60654SVaibhav Hiremath 6609160b7c7SDavid Lin unregister_pm_notifier(&arche_pdata->pm_notifier); 6612923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 662bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 6635993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 6647fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 6657fa60654SVaibhav Hiremath 666ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 667ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 668ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 6697fa60654SVaibhav Hiremath return 0; 6707fa60654SVaibhav Hiremath } 6717fa60654SVaibhav Hiremath 6727fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 6737fa60654SVaibhav Hiremath { 6747fa60654SVaibhav Hiremath /* 6757fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 6767fa60654SVaibhav Hiremath * completely 6777fa60654SVaibhav Hiremath * 6787fa60654SVaibhav Hiremath * TODO: sequence ?? 6797fa60654SVaibhav Hiremath * 6807fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 6817fa60654SVaibhav Hiremath * Precondition: Definition ??? 6827fa60654SVaibhav Hiremath */ 6837fa60654SVaibhav Hiremath return 0; 6847fa60654SVaibhav Hiremath } 6857fa60654SVaibhav Hiremath 6867fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 6877fa60654SVaibhav Hiremath { 6887fa60654SVaibhav Hiremath /* 6897fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 6907fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 6917fa60654SVaibhav Hiremath * OFF state or standby state. 6927fa60654SVaibhav Hiremath * 6937fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 6947fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 6957fa60654SVaibhav Hiremath * 6967fa60654SVaibhav Hiremath */ 6977fa60654SVaibhav Hiremath return 0; 6987fa60654SVaibhav Hiremath } 6997fa60654SVaibhav Hiremath 7007fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 7017fa60654SVaibhav Hiremath arche_platform_suspend, 7027fa60654SVaibhav Hiremath arche_platform_resume); 7037fa60654SVaibhav Hiremath 7047fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 7057fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7067fa60654SVaibhav Hiremath { }, 7077fa60654SVaibhav Hiremath }; 7081e5dd1f8SGreg Kroah-Hartman 7091e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 7101e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7111e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 7121e5dd1f8SGreg Kroah-Hartman { }, 7131e5dd1f8SGreg Kroah-Hartman }; 7141e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 7157fa60654SVaibhav Hiremath 7167fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 7177fa60654SVaibhav Hiremath .probe = arche_platform_probe, 7187fa60654SVaibhav Hiremath .remove = arche_platform_remove, 7197fa60654SVaibhav Hiremath .driver = { 7207fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 7217fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 7221e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 7237fa60654SVaibhav Hiremath } 7247fa60654SVaibhav Hiremath }; 7257fa60654SVaibhav Hiremath 7261e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 7271e5dd1f8SGreg Kroah-Hartman { 7281e5dd1f8SGreg Kroah-Hartman int retval; 7291e5dd1f8SGreg Kroah-Hartman 7301e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 7311e5dd1f8SGreg Kroah-Hartman if (retval) 7321e5dd1f8SGreg Kroah-Hartman return retval; 7331e5dd1f8SGreg Kroah-Hartman 7347b62b61cSViresh Kumar retval = arche_apb_init(); 7351e5dd1f8SGreg Kroah-Hartman if (retval) 7361e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 7371e5dd1f8SGreg Kroah-Hartman 7381e5dd1f8SGreg Kroah-Hartman return retval; 7391e5dd1f8SGreg Kroah-Hartman } 7401e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 7411e5dd1f8SGreg Kroah-Hartman 7421e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 7431e5dd1f8SGreg Kroah-Hartman { 7447b62b61cSViresh Kumar arche_apb_exit(); 7451e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 7461e5dd1f8SGreg Kroah-Hartman } 7471e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 7487fa60654SVaibhav Hiremath 749a974f469SSandeep Patil MODULE_LICENSE("GPL v2"); 7507fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 7517fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 752