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 11816fe18caSVaibhav Hiremath static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata) 11916fe18caSVaibhav Hiremath { 12016fe18caSVaibhav Hiremath /* Assert wake/detect = Detect event from AP */ 12116fe18caSVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 1); 12216fe18caSVaibhav Hiremath 12316fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 12416fe18caSVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 12516fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 12616fe18caSVaibhav Hiremath } 12716fe18caSVaibhav Hiremath 128f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 129f760bbfbSVaibhav Hiremath { 130f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 131f760bbfbSVaibhav Hiremath unsigned long flags; 132f760bbfbSVaibhav Hiremath 133f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 134f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 135f760bbfbSVaibhav Hiremath /* Something is wrong */ 136f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 137f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 138f760bbfbSVaibhav Hiremath } 139f760bbfbSVaibhav Hiremath 140f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START; 141f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 142f760bbfbSVaibhav Hiremath 143f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 144f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 145f760bbfbSVaibhav Hiremath 146f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 147f760bbfbSVaibhav Hiremath /* USB HUB configuration */ 148f760bbfbSVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 149f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 150f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 151f760bbfbSVaibhav Hiremath 152f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 153f760bbfbSVaibhav Hiremath } 154f760bbfbSVaibhav Hiremath 155f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 156f760bbfbSVaibhav Hiremath { 157f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 158f760bbfbSVaibhav Hiremath unsigned long flags; 159f760bbfbSVaibhav Hiremath 160f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 161f760bbfbSVaibhav Hiremath 162f760bbfbSVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio)) { 163f760bbfbSVaibhav Hiremath /* wake/detect rising */ 164f760bbfbSVaibhav Hiremath 165f760bbfbSVaibhav Hiremath /* 166f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 167f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 168f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 169f760bbfbSVaibhav Hiremath */ 170f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 171f760bbfbSVaibhav Hiremath if (time_before(jiffies, 172f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 173f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 174f760bbfbSVaibhav Hiremath /* No harm with cancellation, even if not pending */ 175f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 176f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 177f760bbfbSVaibhav Hiremath } else { 178f760bbfbSVaibhav Hiremath /* Check we are not in middle of irq thread already */ 179f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 180f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 181f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = 182f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_TRIG; 183f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 184f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 185f760bbfbSVaibhav Hiremath } 186f760bbfbSVaibhav Hiremath } 187f760bbfbSVaibhav Hiremath } 188f760bbfbSVaibhav Hiremath } else { 189f760bbfbSVaibhav Hiremath /* wake/detect falling */ 190f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 191f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 192f760bbfbSVaibhav Hiremath /* No harm with cancellation even if it is not pending*/ 193f760bbfbSVaibhav Hiremath cancel_delayed_work(&arche_pdata->delayed_work); 194f760bbfbSVaibhav Hiremath /* 195f760bbfbSVaibhav Hiremath * In the begining, when wake/detect goes low (first time), we assume 196f760bbfbSVaibhav Hiremath * it is meant for coldboot and set the flag. If wake/detect line stays low 197f760bbfbSVaibhav Hiremath * beyond 30msec, then it is coldboot else fallback to standby boot. 198f760bbfbSVaibhav Hiremath */ 199f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT; 200f760bbfbSVaibhav Hiremath } 201f760bbfbSVaibhav Hiremath } 202f760bbfbSVaibhav Hiremath 203f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 204f760bbfbSVaibhav Hiremath 205f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 206f760bbfbSVaibhav Hiremath } 207f760bbfbSVaibhav Hiremath 208758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 209758ca99dSVaibhav Hiremath { 210758ca99dSVaibhav Hiremath int ret; 211758ca99dSVaibhav Hiremath 212599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 213599159b6SVaibhav Hiremath return 0; 214599159b6SVaibhav Hiremath 215758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 216758ca99dSVaibhav Hiremath 217758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 218758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 219758ca99dSVaibhav Hiremath 220758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 221758ca99dSVaibhav Hiremath usleep_range(100, 200); 222758ca99dSVaibhav Hiremath 223758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 224758ca99dSVaibhav Hiremath if (ret) { 225758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 226758ca99dSVaibhav Hiremath ret); 227758ca99dSVaibhav Hiremath return ret; 228758ca99dSVaibhav Hiremath } 229758ca99dSVaibhav Hiremath 230758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 231758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 232758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 233758ca99dSVaibhav Hiremath 234e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; 235e74d04a5SVaibhav Hiremath 236758ca99dSVaibhav Hiremath return 0; 237758ca99dSVaibhav Hiremath } 238758ca99dSVaibhav Hiremath 2397691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 2407691fed2SVaibhav Hiremath { 241599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 242599159b6SVaibhav Hiremath return; 243599159b6SVaibhav Hiremath 2447691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 2457691fed2SVaibhav Hiremath 2467691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2477691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 2487691fed2SVaibhav Hiremath 2497691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 2507691fed2SVaibhav Hiremath 2517691fed2SVaibhav Hiremath usleep_range(100, 200); 2527691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2537691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 2547691fed2SVaibhav Hiremath 2557691fed2SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; 2567691fed2SVaibhav Hiremath 2577691fed2SVaibhav Hiremath } 2587691fed2SVaibhav Hiremath 2595993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 2607fa60654SVaibhav Hiremath { 261f760bbfbSVaibhav Hiremath unsigned long flags; 262f760bbfbSVaibhav Hiremath 263599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 264599159b6SVaibhav Hiremath return; 265599159b6SVaibhav Hiremath 26625847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 26725847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 268b4c95fcaSVaibhav Hiremath /* Send disconnect/detach event to SVC */ 2690786212dSVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 270b4c95fcaSVaibhav Hiremath usleep_range(100, 200); 271f760bbfbSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->lock, flags); 272685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 273f760bbfbSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->lock, flags); 274b4c95fcaSVaibhav Hiremath 275d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 27625847ee7SVaibhav Hiremath } 27725847ee7SVaibhav Hiremath 2787fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 2797fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 2807fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 281e74d04a5SVaibhav Hiremath 282e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 2837fa60654SVaibhav Hiremath } 2847fa60654SVaibhav Hiremath 2852923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 2862923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 2872923c58eSVaibhav Hiremath { 2882923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 2892923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 2902923c58eSVaibhav Hiremath int ret = 0; 2912923c58eSVaibhav Hiremath 2922923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 2932923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 2942923c58eSVaibhav Hiremath return count; 2952923c58eSVaibhav Hiremath 296fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 297fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 298fd60ac58SVaibhav Hiremath 2992923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 300ad4d3f95SVaibhav Hiremath 301ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 302ad4d3f95SVaibhav Hiremath if (ret) 303ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 304ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 3052923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 3062923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 3072923c58eSVaibhav Hiremath return count; 3082923c58eSVaibhav Hiremath 3092923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 31016fe18caSVaibhav Hiremath 31116fe18caSVaibhav Hiremath assert_wakedetect(arche_pdata); 3122923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 3132923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 3142923c58eSVaibhav Hiremath return count; 3152923c58eSVaibhav Hiremath 3162923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 3177691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 3187691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 3197691fed2SVaibhav Hiremath return count; 3207691fed2SVaibhav Hiremath 3217691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 3227691fed2SVaibhav Hiremath * and then enter FW flashing state */ 323fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 324fd60ac58SVaibhav Hiremath 3257691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 326fd60ac58SVaibhav Hiremath 327ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 328ad4d3f95SVaibhav Hiremath if (ret) 329ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 330ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 331ad4d3f95SVaibhav Hiremath 3327691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 333fd60ac58SVaibhav Hiremath 334fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 3352923c58eSVaibhav Hiremath } else { 3362923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 3372923c58eSVaibhav Hiremath ret = -EINVAL; 3382923c58eSVaibhav Hiremath } 3392923c58eSVaibhav Hiremath 3402923c58eSVaibhav Hiremath return ret ? ret : count; 3412923c58eSVaibhav Hiremath } 3422923c58eSVaibhav Hiremath 3432923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 3442923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 3452923c58eSVaibhav Hiremath { 3462923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 3472923c58eSVaibhav Hiremath 3482923c58eSVaibhav Hiremath switch (arche_pdata->state) { 3492923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 3502923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 3512923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 3522923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 3532923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 3542923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 3557691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 3567691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 3572923c58eSVaibhav Hiremath default: 3582923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 3592923c58eSVaibhav Hiremath } 3602923c58eSVaibhav Hiremath } 3612923c58eSVaibhav Hiremath 3622923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 3632923c58eSVaibhav Hiremath 3647fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 3657fa60654SVaibhav Hiremath { 3667fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 3677fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 3687fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 3697fa60654SVaibhav Hiremath int ret; 3707fa60654SVaibhav Hiremath 3717fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 3727fa60654SVaibhav Hiremath if (!arche_pdata) 3737fa60654SVaibhav Hiremath return -ENOMEM; 3747fa60654SVaibhav Hiremath 3757fa60654SVaibhav Hiremath /* setup svc reset gpio */ 3767fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 3777fa60654SVaibhav Hiremath "svc,reset-active-high"); 3787fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 3797fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 3807fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 381f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 3827fa60654SVaibhav Hiremath } 3837fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 3847fa60654SVaibhav Hiremath if (ret) { 3857fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 3867fa60654SVaibhav Hiremath return ret; 3877fa60654SVaibhav Hiremath } 3887fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 3897fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3907fa60654SVaibhav Hiremath if (ret) { 3917fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3927fa60654SVaibhav Hiremath return ret; 3937fa60654SVaibhav Hiremath } 394e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 3957fa60654SVaibhav Hiremath 3967fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 3977fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 3987fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 3997fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 400f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 4017fa60654SVaibhav Hiremath } 4027fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 4037fa60654SVaibhav Hiremath if (ret) { 4047fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 4057fa60654SVaibhav Hiremath return ret; 4067fa60654SVaibhav Hiremath } 4077fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 4087fa60654SVaibhav Hiremath if (ret) { 4097fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 4107fa60654SVaibhav Hiremath return ret; 4117fa60654SVaibhav Hiremath } 4127fa60654SVaibhav Hiremath 4137fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 4147fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 4157fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 4167fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 4177fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 418f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 4197fa60654SVaibhav Hiremath } 4207fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 4217fa60654SVaibhav Hiremath if (ret) { 4227fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 4237fa60654SVaibhav Hiremath return ret; 4247fa60654SVaibhav Hiremath } 4257fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 4267fa60654SVaibhav Hiremath if (ret) { 4277fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 4287fa60654SVaibhav Hiremath return ret; 4297fa60654SVaibhav Hiremath } 4307fa60654SVaibhav Hiremath 4317fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 4327fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 4337fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 4347fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 4357fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 4367fa60654SVaibhav Hiremath return ret; 4377fa60654SVaibhav Hiremath } 4387fa60654SVaibhav Hiremath 4397fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 4407fa60654SVaibhav Hiremath 4417fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 4427fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 4437fa60654SVaibhav Hiremath 444a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 445a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 446a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 447a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 448758ca99dSVaibhav Hiremath return ret; 44972a8c24bSViresh Kumar } 4507fa60654SVaibhav Hiremath 451a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 452a463fc15SVaibhav Hiremath if (ret) { 453a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 454a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 455758ca99dSVaibhav Hiremath return ret; 456a463fc15SVaibhav Hiremath } 457057aad29SMichael Scott /* deassert wake detect */ 458057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 459685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 460a463fc15SVaibhav Hiremath 461a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 462a463fc15SVaibhav Hiremath 463f760bbfbSVaibhav Hiremath spin_lock_init(&arche_pdata->lock); 464f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 465f760bbfbSVaibhav Hiremath gpio_to_irq(arche_pdata->wake_detect_gpio); 466f760bbfbSVaibhav Hiremath 467f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 468f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 469f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 470f760bbfbSVaibhav Hiremath IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, 471f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 472f760bbfbSVaibhav Hiremath if (ret) { 473f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 474f760bbfbSVaibhav Hiremath return ret; 475f760bbfbSVaibhav Hiremath } 476f760bbfbSVaibhav Hiremath /* Enable it only after sending wake/detect event */ 477f760bbfbSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 478f760bbfbSVaibhav Hiremath 4792923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 4802923c58eSVaibhav Hiremath if (ret) { 4812923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 4822923c58eSVaibhav Hiremath return ret; 4832923c58eSVaibhav Hiremath } 4842923c58eSVaibhav Hiremath 485758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 486758ca99dSVaibhav Hiremath if (ret) { 487758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 4886743a6fdSVaibhav Hiremath goto err_coldboot; 489758ca99dSVaibhav Hiremath } 490758ca99dSVaibhav Hiremath 491fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 492fd60ac58SVaibhav Hiremath if (ret) { 493fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 4946743a6fdSVaibhav Hiremath goto err_populate; 495fd60ac58SVaibhav Hiremath } 496fd60ac58SVaibhav Hiremath 49716fe18caSVaibhav Hiremath assert_wakedetect(arche_pdata); 498db5a3bcaSVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); 499fd60ac58SVaibhav Hiremath 5007fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 50172a8c24bSViresh Kumar return 0; 5026743a6fdSVaibhav Hiremath 5036743a6fdSVaibhav Hiremath err_populate: 5046743a6fdSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5056743a6fdSVaibhav Hiremath err_coldboot: 5066743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 5076743a6fdSVaibhav Hiremath return ret; 5087fa60654SVaibhav Hiremath } 5097fa60654SVaibhav Hiremath 510bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 511bc142bbbSVaibhav Hiremath { 512bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 513bc142bbbSVaibhav Hiremath 514bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 515bc142bbbSVaibhav Hiremath 516bc142bbbSVaibhav Hiremath return 0; 517bc142bbbSVaibhav Hiremath } 518bc142bbbSVaibhav Hiremath 5197fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 5207fa60654SVaibhav Hiremath { 5217fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 5227fa60654SVaibhav Hiremath 5232923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 52449e6e04bSVaibhav Hiremath cancel_delayed_work_sync(&arche_pdata->delayed_work); 525bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 5265993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5277fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 5287fa60654SVaibhav Hiremath 529ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 530ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 531ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 5327fa60654SVaibhav Hiremath return 0; 5337fa60654SVaibhav Hiremath } 5347fa60654SVaibhav Hiremath 5357fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 5367fa60654SVaibhav Hiremath { 5377fa60654SVaibhav Hiremath /* 5387fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 5397fa60654SVaibhav Hiremath * completely 5407fa60654SVaibhav Hiremath * 5417fa60654SVaibhav Hiremath * TODO: sequence ?? 5427fa60654SVaibhav Hiremath * 5437fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 5447fa60654SVaibhav Hiremath * Precondition: Definition ??? 5457fa60654SVaibhav Hiremath */ 5467fa60654SVaibhav Hiremath return 0; 5477fa60654SVaibhav Hiremath } 5487fa60654SVaibhav Hiremath 5497fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 5507fa60654SVaibhav Hiremath { 5517fa60654SVaibhav Hiremath /* 5527fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 5537fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 5547fa60654SVaibhav Hiremath * OFF state or standby state. 5557fa60654SVaibhav Hiremath * 5567fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 5577fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 5587fa60654SVaibhav Hiremath * 5597fa60654SVaibhav Hiremath */ 5607fa60654SVaibhav Hiremath return 0; 5617fa60654SVaibhav Hiremath } 5627fa60654SVaibhav Hiremath 5637fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 5647fa60654SVaibhav Hiremath arche_platform_suspend, 5657fa60654SVaibhav Hiremath arche_platform_resume); 5667fa60654SVaibhav Hiremath 5677fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 5687fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5697fa60654SVaibhav Hiremath { }, 5707fa60654SVaibhav Hiremath }; 5711e5dd1f8SGreg Kroah-Hartman 5721e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 5731e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5741e5dd1f8SGreg Kroah-Hartman { }, 5751e5dd1f8SGreg Kroah-Hartman }; 5761e5dd1f8SGreg Kroah-Hartman 5771e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 5781e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 5791e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 5801e5dd1f8SGreg Kroah-Hartman { }, 5811e5dd1f8SGreg Kroah-Hartman }; 5821e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 5837fa60654SVaibhav Hiremath 5847fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 5857fa60654SVaibhav Hiremath .probe = arche_platform_probe, 5867fa60654SVaibhav Hiremath .remove = arche_platform_remove, 5877fa60654SVaibhav Hiremath .driver = { 5887fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 5897fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 5901e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 5917fa60654SVaibhav Hiremath } 5927fa60654SVaibhav Hiremath }; 5937fa60654SVaibhav Hiremath 5941e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 5951e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 5961e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 5971e5dd1f8SGreg Kroah-Hartman .driver = { 5981e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 5991e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 6001e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 6011e5dd1f8SGreg Kroah-Hartman } 6021e5dd1f8SGreg Kroah-Hartman }; 6031e5dd1f8SGreg Kroah-Hartman 6041e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 6051e5dd1f8SGreg Kroah-Hartman { 6061e5dd1f8SGreg Kroah-Hartman int retval; 6071e5dd1f8SGreg Kroah-Hartman 6081e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 6091e5dd1f8SGreg Kroah-Hartman if (retval) 6101e5dd1f8SGreg Kroah-Hartman return retval; 6111e5dd1f8SGreg Kroah-Hartman 6121e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 6131e5dd1f8SGreg Kroah-Hartman if (retval) 6141e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6151e5dd1f8SGreg Kroah-Hartman 6161e5dd1f8SGreg Kroah-Hartman return retval; 6171e5dd1f8SGreg Kroah-Hartman } 6181e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 6191e5dd1f8SGreg Kroah-Hartman 6201e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 6211e5dd1f8SGreg Kroah-Hartman { 6221e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 6231e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6241e5dd1f8SGreg Kroah-Hartman } 6251e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 6267fa60654SVaibhav Hiremath 6277fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 6287fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 6297fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 630