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 24685353c1SVaibhav Hiremath enum svc_wakedetect_state { 25685353c1SVaibhav Hiremath WD_STATE_IDLE, /* Default state = pulled high/low */ 26685353c1SVaibhav Hiremath WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 27685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 28685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 29685353c1SVaibhav Hiremath WD_STATE_COLDBOOT_START, /* Cold boot process started */ 30685353c1SVaibhav Hiremath WD_STATE_STANDBYBOOT_START, /* Not used */ 31685353c1SVaibhav Hiremath }; 32685353c1SVaibhav Hiremath 337fa60654SVaibhav Hiremath struct arche_platform_drvdata { 347fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 357fa60654SVaibhav Hiremath int svc_reset_gpio; 367fa60654SVaibhav Hiremath bool is_reset_act_hi; 377fa60654SVaibhav Hiremath int svc_sysboot_gpio; 38a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 397fa60654SVaibhav Hiremath 40e74d04a5SVaibhav Hiremath enum arche_platform_state state; 41e74d04a5SVaibhav Hiremath 427fa60654SVaibhav Hiremath unsigned int svc_refclk_req; 437fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 447fa60654SVaibhav Hiremath 457fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 467fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 477fa60654SVaibhav Hiremath 487fa60654SVaibhav Hiremath int num_apbs; 49a463fc15SVaibhav Hiremath 50a463fc15SVaibhav Hiremath struct delayed_work delayed_work; 51685353c1SVaibhav Hiremath enum svc_wakedetect_state wake_detect_state; 52685353c1SVaibhav Hiremath 53a463fc15SVaibhav Hiremath struct device *dev; 547fa60654SVaibhav Hiremath }; 557fa60654SVaibhav Hiremath 567fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 577fa60654SVaibhav Hiremath { 587fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 597fa60654SVaibhav Hiremath } 607fa60654SVaibhav Hiremath 61fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data) 62fd60ac58SVaibhav Hiremath { 63fd60ac58SVaibhav Hiremath int ret; 64fd60ac58SVaibhav Hiremath 65fd60ac58SVaibhav Hiremath ret = apb_ctrl_fw_flashing(dev); 66fd60ac58SVaibhav Hiremath if (ret) 67fd60ac58SVaibhav Hiremath dev_warn(dev, "failed to switch to fw flashing state\n"); 68fd60ac58SVaibhav Hiremath 69fd60ac58SVaibhav Hiremath /*Child nodes are independent, so do not exit coldboot operation */ 70fd60ac58SVaibhav Hiremath return 0; 71fd60ac58SVaibhav Hiremath } 72fd60ac58SVaibhav Hiremath 73fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data) 74fd60ac58SVaibhav Hiremath { 75fd60ac58SVaibhav Hiremath apb_ctrl_poweroff(dev); 76fd60ac58SVaibhav Hiremath 77fd60ac58SVaibhav Hiremath return 0; 78fd60ac58SVaibhav Hiremath } 79fd60ac58SVaibhav Hiremath 80a463fc15SVaibhav Hiremath /** 81db5a3bcaSVaibhav Hiremath * hub_conf_delayed_work - Configures USB3613 device to HUB mode 82db5a3bcaSVaibhav Hiremath * 83db5a3bcaSVaibhav Hiremath * The idea here is to split the APB coldboot operation with slow HUB configuration, 84db5a3bcaSVaibhav Hiremath * so that driver response to wake/detect event can be met. 85db5a3bcaSVaibhav Hiremath * So expectation is, once code reaches here, means initial unipro linkup 86db5a3bcaSVaibhav Hiremath * between APB<->Switch was successful, so now just take it to AP. 87a463fc15SVaibhav Hiremath */ 88db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work) 89a463fc15SVaibhav Hiremath { 90a463fc15SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = 91a463fc15SVaibhav Hiremath container_of(work, struct arche_platform_drvdata, delayed_work.work); 92ad4d3f95SVaibhav Hiremath 93ad4d3f95SVaibhav Hiremath /* Enable HUB3613 into HUB mode. */ 94ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(true)) 95ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 96a463fc15SVaibhav Hiremath } 97a463fc15SVaibhav Hiremath 98758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 99758ca99dSVaibhav Hiremath { 100758ca99dSVaibhav Hiremath int ret; 101758ca99dSVaibhav Hiremath 102599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 103599159b6SVaibhav Hiremath return 0; 104599159b6SVaibhav Hiremath 105758ca99dSVaibhav Hiremath dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 106758ca99dSVaibhav Hiremath 107758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 108758ca99dSVaibhav Hiremath arche_pdata->is_reset_act_hi); 109758ca99dSVaibhav Hiremath 110758ca99dSVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); 111758ca99dSVaibhav Hiremath usleep_range(100, 200); 112758ca99dSVaibhav Hiremath 113758ca99dSVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 114758ca99dSVaibhav Hiremath if (ret) { 115758ca99dSVaibhav Hiremath dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 116758ca99dSVaibhav Hiremath ret); 117758ca99dSVaibhav Hiremath return ret; 118758ca99dSVaibhav Hiremath } 119758ca99dSVaibhav Hiremath 120758ca99dSVaibhav Hiremath /* bring SVC out of reset */ 121758ca99dSVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 122758ca99dSVaibhav Hiremath !arche_pdata->is_reset_act_hi); 123758ca99dSVaibhav Hiremath 124e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE; 125e74d04a5SVaibhav Hiremath 126758ca99dSVaibhav Hiremath return 0; 127758ca99dSVaibhav Hiremath } 128758ca99dSVaibhav Hiremath 1297691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 1307691fed2SVaibhav Hiremath { 131599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 132599159b6SVaibhav Hiremath return; 133599159b6SVaibhav Hiremath 1347691fed2SVaibhav Hiremath dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 1357691fed2SVaibhav Hiremath 1367691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1377691fed2SVaibhav Hiremath arche_pdata->is_reset_act_hi); 1387691fed2SVaibhav Hiremath 1397691fed2SVaibhav Hiremath gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); 1407691fed2SVaibhav Hiremath 1417691fed2SVaibhav Hiremath usleep_range(100, 200); 1427691fed2SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1437691fed2SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 1447691fed2SVaibhav Hiremath 1457691fed2SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING; 1467691fed2SVaibhav Hiremath 1477691fed2SVaibhav Hiremath } 1487691fed2SVaibhav Hiremath 1495993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 1507fa60654SVaibhav Hiremath { 151599159b6SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 152599159b6SVaibhav Hiremath return; 153599159b6SVaibhav Hiremath 15425847ee7SVaibhav Hiremath /* If in fw_flashing mode, then no need to repeate things again */ 15525847ee7SVaibhav Hiremath if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 156b4c95fcaSVaibhav Hiremath /* Send disconnect/detach event to SVC */ 157b4c95fcaSVaibhav Hiremath gpio_set_value(arche_pdata->wake_detect_gpio, 0); 158b4c95fcaSVaibhav Hiremath usleep_range(100, 200); 159685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 160b4c95fcaSVaibhav Hiremath 161d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 16225847ee7SVaibhav Hiremath } 16325847ee7SVaibhav Hiremath 1647fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 1657fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1667fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 167e74d04a5SVaibhav Hiremath 168e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 1697fa60654SVaibhav Hiremath } 1707fa60654SVaibhav Hiremath 1712923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev, 1722923c58eSVaibhav Hiremath struct device_attribute *attr, const char *buf, size_t count) 1732923c58eSVaibhav Hiremath { 1742923c58eSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 1752923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 1762923c58eSVaibhav Hiremath int ret = 0; 1772923c58eSVaibhav Hiremath 1782923c58eSVaibhav Hiremath if (sysfs_streq(buf, "off")) { 1792923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 1802923c58eSVaibhav Hiremath return count; 1812923c58eSVaibhav Hiremath 182fd60ac58SVaibhav Hiremath /* If SVC goes down, bring down APB's as well */ 183fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 184fd60ac58SVaibhav Hiremath 1852923c58eSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 186ad4d3f95SVaibhav Hiremath 187ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 188ad4d3f95SVaibhav Hiremath if (ret) 189ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 190ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 1912923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "active")) { 1922923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 1932923c58eSVaibhav Hiremath return count; 1942923c58eSVaibhav Hiremath 1952923c58eSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 1962923c58eSVaibhav Hiremath } else if (sysfs_streq(buf, "standby")) { 1972923c58eSVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 1982923c58eSVaibhav Hiremath return count; 1992923c58eSVaibhav Hiremath 2002923c58eSVaibhav Hiremath dev_warn(arche_pdata->dev, "standby state not supported\n"); 2017691fed2SVaibhav Hiremath } else if (sysfs_streq(buf, "fw_flashing")) { 2027691fed2SVaibhav Hiremath if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 2037691fed2SVaibhav Hiremath return count; 2047691fed2SVaibhav Hiremath 2057691fed2SVaibhav Hiremath /* First we want to make sure we power off everything 2067691fed2SVaibhav Hiremath * and then enter FW flashing state */ 207fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 208fd60ac58SVaibhav Hiremath 2097691fed2SVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 210fd60ac58SVaibhav Hiremath 211ad4d3f95SVaibhav Hiremath ret = usb3613_hub_mode_ctrl(false); 212ad4d3f95SVaibhav Hiremath if (ret) 213ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 214ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 215ad4d3f95SVaibhav Hiremath 2167691fed2SVaibhav Hiremath arche_platform_fw_flashing_seq(arche_pdata); 217fd60ac58SVaibhav Hiremath 218fd60ac58SVaibhav Hiremath device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state); 2192923c58eSVaibhav Hiremath } else { 2202923c58eSVaibhav Hiremath dev_err(arche_pdata->dev, "unknown state\n"); 2212923c58eSVaibhav Hiremath ret = -EINVAL; 2222923c58eSVaibhav Hiremath } 2232923c58eSVaibhav Hiremath 2242923c58eSVaibhav Hiremath return ret ? ret : count; 2252923c58eSVaibhav Hiremath } 2262923c58eSVaibhav Hiremath 2272923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev, 2282923c58eSVaibhav Hiremath struct device_attribute *attr, char *buf) 2292923c58eSVaibhav Hiremath { 2302923c58eSVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 2312923c58eSVaibhav Hiremath 2322923c58eSVaibhav Hiremath switch (arche_pdata->state) { 2332923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_OFF: 2342923c58eSVaibhav Hiremath return sprintf(buf, "off\n"); 2352923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_ACTIVE: 2362923c58eSVaibhav Hiremath return sprintf(buf, "active\n"); 2372923c58eSVaibhav Hiremath case ARCHE_PLATFORM_STATE_STANDBY: 2382923c58eSVaibhav Hiremath return sprintf(buf, "standby\n"); 2397691fed2SVaibhav Hiremath case ARCHE_PLATFORM_STATE_FW_FLASHING: 2407691fed2SVaibhav Hiremath return sprintf(buf, "fw_flashing\n"); 2412923c58eSVaibhav Hiremath default: 2422923c58eSVaibhav Hiremath return sprintf(buf, "unknown state\n"); 2432923c58eSVaibhav Hiremath } 2442923c58eSVaibhav Hiremath } 2452923c58eSVaibhav Hiremath 2462923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state); 2472923c58eSVaibhav Hiremath 2487fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 2497fa60654SVaibhav Hiremath { 2507fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 2517fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 2527fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 2537fa60654SVaibhav Hiremath int ret; 2547fa60654SVaibhav Hiremath 2557fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 2567fa60654SVaibhav Hiremath if (!arche_pdata) 2577fa60654SVaibhav Hiremath return -ENOMEM; 2587fa60654SVaibhav Hiremath 2597fa60654SVaibhav Hiremath /* setup svc reset gpio */ 2607fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 2617fa60654SVaibhav Hiremath "svc,reset-active-high"); 2627fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 2637fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 2647fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 265f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 2667fa60654SVaibhav Hiremath } 2677fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 2687fa60654SVaibhav Hiremath if (ret) { 2697fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 2707fa60654SVaibhav Hiremath return ret; 2717fa60654SVaibhav Hiremath } 2727fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 2737fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 2747fa60654SVaibhav Hiremath if (ret) { 2757fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 2767fa60654SVaibhav Hiremath return ret; 2777fa60654SVaibhav Hiremath } 278e74d04a5SVaibhav Hiremath arche_pdata->state = ARCHE_PLATFORM_STATE_OFF; 2797fa60654SVaibhav Hiremath 2807fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 2817fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 2827fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 2837fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 284f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 2857fa60654SVaibhav Hiremath } 2867fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 2877fa60654SVaibhav Hiremath if (ret) { 2887fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 2897fa60654SVaibhav Hiremath return ret; 2907fa60654SVaibhav Hiremath } 2917fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 2927fa60654SVaibhav Hiremath if (ret) { 2937fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 2947fa60654SVaibhav Hiremath return ret; 2957fa60654SVaibhav Hiremath } 2967fa60654SVaibhav Hiremath 2977fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 2987fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 2997fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 3007fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 3017fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 302f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 3037fa60654SVaibhav Hiremath } 3047fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 3057fa60654SVaibhav Hiremath if (ret) { 3067fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 3077fa60654SVaibhav Hiremath return ret; 3087fa60654SVaibhav Hiremath } 3097fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 3107fa60654SVaibhav Hiremath if (ret) { 3117fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 3127fa60654SVaibhav Hiremath return ret; 3137fa60654SVaibhav Hiremath } 3147fa60654SVaibhav Hiremath 3157fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 3167fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 3177fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 3187fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 3197fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 3207fa60654SVaibhav Hiremath return ret; 3217fa60654SVaibhav Hiremath } 3227fa60654SVaibhav Hiremath 3237fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 3247fa60654SVaibhav Hiremath 3257fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 3267fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 3277fa60654SVaibhav Hiremath 328a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 329a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 330a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 331a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 332758ca99dSVaibhav Hiremath return ret; 33372a8c24bSViresh Kumar } 3347fa60654SVaibhav Hiremath 335a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 336a463fc15SVaibhav Hiremath if (ret) { 337a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 338a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 339758ca99dSVaibhav Hiremath return ret; 340a463fc15SVaibhav Hiremath } 341057aad29SMichael Scott /* deassert wake detect */ 342057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 343685353c1SVaibhav Hiremath arche_pdata->wake_detect_state = WD_STATE_IDLE; 344a463fc15SVaibhav Hiremath 345a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 346a463fc15SVaibhav Hiremath 3472923c58eSVaibhav Hiremath ret = device_create_file(dev, &dev_attr_state); 3482923c58eSVaibhav Hiremath if (ret) { 3492923c58eSVaibhav Hiremath dev_err(dev, "failed to create state file in sysfs\n"); 3502923c58eSVaibhav Hiremath return ret; 3512923c58eSVaibhav Hiremath } 3522923c58eSVaibhav Hiremath 353758ca99dSVaibhav Hiremath ret = arche_platform_coldboot_seq(arche_pdata); 354758ca99dSVaibhav Hiremath if (ret) { 355758ca99dSVaibhav Hiremath dev_err(dev, "Failed to cold boot svc %d\n", ret); 3566743a6fdSVaibhav Hiremath goto err_coldboot; 357758ca99dSVaibhav Hiremath } 358758ca99dSVaibhav Hiremath 359fd60ac58SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 360fd60ac58SVaibhav Hiremath if (ret) { 361fd60ac58SVaibhav Hiremath dev_err(dev, "failed to populate child nodes %d\n", ret); 3626743a6fdSVaibhav Hiremath goto err_populate; 363fd60ac58SVaibhav Hiremath } 364fd60ac58SVaibhav Hiremath 365db5a3bcaSVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work); 366fd60ac58SVaibhav Hiremath 3677fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 36872a8c24bSViresh Kumar return 0; 3696743a6fdSVaibhav Hiremath 3706743a6fdSVaibhav Hiremath err_populate: 3716743a6fdSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 3726743a6fdSVaibhav Hiremath err_coldboot: 3736743a6fdSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 3746743a6fdSVaibhav Hiremath return ret; 3757fa60654SVaibhav Hiremath } 3767fa60654SVaibhav Hiremath 377bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 378bc142bbbSVaibhav Hiremath { 379bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 380bc142bbbSVaibhav Hiremath 381bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 382bc142bbbSVaibhav Hiremath 383bc142bbbSVaibhav Hiremath return 0; 384bc142bbbSVaibhav Hiremath } 385bc142bbbSVaibhav Hiremath 3867fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 3877fa60654SVaibhav Hiremath { 3887fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 3897fa60654SVaibhav Hiremath 3902923c58eSVaibhav Hiremath device_remove_file(&pdev->dev, &dev_attr_state); 39149e6e04bSVaibhav Hiremath cancel_delayed_work_sync(&arche_pdata->delayed_work); 392bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 3935993e2bfSVaibhav Hiremath arche_platform_poweroff_seq(arche_pdata); 3947fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 3957fa60654SVaibhav Hiremath 396ad4d3f95SVaibhav Hiremath if (usb3613_hub_mode_ctrl(false)) 397ad4d3f95SVaibhav Hiremath dev_warn(arche_pdata->dev, "failed to control hub device\n"); 398ad4d3f95SVaibhav Hiremath /* TODO: Should we do anything more here ?? */ 3997fa60654SVaibhav Hiremath return 0; 4007fa60654SVaibhav Hiremath } 4017fa60654SVaibhav Hiremath 4027fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 4037fa60654SVaibhav Hiremath { 4047fa60654SVaibhav Hiremath /* 4057fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 4067fa60654SVaibhav Hiremath * completely 4077fa60654SVaibhav Hiremath * 4087fa60654SVaibhav Hiremath * TODO: sequence ?? 4097fa60654SVaibhav Hiremath * 4107fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 4117fa60654SVaibhav Hiremath * Precondition: Definition ??? 4127fa60654SVaibhav Hiremath */ 4137fa60654SVaibhav Hiremath return 0; 4147fa60654SVaibhav Hiremath } 4157fa60654SVaibhav Hiremath 4167fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 4177fa60654SVaibhav Hiremath { 4187fa60654SVaibhav Hiremath /* 4197fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 4207fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 4217fa60654SVaibhav Hiremath * OFF state or standby state. 4227fa60654SVaibhav Hiremath * 4237fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 4247fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 4257fa60654SVaibhav Hiremath * 4267fa60654SVaibhav Hiremath */ 4277fa60654SVaibhav Hiremath return 0; 4287fa60654SVaibhav Hiremath } 4297fa60654SVaibhav Hiremath 4307fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 4317fa60654SVaibhav Hiremath arche_platform_suspend, 4327fa60654SVaibhav Hiremath arche_platform_resume); 4337fa60654SVaibhav Hiremath 4347fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 4357fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 4367fa60654SVaibhav Hiremath { }, 4377fa60654SVaibhav Hiremath }; 4381e5dd1f8SGreg Kroah-Hartman 4391e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 4401e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 4411e5dd1f8SGreg Kroah-Hartman { }, 4421e5dd1f8SGreg Kroah-Hartman }; 4431e5dd1f8SGreg Kroah-Hartman 4441e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 4451e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 4461e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 4471e5dd1f8SGreg Kroah-Hartman { }, 4481e5dd1f8SGreg Kroah-Hartman }; 4491e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 4507fa60654SVaibhav Hiremath 4517fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 4527fa60654SVaibhav Hiremath .probe = arche_platform_probe, 4537fa60654SVaibhav Hiremath .remove = arche_platform_remove, 4547fa60654SVaibhav Hiremath .driver = { 4557fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 4567fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 4571e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 4587fa60654SVaibhav Hiremath } 4597fa60654SVaibhav Hiremath }; 4607fa60654SVaibhav Hiremath 4611e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 4621e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 4631e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 4641e5dd1f8SGreg Kroah-Hartman .driver = { 4651e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 4661e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 4671e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 4681e5dd1f8SGreg Kroah-Hartman } 4691e5dd1f8SGreg Kroah-Hartman }; 4701e5dd1f8SGreg Kroah-Hartman 4711e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 4721e5dd1f8SGreg Kroah-Hartman { 4731e5dd1f8SGreg Kroah-Hartman int retval; 4741e5dd1f8SGreg Kroah-Hartman 4751e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 4761e5dd1f8SGreg Kroah-Hartman if (retval) 4771e5dd1f8SGreg Kroah-Hartman return retval; 4781e5dd1f8SGreg Kroah-Hartman 4791e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 4801e5dd1f8SGreg Kroah-Hartman if (retval) 4811e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 4821e5dd1f8SGreg Kroah-Hartman 4831e5dd1f8SGreg Kroah-Hartman return retval; 4841e5dd1f8SGreg Kroah-Hartman } 4851e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 4861e5dd1f8SGreg Kroah-Hartman 4871e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 4881e5dd1f8SGreg Kroah-Hartman { 4891e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 4901e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 4911e5dd1f8SGreg Kroah-Hartman } 4921e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 4937fa60654SVaibhav Hiremath 4947fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 4957fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 4967fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 497