1eb50fd3aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 27fa60654SVaibhav Hiremath /* 37fa60654SVaibhav Hiremath * Arche Platform driver to enable Unipro link. 47fa60654SVaibhav Hiremath * 57fa60654SVaibhav Hiremath * Copyright 2014-2015 Google Inc. 67fa60654SVaibhav Hiremath * Copyright 2014-2015 Linaro Ltd. 77fa60654SVaibhav Hiremath */ 87fa60654SVaibhav Hiremath 97fa60654SVaibhav Hiremath #include <linux/clk.h> 10a463fc15SVaibhav Hiremath #include <linux/delay.h> 114207b587SNishad Kamdar #include <linux/gpio/consumer.h> 123b858df0SViresh Kumar #include <linux/init.h> 133b858df0SViresh Kumar #include <linux/module.h> 143b858df0SViresh Kumar #include <linux/of_platform.h> 157fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h> 163b858df0SViresh Kumar #include <linux/platform_device.h> 173b858df0SViresh Kumar #include <linux/pm.h> 18f760bbfbSVaibhav Hiremath #include <linux/interrupt.h> 19f760bbfbSVaibhav Hiremath #include <linux/irq.h> 209160b7c7SDavid Lin #include <linux/suspend.h> 21f760bbfbSVaibhav Hiremath #include <linux/time.h> 221e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 23970dc85bSBryan O'Donoghue #include "greybus.h" 247fa60654SVaibhav Hiremath 252eccd4aaSJohan Hovold #if IS_ENABLED(CONFIG_USB_HSIC_USB3613) 26ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h> 272eccd4aaSJohan Hovold #else 282eccd4aaSJohan Hovold static inline int usb3613_hub_mode_ctrl(bool unused) 292eccd4aaSJohan Hovold { 302eccd4aaSJohan Hovold return 0; 312eccd4aaSJohan Hovold } 322eccd4aaSJohan Hovold #endif 33ad4d3f95SVaibhav Hiremath 34f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS 30 35f760bbfbSVaibhav Hiremath 36685353c1SVaibhav Hiremath enum svc_wakedetect_state { 37685353c1SVaibhav Hiremath WD_STATE_IDLE, /* Default state = pulled high/low */ 38685353c1SVaibhav Hiremath WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 39685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 40685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 41685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_START, /* Cold boot process started */ 42685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_START, /* Not used */ 43685353c1SVaibhav Hiremath }; 44685353c1SVaibhav Hiremath 457fa60654SVaibhav Hiremath struct arche_platform_drvdata { 467fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 474207b587SNishad Kamdar struct gpio_desc *svc_reset; 487fa60654SVaibhav Hiremath bool is_reset_act_hi; 494207b587SNishad Kamdar struct gpio_desc *svc_sysboot; 504207b587SNishad Kamdar struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 517fa60654SVaibhav Hiremath 52e74d04a5SVaibhav Hiremath enum arche_platform_state state; 53e74d04a5SVaibhav Hiremath 544207b587SNishad Kamdar struct gpio_desc *svc_refclk_req; 557fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 567fa60654SVaibhav Hiremath 577fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 587fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 597fa60654SVaibhav Hiremath 607fa60654SVaibhav Hiremath int num_apbs; 61a463fc15SVaibhav Hiremath 62685353c1SVaibhav Hiremath enum svc_wakedetect_state wake_detect_state; 63f760bbfbSVaibhav Hiremath int wake_detect_irq; 646c1ca56dSVaibhav Hiremath spinlock_t wake_lock; /* Protect wake_detect_state */ 65886aba55SVaibhav Hiremath struct mutex platform_state_mutex; /* Protect state */ 66f760bbfbSVaibhav Hiremath unsigned long wake_detect_start; 679160b7c7SDavid Lin struct notifier_block pm_notifier; 68685353c1SVaibhav Hiremath 69a463fc15SVaibhav Hiremath struct device *dev; 707fa60654SVaibhav Hiremath }; 717fa60654SVaibhav Hiremath 72970dc85bSBryan O'Donoghue /* Requires calling context to hold arche_pdata->platform_state_mutex */ 73c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, 74c4058b79SBryan O'Donoghue enum arche_platform_state state) 75c4058b79SBryan O'Donoghue { 76c4058b79SBryan O'Donoghue arche_pdata->state = state; 77c4058b79SBryan O'Donoghue } 78c4058b79SBryan O'Donoghue 79c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */ 80c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state( 81c4058b79SBryan O'Donoghue struct arche_platform_drvdata *arche_pdata, 82c4058b79SBryan O'Donoghue enum svc_wakedetect_state state) 83c4058b79SBryan O'Donoghue { 84c4058b79SBryan O'Donoghue arche_pdata->wake_detect_state = state; 85c4058b79SBryan O'Donoghue } 86c4058b79SBryan O'Donoghue 874207b587SNishad Kamdar static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff) 887fa60654SVaibhav Hiremath { 894207b587SNishad Kamdar gpiod_set_raw_value(gpio, onoff); 907fa60654SVaibhav Hiremath } 917fa60654SVaibhav Hiremath 92f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data) 93f760bbfbSVaibhav Hiremath { 94f760bbfbSVaibhav Hiremath int ret; 95f760bbfbSVaibhav Hiremath 96f760bbfbSVaibhav Hiremath ret = apb_ctrl_coldboot(dev); 97f760bbfbSVaibhav Hiremath if (ret) 98f760bbfbSVaibhav Hiremath dev_warn(dev, "failed to coldboot\n"); 99f760bbfbSVaibhav Hiremath 100f760bbfbSVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 101f760bbfbSVaibhav Hiremath return 0; 102f760bbfbSVaibhav Hiremath } 103f760bbfbSVaibhav Hiremath 104fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 105fd60ac58SVaibhav Hiremath { 106fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 107fd60ac58SVaibhav Hiremath 108e915ce48SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 109e915ce48SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 110e915ce48SVaibhav Hiremath dev_warn(dev, "failed to control hub device\n"); 111e915ce48SVaibhav Hiremath 112fd60ac58SVaibhav Hiremath return 0; 113fd60ac58SVaibhav Hiremath } 114fd60ac58SVaibhav Hiremath 1157ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) 11616fe18caSVaibhav Hiremath { 11716fe18caSVaibhav Hiremath /* Enable interrupt here, to read event back from SVC */ 11816fe18caSVaibhav Hiremath enable_irq(arche_pdata->wake_detect_irq); 11916fe18caSVaibhav Hiremath } 12016fe18caSVaibhav Hiremath 121f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 122f760bbfbSVaibhav Hiremath { 123f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 124f760bbfbSVaibhav Hiremath unsigned long flags; 125f760bbfbSVaibhav Hiremath 1266c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 127f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 128f760bbfbSVaibhav Hiremath /* Something is wrong */ 1296c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 130f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 131f760bbfbSVaibhav Hiremath } 132f760bbfbSVaibhav Hiremath 133c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 134c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_START); 1356c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 136f760bbfbSVaibhav Hiremath 137ff788de0SVaibhav Hiremath /* It should complete power cycle, so first make sure it is poweroff */ 138ff788de0SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 139e915ce48SVaibhav Hiremath 140f760bbfbSVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 141f760bbfbSVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 142f760bbfbSVaibhav Hiremath 143a7ddda1fSVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 144a7ddda1fSVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 145a7ddda1fSVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 146a7ddda1fSVaibhav Hiremath 1476c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 148c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 1496c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 150f760bbfbSVaibhav Hiremath 151f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 152f760bbfbSVaibhav Hiremath } 153f760bbfbSVaibhav Hiremath 154f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 155f760bbfbSVaibhav Hiremath { 156f760bbfbSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = devid; 157f760bbfbSVaibhav Hiremath unsigned long flags; 158f760bbfbSVaibhav Hiremath 1596c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 160f760bbfbSVaibhav Hiremath 1614207b587SNishad Kamdar if (gpiod_get_value(arche_pdata->wake_detect)) { 162f760bbfbSVaibhav Hiremath /* wake/detect rising */ 163f760bbfbSVaibhav Hiremath 164f760bbfbSVaibhav Hiremath /* 165f760bbfbSVaibhav Hiremath * If wake/detect line goes high after low, within less than 166f760bbfbSVaibhav Hiremath * 30msec, then standby boot sequence is initiated, which is not 167f760bbfbSVaibhav Hiremath * supported/implemented as of now. So ignore it. 168f760bbfbSVaibhav Hiremath */ 169f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 170f760bbfbSVaibhav Hiremath if (time_before(jiffies, 171f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start + 172f760bbfbSVaibhav Hiremath msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 173c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 174c4058b79SBryan O'Donoghue WD_STATE_IDLE); 175f760bbfbSVaibhav Hiremath } else { 176bf766625SMitchell Tasman /* 177bf766625SMitchell Tasman * Check we are not in middle of irq thread 178bf766625SMitchell Tasman * already 179bf766625SMitchell Tasman */ 180f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state != 181f760bbfbSVaibhav Hiremath WD_STATE_COLDBOOT_START) { 182c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 183c4058b79SBryan O'Donoghue WD_STATE_COLDBOOT_TRIG); 1846c1ca56dSVaibhav Hiremath spin_unlock_irqrestore( 1856c1ca56dSVaibhav Hiremath &arche_pdata->wake_lock, 1866c1ca56dSVaibhav Hiremath flags); 187f760bbfbSVaibhav Hiremath return IRQ_WAKE_THREAD; 188f760bbfbSVaibhav Hiremath } 189f760bbfbSVaibhav Hiremath } 190f760bbfbSVaibhav Hiremath } 191f760bbfbSVaibhav Hiremath } else { 192f760bbfbSVaibhav Hiremath /* wake/detect falling */ 193f760bbfbSVaibhav Hiremath if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 194f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_start = jiffies; 195f760bbfbSVaibhav Hiremath /* 1964a27e3e0SEames Trinh * In the beginning, when wake/detect goes low 197461ab807SGioh Kim * (first time), we assume it is meant for coldboot 198461ab807SGioh Kim * and set the flag. If wake/detect line stays low 199461ab807SGioh Kim * beyond 30msec, then it is coldboot else fallback 200461ab807SGioh Kim * to standby boot. 201f760bbfbSVaibhav Hiremath */ 202c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 203c4058b79SBryan O'Donoghue WD_STATE_BOOT_INIT); 204f760bbfbSVaibhav Hiremath } 205f760bbfbSVaibhav Hiremath } 206f760bbfbSVaibhav Hiremath 2076c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 208f760bbfbSVaibhav Hiremath 209f760bbfbSVaibhav Hiremath return IRQ_HANDLED; 210f760bbfbSVaibhav Hiremath } 211f760bbfbSVaibhav Hiremath 212886aba55SVaibhav Hiremath /* 213886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 214886aba55SVaibhav Hiremath */ 215461ab807SGioh Kim static int 216461ab807SGioh Kim arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 217758ca99dSVaibhav Hiremath { 218758ca99dSVaibhav Hiremath int ret; 219758ca99dSVaibhav Hiremath 220599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 221599159b6SVaibhav Hiremath return 0; 222599159b6SVaibhav Hiremath 223758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 224758ca99dSVaibhav Hiremath 2254207b587SNishad Kamdar svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 226758ca99dSVaibhav Hiremath 2274207b587SNishad Kamdar gpiod_set_value(arche_pdata->svc_sysboot, 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 */ 2384207b587SNishad Kamdar svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi); 239758ca99dSVaibhav Hiremath 240c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); 241e74d04a5SVaibhav Hiremath 242758ca99dSVaibhav Hiremath return 0; 243758ca99dSVaibhav Hiremath } 244758ca99dSVaibhav Hiremath 245886aba55SVaibhav Hiremath /* 246886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 247886aba55SVaibhav Hiremath */ 248461ab807SGioh Kim static int 249461ab807SGioh Kim arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 2507691fed2SVaibhav Hiremath { 2517a867d14SVaibhav Hiremath int ret; 2527a867d14SVaibhav Hiremath 253599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 254c61a8b49SVaibhav Hiremath return 0; 255599159b6SVaibhav Hiremath 2567691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 2577691fed2SVaibhav Hiremath 2584207b587SNishad Kamdar svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 2597691fed2SVaibhav Hiremath 2604207b587SNishad Kamdar gpiod_set_value(arche_pdata->svc_sysboot, 1); 2617691fed2SVaibhav Hiremath 2627691fed2SVaibhav Hiremath usleep_range(100, 200); 2637a867d14SVaibhav Hiremath 2647a867d14SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 2657a867d14SVaibhav Hiremath if (ret) { 2667a867d14SVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 2677a867d14SVaibhav Hiremath ret); 2687a867d14SVaibhav Hiremath return ret; 2697a867d14SVaibhav Hiremath } 2707a867d14SVaibhav Hiremath 2714207b587SNishad Kamdar svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi); 2727691fed2SVaibhav Hiremath 273c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); 2747691fed2SVaibhav Hiremath 275c61a8b49SVaibhav Hiremath return 0; 2767691fed2SVaibhav Hiremath } 2777691fed2SVaibhav Hiremath 278886aba55SVaibhav Hiremath /* 279886aba55SVaibhav Hiremath * Requires arche_pdata->platform_state_mutex to be held 280886aba55SVaibhav Hiremath */ 281461ab807SGioh Kim static void 282461ab807SGioh Kim arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 2837fa60654SVaibhav Hiremath { 284f760bbfbSVaibhav Hiremath unsigned long flags; 285f760bbfbSVaibhav Hiremath 286599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 287599159b6SVaibhav Hiremath return; 288599159b6SVaibhav Hiremath 28925847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 29025847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 291d2320b2dSVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 29216fd976cSVaibhav Hiremath 2936c1ca56dSVaibhav Hiremath spin_lock_irqsave(&arche_pdata->wake_lock, flags); 294c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, 295c4058b79SBryan O'Donoghue WD_STATE_IDLE); 2966c1ca56dSVaibhav Hiremath spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 2977a867d14SVaibhav Hiremath } 298b4c95fcaSVaibhav Hiremath 299d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 30025847ee7SVaibhav Hiremath 3017fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 3024207b587SNishad Kamdar svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 303e74d04a5SVaibhav Hiremath 304c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 3057fa60654SVaibhav Hiremath } 3067fa60654SVaibhav Hiremath 3072923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 30807df5b7cSKamal Heib struct device_attribute *attr, 30907df5b7cSKamal Heib const char *buf, size_t count) 3102923c58eSVaibhav Hiremath { 3111dab154eSWolfram Sang struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 3122923c58eSVaibhav Hiremath int ret = 0; 3132923c58eSVaibhav Hiremath 314886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 315886aba55SVaibhav Hiremath 3162923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 3172923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 318886aba55SVaibhav Hiremath goto exit; 3192923c58eSVaibhav Hiremath 320fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 321fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 322fd60ac58SVaibhav Hiremath 3232923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 324ad4d3f95SVaibhav Hiremath 3252923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 3262923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 327886aba55SVaibhav Hiremath goto exit; 3282923c58eSVaibhav Hiremath 3290b1283e3SVaibhav Hiremath /* First we want to make sure we power off everything 330cf3ba55dSElise Lennion * and then activate back again 331cf3ba55dSElise Lennion */ 3320b1283e3SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 3330b1283e3SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 3340b1283e3SVaibhav Hiremath 3357ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 3362923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 3370b1283e3SVaibhav Hiremath if (ret) 3380b1283e3SVaibhav Hiremath goto exit; 33916fe18caSVaibhav Hiremath 3402923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 3412923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 342886aba55SVaibhav Hiremath goto exit; 3432923c58eSVaibhav Hiremath 3442923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 3457691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 3467691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 347886aba55SVaibhav Hiremath goto exit; 3487691fed2SVaibhav Hiremath 349921dbe52SVaibhav Hiremath /* 350921dbe52SVaibhav Hiremath * Here we only control SVC. 351921dbe52SVaibhav Hiremath * 352921dbe52SVaibhav Hiremath * In case of FW_FLASHING mode we do not want to control 353921dbe52SVaibhav Hiremath * APBs, as in case of V2, SPI bus is shared between both 354921dbe52SVaibhav Hiremath * the APBs. So let user chose which APB he wants to flash. 355921dbe52SVaibhav Hiremath */ 3567691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 357fd60ac58SVaibhav Hiremath 358c61a8b49SVaibhav Hiremath ret = arche_platform_fw_flashing_seq(arche_pdata); 359c61a8b49SVaibhav Hiremath if (ret) 360c61a8b49SVaibhav Hiremath goto exit; 3612923c58eSVaibhav Hiremath } else { 3622923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 3632923c58eSVaibhav Hiremath ret = -EINVAL; 3642923c58eSVaibhav Hiremath } 3652923c58eSVaibhav Hiremath 366886aba55SVaibhav Hiremath exit: 367886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 3682923c58eSVaibhav Hiremath return ret ? ret : count; 3692923c58eSVaibhav Hiremath } 3702923c58eSVaibhav Hiremath 3712923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 3722923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 3732923c58eSVaibhav Hiremath { 3742923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 3752923c58eSVaibhav Hiremath 3762923c58eSVaibhav Hiremath switch (arche_pdata->state) { 3772923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 3782923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 3792923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 3802923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 3812923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 3822923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 3837691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 3847691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 3852923c58eSVaibhav Hiremath default: 3862923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 3872923c58eSVaibhav Hiremath } 3882923c58eSVaibhav Hiremath } 3892923c58eSVaibhav Hiremath 3902923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 3912923c58eSVaibhav Hiremath 3929160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier, 3939160b7c7SDavid Lin unsigned long pm_event, void *unused) 3949160b7c7SDavid Lin { 3959160b7c7SDavid Lin struct arche_platform_drvdata *arche_pdata = 3969160b7c7SDavid Lin container_of(notifier, struct arche_platform_drvdata, 3979160b7c7SDavid Lin pm_notifier); 398886aba55SVaibhav Hiremath int ret = NOTIFY_DONE; 3999160b7c7SDavid Lin 400886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 4019160b7c7SDavid Lin switch (pm_event) { 4029160b7c7SDavid Lin case PM_SUSPEND_PREPARE: 403886aba55SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 404886aba55SVaibhav Hiremath ret = NOTIFY_STOP; 405886aba55SVaibhav Hiremath break; 406886aba55SVaibhav Hiremath } 4079160b7c7SDavid Lin device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 4089160b7c7SDavid Lin arche_platform_poweroff_seq(arche_pdata); 4099160b7c7SDavid Lin break; 4109160b7c7SDavid Lin case PM_POST_SUSPEND: 4118ef0b538SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) 4128ef0b538SVaibhav Hiremath break; 4138ef0b538SVaibhav Hiremath 4147ba535ecSVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 41516fd976cSVaibhav Hiremath arche_platform_coldboot_seq(arche_pdata); 4169160b7c7SDavid Lin break; 4179160b7c7SDavid Lin default: 4189160b7c7SDavid Lin break; 4199160b7c7SDavid Lin } 420886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 4219160b7c7SDavid Lin 422886aba55SVaibhav Hiremath return ret; 4239160b7c7SDavid Lin } 4249160b7c7SDavid Lin 4257fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 4267fa60654SVaibhav Hiremath { 4277fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 4287fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 4297fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 4307fa60654SVaibhav Hiremath int ret; 4314207b587SNishad Kamdar unsigned int flags; 4327fa60654SVaibhav Hiremath 433461ab807SGioh Kim arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), 434461ab807SGioh Kim GFP_KERNEL); 4357fa60654SVaibhav Hiremath if (!arche_pdata) 4367fa60654SVaibhav Hiremath return -ENOMEM; 4377fa60654SVaibhav Hiremath 4387fa60654SVaibhav Hiremath /* setup svc reset gpio */ 4397fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 4407fa60654SVaibhav Hiremath "svc,reset-active-high"); 4414207b587SNishad Kamdar if (arche_pdata->is_reset_act_hi) 4424207b587SNishad Kamdar flags = GPIOD_OUT_HIGH; 4434207b587SNishad Kamdar else 4444207b587SNishad Kamdar flags = GPIOD_OUT_LOW; 4454207b587SNishad Kamdar 4464207b587SNishad Kamdar arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags); 4474207b587SNishad Kamdar if (IS_ERR(arche_pdata->svc_reset)) { 4484207b587SNishad Kamdar ret = PTR_ERR(arche_pdata->svc_reset); 4494207b587SNishad Kamdar dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret); 4507fa60654SVaibhav Hiremath return ret; 4517fa60654SVaibhav Hiremath } 452c4058b79SBryan O'Donoghue arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 4537fa60654SVaibhav Hiremath 4544207b587SNishad Kamdar arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot", 4554207b587SNishad Kamdar GPIOD_OUT_LOW); 4564207b587SNishad Kamdar if (IS_ERR(arche_pdata->svc_sysboot)) { 4574207b587SNishad Kamdar ret = PTR_ERR(arche_pdata->svc_sysboot); 4584207b587SNishad Kamdar dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret); 4597fa60654SVaibhav Hiremath return ret; 4607fa60654SVaibhav Hiremath } 4617fa60654SVaibhav Hiremath 4627fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 4634207b587SNishad Kamdar arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req", 4644207b587SNishad Kamdar GPIOD_IN); 4654207b587SNishad Kamdar if (IS_ERR(arche_pdata->svc_refclk_req)) { 4664207b587SNishad Kamdar ret = PTR_ERR(arche_pdata->svc_refclk_req); 4674207b587SNishad Kamdar dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret); 4687fa60654SVaibhav Hiremath return ret; 4697fa60654SVaibhav Hiremath } 4707fa60654SVaibhav Hiremath 4717fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 4727fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 4737fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 4747fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 4757fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 4767fa60654SVaibhav Hiremath return ret; 4777fa60654SVaibhav Hiremath } 4787fa60654SVaibhav Hiremath 4797fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 4807fa60654SVaibhav Hiremath 4817fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 4827fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 4837fa60654SVaibhav Hiremath 4844207b587SNishad Kamdar arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect", 4854207b587SNishad Kamdar GPIOD_IN); 4864207b587SNishad Kamdar if (IS_ERR(arche_pdata->wake_detect)) { 4874207b587SNishad Kamdar ret = PTR_ERR(arche_pdata->wake_detect); 4884207b587SNishad Kamdar dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret); 489758ca99dSVaibhav Hiremath return ret; 490a463fc15SVaibhav Hiremath } 49116fd976cSVaibhav Hiremath 492c4058b79SBryan O'Donoghue arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 493a463fc15SVaibhav Hiremath 494a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 495a463fc15SVaibhav Hiremath 4966c1ca56dSVaibhav Hiremath spin_lock_init(&arche_pdata->wake_lock); 497886aba55SVaibhav Hiremath mutex_init(&arche_pdata->platform_state_mutex); 498f760bbfbSVaibhav Hiremath arche_pdata->wake_detect_irq = 4994207b587SNishad Kamdar gpiod_to_irq(arche_pdata->wake_detect); 500f760bbfbSVaibhav Hiremath 501f760bbfbSVaibhav Hiremath ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 502f760bbfbSVaibhav Hiremath arche_platform_wd_irq, 503f760bbfbSVaibhav Hiremath arche_platform_wd_irq_thread, 504461ab807SGioh Kim IRQF_TRIGGER_FALLING | 505461ab807SGioh Kim IRQF_TRIGGER_RISING | IRQF_ONESHOT, 506f760bbfbSVaibhav Hiremath dev_name(dev), arche_pdata); 507f760bbfbSVaibhav Hiremath if (ret) { 508f760bbfbSVaibhav Hiremath dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 509f760bbfbSVaibhav Hiremath return ret; 510f760bbfbSVaibhav Hiremath } 511d29b67d4SVaibhav Hiremath disable_irq(arche_pdata->wake_detect_irq); 512f760bbfbSVaibhav Hiremath 5132923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 5142923c58eSVaibhav Hiremath if (ret) { 5152923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 5162923c58eSVaibhav Hiremath return ret; 5172923c58eSVaibhav Hiremath } 5182923c58eSVaibhav Hiremath 519d29b67d4SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 520d29b67d4SVaibhav Hiremath if (ret) { 521d29b67d4SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 522d29b67d4SVaibhav Hiremath goto err_device_remove; 523d29b67d4SVaibhav Hiremath } 524d29b67d4SVaibhav Hiremath 525d29b67d4SVaibhav Hiremath arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; 526d29b67d4SVaibhav Hiremath ret = register_pm_notifier(&arche_pdata->pm_notifier); 527d29b67d4SVaibhav Hiremath 528d29b67d4SVaibhav Hiremath if (ret) { 529d29b67d4SVaibhav Hiremath dev_err(dev, "failed to register pm notifier %d\n", ret); 530d29b67d4SVaibhav Hiremath goto err_device_remove; 531d29b67d4SVaibhav Hiremath } 532d29b67d4SVaibhav Hiremath 533d29b67d4SVaibhav Hiremath /* Explicitly power off if requested */ 534d29b67d4SVaibhav Hiremath if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { 535886aba55SVaibhav Hiremath mutex_lock(&arche_pdata->platform_state_mutex); 536758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 537758ca99dSVaibhav Hiremath if (ret) { 538758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 5396743a6fdSVaibhav Hiremath goto err_coldboot; 540758ca99dSVaibhav Hiremath } 541d29b67d4SVaibhav Hiremath arche_platform_wd_irq_en(arche_pdata); 542886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 5439160b7c7SDavid Lin } 5449160b7c7SDavid Lin 5457fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 54672a8c24bSViresh Kumar return 0; 5476743a6fdSVaibhav Hiremath 5486743a6fdSVaibhav Hiremath err_coldboot: 549886aba55SVaibhav Hiremath mutex_unlock(&arche_pdata->platform_state_mutex); 550d29b67d4SVaibhav Hiremath err_device_remove: 5516743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 5526743a6fdSVaibhav Hiremath return ret; 5537fa60654SVaibhav Hiremath } 5547fa60654SVaibhav Hiremath 555bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 556bc142bbbSVaibhav Hiremath { 557bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 558bc142bbbSVaibhav Hiremath 559bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 560bc142bbbSVaibhav Hiremath 561bc142bbbSVaibhav Hiremath return 0; 562bc142bbbSVaibhav Hiremath } 563bc142bbbSVaibhav Hiremath 5647fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 5657fa60654SVaibhav Hiremath { 5667fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 5677fa60654SVaibhav Hiremath 5689160b7c7SDavid Lin unregister_pm_notifier(&arche_pdata->pm_notifier); 5692923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 570bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 5715993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 5727fa60654SVaibhav Hiremath 573ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 574ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 575ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 5767fa60654SVaibhav Hiremath return 0; 5777fa60654SVaibhav Hiremath } 5787fa60654SVaibhav Hiremath 5790687090aSArnd Bergmann static __maybe_unused int arche_platform_suspend(struct device *dev) 5807fa60654SVaibhav Hiremath { 5817fa60654SVaibhav Hiremath /* 5827fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 5837fa60654SVaibhav Hiremath * completely 5847fa60654SVaibhav Hiremath * 5857fa60654SVaibhav Hiremath * TODO: sequence ?? 5867fa60654SVaibhav Hiremath * 5877fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 5887fa60654SVaibhav Hiremath * Precondition: Definition ??? 5897fa60654SVaibhav Hiremath */ 5907fa60654SVaibhav Hiremath return 0; 5917fa60654SVaibhav Hiremath } 5927fa60654SVaibhav Hiremath 5930687090aSArnd Bergmann static __maybe_unused int arche_platform_resume(struct device *dev) 5947fa60654SVaibhav Hiremath { 5957fa60654SVaibhav Hiremath /* 5967fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 5977fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 5987fa60654SVaibhav Hiremath * OFF state or standby state. 5997fa60654SVaibhav Hiremath * 6007fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 6017fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 6027fa60654SVaibhav Hiremath * 6037fa60654SVaibhav Hiremath */ 6047fa60654SVaibhav Hiremath return 0; 6057fa60654SVaibhav Hiremath } 6067fa60654SVaibhav Hiremath 6071f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev) 6081f77b363SDavid Lin { 6091f77b363SDavid Lin struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 6101f77b363SDavid Lin 6111f77b363SDavid Lin arche_platform_poweroff_seq(arche_pdata); 6121f77b363SDavid Lin 6131f77b363SDavid Lin usb3613_hub_mode_ctrl(false); 6141f77b363SDavid Lin } 6151f77b363SDavid Lin 6167fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 6177fa60654SVaibhav Hiremath arche_platform_suspend, 6187fa60654SVaibhav Hiremath arche_platform_resume); 6197fa60654SVaibhav Hiremath 620cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_platform_of_match[] = { 621bf766625SMitchell Tasman /* Use PID/VID of SVC device */ 622bf766625SMitchell Tasman { .compatible = "google,arche-platform", }, 6237fa60654SVaibhav Hiremath { }, 6247fa60654SVaibhav Hiremath }; 6251e5dd1f8SGreg Kroah-Hartman 626cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_combined_id[] = { 627bf766625SMitchell Tasman /* Use PID/VID of SVC device */ 628bf766625SMitchell Tasman { .compatible = "google,arche-platform", }, 6291e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 6301e5dd1f8SGreg Kroah-Hartman { }, 6311e5dd1f8SGreg Kroah-Hartman }; 6321e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 6337fa60654SVaibhav Hiremath 6347fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 6357fa60654SVaibhav Hiremath .probe = arche_platform_probe, 6367fa60654SVaibhav Hiremath .remove = arche_platform_remove, 6371f77b363SDavid Lin .shutdown = arche_platform_shutdown, 6387fa60654SVaibhav Hiremath .driver = { 6397fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 6407fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 6411e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 6427fa60654SVaibhav Hiremath } 6437fa60654SVaibhav Hiremath }; 6447fa60654SVaibhav Hiremath 6451e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 6461e5dd1f8SGreg Kroah-Hartman { 6471e5dd1f8SGreg Kroah-Hartman int retval; 6481e5dd1f8SGreg Kroah-Hartman 6491e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 6501e5dd1f8SGreg Kroah-Hartman if (retval) 6511e5dd1f8SGreg Kroah-Hartman return retval; 6521e5dd1f8SGreg Kroah-Hartman 6537b62b61cSViresh Kumar retval = arche_apb_init(); 6541e5dd1f8SGreg Kroah-Hartman if (retval) 6551e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6561e5dd1f8SGreg Kroah-Hartman 6571e5dd1f8SGreg Kroah-Hartman return retval; 6581e5dd1f8SGreg Kroah-Hartman } 6591e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 6601e5dd1f8SGreg Kroah-Hartman 6611e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 6621e5dd1f8SGreg Kroah-Hartman { 6637b62b61cSViresh Kumar arche_apb_exit(); 6641e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 6651e5dd1f8SGreg Kroah-Hartman } 6661e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 6677fa60654SVaibhav Hiremath 668a974f469SSandeep Patil MODULE_LICENSE("GPL v2"); 6697fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 6707fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 671