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