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 477fa60654SVaibhav Hiremath unsigned 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 97fd60ac58SVaibhav Hiremath return 0; 98fd60ac58SVaibhav Hiremath } 99fd60ac58SVaibhav Hiremath 100a463fc15SVaibhav Hiremath /** 101db5a3bcaSVaibhav Hiremath * hub_conf_delayed_work - Configures USB3613 device to HUB mode 102db5a3bcaSVaibhav Hiremath * 103db5a3bcaSVaibhav Hiremath * The idea here is to split the APB coldboot operation with slow HUB configuration, 104db5a3bcaSVaibhav Hiremath * so that driver response to wake/detect event can be met. 105db5a3bcaSVaibhav Hiremath * So expectation is, once code reaches here, means initial unipro linkup 106db5a3bcaSVaibhav Hiremath * between APB<->Switch was successful, so now just take it to AP. 107a463fc15SVaibhav Hiremath */ 108db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work) 109a463fc15SVaibhav Hiremath { 110a463fc15SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = 111a463fc15SVaibhav Hiremath container_of(work, struct arche_platform_drvdata, delayed_work.work); 112ad4d3f95SVaibhav Hiremath 113ad4d3f95SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 114ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 115ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 116a463fc15SVaibhav Hiremath } 117a463fc15SVaibhav Hiremath 118f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 119f760bbfbSVaibhav Hiremath { 120f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 121f760bbfbSVaibhav Hiremath unsigned long flags; 122f760bbfbSVaibhav Hiremath 123f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 124f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 125f760bbfbSVaibhav Hiremath /* Something is wrong */ 126f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 127f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 128f760bbfbSVaibhav Hiremath } 129f760bbfbSVaibhav Hiremath 130f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; 131f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 132f760bbfbSVaibhav Hiremath 133f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 134f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 135f760bbfbSVaibhav Hiremath 136f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 137f760bbfbSVaibhav Hiremath /* USB HUB configuration */ 138f760bbfbSVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 139f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 140f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 141f760bbfbSVaibhav Hiremath 142f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 143f760bbfbSVaibhav Hiremath } 144f760bbfbSVaibhav Hiremath 145f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 146f760bbfbSVaibhav Hiremath { 147f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 148f760bbfbSVaibhav Hiremath unsigned long flags; 149f760bbfbSVaibhav Hiremath 150f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 151f760bbfbSVaibhav Hiremath 152f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 153f760bbfbSVaibhav Hiremath /* wake/detect rising */ 154f760bbfbSVaibhav Hiremath 155f760bbfbSVaibhav Hiremath /* 156f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 157f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 158f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 159f760bbfbSVaibhav Hiremath */ 160f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 161f760bbfbSVaibhav Hiremath if (time_before(jiffies, 162f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 163f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 164f760bbfbSVaibhav Hiremath /* No harm with cancellation, even if not pending */ 165f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 166f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 167f760bbfbSVaibhav Hiremath } else { 168f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 169f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 170f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 171f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = 172f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_TRIG; 173f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 174f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 175f760bbfbSVaibhav Hiremath } 176f760bbfbSVaibhav Hiremath } 177f760bbfbSVaibhav Hiremath } 178f760bbfbSVaibhav Hiremath } else { 179f760bbfbSVaibhav Hiremath /* wake/detect falling */ 180f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 181f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 182f760bbfbSVaibhav Hiremath /* No harm with cancellation even if it is not pending*/ 183f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 184f760bbfbSVaibhav Hiremath /* 185f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 186f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 187f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 188f760bbfbSVaibhav Hiremath */ 189f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT; 190f760bbfbSVaibhav Hiremath } 191f760bbfbSVaibhav Hiremath } 192f760bbfbSVaibhav Hiremath 193f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 194f760bbfbSVaibhav Hiremath 195f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 196f760bbfbSVaibhav Hiremath } 197f760bbfbSVaibhav Hiremath 198758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 199758ca99dSVaibhav Hiremath { 200758ca99dSVaibhav Hiremath int ret; 201758ca99dSVaibhav Hiremath 202599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 203599159b6SVaibhav Hiremath return 0; 204599159b6SVaibhav Hiremath 205758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 206758ca99dSVaibhav Hiremath 207758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 208758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 209758ca99dSVaibhav Hiremath 210758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 211758ca99dSVaibhav Hiremath usleep_range(100, 200); 212758ca99dSVaibhav Hiremath 213758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 214758ca99dSVaibhav Hiremath if (ret) { 215758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 216758ca99dSVaibhav Hiremath ret); 217758ca99dSVaibhav Hiremath return ret; 218758ca99dSVaibhav Hiremath } 219758ca99dSVaibhav Hiremath 220758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 221758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 222758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 223758ca99dSVaibhav Hiremath 224e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; 225e74d04a5SVaibhav Hiremath 226758ca99dSVaibhav Hiremath return 0; 227758ca99dSVaibhav Hiremath } 228758ca99dSVaibhav Hiremath 2297691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 2307691fed2SVaibhav Hiremath { 231599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 232599159b6SVaibhav Hiremath return; 233599159b6SVaibhav Hiremath 2347691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 2357691fed2SVaibhav Hiremath 2367691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2377691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 2387691fed2SVaibhav Hiremath 2397691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 2407691fed2SVaibhav Hiremath 2417691fed2SVaibhav Hiremath usleep_range(100, 200); 2427691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2437691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 2447691fed2SVaibhav Hiremath 2457691fed2SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; 2467691fed2SVaibhav Hiremath 2477691fed2SVaibhav Hiremath } 2487691fed2SVaibhav Hiremath 2495993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 2507fa60654SVaibhav Hiremath { 251f760bbfbSVaibhav Hiremath unsigned long flags; 252f760bbfbSVaibhav Hiremath 253599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 254599159b6SVaibhav Hiremath return; 255599159b6SVaibhav Hiremath 25625847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 25725847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 258b4c95fcaSVaibhav Hiremath /* Send disconnect/detach event to SVC */ 259b4c95fcaSVaibhav Hiremath gpio_set_value(arche_pdata->wake_detect_gpio, 0); 260b4c95fcaSVaibhav Hiremath usleep_range(100, 200); 261f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 262685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 263f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 264b4c95fcaSVaibhav Hiremath 265d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 26625847ee7SVaibhav Hiremath } 26725847ee7SVaibhav Hiremath 2687fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 2697fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2707fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 271e74d04a5SVaibhav Hiremath 272e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 2737fa60654SVaibhav Hiremath } 2747fa60654SVaibhav Hiremath 2752923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 2762923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 2772923c58eSVaibhav Hiremath { 2782923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 2792923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 2802923c58eSVaibhav Hiremath int ret = 0; 2812923c58eSVaibhav Hiremath 2822923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 2832923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 2842923c58eSVaibhav Hiremath return count; 2852923c58eSVaibhav Hiremath 286fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 287fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 288fd60ac58SVaibhav Hiremath 2892923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 290ad4d3f95SVaibhav Hiremath 291ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 292ad4d3f95SVaibhav Hiremath if (ret) 293ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 294ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 2952923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 2962923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 2972923c58eSVaibhav Hiremath return count; 2982923c58eSVaibhav Hiremath 2992923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 3002923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 3012923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 3022923c58eSVaibhav Hiremath return count; 3032923c58eSVaibhav Hiremath 3042923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 3057691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 3067691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 3077691fed2SVaibhav Hiremath return count; 3087691fed2SVaibhav Hiremath 3097691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 3107691fed2SVaibhav Hiremath * and then enter FW flashing state */ 311fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 312fd60ac58SVaibhav Hiremath 3137691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 314fd60ac58SVaibhav Hiremath 315ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 316ad4d3f95SVaibhav Hiremath if (ret) 317ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 318ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 319ad4d3f95SVaibhav Hiremath 3207691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 321fd60ac58SVaibhav Hiremath 322fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 3232923c58eSVaibhav Hiremath } else { 3242923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 3252923c58eSVaibhav Hiremath ret = -EINVAL; 3262923c58eSVaibhav Hiremath } 3272923c58eSVaibhav Hiremath 3282923c58eSVaibhav Hiremath return ret ? ret : count; 3292923c58eSVaibhav Hiremath } 3302923c58eSVaibhav Hiremath 3312923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 3322923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 3332923c58eSVaibhav Hiremath { 3342923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 3352923c58eSVaibhav Hiremath 3362923c58eSVaibhav Hiremath switch (arche_pdata->state) { 3372923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 3382923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 3392923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 3402923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 3412923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 3422923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 3437691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 3447691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 3452923c58eSVaibhav Hiremath default: 3462923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 3472923c58eSVaibhav Hiremath } 3482923c58eSVaibhav Hiremath } 3492923c58eSVaibhav Hiremath 3502923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 3512923c58eSVaibhav Hiremath 3527fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 3537fa60654SVaibhav Hiremath { 3547fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 3557fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 3567fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 3577fa60654SVaibhav Hiremath int ret; 3587fa60654SVaibhav Hiremath 3597fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 3607fa60654SVaibhav Hiremath if (!arche_pdata) 3617fa60654SVaibhav Hiremath return -ENOMEM; 3627fa60654SVaibhav Hiremath 3637fa60654SVaibhav Hiremath /* setup svc reset gpio */ 3647fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 3657fa60654SVaibhav Hiremath "svc,reset-active-high"); 3667fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 3677fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 3687fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 369f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 3707fa60654SVaibhav Hiremath } 3717fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 3727fa60654SVaibhav Hiremath if (ret) { 3737fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 3747fa60654SVaibhav Hiremath return ret; 3757fa60654SVaibhav Hiremath } 3767fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 3777fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3787fa60654SVaibhav Hiremath if (ret) { 3797fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3807fa60654SVaibhav Hiremath return ret; 3817fa60654SVaibhav Hiremath } 382e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 3837fa60654SVaibhav Hiremath 3847fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 3857fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 3867fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 3877fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 388f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 3897fa60654SVaibhav Hiremath } 3907fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 3917fa60654SVaibhav Hiremath if (ret) { 3927fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 3937fa60654SVaibhav Hiremath return ret; 3947fa60654SVaibhav Hiremath } 3957fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 3967fa60654SVaibhav Hiremath if (ret) { 3977fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3987fa60654SVaibhav Hiremath return ret; 3997fa60654SVaibhav Hiremath } 4007fa60654SVaibhav Hiremath 4017fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 4027fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 4037fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 4047fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 4057fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 406f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 4077fa60654SVaibhav Hiremath } 4087fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 4097fa60654SVaibhav Hiremath if (ret) { 4107fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 4117fa60654SVaibhav Hiremath return ret; 4127fa60654SVaibhav Hiremath } 4137fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 4147fa60654SVaibhav Hiremath if (ret) { 4157fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 4167fa60654SVaibhav Hiremath return ret; 4177fa60654SVaibhav Hiremath } 4187fa60654SVaibhav Hiremath 4197fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 4207fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 4217fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 4227fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 4237fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 4247fa60654SVaibhav Hiremath return ret; 4257fa60654SVaibhav Hiremath } 4267fa60654SVaibhav Hiremath 4277fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 4287fa60654SVaibhav Hiremath 4297fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 4307fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 4317fa60654SVaibhav Hiremath 432a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 433a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 434a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 435a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 436758ca99dSVaibhav Hiremath return ret; 43772a8c24bSViresh Kumar } 4387fa60654SVaibhav Hiremath 439a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 440a463fc15SVaibhav Hiremath if (ret) { 441a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 442a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 443758ca99dSVaibhav Hiremath return ret; 444a463fc15SVaibhav Hiremath } 445057aad29SMichael Scott /* deassert wake detect */ 446057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 447685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 448a463fc15SVaibhav Hiremath 449a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 450a463fc15SVaibhav Hiremath 451f760bbfbSVaibhav Hiremath spin_lock_init(&arche_pdata->lock); 452f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 453f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 454f760bbfbSVaibhav Hiremath 455f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 456f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 457f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 458f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 459f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 460f760bbfbSVaibhav Hiremath if (ret) { 461f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 462f760bbfbSVaibhav Hiremath return ret; 463f760bbfbSVaibhav Hiremath } 464f760bbfbSVaibhav Hiremath /* Enable it only after sending wake/detect event */ 465f760bbfbSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 466f760bbfbSVaibhav Hiremath 4672923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 4682923c58eSVaibhav Hiremath if (ret) { 4692923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 4702923c58eSVaibhav Hiremath return ret; 4712923c58eSVaibhav Hiremath } 4722923c58eSVaibhav Hiremath 473758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 474758ca99dSVaibhav Hiremath if (ret) { 475758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 4766743a6fdSVaibhav Hiremath goto err_coldboot; 477758ca99dSVaibhav Hiremath } 478758ca99dSVaibhav Hiremath 479fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 480fd60ac58SVaibhav Hiremath if (ret) { 481fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 4826743a6fdSVaibhav Hiremath goto err_populate; 483fd60ac58SVaibhav Hiremath } 484fd60ac58SVaibhav Hiremath 485db5a3bcaSVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); 486fd60ac58SVaibhav Hiremath 4877fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 48872a8c24bSViresh Kumar return 0; 4896743a6fdSVaibhav Hiremath 4906743a6fdSVaibhav Hiremath err_populate: 4916743a6fdSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 4926743a6fdSVaibhav Hiremath err_coldboot: 4936743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 4946743a6fdSVaibhav Hiremath return ret; 4957fa60654SVaibhav Hiremath } 4967fa60654SVaibhav Hiremath 497bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 498bc142bbbSVaibhav Hiremath { 499bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 500bc142bbbSVaibhav Hiremath 501bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 502bc142bbbSVaibhav Hiremath 503bc142bbbSVaibhav Hiremath return 0; 504bc142bbbSVaibhav Hiremath } 505bc142bbbSVaibhav Hiremath 5067fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 5077fa60654SVaibhav Hiremath { 5087fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 5097fa60654SVaibhav Hiremath 5102923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 51149e6e04bSVaibhav Hiremath cancel_delayed_work_sync(&arche_pdata->delayed_work); 512bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 5135993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5147fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 5157fa60654SVaibhav Hiremath 516ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 517ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 518ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 5197fa60654SVaibhav Hiremath return 0; 5207fa60654SVaibhav Hiremath } 5217fa60654SVaibhav Hiremath 5227fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 5237fa60654SVaibhav Hiremath { 5247fa60654SVaibhav Hiremath /* 5257fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 5267fa60654SVaibhav Hiremath * completely 5277fa60654SVaibhav Hiremath * 5287fa60654SVaibhav Hiremath * TODO: sequence ?? 5297fa60654SVaibhav Hiremath * 5307fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 5317fa60654SVaibhav Hiremath * Precondition: Definition ??? 5327fa60654SVaibhav Hiremath */ 5337fa60654SVaibhav Hiremath return 0; 5347fa60654SVaibhav Hiremath } 5357fa60654SVaibhav Hiremath 5367fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 5377fa60654SVaibhav Hiremath { 5387fa60654SVaibhav Hiremath /* 5397fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 5407fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 5417fa60654SVaibhav Hiremath * OFF state or standby state. 5427fa60654SVaibhav Hiremath * 5437fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 5447fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 5457fa60654SVaibhav Hiremath * 5467fa60654SVaibhav Hiremath */ 5477fa60654SVaibhav Hiremath return 0; 5487fa60654SVaibhav Hiremath } 5497fa60654SVaibhav Hiremath 5507fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 5517fa60654SVaibhav Hiremath arche_platform_suspend, 5527fa60654SVaibhav Hiremath arche_platform_resume); 5537fa60654SVaibhav Hiremath 5547fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 5557fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5567fa60654SVaibhav Hiremath { }, 5577fa60654SVaibhav Hiremath }; 5581e5dd1f8SGreg Kroah-Hartman 5591e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 5601e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5611e5dd1f8SGreg Kroah-Hartman { }, 5621e5dd1f8SGreg Kroah-Hartman }; 5631e5dd1f8SGreg Kroah-Hartman 5641e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 5651e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5661e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5671e5dd1f8SGreg Kroah-Hartman { }, 5681e5dd1f8SGreg Kroah-Hartman }; 5691e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 5707fa60654SVaibhav Hiremath 5717fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 5727fa60654SVaibhav Hiremath .probe = arche_platform_probe, 5737fa60654SVaibhav Hiremath .remove = arche_platform_remove, 5747fa60654SVaibhav Hiremath .driver = { 5757fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 5767fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 5771e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 5787fa60654SVaibhav Hiremath } 5797fa60654SVaibhav Hiremath }; 5807fa60654SVaibhav Hiremath 5811e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 5821e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 5831e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 5841e5dd1f8SGreg Kroah-Hartman .driver = { 5851e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 5861e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 5871e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 5881e5dd1f8SGreg Kroah-Hartman } 5891e5dd1f8SGreg Kroah-Hartman }; 5901e5dd1f8SGreg Kroah-Hartman 5911e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 5921e5dd1f8SGreg Kroah-Hartman { 5931e5dd1f8SGreg Kroah-Hartman int retval; 5941e5dd1f8SGreg Kroah-Hartman 5951e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 5961e5dd1f8SGreg Kroah-Hartman if (retval) 5971e5dd1f8SGreg Kroah-Hartman return retval; 5981e5dd1f8SGreg Kroah-Hartman 5991e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 6001e5dd1f8SGreg Kroah-Hartman if (retval) 6011e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6021e5dd1f8SGreg Kroah-Hartman 6031e5dd1f8SGreg Kroah-Hartman return retval; 6041e5dd1f8SGreg Kroah-Hartman } 6051e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 6061e5dd1f8SGreg Kroah-Hartman 6071e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 6081e5dd1f8SGreg Kroah-Hartman { 6091e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 6101e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6111e5dd1f8SGreg Kroah-Hartman } 6121e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 6137fa60654SVaibhav Hiremath 6147fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 6157fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 6167fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 617