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> 22f760bbfbSVaibhav Hiremath #include <linux/time.h> 231e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 247fa60654SVaibhav Hiremath 25ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h> 26ad4d3f95SVaibhav Hiremath 27f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS 30 28f760bbfbSVaibhav Hiremath 29685353c1SVaibhav Hiremath enum svc_wakedetect_state { 30685353c1SVaibhav Hiremath WD_STATE_IDLE, /* Default state = pulled high/low */ 31685353c1SVaibhav Hiremath WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 32685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 33685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 34685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_START, /* Cold boot process started */ 35685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_START, /* Not used */ 36685353c1SVaibhav Hiremath }; 37685353c1SVaibhav Hiremath 387fa60654SVaibhav Hiremath struct arche_platform_drvdata { 397fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 407fa60654SVaibhav Hiremath int svc_reset_gpio; 417fa60654SVaibhav Hiremath bool is_reset_act_hi; 427fa60654SVaibhav Hiremath int svc_sysboot_gpio; 43a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 447fa60654SVaibhav Hiremath 45e74d04a5SVaibhav Hiremath enum arche_platform_state state; 46e74d04a5SVaibhav Hiremath 477fe93014SDavid Lin int svc_refclk_req; 487fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 497fa60654SVaibhav Hiremath 507fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 517fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 527fa60654SVaibhav Hiremath 537fa60654SVaibhav Hiremath int num_apbs; 54a463fc15SVaibhav Hiremath 55a463fc15SVaibhav Hiremath struct delayed_work delayed_work; 56685353c1SVaibhav Hiremath enum svc_wakedetect_state wake_detect_state; 57f760bbfbSVaibhav Hiremath int wake_detect_irq; 58f760bbfbSVaibhav Hiremath spinlock_t lock; 59f760bbfbSVaibhav Hiremath unsigned long wake_detect_start; 60685353c1SVaibhav Hiremath 61a463fc15SVaibhav Hiremath struct device *dev; 627fa60654SVaibhav Hiremath }; 637fa60654SVaibhav Hiremath 647fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 657fa60654SVaibhav Hiremath { 667fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 677fa60654SVaibhav Hiremath } 687fa60654SVaibhav Hiremath 69f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data) 70f760bbfbSVaibhav Hiremath { 71f760bbfbSVaibhav Hiremath int ret; 72f760bbfbSVaibhav Hiremath 73f760bbfbSVaibhav Hiremath ret = apb_ctrl_coldboot(dev); 74f760bbfbSVaibhav Hiremath if (ret) 75f760bbfbSVaibhav Hiremath dev_warn(dev, "failed to coldboot\n"); 76f760bbfbSVaibhav Hiremath 77f760bbfbSVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 78f760bbfbSVaibhav Hiremath return 0; 79f760bbfbSVaibhav Hiremath } 80f760bbfbSVaibhav Hiremath 81fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data) 82fd60ac58SVaibhav Hiremath { 83fd60ac58SVaibhav Hiremath int ret; 84fd60ac58SVaibhav Hiremath 85fd60ac58SVaibhav Hiremath ret = apb_ctrl_fw_flashing(dev); 86fd60ac58SVaibhav Hiremath if (ret) 87fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to switch to fw flashing state\n"); 88fd60ac58SVaibhav Hiremath 89fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 90fd60ac58SVaibhav Hiremath return 0; 91fd60ac58SVaibhav Hiremath } 92fd60ac58SVaibhav Hiremath 93fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 94fd60ac58SVaibhav Hiremath { 95fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 96fd60ac58SVaibhav Hiremath 97e915ce48SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 98e915ce48SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 99e915ce48SVaibhav Hiremath dev_warn(dev, "failed to control hub device\n"); 100e915ce48SVaibhav Hiremath 101fd60ac58SVaibhav Hiremath return 0; 102fd60ac58SVaibhav Hiremath } 103fd60ac58SVaibhav Hiremath 104a463fc15SVaibhav Hiremath /** 105db5a3bcaSVaibhav Hiremath * hub_conf_delayed_work - Configures USB3613 device to HUB mode 106db5a3bcaSVaibhav Hiremath * 107db5a3bcaSVaibhav Hiremath * The idea here is to split the APB coldboot operation with slow HUB configuration, 108db5a3bcaSVaibhav Hiremath * so that driver response to wake/detect event can be met. 109db5a3bcaSVaibhav Hiremath * So expectation is, once code reaches here, means initial unipro linkup 110db5a3bcaSVaibhav Hiremath * between APB<->Switch was successful, so now just take it to AP. 111a463fc15SVaibhav Hiremath */ 112db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work) 113a463fc15SVaibhav Hiremath { 114a463fc15SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = 115a463fc15SVaibhav Hiremath container_of(work, struct arche_platform_drvdata, delayed_work.work); 116ad4d3f95SVaibhav Hiremath 117ad4d3f95SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 118ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 119ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 120a463fc15SVaibhav Hiremath } 121a463fc15SVaibhav Hiremath 12216fe18caSVaibhav Hiremath static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) 12316fe18caSVaibhav Hiremath { 12416fe18caSVaibhav Hiremath /* Assert wake/detect = Detect event from AP */ 12516fe18caSVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 1); 12616fe18caSVaibhav Hiremath 12716fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 12816fe18caSVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 12916fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 13016fe18caSVaibhav Hiremath } 13116fe18caSVaibhav Hiremath 132f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 133f760bbfbSVaibhav Hiremath { 134f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 135f760bbfbSVaibhav Hiremath unsigned long flags; 136f760bbfbSVaibhav Hiremath 137f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 138f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 139f760bbfbSVaibhav Hiremath /* Something is wrong */ 140f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 141f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 142f760bbfbSVaibhav Hiremath } 143f760bbfbSVaibhav Hiremath 144f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; 145f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 146f760bbfbSVaibhav Hiremath 147ff788de0SVaibhav Hiremath /* It should complete power cycle, so first make sure it is poweroff */ 148ff788de0SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 149e915ce48SVaibhav Hiremath 150f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 151f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 152f760bbfbSVaibhav Hiremath 153f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 154f760bbfbSVaibhav Hiremath /* USB HUB configuration */ 155f760bbfbSVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 156f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 157f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 158f760bbfbSVaibhav Hiremath 159f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 160f760bbfbSVaibhav Hiremath } 161f760bbfbSVaibhav Hiremath 162f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 163f760bbfbSVaibhav Hiremath { 164f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 165f760bbfbSVaibhav Hiremath unsigned long flags; 166f760bbfbSVaibhav Hiremath 167f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 168f760bbfbSVaibhav Hiremath 169f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 170f760bbfbSVaibhav Hiremath /* wake/detect rising */ 171f760bbfbSVaibhav Hiremath 172f760bbfbSVaibhav Hiremath /* 173f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 174f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 175f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 176f760bbfbSVaibhav Hiremath */ 177f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 178f760bbfbSVaibhav Hiremath if (time_before(jiffies, 179f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 180f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 181f760bbfbSVaibhav Hiremath /* No harm with cancellation, even if not pending */ 182f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 183f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 184f760bbfbSVaibhav Hiremath } else { 185f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 186f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 187f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 188f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = 189f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_TRIG; 190f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 191f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 192f760bbfbSVaibhav Hiremath } 193f760bbfbSVaibhav Hiremath } 194f760bbfbSVaibhav Hiremath } 195f760bbfbSVaibhav Hiremath } else { 196f760bbfbSVaibhav Hiremath /* wake/detect falling */ 197f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 198f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 199f760bbfbSVaibhav Hiremath /* No harm with cancellation even if it is not pending*/ 200f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 201f760bbfbSVaibhav Hiremath /* 202f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 203f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 204f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 205f760bbfbSVaibhav Hiremath */ 206f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT; 207f760bbfbSVaibhav Hiremath } 208f760bbfbSVaibhav Hiremath } 209f760bbfbSVaibhav Hiremath 210f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 211f760bbfbSVaibhav Hiremath 212f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 213f760bbfbSVaibhav Hiremath } 214f760bbfbSVaibhav Hiremath 215758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 216758ca99dSVaibhav Hiremath { 217758ca99dSVaibhav Hiremath int ret; 218758ca99dSVaibhav Hiremath 219599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 220599159b6SVaibhav Hiremath return 0; 221599159b6SVaibhav Hiremath 222758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 223758ca99dSVaibhav Hiremath 224758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 225758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 226758ca99dSVaibhav Hiremath 227758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 228758ca99dSVaibhav Hiremath usleep_range(100, 200); 229758ca99dSVaibhav Hiremath 230758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 231758ca99dSVaibhav Hiremath if (ret) { 232758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 233758ca99dSVaibhav Hiremath ret); 234758ca99dSVaibhav Hiremath return ret; 235758ca99dSVaibhav Hiremath } 236758ca99dSVaibhav Hiremath 237758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 238758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 239758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 240758ca99dSVaibhav Hiremath 241e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; 242e74d04a5SVaibhav Hiremath 243758ca99dSVaibhav Hiremath return 0; 244758ca99dSVaibhav Hiremath } 245758ca99dSVaibhav Hiremath 2467691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 2477691fed2SVaibhav Hiremath { 248599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 249599159b6SVaibhav Hiremath return; 250599159b6SVaibhav Hiremath 2517691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 2527691fed2SVaibhav Hiremath 2537691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2547691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 2557691fed2SVaibhav Hiremath 2567691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 2577691fed2SVaibhav Hiremath 2587691fed2SVaibhav Hiremath usleep_range(100, 200); 2597691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2607691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 2617691fed2SVaibhav Hiremath 2627691fed2SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; 2637691fed2SVaibhav Hiremath 2647691fed2SVaibhav Hiremath } 2657691fed2SVaibhav Hiremath 2665993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 2677fa60654SVaibhav Hiremath { 268f760bbfbSVaibhav Hiremath unsigned long flags; 269f760bbfbSVaibhav Hiremath 270599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 271599159b6SVaibhav Hiremath return; 272599159b6SVaibhav Hiremath 27325847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 27425847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 275d2320b2dSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 276b4c95fcaSVaibhav Hiremath /* Send disconnect/detach event to SVC */ 2770786212dSVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 278b4c95fcaSVaibhav Hiremath usleep_range(100, 200); 279f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 280685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 281f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 282b4c95fcaSVaibhav Hiremath 283d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 28425847ee7SVaibhav Hiremath } 28525847ee7SVaibhav Hiremath 2867fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 2877fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2887fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 289e74d04a5SVaibhav Hiremath 290e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 2917fa60654SVaibhav Hiremath } 2927fa60654SVaibhav Hiremath 2932923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 2942923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 2952923c58eSVaibhav Hiremath { 2962923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 2972923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 2982923c58eSVaibhav Hiremath int ret = 0; 2992923c58eSVaibhav Hiremath 3002923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 3012923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 3022923c58eSVaibhav Hiremath return count; 3032923c58eSVaibhav Hiremath 304fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 305fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 306fd60ac58SVaibhav Hiremath 3072923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 308ad4d3f95SVaibhav Hiremath 3092923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 3102923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 3112923c58eSVaibhav Hiremath return count; 3122923c58eSVaibhav Hiremath 3132923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 31416fe18caSVaibhav Hiremath 31516fe18caSVaibhav Hiremath assert_wakedetect(arche_pdata); 3162923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 3172923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 3182923c58eSVaibhav Hiremath return count; 3192923c58eSVaibhav Hiremath 3202923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 3217691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 3227691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 3237691fed2SVaibhav Hiremath return count; 3247691fed2SVaibhav Hiremath 3257691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 3267691fed2SVaibhav Hiremath * and then enter FW flashing state */ 327fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 328fd60ac58SVaibhav Hiremath 3297691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 330fd60ac58SVaibhav Hiremath 3317691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 332fd60ac58SVaibhav Hiremath 333fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 3342923c58eSVaibhav Hiremath } else { 3352923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 3362923c58eSVaibhav Hiremath ret = -EINVAL; 3372923c58eSVaibhav Hiremath } 3382923c58eSVaibhav Hiremath 3392923c58eSVaibhav Hiremath return ret ? ret : count; 3402923c58eSVaibhav Hiremath } 3412923c58eSVaibhav Hiremath 3422923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 3432923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 3442923c58eSVaibhav Hiremath { 3452923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 3462923c58eSVaibhav Hiremath 3472923c58eSVaibhav Hiremath switch (arche_pdata->state) { 3482923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 3492923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 3502923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 3512923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 3522923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 3532923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 3547691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 3557691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 3562923c58eSVaibhav Hiremath default: 3572923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 3582923c58eSVaibhav Hiremath } 3592923c58eSVaibhav Hiremath } 3602923c58eSVaibhav Hiremath 3612923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 3622923c58eSVaibhav Hiremath 3637fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 3647fa60654SVaibhav Hiremath { 3657fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 3667fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 3677fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 3687fa60654SVaibhav Hiremath int ret; 3697fa60654SVaibhav Hiremath 3707fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 3717fa60654SVaibhav Hiremath if (!arche_pdata) 3727fa60654SVaibhav Hiremath return -ENOMEM; 3737fa60654SVaibhav Hiremath 3747fa60654SVaibhav Hiremath /* setup svc reset gpio */ 3757fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 3767fa60654SVaibhav Hiremath "svc,reset-active-high"); 3777fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 3787fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 3797fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 380f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 3817fa60654SVaibhav Hiremath } 3827fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 3837fa60654SVaibhav Hiremath if (ret) { 3847fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 3857fa60654SVaibhav Hiremath return ret; 3867fa60654SVaibhav Hiremath } 3877fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 3887fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3897fa60654SVaibhav Hiremath if (ret) { 3907fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3917fa60654SVaibhav Hiremath return ret; 3927fa60654SVaibhav Hiremath } 393e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 3947fa60654SVaibhav Hiremath 3957fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 3967fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 3977fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 3987fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 399f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 4007fa60654SVaibhav Hiremath } 4017fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 4027fa60654SVaibhav Hiremath if (ret) { 4037fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 4047fa60654SVaibhav Hiremath return ret; 4057fa60654SVaibhav Hiremath } 4067fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 4077fa60654SVaibhav Hiremath if (ret) { 4087fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 4097fa60654SVaibhav Hiremath return ret; 4107fa60654SVaibhav Hiremath } 4117fa60654SVaibhav Hiremath 4127fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 4137fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 4147fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 4157fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 4167fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 417f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 4187fa60654SVaibhav Hiremath } 4197fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 4207fa60654SVaibhav Hiremath if (ret) { 4217fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 4227fa60654SVaibhav Hiremath return ret; 4237fa60654SVaibhav Hiremath } 4247fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 4257fa60654SVaibhav Hiremath if (ret) { 4267fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 4277fa60654SVaibhav Hiremath return ret; 4287fa60654SVaibhav Hiremath } 4297fa60654SVaibhav Hiremath 4307fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 4317fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 4327fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 4337fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 4347fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 4357fa60654SVaibhav Hiremath return ret; 4367fa60654SVaibhav Hiremath } 4377fa60654SVaibhav Hiremath 4387fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 4397fa60654SVaibhav Hiremath 4407fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 4417fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 4427fa60654SVaibhav Hiremath 443a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 444a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 445a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 446a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 447758ca99dSVaibhav Hiremath return ret; 44872a8c24bSViresh Kumar } 4497fa60654SVaibhav Hiremath 450a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 451a463fc15SVaibhav Hiremath if (ret) { 452a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 453a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 454758ca99dSVaibhav Hiremath return ret; 455a463fc15SVaibhav Hiremath } 456057aad29SMichael Scott /* deassert wake detect */ 457057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 458685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 459a463fc15SVaibhav Hiremath 460a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 461a463fc15SVaibhav Hiremath 462f760bbfbSVaibhav Hiremath spin_lock_init(&arche_pdata->lock); 463f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 464f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 465f760bbfbSVaibhav Hiremath 466f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 467f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 468f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 469f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 470f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 471f760bbfbSVaibhav Hiremath if (ret) { 472f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 473f760bbfbSVaibhav Hiremath return ret; 474f760bbfbSVaibhav Hiremath } 475f760bbfbSVaibhav Hiremath /* Enable it only after sending wake/detect event */ 476f760bbfbSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 477f760bbfbSVaibhav Hiremath 4782923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 4792923c58eSVaibhav Hiremath if (ret) { 4802923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 4812923c58eSVaibhav Hiremath return ret; 4822923c58eSVaibhav Hiremath } 4832923c58eSVaibhav Hiremath 484758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 485758ca99dSVaibhav Hiremath if (ret) { 486758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 4876743a6fdSVaibhav Hiremath goto err_coldboot; 488758ca99dSVaibhav Hiremath } 489758ca99dSVaibhav Hiremath 490fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 491fd60ac58SVaibhav Hiremath if (ret) { 492fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 4936743a6fdSVaibhav Hiremath goto err_populate; 494fd60ac58SVaibhav Hiremath } 495fd60ac58SVaibhav Hiremath 49616fe18caSVaibhav Hiremath assert_wakedetect(arche_pdata); 497db5a3bcaSVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); 498fd60ac58SVaibhav Hiremath 4997fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 50072a8c24bSViresh Kumar return 0; 5016743a6fdSVaibhav Hiremath 5026743a6fdSVaibhav Hiremath err_populate: 5036743a6fdSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5046743a6fdSVaibhav Hiremath err_coldboot: 5056743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 5066743a6fdSVaibhav Hiremath return ret; 5077fa60654SVaibhav Hiremath } 5087fa60654SVaibhav Hiremath 509bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 510bc142bbbSVaibhav Hiremath { 511bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 512bc142bbbSVaibhav Hiremath 513bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 514bc142bbbSVaibhav Hiremath 515bc142bbbSVaibhav Hiremath return 0; 516bc142bbbSVaibhav Hiremath } 517bc142bbbSVaibhav Hiremath 5187fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 5197fa60654SVaibhav Hiremath { 5207fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 5217fa60654SVaibhav Hiremath 5222923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 52349e6e04bSVaibhav Hiremath cancel_delayed_work_sync(&arche_pdata->delayed_work); 524bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 5255993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5267fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 5277fa60654SVaibhav Hiremath 528ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 529ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 530ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 5317fa60654SVaibhav Hiremath return 0; 5327fa60654SVaibhav Hiremath } 5337fa60654SVaibhav Hiremath 5347fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 5357fa60654SVaibhav Hiremath { 5367fa60654SVaibhav Hiremath /* 5377fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 5387fa60654SVaibhav Hiremath * completely 5397fa60654SVaibhav Hiremath * 5407fa60654SVaibhav Hiremath * TODO: sequence ?? 5417fa60654SVaibhav Hiremath * 5427fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 5437fa60654SVaibhav Hiremath * Precondition: Definition ??? 5447fa60654SVaibhav Hiremath */ 5457fa60654SVaibhav Hiremath return 0; 5467fa60654SVaibhav Hiremath } 5477fa60654SVaibhav Hiremath 5487fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 5497fa60654SVaibhav Hiremath { 5507fa60654SVaibhav Hiremath /* 5517fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 5527fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 5537fa60654SVaibhav Hiremath * OFF state or standby state. 5547fa60654SVaibhav Hiremath * 5557fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 5567fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 5577fa60654SVaibhav Hiremath * 5587fa60654SVaibhav Hiremath */ 5597fa60654SVaibhav Hiremath return 0; 5607fa60654SVaibhav Hiremath } 5617fa60654SVaibhav Hiremath 5627fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 5637fa60654SVaibhav Hiremath arche_platform_suspend, 5647fa60654SVaibhav Hiremath arche_platform_resume); 5657fa60654SVaibhav Hiremath 5667fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 5677fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5687fa60654SVaibhav Hiremath { }, 5697fa60654SVaibhav Hiremath }; 5701e5dd1f8SGreg Kroah-Hartman 5711e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 5721e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5731e5dd1f8SGreg Kroah-Hartman { }, 5741e5dd1f8SGreg Kroah-Hartman }; 5751e5dd1f8SGreg Kroah-Hartman 5761e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 5771e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5781e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5791e5dd1f8SGreg Kroah-Hartman { }, 5801e5dd1f8SGreg Kroah-Hartman }; 5811e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 5827fa60654SVaibhav Hiremath 5837fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 5847fa60654SVaibhav Hiremath .probe = arche_platform_probe, 5857fa60654SVaibhav Hiremath .remove = arche_platform_remove, 5867fa60654SVaibhav Hiremath .driver = { 5877fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 5887fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 5891e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 5907fa60654SVaibhav Hiremath } 5917fa60654SVaibhav Hiremath }; 5927fa60654SVaibhav Hiremath 5931e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 5941e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 5951e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 5961e5dd1f8SGreg Kroah-Hartman .driver = { 5971e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 5981e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 5991e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 6001e5dd1f8SGreg Kroah-Hartman } 6011e5dd1f8SGreg Kroah-Hartman }; 6021e5dd1f8SGreg Kroah-Hartman 6031e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 6041e5dd1f8SGreg Kroah-Hartman { 6051e5dd1f8SGreg Kroah-Hartman int retval; 6061e5dd1f8SGreg Kroah-Hartman 6071e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 6081e5dd1f8SGreg Kroah-Hartman if (retval) 6091e5dd1f8SGreg Kroah-Hartman return retval; 6101e5dd1f8SGreg Kroah-Hartman 6111e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 6121e5dd1f8SGreg Kroah-Hartman if (retval) 6131e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6141e5dd1f8SGreg Kroah-Hartman 6151e5dd1f8SGreg Kroah-Hartman return retval; 6161e5dd1f8SGreg Kroah-Hartman } 6171e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 6181e5dd1f8SGreg Kroah-Hartman 6191e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 6201e5dd1f8SGreg Kroah-Hartman { 6211e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 6221e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6231e5dd1f8SGreg Kroah-Hartman } 6241e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 6257fa60654SVaibhav Hiremath 6267fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 6277fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 6287fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 629