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> 201e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 217fa60654SVaibhav Hiremath 22ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h> 23ad4d3f95SVaibhav Hiremath 247fa60654SVaibhav Hiremath struct arche_platform_drvdata { 257fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 267fa60654SVaibhav Hiremath int svc_reset_gpio; 277fa60654SVaibhav Hiremath bool is_reset_act_hi; 287fa60654SVaibhav Hiremath int svc_sysboot_gpio; 29a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 307fa60654SVaibhav Hiremath 31e74d04a5SVaibhav Hiremath enum arche_platform_state state; 32e74d04a5SVaibhav Hiremath 337fa60654SVaibhav Hiremath unsigned int svc_refclk_req; 347fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 357fa60654SVaibhav Hiremath 367fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 377fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 387fa60654SVaibhav Hiremath 397fa60654SVaibhav Hiremath int num_apbs; 40a463fc15SVaibhav Hiremath 41a463fc15SVaibhav Hiremath struct delayed_work delayed_work; 42a463fc15SVaibhav Hiremath struct device *dev; 437fa60654SVaibhav Hiremath }; 447fa60654SVaibhav Hiremath 457fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 467fa60654SVaibhav Hiremath { 477fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 487fa60654SVaibhav Hiremath } 497fa60654SVaibhav Hiremath 50fd60ac58SVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data) 51fd60ac58SVaibhav Hiremath { 52fd60ac58SVaibhav Hiremath int ret; 53fd60ac58SVaibhav Hiremath 54fd60ac58SVaibhav Hiremath ret = apb_ctrl_coldboot(dev); 55fd60ac58SVaibhav Hiremath if (ret) 56fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to coldboot\n"); 57fd60ac58SVaibhav Hiremath 58fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 59fd60ac58SVaibhav Hiremath return 0; 60fd60ac58SVaibhav Hiremath } 61fd60ac58SVaibhav Hiremath 62fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data) 63fd60ac58SVaibhav Hiremath { 64fd60ac58SVaibhav Hiremath int ret; 65fd60ac58SVaibhav Hiremath 66fd60ac58SVaibhav Hiremath ret = apb_ctrl_fw_flashing(dev); 67fd60ac58SVaibhav Hiremath if (ret) 68fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to switch to fw flashing state\n"); 69fd60ac58SVaibhav Hiremath 70fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 71fd60ac58SVaibhav Hiremath return 0; 72fd60ac58SVaibhav Hiremath } 73fd60ac58SVaibhav Hiremath 74fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 75fd60ac58SVaibhav Hiremath { 76fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 77fd60ac58SVaibhav Hiremath 78fd60ac58SVaibhav Hiremath return 0; 79fd60ac58SVaibhav Hiremath } 80fd60ac58SVaibhav Hiremath 81a463fc15SVaibhav Hiremath /** 82a463fc15SVaibhav Hiremath * svc_delayed_work - Time to give SVC to boot. 83a463fc15SVaibhav Hiremath */ 84a463fc15SVaibhav Hiremath static void svc_delayed_work(struct work_struct *work) 85a463fc15SVaibhav Hiremath { 86a463fc15SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = 87a463fc15SVaibhav Hiremath container_of(work, struct arche_platform_drvdata, delayed_work.work); 88a3043d9eSMichael Scott int timeout = 50; 89a463fc15SVaibhav Hiremath 90a463fc15SVaibhav Hiremath /* 91a463fc15SVaibhav Hiremath * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin 92a463fc15SVaibhav Hiremath * deasserted (LOW in this case) 93a463fc15SVaibhav Hiremath * 2.1. SVC allows 360 milliseconds to elapse after switch boots to work 94a463fc15SVaibhav Hiremath * around bug described in ENG-330. 95a463fc15SVaibhav Hiremath * 2.2. AP asserts wake/detect pin (HIGH) (this can proceed in parallel with 2.1) 96a463fc15SVaibhav Hiremath * 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP 97a463fc15SVaibhav Hiremath * 4. AP receives "wake out" signal, takes AP Bridges through their power 98a463fc15SVaibhav Hiremath * on reset sequence as defined in the bridge ASIC reference manuals 99a463fc15SVaibhav Hiremath * 5. AP takes USB3613 through its power on reset sequence 100a463fc15SVaibhav Hiremath * 6. AP enumerates AP Bridges 101a463fc15SVaibhav Hiremath */ 102a463fc15SVaibhav Hiremath gpio_set_value(arche_pdata->wake_detect_gpio, 1); 103a463fc15SVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 104a463fc15SVaibhav Hiremath do { 105a463fc15SVaibhav Hiremath /* Read the wake_detect GPIO, for WAKE_OUT event from SVC */ 106a463fc15SVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) 107a463fc15SVaibhav Hiremath break; 108a463fc15SVaibhav Hiremath 109a3043d9eSMichael Scott msleep(100); 110a463fc15SVaibhav Hiremath } while(timeout--); 111a463fc15SVaibhav Hiremath 112fd60ac58SVaibhav Hiremath if (timeout < 0) { 113a463fc15SVaibhav Hiremath /* FIXME: We may want to limit retries here */ 114fd60ac58SVaibhav Hiremath dev_warn(arche_pdata->dev, 115fd60ac58SVaibhav Hiremath "Timed out on wake/detect, rescheduling handshake\n"); 116a463fc15SVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 117a463fc15SVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 118fd60ac58SVaibhav Hiremath return; 119fd60ac58SVaibhav Hiremath } 120fd60ac58SVaibhav Hiremath 121fd60ac58SVaibhav Hiremath /* Bring APB out of reset: cold boot sequence */ 122fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 123fd60ac58SVaibhav Hiremath 124fd60ac58SVaibhav Hiremath /* re-assert wake_detect to confirm SVC WAKE_OUT */ 125fd60ac58SVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 1); 126ad4d3f95SVaibhav Hiremath 127ad4d3f95SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 128ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 129ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 130a463fc15SVaibhav Hiremath } 131a463fc15SVaibhav Hiremath 132758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 133758ca99dSVaibhav Hiremath { 134758ca99dSVaibhav Hiremath int ret; 135758ca99dSVaibhav Hiremath 136599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 137599159b6SVaibhav Hiremath return 0; 138599159b6SVaibhav Hiremath 139758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 140758ca99dSVaibhav Hiremath 141758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 142758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 143758ca99dSVaibhav Hiremath 144758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 145758ca99dSVaibhav Hiremath usleep_range(100, 200); 146758ca99dSVaibhav Hiremath 147758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 148758ca99dSVaibhav Hiremath if (ret) { 149758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 150758ca99dSVaibhav Hiremath ret); 151758ca99dSVaibhav Hiremath return ret; 152758ca99dSVaibhav Hiremath } 153758ca99dSVaibhav Hiremath 154758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 155758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 156758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 157758ca99dSVaibhav Hiremath 158e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; 159e74d04a5SVaibhav Hiremath 160758ca99dSVaibhav Hiremath return 0; 161758ca99dSVaibhav Hiremath } 162758ca99dSVaibhav Hiremath 1637691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 1647691fed2SVaibhav Hiremath { 165599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 166599159b6SVaibhav Hiremath return; 167599159b6SVaibhav Hiremath 1687691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 1697691fed2SVaibhav Hiremath 1707691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1717691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 1727691fed2SVaibhav Hiremath 1737691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 1747691fed2SVaibhav Hiremath 1757691fed2SVaibhav Hiremath usleep_range(100, 200); 1767691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1777691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 1787691fed2SVaibhav Hiremath 1797691fed2SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; 1807691fed2SVaibhav Hiremath 1817691fed2SVaibhav Hiremath } 1827691fed2SVaibhav Hiremath 1835993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 1847fa60654SVaibhav Hiremath { 185599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 186599159b6SVaibhav Hiremath return; 187599159b6SVaibhav Hiremath 188b4c95fcaSVaibhav Hiremath /* Send disconnect/detach event to SVC */ 189b4c95fcaSVaibhav Hiremath gpio_set_value(arche_pdata->wake_detect_gpio, 0); 190b4c95fcaSVaibhav Hiremath usleep_range(100, 200); 191b4c95fcaSVaibhav Hiremath 192d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 1937fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 1947fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1957fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 196e74d04a5SVaibhav Hiremath 197e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 1987fa60654SVaibhav Hiremath } 1997fa60654SVaibhav Hiremath 2002923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 2012923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 2022923c58eSVaibhav Hiremath { 2032923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 2042923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 2052923c58eSVaibhav Hiremath int ret = 0; 2062923c58eSVaibhav Hiremath 2072923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 2082923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 2092923c58eSVaibhav Hiremath return count; 2102923c58eSVaibhav Hiremath 211fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 212fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 213fd60ac58SVaibhav Hiremath 2142923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 215ad4d3f95SVaibhav Hiremath 216ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 217ad4d3f95SVaibhav Hiremath if (ret) 218ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 219ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 2202923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 2212923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 2222923c58eSVaibhav Hiremath return count; 2232923c58eSVaibhav Hiremath 2242923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 225fd60ac58SVaibhav Hiremath /* Give enough time for SVC to boot and then handshake with SVC */ 226fd60ac58SVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 2272923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 2282923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 2292923c58eSVaibhav Hiremath return count; 2302923c58eSVaibhav Hiremath 2312923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 2327691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 2337691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 2347691fed2SVaibhav Hiremath return count; 2357691fed2SVaibhav Hiremath 2367691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 2377691fed2SVaibhav Hiremath * and then enter FW flashing state */ 238fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 239fd60ac58SVaibhav Hiremath 2407691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 241fd60ac58SVaibhav Hiremath 242ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 243ad4d3f95SVaibhav Hiremath if (ret) 244ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 245ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 246ad4d3f95SVaibhav Hiremath 2477691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 248fd60ac58SVaibhav Hiremath 249fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 2502923c58eSVaibhav Hiremath } else { 2512923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 2522923c58eSVaibhav Hiremath ret = -EINVAL; 2532923c58eSVaibhav Hiremath } 2542923c58eSVaibhav Hiremath 2552923c58eSVaibhav Hiremath return ret ? ret : count; 2562923c58eSVaibhav Hiremath } 2572923c58eSVaibhav Hiremath 2582923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 2592923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 2602923c58eSVaibhav Hiremath { 2612923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 2622923c58eSVaibhav Hiremath 2632923c58eSVaibhav Hiremath switch (arche_pdata->state) { 2642923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 2652923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 2662923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 2672923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 2682923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 2692923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 2707691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 2717691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 2722923c58eSVaibhav Hiremath default: 2732923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 2742923c58eSVaibhav Hiremath } 2752923c58eSVaibhav Hiremath } 2762923c58eSVaibhav Hiremath 2772923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 2782923c58eSVaibhav Hiremath 2797fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 2807fa60654SVaibhav Hiremath { 2817fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 2827fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 2837fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 2847fa60654SVaibhav Hiremath int ret; 2857fa60654SVaibhav Hiremath 2867fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 2877fa60654SVaibhav Hiremath if (!arche_pdata) 2887fa60654SVaibhav Hiremath return -ENOMEM; 2897fa60654SVaibhav Hiremath 2907fa60654SVaibhav Hiremath /* setup svc reset gpio */ 2917fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 2927fa60654SVaibhav Hiremath "svc,reset-active-high"); 2937fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 2947fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 2957fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 296f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 2977fa60654SVaibhav Hiremath } 2987fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 2997fa60654SVaibhav Hiremath if (ret) { 3007fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 3017fa60654SVaibhav Hiremath return ret; 3027fa60654SVaibhav Hiremath } 3037fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 3047fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 3057fa60654SVaibhav Hiremath if (ret) { 3067fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3077fa60654SVaibhav Hiremath return ret; 3087fa60654SVaibhav Hiremath } 309e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 3107fa60654SVaibhav Hiremath 3117fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 3127fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 3137fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 3147fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 315f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 3167fa60654SVaibhav Hiremath } 3177fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 3187fa60654SVaibhav Hiremath if (ret) { 3197fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 3207fa60654SVaibhav Hiremath return ret; 3217fa60654SVaibhav Hiremath } 3227fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 3237fa60654SVaibhav Hiremath if (ret) { 3247fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 3257fa60654SVaibhav Hiremath return ret; 3267fa60654SVaibhav Hiremath } 3277fa60654SVaibhav Hiremath 3287fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 3297fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 3307fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 3317fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 3327fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 333f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 3347fa60654SVaibhav Hiremath } 3357fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 3367fa60654SVaibhav Hiremath if (ret) { 3377fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 3387fa60654SVaibhav Hiremath return ret; 3397fa60654SVaibhav Hiremath } 3407fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 3417fa60654SVaibhav Hiremath if (ret) { 3427fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 3437fa60654SVaibhav Hiremath return ret; 3447fa60654SVaibhav Hiremath } 3457fa60654SVaibhav Hiremath 3467fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 3477fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 3487fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 3497fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 3507fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 3517fa60654SVaibhav Hiremath return ret; 3527fa60654SVaibhav Hiremath } 3537fa60654SVaibhav Hiremath 3547fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 3557fa60654SVaibhav Hiremath 3567fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 3577fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 3587fa60654SVaibhav Hiremath 359a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 360a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 361a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 362a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 363758ca99dSVaibhav Hiremath return ret; 36472a8c24bSViresh Kumar } 3657fa60654SVaibhav Hiremath 366a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 367a463fc15SVaibhav Hiremath if (ret) { 368a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 369a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 370758ca99dSVaibhav Hiremath return ret; 371a463fc15SVaibhav Hiremath } 372057aad29SMichael Scott /* deassert wake detect */ 373057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 374a463fc15SVaibhav Hiremath 375a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 376a463fc15SVaibhav Hiremath 3772923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 3782923c58eSVaibhav Hiremath if (ret) { 3792923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 3802923c58eSVaibhav Hiremath return ret; 3812923c58eSVaibhav Hiremath } 3822923c58eSVaibhav Hiremath 383758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 384758ca99dSVaibhav Hiremath if (ret) { 385758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 386758ca99dSVaibhav Hiremath return ret; 387758ca99dSVaibhav Hiremath } 388758ca99dSVaibhav Hiremath 389fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 390fd60ac58SVaibhav Hiremath if (ret) { 391fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 392fd60ac58SVaibhav Hiremath return ret; 393fd60ac58SVaibhav Hiremath } 394fd60ac58SVaibhav Hiremath 395fd60ac58SVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); 396fd60ac58SVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 397fd60ac58SVaibhav Hiremath 3987fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 39972a8c24bSViresh Kumar return 0; 4007fa60654SVaibhav Hiremath } 4017fa60654SVaibhav Hiremath 402bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 403bc142bbbSVaibhav Hiremath { 404bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 405bc142bbbSVaibhav Hiremath 406bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 407bc142bbbSVaibhav Hiremath 408bc142bbbSVaibhav Hiremath return 0; 409bc142bbbSVaibhav Hiremath } 410bc142bbbSVaibhav Hiremath 4117fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 4127fa60654SVaibhav Hiremath { 4137fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 4147fa60654SVaibhav Hiremath 4152923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 41649e6e04bSVaibhav Hiremath cancel_delayed_work_sync(&arche_pdata->delayed_work); 417bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 4185993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 4197fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 4207fa60654SVaibhav Hiremath 421ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 422ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 423ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 4247fa60654SVaibhav Hiremath return 0; 4257fa60654SVaibhav Hiremath } 4267fa60654SVaibhav Hiremath 4277fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 4287fa60654SVaibhav Hiremath { 4297fa60654SVaibhav Hiremath /* 4307fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 4317fa60654SVaibhav Hiremath * completely 4327fa60654SVaibhav Hiremath * 4337fa60654SVaibhav Hiremath * TODO: sequence ?? 4347fa60654SVaibhav Hiremath * 4357fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 4367fa60654SVaibhav Hiremath * Precondition: Definition ??? 4377fa60654SVaibhav Hiremath */ 4387fa60654SVaibhav Hiremath return 0; 4397fa60654SVaibhav Hiremath } 4407fa60654SVaibhav Hiremath 4417fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 4427fa60654SVaibhav Hiremath { 4437fa60654SVaibhav Hiremath /* 4447fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 4457fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 4467fa60654SVaibhav Hiremath * OFF state or standby state. 4477fa60654SVaibhav Hiremath * 4487fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 4497fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 4507fa60654SVaibhav Hiremath * 4517fa60654SVaibhav Hiremath */ 4527fa60654SVaibhav Hiremath return 0; 4537fa60654SVaibhav Hiremath } 4547fa60654SVaibhav Hiremath 4557fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 4567fa60654SVaibhav Hiremath arche_platform_suspend, 4577fa60654SVaibhav Hiremath arche_platform_resume); 4587fa60654SVaibhav Hiremath 4597fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 4607fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 4617fa60654SVaibhav Hiremath { }, 4627fa60654SVaibhav Hiremath }; 4631e5dd1f8SGreg Kroah-Hartman 4641e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 4651e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 4661e5dd1f8SGreg Kroah-Hartman { }, 4671e5dd1f8SGreg Kroah-Hartman }; 4681e5dd1f8SGreg Kroah-Hartman 4691e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 4701e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 4711e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 4721e5dd1f8SGreg Kroah-Hartman { }, 4731e5dd1f8SGreg Kroah-Hartman }; 4741e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 4757fa60654SVaibhav Hiremath 4767fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 4777fa60654SVaibhav Hiremath .probe = arche_platform_probe, 4787fa60654SVaibhav Hiremath .remove = arche_platform_remove, 4797fa60654SVaibhav Hiremath .driver = { 4807fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 4817fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 4821e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 4837fa60654SVaibhav Hiremath } 4847fa60654SVaibhav Hiremath }; 4857fa60654SVaibhav Hiremath 4861e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 4871e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 4881e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 4891e5dd1f8SGreg Kroah-Hartman .driver = { 4901e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 4911e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 4921e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 4931e5dd1f8SGreg Kroah-Hartman } 4941e5dd1f8SGreg Kroah-Hartman }; 4951e5dd1f8SGreg Kroah-Hartman 4961e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 4971e5dd1f8SGreg Kroah-Hartman { 4981e5dd1f8SGreg Kroah-Hartman int retval; 4991e5dd1f8SGreg Kroah-Hartman 5001e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 5011e5dd1f8SGreg Kroah-Hartman if (retval) 5021e5dd1f8SGreg Kroah-Hartman return retval; 5031e5dd1f8SGreg Kroah-Hartman 5041e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 5051e5dd1f8SGreg Kroah-Hartman if (retval) 5061e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 5071e5dd1f8SGreg Kroah-Hartman 5081e5dd1f8SGreg Kroah-Hartman return retval; 5091e5dd1f8SGreg Kroah-Hartman } 5101e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 5111e5dd1f8SGreg Kroah-Hartman 5121e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 5131e5dd1f8SGreg Kroah-Hartman { 5141e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 5151e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 5161e5dd1f8SGreg Kroah-Hartman } 5171e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 5187fa60654SVaibhav Hiremath 5197fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 5207fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 5217fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 522