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" 25970dc85bSBryan O'Donoghue #include "greybus.h" 267fa60654SVaibhav Hiremath 27ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h> 28ad4d3f95SVaibhav Hiremath 29f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS 30 30f760bbfbSVaibhav Hiremath 31685353c1SVaibhav Hiremath enum svc_wakedetect_state { 32685353c1SVaibhav Hiremath WD_STATE_IDLE, /* Default state = pulled high/low */ 33685353c1SVaibhav Hiremath WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 34685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 35685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 36685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_START, /* Cold boot process started */ 37685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_START, /* Not used */ 38970dc85bSBryan O'Donoghue WD_STATE_TIMESYNC, 39685353c1SVaibhav Hiremath }; 40685353c1SVaibhav Hiremath 417fa60654SVaibhav Hiremath struct arche_platform_drvdata { 427fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 437fa60654SVaibhav Hiremath int svc_reset_gpio; 447fa60654SVaibhav Hiremath bool is_reset_act_hi; 457fa60654SVaibhav Hiremath int svc_sysboot_gpio; 46a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 477fa60654SVaibhav Hiremath 48e74d04a5SVaibhav Hiremath enum arche_platform_state state; 49e74d04a5SVaibhav Hiremath 507fe93014SDavid Lin int svc_refclk_req; 517fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 527fa60654SVaibhav Hiremath 537fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 547fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 557fa60654SVaibhav Hiremath 567fa60654SVaibhav Hiremath int num_apbs; 57a463fc15SVaibhav Hiremath 58685353c1SVaibhav Hiremath enum svc_wakedetect_state wake_detect_state; 59f760bbfbSVaibhav Hiremath int wake_detect_irq; 606c1ca56dSVaibhav Hiremath spinlock_t wake_lock; /* Protect wake_detect_state */ 61886aba55SVaibhav Hiremath struct mutex platform_state_mutex; /* Protect state */ 62970dc85bSBryan O'Donoghue wait_queue_head_t wq; /* WQ for arche_pdata->state */ 63f760bbfbSVaibhav Hiremath unsigned long wake_detect_start; 649160b7c7SDavid Lin struct notifier_block pm_notifier; 65685353c1SVaibhav Hiremath 66a463fc15SVaibhav Hiremath struct device *dev; 67970dc85bSBryan O'Donoghue struct gb_timesync_svc *timesync_svc_pdata; 687fa60654SVaibhav Hiremath }; 697fa60654SVaibhav Hiremath 70970dc85bSBryan O'Donoghue static int arche_apb_bootret_assert(struct device *dev, void *data) 71970dc85bSBryan O'Donoghue { 72970dc85bSBryan O'Donoghue apb_bootret_assert(dev); 73970dc85bSBryan O'Donoghue return 0; 74970dc85bSBryan O'Donoghue } 75970dc85bSBryan O'Donoghue 76970dc85bSBryan O'Donoghue static int arche_apb_bootret_deassert(struct device *dev, void *data) 77970dc85bSBryan O'Donoghue { 78970dc85bSBryan O'Donoghue apb_bootret_deassert(dev); 79970dc85bSBryan O'Donoghue return 0; 80970dc85bSBryan O'Donoghue } 81970dc85bSBryan O'Donoghue 82970dc85bSBryan O'Donoghue /* Requires calling context to hold arche_pdata->platform_state_mutex */ 83c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, 84c4058b79SBryan O'Donoghue enum arche_platform_state state) 85c4058b79SBryan O'Donoghue { 86c4058b79SBryan O'Donoghue arche_pdata->state = state; 87c4058b79SBryan O'Donoghue } 88c4058b79SBryan O'Donoghue 89886aba55SVaibhav Hiremath /* 90886aba55SVaibhav Hiremath * arche_platform_change_state: Change the operational state 91886aba55SVaibhav Hiremath * 92886aba55SVaibhav Hiremath * This exported function allows external drivers to change the state 93886aba55SVaibhav Hiremath * of the arche-platform driver. 94886aba55SVaibhav Hiremath * Note that this function only supports transitions between two states 95886aba55SVaibhav Hiremath * with limited functionality. 96886aba55SVaibhav Hiremath * 97886aba55SVaibhav Hiremath * - ARCHE_PLATFORM_STATE_TIME_SYNC: 98886aba55SVaibhav Hiremath * Once set, allows timesync operations between SVC <=> AP and makes 99886aba55SVaibhav Hiremath * sure that arche-platform driver ignores any subsequent events/pulses 100886aba55SVaibhav Hiremath * from SVC over wake/detect. 101886aba55SVaibhav Hiremath * 102886aba55SVaibhav Hiremath * - ARCHE_PLATFORM_STATE_ACTIVE: 103886aba55SVaibhav Hiremath * Puts back driver to active state, where any pulse from SVC on wake/detect 104886aba55SVaibhav Hiremath * line would trigger either cold/standby boot. 105886aba55SVaibhav Hiremath * Note: Transition request from this function does not trigger cold/standby 106886aba55SVaibhav Hiremath * boot. It just puts back driver book keeping variable back to ACTIVE 107886aba55SVaibhav Hiremath * state and restores the interrupt. 108886aba55SVaibhav Hiremath * 109886aba55SVaibhav Hiremath * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently 110886aba55SVaibhav Hiremath * satisfy the requested state-transition or -EINVAL for all other 111886aba55SVaibhav Hiremath * state-transition requests. 112886aba55SVaibhav Hiremath */ 113970dc85bSBryan O'Donoghue int arche_platform_change_state(enum arche_platform_state state, 114970dc85bSBryan O'Donoghue struct gb_timesync_svc *timesync_svc_pdata) 115886aba55SVaibhav Hiremath { 116886aba55SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 117886aba55SVaibhav Hiremath struct platform_device *pdev; 118886aba55SVaibhav Hiremath struct device_node *np; 119886aba55SVaibhav Hiremath int ret = -EAGAIN; 120886aba55SVaibhav Hiremath unsigned long flags; 121886aba55SVaibhav Hiremath 122886aba55SVaibhav Hiremath np = of_find_compatible_node(NULL, NULL, "google,arche-platform"); 123886aba55SVaibhav Hiremath if (!np) { 124886aba55SVaibhav Hiremath pr_err("google,arche-platform device node not found\n"); 125886aba55SVaibhav Hiremath return -ENODEV; 126886aba55SVaibhav Hiremath } 127886aba55SVaibhav Hiremath 128886aba55SVaibhav Hiremath pdev = of_find_device_by_node(np); 129886aba55SVaibhav Hiremath if (!pdev) { 130886aba55SVaibhav Hiremath pr_err("arche-platform device not found\n"); 131886aba55SVaibhav Hiremath return -ENODEV; 132886aba55SVaibhav Hiremath } 133886aba55SVaibhav Hiremath 134886aba55SVaibhav Hiremath arche_pdata = platform_get_drvdata(pdev); 135886aba55SVaibhav Hiremath 136886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 137886aba55SVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 138886aba55SVaibhav Hiremath 139886aba55SVaibhav Hiremath if (arche_pdata->state == state) { 140886aba55SVaibhav Hiremath ret = 0; 141886aba55SVaibhav Hiremath goto exit; 142886aba55SVaibhav Hiremath } 143886aba55SVaibhav Hiremath 144c4058b79SBryan O'Donoghue switch (state) { 145c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_TIME_SYNC: 146970dc85bSBryan O'Donoghue if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 147970dc85bSBryan O'Donoghue ret = -EINVAL; 148970dc85bSBryan O'Donoghue goto exit; 149970dc85bSBryan O'Donoghue } 150970dc85bSBryan O'Donoghue if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { 151970dc85bSBryan O'Donoghue dev_err(arche_pdata->dev, 152970dc85bSBryan O'Donoghue "driver busy with wake/detect line ops\n"); 153970dc85bSBryan O'Donoghue goto exit; 154970dc85bSBryan O'Donoghue } 155970dc85bSBryan O'Donoghue device_for_each_child(arche_pdata->dev, NULL, 156970dc85bSBryan O'Donoghue arche_apb_bootret_assert); 157970dc85bSBryan O'Donoghue arche_pdata->wake_detect_state = WD_STATE_TIMESYNC; 158c4058b79SBryan O'Donoghue break; 159c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_ACTIVE: 160970dc85bSBryan O'Donoghue if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) { 161970dc85bSBryan O'Donoghue ret = -EINVAL; 162970dc85bSBryan O'Donoghue goto exit; 163970dc85bSBryan O'Donoghue } 164970dc85bSBryan O'Donoghue device_for_each_child(arche_pdata->dev, NULL, 165970dc85bSBryan O'Donoghue arche_apb_bootret_deassert); 166970dc85bSBryan O'Donoghue arche_pdata->wake_detect_state = WD_STATE_IDLE; 167c4058b79SBryan O'Donoghue break; 168c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_OFF: 169c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_STANDBY: 170c4058b79SBryan O'Donoghue case ARCHE_PLATFORM_STATE_FW_FLASHING: 171886aba55SVaibhav Hiremath dev_err(arche_pdata->dev, "busy, request to retry later\n"); 172886aba55SVaibhav Hiremath goto exit; 173c4058b79SBryan O'Donoghue default: 174c4058b79SBryan O'Donoghue ret = -EINVAL; 175c4058b79SBryan O'Donoghue dev_err(arche_pdata->dev, 176c4058b79SBryan O'Donoghue "invalid state transition request\n"); 177c4058b79SBryan O'Donoghue goto exit; 178886aba55SVaibhav Hiremath } 179970dc85bSBryan O'Donoghue arche_pdata->timesync_svc_pdata = timesync_svc_pdata; 180c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, state); 181970dc85bSBryan O'Donoghue if (state == ARCHE_PLATFORM_STATE_ACTIVE) 182970dc85bSBryan O'Donoghue wake_up(&arche_pdata->wq); 183970dc85bSBryan O'Donoghue 184c4058b79SBryan O'Donoghue ret = 0; 185886aba55SVaibhav Hiremath exit: 186886aba55SVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 187886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 188886aba55SVaibhav Hiremath of_node_put(np); 189886aba55SVaibhav Hiremath return ret; 190886aba55SVaibhav Hiremath } 191886aba55SVaibhav Hiremath EXPORT_SYMBOL_GPL(arche_platform_change_state); 192886aba55SVaibhav Hiremath 193c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */ 194c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state( 195c4058b79SBryan O'Donoghue struct arche_platform_drvdata *arche_pdata, 196c4058b79SBryan O'Donoghue enum svc_wakedetect_state state) 197c4058b79SBryan O'Donoghue { 198c4058b79SBryan O'Donoghue arche_pdata->wake_detect_state = state; 199c4058b79SBryan O'Donoghue } 200c4058b79SBryan O'Donoghue 2017fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 2027fa60654SVaibhav Hiremath { 2037fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 2047fa60654SVaibhav Hiremath } 2057fa60654SVaibhav Hiremath 206f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data) 207f760bbfbSVaibhav Hiremath { 208f760bbfbSVaibhav Hiremath int ret; 209f760bbfbSVaibhav Hiremath 210f760bbfbSVaibhav Hiremath ret = apb_ctrl_coldboot(dev); 211f760bbfbSVaibhav Hiremath if (ret) 212f760bbfbSVaibhav Hiremath dev_warn(dev, "failed to coldboot\n"); 213f760bbfbSVaibhav Hiremath 214f760bbfbSVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 215f760bbfbSVaibhav Hiremath return 0; 216f760bbfbSVaibhav Hiremath } 217f760bbfbSVaibhav Hiremath 218fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 219fd60ac58SVaibhav Hiremath { 220fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 221fd60ac58SVaibhav Hiremath 222e915ce48SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 223e915ce48SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 224e915ce48SVaibhav Hiremath dev_warn(dev, "failed to control hub device\n"); 225e915ce48SVaibhav Hiremath 226fd60ac58SVaibhav Hiremath return 0; 227fd60ac58SVaibhav Hiremath } 228fd60ac58SVaibhav Hiremath 2297ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) 23016fe18caSVaibhav Hiremath { 23116fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 23216fe18caSVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 23316fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 23416fe18caSVaibhav Hiremath } 23516fe18caSVaibhav Hiremath 236f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 237f760bbfbSVaibhav Hiremath { 238f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 239f760bbfbSVaibhav Hiremath unsigned long flags; 240f760bbfbSVaibhav Hiremath 2416c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 242f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 243f760bbfbSVaibhav Hiremath /* Something is wrong */ 2446c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 245f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 246f760bbfbSVaibhav Hiremath } 247f760bbfbSVaibhav Hiremath 248c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 249c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_START); 2506c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 251f760bbfbSVaibhav Hiremath 252ff788de0SVaibhav Hiremath /* It should complete power cycle, so first make sure it is poweroff */ 253ff788de0SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 254e915ce48SVaibhav Hiremath 255f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 256f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 257f760bbfbSVaibhav Hiremath 258a7ddda1fSVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 259a7ddda1fSVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 260a7ddda1fSVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 261a7ddda1fSVaibhav Hiremath 2626c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 263c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 2646c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 265f760bbfbSVaibhav Hiremath 266f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 267f760bbfbSVaibhav Hiremath } 268f760bbfbSVaibhav Hiremath 269f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 270f760bbfbSVaibhav Hiremath { 271f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 272f760bbfbSVaibhav Hiremath unsigned long flags; 273f760bbfbSVaibhav Hiremath 2746c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 275f760bbfbSVaibhav Hiremath 276970dc85bSBryan O'Donoghue if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) { 277970dc85bSBryan O'Donoghue gb_timesync_irq(arche_pdata->timesync_svc_pdata); 278970dc85bSBryan O'Donoghue goto exit; 279970dc85bSBryan O'Donoghue } 280970dc85bSBryan O'Donoghue 281f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 282f760bbfbSVaibhav Hiremath /* wake/detect rising */ 283f760bbfbSVaibhav Hiremath 284f760bbfbSVaibhav Hiremath /* 285f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 286f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 287f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 288f760bbfbSVaibhav Hiremath */ 289f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 290f760bbfbSVaibhav Hiremath if (time_before(jiffies, 291f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 292f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 293c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 294c4058b79SBryan O'Donoghue WD_STATE_IDLE); 295f760bbfbSVaibhav Hiremath } else { 296f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 297f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 298f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 299c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 300c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_TRIG); 3016c1ca56dSVaibhav Hiremath spin_unlock_irqrestore( 3026c1ca56dSVaibhav Hiremath &arche_pdata->wake_lock, 3036c1ca56dSVaibhav Hiremath flags); 304f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 305f760bbfbSVaibhav Hiremath } 306f760bbfbSVaibhav Hiremath } 307f760bbfbSVaibhav Hiremath } 308f760bbfbSVaibhav Hiremath } else { 309f760bbfbSVaibhav Hiremath /* wake/detect falling */ 310f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 311f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 312f760bbfbSVaibhav Hiremath /* 313f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 314f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 315f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 316f760bbfbSVaibhav Hiremath */ 317c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 318c4058b79SBryan O'Donoghue WD_STATE_BOOT_INIT); 319f760bbfbSVaibhav Hiremath } 320f760bbfbSVaibhav Hiremath } 321f760bbfbSVaibhav Hiremath 322970dc85bSBryan O'Donoghue exit: 3236c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 324f760bbfbSVaibhav Hiremath 325f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 326f760bbfbSVaibhav Hiremath } 327f760bbfbSVaibhav Hiremath 328886aba55SVaibhav Hiremath /* 329886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 330886aba55SVaibhav Hiremath */ 331758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 332758ca99dSVaibhav Hiremath { 333758ca99dSVaibhav Hiremath int ret; 334758ca99dSVaibhav Hiremath 335599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 336599159b6SVaibhav Hiremath return 0; 337599159b6SVaibhav Hiremath 338758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 339758ca99dSVaibhav Hiremath 340758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 341758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 342758ca99dSVaibhav Hiremath 343758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 344758ca99dSVaibhav Hiremath usleep_range(100, 200); 345758ca99dSVaibhav Hiremath 346758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 347758ca99dSVaibhav Hiremath if (ret) { 348758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 349758ca99dSVaibhav Hiremath ret); 350758ca99dSVaibhav Hiremath return ret; 351758ca99dSVaibhav Hiremath } 352758ca99dSVaibhav Hiremath 353758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 354758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 355758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 356758ca99dSVaibhav Hiremath 357c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); 358e74d04a5SVaibhav Hiremath 359758ca99dSVaibhav Hiremath return 0; 360758ca99dSVaibhav Hiremath } 361758ca99dSVaibhav Hiremath 362886aba55SVaibhav Hiremath /* 363886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 364886aba55SVaibhav Hiremath */ 365c61a8b49SVaibhav Hiremath static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 3667691fed2SVaibhav Hiremath { 3677a867d14SVaibhav Hiremath int ret; 3687a867d14SVaibhav Hiremath 369599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 370c61a8b49SVaibhav Hiremath return 0; 371599159b6SVaibhav Hiremath 3727691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 3737691fed2SVaibhav Hiremath 3747691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3757691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3767691fed2SVaibhav Hiremath 3777691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 3787691fed2SVaibhav Hiremath 3797691fed2SVaibhav Hiremath usleep_range(100, 200); 3807a867d14SVaibhav Hiremath 3817a867d14SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 3827a867d14SVaibhav Hiremath if (ret) { 3837a867d14SVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 3847a867d14SVaibhav Hiremath ret); 3857a867d14SVaibhav Hiremath return ret; 3867a867d14SVaibhav Hiremath } 3877a867d14SVaibhav Hiremath 3887691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3897691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 3907691fed2SVaibhav Hiremath 391c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); 3927691fed2SVaibhav Hiremath 393c61a8b49SVaibhav Hiremath return 0; 3947691fed2SVaibhav Hiremath } 3957691fed2SVaibhav Hiremath 396886aba55SVaibhav Hiremath /* 397886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 398886aba55SVaibhav Hiremath */ 3995993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 4007fa60654SVaibhav Hiremath { 401f760bbfbSVaibhav Hiremath unsigned long flags; 402f760bbfbSVaibhav Hiremath 403599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 404599159b6SVaibhav Hiremath return; 405599159b6SVaibhav Hiremath 40625847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 40725847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 408d2320b2dSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 40916fd976cSVaibhav Hiremath 4106c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 411c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 412c4058b79SBryan O'Donoghue WD_STATE_IDLE); 4136c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 4147a867d14SVaibhav Hiremath } 415b4c95fcaSVaibhav Hiremath 416d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 41725847ee7SVaibhav Hiremath 4187fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 4197fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 4207fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 421e74d04a5SVaibhav Hiremath 422c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 4237fa60654SVaibhav Hiremath } 4247fa60654SVaibhav Hiremath 4252923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 4262923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 4272923c58eSVaibhav Hiremath { 4282923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 4292923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 4302923c58eSVaibhav Hiremath int ret = 0; 4312923c58eSVaibhav Hiremath 432970dc85bSBryan O'Donoghue retry: 433886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 434970dc85bSBryan O'Donoghue if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) { 435970dc85bSBryan O'Donoghue mutex_unlock(&arche_pdata->platform_state_mutex); 436970dc85bSBryan O'Donoghue ret = wait_event_interruptible( 437970dc85bSBryan O'Donoghue arche_pdata->wq, 438970dc85bSBryan O'Donoghue arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC); 439970dc85bSBryan O'Donoghue if (ret) 440970dc85bSBryan O'Donoghue return ret; 441970dc85bSBryan O'Donoghue goto retry; 442970dc85bSBryan O'Donoghue } 443886aba55SVaibhav Hiremath 4442923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 4452923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 446886aba55SVaibhav Hiremath goto exit; 4472923c58eSVaibhav Hiremath 448fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 449fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 450fd60ac58SVaibhav Hiremath 4512923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 452ad4d3f95SVaibhav Hiremath 4532923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 4542923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 455886aba55SVaibhav Hiremath goto exit; 4562923c58eSVaibhav Hiremath 4570b1283e3SVaibhav Hiremath /* First we want to make sure we power off everything 4580b1283e3SVaibhav Hiremath * and then activate back again */ 4590b1283e3SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 4600b1283e3SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 4610b1283e3SVaibhav Hiremath 4627ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 4632923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 4640b1283e3SVaibhav Hiremath if (ret) 4650b1283e3SVaibhav Hiremath goto exit; 46616fe18caSVaibhav Hiremath 4672923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 4682923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 469886aba55SVaibhav Hiremath goto exit; 4702923c58eSVaibhav Hiremath 4712923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 4727691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 4737691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 474886aba55SVaibhav Hiremath goto exit; 4757691fed2SVaibhav Hiremath 476921dbe52SVaibhav Hiremath /* 477921dbe52SVaibhav Hiremath * Here we only control SVC. 478921dbe52SVaibhav Hiremath * 479921dbe52SVaibhav Hiremath * In case of FW_FLASHING mode we do not want to control 480921dbe52SVaibhav Hiremath * APBs, as in case of V2, SPI bus is shared between both 481921dbe52SVaibhav Hiremath * the APBs. So let user chose which APB he wants to flash. 482921dbe52SVaibhav Hiremath */ 4837691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 484fd60ac58SVaibhav Hiremath 485c61a8b49SVaibhav Hiremath ret = arche_platform_fw_flashing_seq(arche_pdata); 486c61a8b49SVaibhav Hiremath if (ret) 487c61a8b49SVaibhav Hiremath goto exit; 4882923c58eSVaibhav Hiremath } else { 4892923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 4902923c58eSVaibhav Hiremath ret = -EINVAL; 4912923c58eSVaibhav Hiremath } 4922923c58eSVaibhav Hiremath 493886aba55SVaibhav Hiremath exit: 494886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 4952923c58eSVaibhav Hiremath return ret ? ret : count; 4962923c58eSVaibhav Hiremath } 4972923c58eSVaibhav Hiremath 4982923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 4992923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 5002923c58eSVaibhav Hiremath { 5012923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 5022923c58eSVaibhav Hiremath 5032923c58eSVaibhav Hiremath switch (arche_pdata->state) { 5042923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 5052923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 5062923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 5072923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 5082923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 5092923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 5107691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 5117691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 512886aba55SVaibhav Hiremath case ARCHE_PLATFORM_STATE_TIME_SYNC: 513886aba55SVaibhav Hiremath return sprintf(buf, "time_sync\n"); 5142923c58eSVaibhav Hiremath default: 5152923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 5162923c58eSVaibhav Hiremath } 5172923c58eSVaibhav Hiremath } 5182923c58eSVaibhav Hiremath 5192923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 5202923c58eSVaibhav Hiremath 5219160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier, 5229160b7c7SDavid Lin unsigned long pm_event, void *unused) 5239160b7c7SDavid Lin { 5249160b7c7SDavid Lin struct arche_platform_drvdata *arche_pdata = 5259160b7c7SDavid Lin container_of(notifier, struct arche_platform_drvdata, 5269160b7c7SDavid Lin pm_notifier); 527886aba55SVaibhav Hiremath int ret = NOTIFY_DONE; 5289160b7c7SDavid Lin 529886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 5309160b7c7SDavid Lin switch (pm_event) { 5319160b7c7SDavid Lin case PM_SUSPEND_PREPARE: 532886aba55SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 533886aba55SVaibhav Hiremath ret = NOTIFY_STOP; 534886aba55SVaibhav Hiremath break; 535886aba55SVaibhav Hiremath } 5369160b7c7SDavid Lin device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 5379160b7c7SDavid Lin arche_platform_poweroff_seq(arche_pdata); 5389160b7c7SDavid Lin break; 5399160b7c7SDavid Lin case PM_POST_SUSPEND: 5408ef0b538SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) 5418ef0b538SVaibhav Hiremath break; 5428ef0b538SVaibhav Hiremath 5437ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 54416fd976cSVaibhav Hiremath arche_platform_coldboot_seq(arche_pdata); 5459160b7c7SDavid Lin break; 5469160b7c7SDavid Lin default: 5479160b7c7SDavid Lin break; 5489160b7c7SDavid Lin } 549886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 5509160b7c7SDavid Lin 551886aba55SVaibhav Hiremath return ret; 5529160b7c7SDavid Lin } 5539160b7c7SDavid Lin 5547fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 5557fa60654SVaibhav Hiremath { 5567fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 5577fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 5587fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 5597fa60654SVaibhav Hiremath int ret; 5607fa60654SVaibhav Hiremath 5617fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 5627fa60654SVaibhav Hiremath if (!arche_pdata) 5637fa60654SVaibhav Hiremath return -ENOMEM; 5647fa60654SVaibhav Hiremath 5657fa60654SVaibhav Hiremath /* setup svc reset gpio */ 5667fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 5677fa60654SVaibhav Hiremath "svc,reset-active-high"); 5687fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 5697fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 5707fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 571f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 5727fa60654SVaibhav Hiremath } 5737fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 5747fa60654SVaibhav Hiremath if (ret) { 5757fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 5767fa60654SVaibhav Hiremath return ret; 5777fa60654SVaibhav Hiremath } 5787fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 5797fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 5807fa60654SVaibhav Hiremath if (ret) { 5817fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 5827fa60654SVaibhav Hiremath return ret; 5837fa60654SVaibhav Hiremath } 584c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 5857fa60654SVaibhav Hiremath 5867fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 5877fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 5887fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 5897fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 590f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 5917fa60654SVaibhav Hiremath } 5927fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 5937fa60654SVaibhav Hiremath if (ret) { 5947fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 5957fa60654SVaibhav Hiremath return ret; 5967fa60654SVaibhav Hiremath } 5977fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 5987fa60654SVaibhav Hiremath if (ret) { 5997fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 6007fa60654SVaibhav Hiremath return ret; 6017fa60654SVaibhav Hiremath } 6027fa60654SVaibhav Hiremath 6037fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 6047fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 6057fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 6067fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 6077fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 608f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 6097fa60654SVaibhav Hiremath } 6107fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 6117fa60654SVaibhav Hiremath if (ret) { 6127fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 6137fa60654SVaibhav Hiremath return ret; 6147fa60654SVaibhav Hiremath } 6157fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 6167fa60654SVaibhav Hiremath if (ret) { 6177fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 6187fa60654SVaibhav Hiremath return ret; 6197fa60654SVaibhav Hiremath } 6207fa60654SVaibhav Hiremath 6217fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 6227fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 6237fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 6247fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 6257fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 6267fa60654SVaibhav Hiremath return ret; 6277fa60654SVaibhav Hiremath } 6287fa60654SVaibhav Hiremath 6297fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 6307fa60654SVaibhav Hiremath 6317fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 6327fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 6337fa60654SVaibhav Hiremath 634a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 635a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 636a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 6375a420d15SEva Rachel Retuya return arche_pdata->wake_detect_gpio; 63872a8c24bSViresh Kumar } 6397fa60654SVaibhav Hiremath 640a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 641a463fc15SVaibhav Hiremath if (ret) { 642a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 643a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 644758ca99dSVaibhav Hiremath return ret; 645a463fc15SVaibhav Hiremath } 64616fd976cSVaibhav Hiremath 647c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 648a463fc15SVaibhav Hiremath 649a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 650a463fc15SVaibhav Hiremath 6516c1ca56dSVaibhav Hiremath spin_lock_init(&arche_pdata->wake_lock); 652886aba55SVaibhav Hiremath mutex_init(&arche_pdata->platform_state_mutex); 653970dc85bSBryan O'Donoghue init_waitqueue_head(&arche_pdata->wq); 654f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 655f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 656f760bbfbSVaibhav Hiremath 657f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 658f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 659f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 660f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 661f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 662f760bbfbSVaibhav Hiremath if (ret) { 663f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 664f760bbfbSVaibhav Hiremath return ret; 665f760bbfbSVaibhav Hiremath } 666d29b67d4SVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 667f760bbfbSVaibhav Hiremath 6682923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 6692923c58eSVaibhav Hiremath if (ret) { 6702923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 6712923c58eSVaibhav Hiremath return ret; 6722923c58eSVaibhav Hiremath } 6732923c58eSVaibhav Hiremath 674d29b67d4SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 675d29b67d4SVaibhav Hiremath if (ret) { 676d29b67d4SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 677d29b67d4SVaibhav Hiremath goto err_device_remove; 678d29b67d4SVaibhav Hiremath } 679d29b67d4SVaibhav Hiremath 680d29b67d4SVaibhav Hiremath arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; 681d29b67d4SVaibhav Hiremath ret = register_pm_notifier(&arche_pdata->pm_notifier); 682d29b67d4SVaibhav Hiremath 683d29b67d4SVaibhav Hiremath if (ret) { 684d29b67d4SVaibhav Hiremath dev_err(dev, "failed to register pm notifier %d\n", ret); 685d29b67d4SVaibhav Hiremath goto err_device_remove; 686d29b67d4SVaibhav Hiremath } 687d29b67d4SVaibhav Hiremath 688d29b67d4SVaibhav Hiremath /* Register callback pointer */ 689d29b67d4SVaibhav Hiremath arche_platform_change_state_cb = arche_platform_change_state; 690d29b67d4SVaibhav Hiremath 691d29b67d4SVaibhav Hiremath /* Explicitly power off if requested */ 692d29b67d4SVaibhav Hiremath if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { 693886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 694758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 695758ca99dSVaibhav Hiremath if (ret) { 696758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 6976743a6fdSVaibhav Hiremath goto err_coldboot; 698758ca99dSVaibhav Hiremath } 699d29b67d4SVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 700886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 7019160b7c7SDavid Lin } 7029160b7c7SDavid Lin 7037fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 70472a8c24bSViresh Kumar return 0; 7056743a6fdSVaibhav Hiremath 7066743a6fdSVaibhav Hiremath err_coldboot: 707886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 708d29b67d4SVaibhav Hiremath err_device_remove: 7096743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 7106743a6fdSVaibhav Hiremath return ret; 7117fa60654SVaibhav Hiremath } 7127fa60654SVaibhav Hiremath 713bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 714bc142bbbSVaibhav Hiremath { 715bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 716bc142bbbSVaibhav Hiremath 717bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 718bc142bbbSVaibhav Hiremath 719bc142bbbSVaibhav Hiremath return 0; 720bc142bbbSVaibhav Hiremath } 721bc142bbbSVaibhav Hiremath 7227fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 7237fa60654SVaibhav Hiremath { 7247fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 7257fa60654SVaibhav Hiremath 7269160b7c7SDavid Lin unregister_pm_notifier(&arche_pdata->pm_notifier); 7272923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 728bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 7295993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 7307fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 7317fa60654SVaibhav Hiremath 732ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 733ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 734ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 7357fa60654SVaibhav Hiremath return 0; 7367fa60654SVaibhav Hiremath } 7377fa60654SVaibhav Hiremath 7387fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 7397fa60654SVaibhav Hiremath { 7407fa60654SVaibhav Hiremath /* 7417fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 7427fa60654SVaibhav Hiremath * completely 7437fa60654SVaibhav Hiremath * 7447fa60654SVaibhav Hiremath * TODO: sequence ?? 7457fa60654SVaibhav Hiremath * 7467fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 7477fa60654SVaibhav Hiremath * Precondition: Definition ??? 7487fa60654SVaibhav Hiremath */ 7497fa60654SVaibhav Hiremath return 0; 7507fa60654SVaibhav Hiremath } 7517fa60654SVaibhav Hiremath 7527fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 7537fa60654SVaibhav Hiremath { 7547fa60654SVaibhav Hiremath /* 7557fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 7567fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 7577fa60654SVaibhav Hiremath * OFF state or standby state. 7587fa60654SVaibhav Hiremath * 7597fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 7607fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 7617fa60654SVaibhav Hiremath * 7627fa60654SVaibhav Hiremath */ 7637fa60654SVaibhav Hiremath return 0; 7647fa60654SVaibhav Hiremath } 7657fa60654SVaibhav Hiremath 7661f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev) 7671f77b363SDavid Lin { 7681f77b363SDavid Lin struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 7691f77b363SDavid Lin 7701f77b363SDavid Lin arche_platform_poweroff_seq(arche_pdata); 7711f77b363SDavid Lin 7721f77b363SDavid Lin usb3613_hub_mode_ctrl(false); 7731f77b363SDavid Lin } 7741f77b363SDavid Lin 7757fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 7767fa60654SVaibhav Hiremath arche_platform_suspend, 7777fa60654SVaibhav Hiremath arche_platform_resume); 7787fa60654SVaibhav Hiremath 7797fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 7807fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7817fa60654SVaibhav Hiremath { }, 7827fa60654SVaibhav Hiremath }; 7831e5dd1f8SGreg Kroah-Hartman 7841e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 7851e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7861e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 7871e5dd1f8SGreg Kroah-Hartman { }, 7881e5dd1f8SGreg Kroah-Hartman }; 7891e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 7907fa60654SVaibhav Hiremath 7917fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 7927fa60654SVaibhav Hiremath .probe = arche_platform_probe, 7937fa60654SVaibhav Hiremath .remove = arche_platform_remove, 7941f77b363SDavid Lin .shutdown = arche_platform_shutdown, 7957fa60654SVaibhav Hiremath .driver = { 7967fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 7977fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 7981e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 7997fa60654SVaibhav Hiremath } 8007fa60654SVaibhav Hiremath }; 8017fa60654SVaibhav Hiremath 8021e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 8031e5dd1f8SGreg Kroah-Hartman { 8041e5dd1f8SGreg Kroah-Hartman int retval; 8051e5dd1f8SGreg Kroah-Hartman 8061e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 8071e5dd1f8SGreg Kroah-Hartman if (retval) 8081e5dd1f8SGreg Kroah-Hartman return retval; 8091e5dd1f8SGreg Kroah-Hartman 8107b62b61cSViresh Kumar retval = arche_apb_init(); 8111e5dd1f8SGreg Kroah-Hartman if (retval) 8121e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 8131e5dd1f8SGreg Kroah-Hartman 8141e5dd1f8SGreg Kroah-Hartman return retval; 8151e5dd1f8SGreg Kroah-Hartman } 8161e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 8171e5dd1f8SGreg Kroah-Hartman 8181e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 8191e5dd1f8SGreg Kroah-Hartman { 8207b62b61cSViresh Kumar arche_apb_exit(); 8211e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 8221e5dd1f8SGreg Kroah-Hartman } 8231e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 8247fa60654SVaibhav Hiremath 825a974f469SSandeep Patil MODULE_LICENSE("GPL v2"); 8267fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 8277fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 828