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_fw_flashing_state(struct device *dev, void *data) 219fd60ac58SVaibhav Hiremath { 220fd60ac58SVaibhav Hiremath int ret; 221fd60ac58SVaibhav Hiremath 222fd60ac58SVaibhav Hiremath ret = apb_ctrl_fw_flashing(dev); 223fd60ac58SVaibhav Hiremath if (ret) 224fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to switch to fw flashing state\n"); 225fd60ac58SVaibhav Hiremath 226fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 227fd60ac58SVaibhav Hiremath return 0; 228fd60ac58SVaibhav Hiremath } 229fd60ac58SVaibhav Hiremath 230fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 231fd60ac58SVaibhav Hiremath { 232fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 233fd60ac58SVaibhav Hiremath 234e915ce48SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 235e915ce48SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 236e915ce48SVaibhav Hiremath dev_warn(dev, "failed to control hub device\n"); 237e915ce48SVaibhav Hiremath 238fd60ac58SVaibhav Hiremath return 0; 239fd60ac58SVaibhav Hiremath } 240fd60ac58SVaibhav Hiremath 2417ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) 24216fe18caSVaibhav Hiremath { 24316fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 24416fe18caSVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 24516fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 24616fe18caSVaibhav Hiremath } 24716fe18caSVaibhav Hiremath 248f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(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 if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 255f760bbfbSVaibhav Hiremath /* Something is wrong */ 2566c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 257f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 258f760bbfbSVaibhav Hiremath } 259f760bbfbSVaibhav Hiremath 260c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 261c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_START); 2626c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 263f760bbfbSVaibhav Hiremath 264ff788de0SVaibhav Hiremath /* It should complete power cycle, so first make sure it is poweroff */ 265ff788de0SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 266e915ce48SVaibhav Hiremath 267f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 268f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 269f760bbfbSVaibhav Hiremath 270a7ddda1fSVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 271a7ddda1fSVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 272a7ddda1fSVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 273a7ddda1fSVaibhav Hiremath 2746c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 275c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 2766c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 277f760bbfbSVaibhav Hiremath 278f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 279f760bbfbSVaibhav Hiremath } 280f760bbfbSVaibhav Hiremath 281f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 282f760bbfbSVaibhav Hiremath { 283f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 284f760bbfbSVaibhav Hiremath unsigned long flags; 285f760bbfbSVaibhav Hiremath 2866c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 287f760bbfbSVaibhav Hiremath 288970dc85bSBryan O'Donoghue if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) { 289970dc85bSBryan O'Donoghue gb_timesync_irq(arche_pdata->timesync_svc_pdata); 290970dc85bSBryan O'Donoghue goto exit; 291970dc85bSBryan O'Donoghue } 292970dc85bSBryan O'Donoghue 293f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 294f760bbfbSVaibhav Hiremath /* wake/detect rising */ 295f760bbfbSVaibhav Hiremath 296f760bbfbSVaibhav Hiremath /* 297f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 298f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 299f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 300f760bbfbSVaibhav Hiremath */ 301f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 302f760bbfbSVaibhav Hiremath if (time_before(jiffies, 303f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 304f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 305c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 306c4058b79SBryan O'Donoghue WD_STATE_IDLE); 307f760bbfbSVaibhav Hiremath } else { 308f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 309f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 310f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 311c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 312c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_TRIG); 3136c1ca56dSVaibhav Hiremath spin_unlock_irqrestore( 3146c1ca56dSVaibhav Hiremath &arche_pdata->wake_lock, 3156c1ca56dSVaibhav Hiremath flags); 316f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 317f760bbfbSVaibhav Hiremath } 318f760bbfbSVaibhav Hiremath } 319f760bbfbSVaibhav Hiremath } 320f760bbfbSVaibhav Hiremath } else { 321f760bbfbSVaibhav Hiremath /* wake/detect falling */ 322f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 323f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 324f760bbfbSVaibhav Hiremath /* 325f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 326f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 327f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 328f760bbfbSVaibhav Hiremath */ 329c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 330c4058b79SBryan O'Donoghue WD_STATE_BOOT_INIT); 331f760bbfbSVaibhav Hiremath } 332f760bbfbSVaibhav Hiremath } 333f760bbfbSVaibhav Hiremath 334970dc85bSBryan O'Donoghue exit: 3356c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 336f760bbfbSVaibhav Hiremath 337f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 338f760bbfbSVaibhav Hiremath } 339f760bbfbSVaibhav Hiremath 340886aba55SVaibhav Hiremath /* 341886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 342886aba55SVaibhav Hiremath */ 343758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 344758ca99dSVaibhav Hiremath { 345758ca99dSVaibhav Hiremath int ret; 346758ca99dSVaibhav Hiremath 347599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 348599159b6SVaibhav Hiremath return 0; 349599159b6SVaibhav Hiremath 350758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 351758ca99dSVaibhav Hiremath 352758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 353758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 354758ca99dSVaibhav Hiremath 355758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 356758ca99dSVaibhav Hiremath usleep_range(100, 200); 357758ca99dSVaibhav Hiremath 358758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 359758ca99dSVaibhav Hiremath if (ret) { 360758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 361758ca99dSVaibhav Hiremath ret); 362758ca99dSVaibhav Hiremath return ret; 363758ca99dSVaibhav Hiremath } 364758ca99dSVaibhav Hiremath 365758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 366758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 367758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 368758ca99dSVaibhav Hiremath 369c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); 370e74d04a5SVaibhav Hiremath 371758ca99dSVaibhav Hiremath return 0; 372758ca99dSVaibhav Hiremath } 373758ca99dSVaibhav Hiremath 374886aba55SVaibhav Hiremath /* 375886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 376886aba55SVaibhav Hiremath */ 377c61a8b49SVaibhav Hiremath static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 3787691fed2SVaibhav Hiremath { 3797a867d14SVaibhav Hiremath int ret; 3807a867d14SVaibhav Hiremath 381599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 382c61a8b49SVaibhav Hiremath return 0; 383599159b6SVaibhav Hiremath 3847691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 3857691fed2SVaibhav Hiremath 3867691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 3877691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3887691fed2SVaibhav Hiremath 3897691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 3907691fed2SVaibhav Hiremath 3917691fed2SVaibhav Hiremath usleep_range(100, 200); 3927a867d14SVaibhav Hiremath 3937a867d14SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 3947a867d14SVaibhav Hiremath if (ret) { 3957a867d14SVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 3967a867d14SVaibhav Hiremath ret); 3977a867d14SVaibhav Hiremath return ret; 3987a867d14SVaibhav Hiremath } 3997a867d14SVaibhav Hiremath 4007691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 4017691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 4027691fed2SVaibhav Hiremath 403c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); 4047691fed2SVaibhav Hiremath 405c61a8b49SVaibhav Hiremath return 0; 4067691fed2SVaibhav Hiremath } 4077691fed2SVaibhav Hiremath 408886aba55SVaibhav Hiremath /* 409886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 410886aba55SVaibhav Hiremath */ 4115993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 4127fa60654SVaibhav Hiremath { 413f760bbfbSVaibhav Hiremath unsigned long flags; 414f760bbfbSVaibhav Hiremath 415599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 416599159b6SVaibhav Hiremath return; 417599159b6SVaibhav Hiremath 41825847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 41925847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 420d2320b2dSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 42116fd976cSVaibhav Hiremath 4226c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 423c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 424c4058b79SBryan O'Donoghue WD_STATE_IDLE); 4256c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 4267a867d14SVaibhav Hiremath } 427b4c95fcaSVaibhav Hiremath 428d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 42925847ee7SVaibhav Hiremath 4307fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 4317fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 4327fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 433e74d04a5SVaibhav Hiremath 434c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 4357fa60654SVaibhav Hiremath } 4367fa60654SVaibhav Hiremath 4372923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 4382923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 4392923c58eSVaibhav Hiremath { 4402923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 4412923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 4422923c58eSVaibhav Hiremath int ret = 0; 4432923c58eSVaibhav Hiremath 444970dc85bSBryan O'Donoghue retry: 445886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 446970dc85bSBryan O'Donoghue if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) { 447970dc85bSBryan O'Donoghue mutex_unlock(&arche_pdata->platform_state_mutex); 448970dc85bSBryan O'Donoghue ret = wait_event_interruptible( 449970dc85bSBryan O'Donoghue arche_pdata->wq, 450970dc85bSBryan O'Donoghue arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC); 451970dc85bSBryan O'Donoghue if (ret) 452970dc85bSBryan O'Donoghue return ret; 453970dc85bSBryan O'Donoghue goto retry; 454970dc85bSBryan O'Donoghue } 455886aba55SVaibhav Hiremath 4562923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 4572923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 458886aba55SVaibhav Hiremath goto exit; 4592923c58eSVaibhav Hiremath 460fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 461fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 462fd60ac58SVaibhav Hiremath 4632923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 464ad4d3f95SVaibhav Hiremath 4652923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 4662923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 467886aba55SVaibhav Hiremath goto exit; 4682923c58eSVaibhav Hiremath 4690b1283e3SVaibhav Hiremath /* First we want to make sure we power off everything 4700b1283e3SVaibhav Hiremath * and then activate back again */ 4710b1283e3SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 4720b1283e3SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 4730b1283e3SVaibhav Hiremath 4747ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 4752923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 4760b1283e3SVaibhav Hiremath if (ret) 4770b1283e3SVaibhav Hiremath goto exit; 47816fe18caSVaibhav Hiremath 4792923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 4802923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 481886aba55SVaibhav Hiremath goto exit; 4822923c58eSVaibhav Hiremath 4832923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 4847691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 4857691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 486886aba55SVaibhav Hiremath goto exit; 4877691fed2SVaibhav Hiremath 4887691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 4897691fed2SVaibhav Hiremath * and then enter FW flashing state */ 490fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 491fd60ac58SVaibhav Hiremath 4927691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 493fd60ac58SVaibhav Hiremath 494c61a8b49SVaibhav Hiremath ret = arche_platform_fw_flashing_seq(arche_pdata); 495c61a8b49SVaibhav Hiremath if (ret) 496c61a8b49SVaibhav Hiremath goto exit; 497fd60ac58SVaibhav Hiremath 498fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 4992923c58eSVaibhav Hiremath } else { 5002923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 5012923c58eSVaibhav Hiremath ret = -EINVAL; 5022923c58eSVaibhav Hiremath } 5032923c58eSVaibhav Hiremath 504886aba55SVaibhav Hiremath exit: 505886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 5062923c58eSVaibhav Hiremath return ret ? ret : count; 5072923c58eSVaibhav Hiremath } 5082923c58eSVaibhav Hiremath 5092923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 5102923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 5112923c58eSVaibhav Hiremath { 5122923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 5132923c58eSVaibhav Hiremath 5142923c58eSVaibhav Hiremath switch (arche_pdata->state) { 5152923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 5162923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 5172923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 5182923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 5192923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 5202923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 5217691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 5227691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 523886aba55SVaibhav Hiremath case ARCHE_PLATFORM_STATE_TIME_SYNC: 524886aba55SVaibhav Hiremath return sprintf(buf, "time_sync\n"); 5252923c58eSVaibhav Hiremath default: 5262923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 5272923c58eSVaibhav Hiremath } 5282923c58eSVaibhav Hiremath } 5292923c58eSVaibhav Hiremath 5302923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 5312923c58eSVaibhav Hiremath 5329160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier, 5339160b7c7SDavid Lin unsigned long pm_event, void *unused) 5349160b7c7SDavid Lin { 5359160b7c7SDavid Lin struct arche_platform_drvdata *arche_pdata = 5369160b7c7SDavid Lin container_of(notifier, struct arche_platform_drvdata, 5379160b7c7SDavid Lin pm_notifier); 538886aba55SVaibhav Hiremath int ret = NOTIFY_DONE; 5399160b7c7SDavid Lin 540886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 5419160b7c7SDavid Lin switch (pm_event) { 5429160b7c7SDavid Lin case PM_SUSPEND_PREPARE: 543886aba55SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 544886aba55SVaibhav Hiremath ret = NOTIFY_STOP; 545886aba55SVaibhav Hiremath break; 546886aba55SVaibhav Hiremath } 5479160b7c7SDavid Lin device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 5489160b7c7SDavid Lin arche_platform_poweroff_seq(arche_pdata); 5499160b7c7SDavid Lin break; 5509160b7c7SDavid Lin case PM_POST_SUSPEND: 5518ef0b538SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) 5528ef0b538SVaibhav Hiremath break; 5538ef0b538SVaibhav Hiremath 5547ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 55516fd976cSVaibhav Hiremath arche_platform_coldboot_seq(arche_pdata); 5569160b7c7SDavid Lin break; 5579160b7c7SDavid Lin default: 5589160b7c7SDavid Lin break; 5599160b7c7SDavid Lin } 560886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 5619160b7c7SDavid Lin 562886aba55SVaibhav Hiremath return ret; 5639160b7c7SDavid Lin } 5649160b7c7SDavid Lin 5657fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 5667fa60654SVaibhav Hiremath { 5677fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 5687fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 5697fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 5707fa60654SVaibhav Hiremath int ret; 5717fa60654SVaibhav Hiremath 5727fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 5737fa60654SVaibhav Hiremath if (!arche_pdata) 5747fa60654SVaibhav Hiremath return -ENOMEM; 5757fa60654SVaibhav Hiremath 5767fa60654SVaibhav Hiremath /* setup svc reset gpio */ 5777fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 5787fa60654SVaibhav Hiremath "svc,reset-active-high"); 5797fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 5807fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 5817fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 582f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 5837fa60654SVaibhav Hiremath } 5847fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 5857fa60654SVaibhav Hiremath if (ret) { 5867fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 5877fa60654SVaibhav Hiremath return ret; 5887fa60654SVaibhav Hiremath } 5897fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 5907fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 5917fa60654SVaibhav Hiremath if (ret) { 5927fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 5937fa60654SVaibhav Hiremath return ret; 5947fa60654SVaibhav Hiremath } 595c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 5967fa60654SVaibhav Hiremath 5977fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 5987fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 5997fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 6007fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 601f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 6027fa60654SVaibhav Hiremath } 6037fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 6047fa60654SVaibhav Hiremath if (ret) { 6057fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 6067fa60654SVaibhav Hiremath return ret; 6077fa60654SVaibhav Hiremath } 6087fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 6097fa60654SVaibhav Hiremath if (ret) { 6107fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 6117fa60654SVaibhav Hiremath return ret; 6127fa60654SVaibhav Hiremath } 6137fa60654SVaibhav Hiremath 6147fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 6157fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 6167fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 6177fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 6187fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 619f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 6207fa60654SVaibhav Hiremath } 6217fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 6227fa60654SVaibhav Hiremath if (ret) { 6237fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 6247fa60654SVaibhav Hiremath return ret; 6257fa60654SVaibhav Hiremath } 6267fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 6277fa60654SVaibhav Hiremath if (ret) { 6287fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 6297fa60654SVaibhav Hiremath return ret; 6307fa60654SVaibhav Hiremath } 6317fa60654SVaibhav Hiremath 6327fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 6337fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 6347fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 6357fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 6367fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 6377fa60654SVaibhav Hiremath return ret; 6387fa60654SVaibhav Hiremath } 6397fa60654SVaibhav Hiremath 6407fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 6417fa60654SVaibhav Hiremath 6427fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 6437fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 6447fa60654SVaibhav Hiremath 645a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 646a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 647a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 648a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 649758ca99dSVaibhav Hiremath return ret; 65072a8c24bSViresh Kumar } 6517fa60654SVaibhav Hiremath 652a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 653a463fc15SVaibhav Hiremath if (ret) { 654a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 655a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 656758ca99dSVaibhav Hiremath return ret; 657a463fc15SVaibhav Hiremath } 65816fd976cSVaibhav Hiremath 659c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 660a463fc15SVaibhav Hiremath 661a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 662a463fc15SVaibhav Hiremath 6636c1ca56dSVaibhav Hiremath spin_lock_init(&arche_pdata->wake_lock); 664886aba55SVaibhav Hiremath mutex_init(&arche_pdata->platform_state_mutex); 665970dc85bSBryan O'Donoghue init_waitqueue_head(&arche_pdata->wq); 666f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 667f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 668f760bbfbSVaibhav Hiremath 669f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 670f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 671f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 672f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 673f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 674f760bbfbSVaibhav Hiremath if (ret) { 675f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 676f760bbfbSVaibhav Hiremath return ret; 677f760bbfbSVaibhav Hiremath } 678d29b67d4SVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 679f760bbfbSVaibhav Hiremath 6802923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 6812923c58eSVaibhav Hiremath if (ret) { 6822923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 6832923c58eSVaibhav Hiremath return ret; 6842923c58eSVaibhav Hiremath } 6852923c58eSVaibhav Hiremath 686d29b67d4SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 687d29b67d4SVaibhav Hiremath if (ret) { 688d29b67d4SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 689d29b67d4SVaibhav Hiremath goto err_device_remove; 690d29b67d4SVaibhav Hiremath } 691d29b67d4SVaibhav Hiremath 692d29b67d4SVaibhav Hiremath arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; 693d29b67d4SVaibhav Hiremath ret = register_pm_notifier(&arche_pdata->pm_notifier); 694d29b67d4SVaibhav Hiremath 695d29b67d4SVaibhav Hiremath if (ret) { 696d29b67d4SVaibhav Hiremath dev_err(dev, "failed to register pm notifier %d\n", ret); 697d29b67d4SVaibhav Hiremath goto err_device_remove; 698d29b67d4SVaibhav Hiremath } 699d29b67d4SVaibhav Hiremath 700d29b67d4SVaibhav Hiremath /* Register callback pointer */ 701d29b67d4SVaibhav Hiremath arche_platform_change_state_cb = arche_platform_change_state; 702d29b67d4SVaibhav Hiremath 703d29b67d4SVaibhav Hiremath /* Explicitly power off if requested */ 704d29b67d4SVaibhav Hiremath if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { 705886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 706758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 707758ca99dSVaibhav Hiremath if (ret) { 708758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 7096743a6fdSVaibhav Hiremath goto err_coldboot; 710758ca99dSVaibhav Hiremath } 711d29b67d4SVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 712886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 7139160b7c7SDavid Lin } 7149160b7c7SDavid Lin 7157fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 71672a8c24bSViresh Kumar return 0; 7176743a6fdSVaibhav Hiremath 7186743a6fdSVaibhav Hiremath err_coldboot: 719886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 720d29b67d4SVaibhav Hiremath err_device_remove: 7216743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 7226743a6fdSVaibhav Hiremath return ret; 7237fa60654SVaibhav Hiremath } 7247fa60654SVaibhav Hiremath 725bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 726bc142bbbSVaibhav Hiremath { 727bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 728bc142bbbSVaibhav Hiremath 729bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 730bc142bbbSVaibhav Hiremath 731bc142bbbSVaibhav Hiremath return 0; 732bc142bbbSVaibhav Hiremath } 733bc142bbbSVaibhav Hiremath 7347fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 7357fa60654SVaibhav Hiremath { 7367fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 7377fa60654SVaibhav Hiremath 7389160b7c7SDavid Lin unregister_pm_notifier(&arche_pdata->pm_notifier); 7392923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 740bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 7415993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 7427fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 7437fa60654SVaibhav Hiremath 744ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 745ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 746ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 7477fa60654SVaibhav Hiremath return 0; 7487fa60654SVaibhav Hiremath } 7497fa60654SVaibhav Hiremath 7507fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 7517fa60654SVaibhav Hiremath { 7527fa60654SVaibhav Hiremath /* 7537fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 7547fa60654SVaibhav Hiremath * completely 7557fa60654SVaibhav Hiremath * 7567fa60654SVaibhav Hiremath * TODO: sequence ?? 7577fa60654SVaibhav Hiremath * 7587fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 7597fa60654SVaibhav Hiremath * Precondition: Definition ??? 7607fa60654SVaibhav Hiremath */ 7617fa60654SVaibhav Hiremath return 0; 7627fa60654SVaibhav Hiremath } 7637fa60654SVaibhav Hiremath 7647fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 7657fa60654SVaibhav Hiremath { 7667fa60654SVaibhav Hiremath /* 7677fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 7687fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 7697fa60654SVaibhav Hiremath * OFF state or standby state. 7707fa60654SVaibhav Hiremath * 7717fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 7727fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 7737fa60654SVaibhav Hiremath * 7747fa60654SVaibhav Hiremath */ 7757fa60654SVaibhav Hiremath return 0; 7767fa60654SVaibhav Hiremath } 7777fa60654SVaibhav Hiremath 7781f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev) 7791f77b363SDavid Lin { 7801f77b363SDavid Lin struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 7811f77b363SDavid Lin 7821f77b363SDavid Lin arche_platform_poweroff_seq(arche_pdata); 7831f77b363SDavid Lin 7841f77b363SDavid Lin usb3613_hub_mode_ctrl(false); 7851f77b363SDavid Lin } 7861f77b363SDavid Lin 7877fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 7887fa60654SVaibhav Hiremath arche_platform_suspend, 7897fa60654SVaibhav Hiremath arche_platform_resume); 7907fa60654SVaibhav Hiremath 7917fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 7927fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7937fa60654SVaibhav Hiremath { }, 7947fa60654SVaibhav Hiremath }; 7951e5dd1f8SGreg Kroah-Hartman 7961e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 7971e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 7981e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 7991e5dd1f8SGreg Kroah-Hartman { }, 8001e5dd1f8SGreg Kroah-Hartman }; 8011e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 8027fa60654SVaibhav Hiremath 8037fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 8047fa60654SVaibhav Hiremath .probe = arche_platform_probe, 8057fa60654SVaibhav Hiremath .remove = arche_platform_remove, 8061f77b363SDavid Lin .shutdown = arche_platform_shutdown, 8077fa60654SVaibhav Hiremath .driver = { 8087fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 8097fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 8101e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 8117fa60654SVaibhav Hiremath } 8127fa60654SVaibhav Hiremath }; 8137fa60654SVaibhav Hiremath 8141e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 8151e5dd1f8SGreg Kroah-Hartman { 8161e5dd1f8SGreg Kroah-Hartman int retval; 8171e5dd1f8SGreg Kroah-Hartman 8181e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 8191e5dd1f8SGreg Kroah-Hartman if (retval) 8201e5dd1f8SGreg Kroah-Hartman return retval; 8211e5dd1f8SGreg Kroah-Hartman 8227b62b61cSViresh Kumar retval = arche_apb_init(); 8231e5dd1f8SGreg Kroah-Hartman if (retval) 8241e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 8251e5dd1f8SGreg Kroah-Hartman 8261e5dd1f8SGreg Kroah-Hartman return retval; 8271e5dd1f8SGreg Kroah-Hartman } 8281e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 8291e5dd1f8SGreg Kroah-Hartman 8301e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 8311e5dd1f8SGreg Kroah-Hartman { 8327b62b61cSViresh Kumar arche_apb_exit(); 8331e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 8341e5dd1f8SGreg Kroah-Hartman } 8351e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 8367fa60654SVaibhav Hiremath 837a974f469SSandeep Patil MODULE_LICENSE("GPL v2"); 8387fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 8397fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 840