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_fw_flashing_state(struct device *dev, void *data)
51fd60ac58SVaibhav Hiremath {
52fd60ac58SVaibhav Hiremath 	int ret;
53fd60ac58SVaibhav Hiremath 
54fd60ac58SVaibhav Hiremath 	ret = apb_ctrl_fw_flashing(dev);
55fd60ac58SVaibhav Hiremath 	if (ret)
56fd60ac58SVaibhav Hiremath 		dev_warn(dev, "failed to switch to fw flashing state\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_poweroff(struct device *dev, void *data)
63fd60ac58SVaibhav Hiremath {
64fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
65fd60ac58SVaibhav Hiremath 
66fd60ac58SVaibhav Hiremath 	return 0;
67fd60ac58SVaibhav Hiremath }
68fd60ac58SVaibhav Hiremath 
69a463fc15SVaibhav Hiremath /**
70db5a3bcaSVaibhav Hiremath  * hub_conf_delayed_work - Configures USB3613 device to HUB mode
71db5a3bcaSVaibhav Hiremath  *
72db5a3bcaSVaibhav Hiremath  * The idea here is to split the APB coldboot operation with slow HUB configuration,
73db5a3bcaSVaibhav Hiremath  * so that driver response to wake/detect event can be met.
74db5a3bcaSVaibhav Hiremath  * So expectation is, once code reaches here, means initial unipro linkup
75db5a3bcaSVaibhav Hiremath  * between APB<->Switch was successful, so now just take it to AP.
76a463fc15SVaibhav Hiremath  */
77db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work)
78a463fc15SVaibhav Hiremath {
79a463fc15SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata =
80a463fc15SVaibhav Hiremath 		container_of(work, struct arche_platform_drvdata, delayed_work.work);
81ad4d3f95SVaibhav Hiremath 
82ad4d3f95SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
83ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
84ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
85a463fc15SVaibhav Hiremath }
86a463fc15SVaibhav Hiremath 
87758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
88758ca99dSVaibhav Hiremath {
89758ca99dSVaibhav Hiremath 	int ret;
90758ca99dSVaibhav Hiremath 
91599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
92599159b6SVaibhav Hiremath 		return 0;
93599159b6SVaibhav Hiremath 
94758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
95758ca99dSVaibhav Hiremath 
96758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
97758ca99dSVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
98758ca99dSVaibhav Hiremath 
99758ca99dSVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
100758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
101758ca99dSVaibhav Hiremath 
102758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
103758ca99dSVaibhav Hiremath 	if (ret) {
104758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
105758ca99dSVaibhav Hiremath 				ret);
106758ca99dSVaibhav Hiremath 		return ret;
107758ca99dSVaibhav Hiremath 	}
108758ca99dSVaibhav Hiremath 
109758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
110758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
111758ca99dSVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
112758ca99dSVaibhav Hiremath 
113e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
114e74d04a5SVaibhav Hiremath 
115758ca99dSVaibhav Hiremath 	return 0;
116758ca99dSVaibhav Hiremath }
117758ca99dSVaibhav Hiremath 
1187691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
1197691fed2SVaibhav Hiremath {
120599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
121599159b6SVaibhav Hiremath 		return;
122599159b6SVaibhav Hiremath 
1237691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
1247691fed2SVaibhav Hiremath 
1257691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
1267691fed2SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
1277691fed2SVaibhav Hiremath 
1287691fed2SVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
1297691fed2SVaibhav Hiremath 
1307691fed2SVaibhav Hiremath 	usleep_range(100, 200);
1317691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
1327691fed2SVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
1337691fed2SVaibhav Hiremath 
1347691fed2SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
1357691fed2SVaibhav Hiremath 
1367691fed2SVaibhav Hiremath }
1377691fed2SVaibhav Hiremath 
1385993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
1397fa60654SVaibhav Hiremath {
140599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
141599159b6SVaibhav Hiremath 		return;
142599159b6SVaibhav Hiremath 
14325847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
14425847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
145b4c95fcaSVaibhav Hiremath 		/* Send disconnect/detach event to SVC */
146b4c95fcaSVaibhav Hiremath 		gpio_set_value(arche_pdata->wake_detect_gpio, 0);
147b4c95fcaSVaibhav Hiremath 		usleep_range(100, 200);
148b4c95fcaSVaibhav Hiremath 
149d8b16338SVaibhav Hiremath 		clk_disable_unprepare(arche_pdata->svc_ref_clk);
15025847ee7SVaibhav Hiremath 	}
15125847ee7SVaibhav Hiremath 
1527fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
1537fa60654SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
1547fa60654SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
155e74d04a5SVaibhav Hiremath 
156e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
1577fa60654SVaibhav Hiremath }
1587fa60654SVaibhav Hiremath 
1592923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
1602923c58eSVaibhav Hiremath 		struct device_attribute *attr, const char *buf, size_t count)
1612923c58eSVaibhav Hiremath {
1622923c58eSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
1632923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
1642923c58eSVaibhav Hiremath 	int ret = 0;
1652923c58eSVaibhav Hiremath 
1662923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
1672923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
1682923c58eSVaibhav Hiremath 			return count;
1692923c58eSVaibhav Hiremath 
170fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
171fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
172fd60ac58SVaibhav Hiremath 
1732923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
174ad4d3f95SVaibhav Hiremath 
175ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
176ad4d3f95SVaibhav Hiremath 		if (ret)
177ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
178ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
1792923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
1802923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
1812923c58eSVaibhav Hiremath 			return count;
1822923c58eSVaibhav Hiremath 
1832923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
1842923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
1852923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
1862923c58eSVaibhav Hiremath 			return count;
1872923c58eSVaibhav Hiremath 
1882923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
1897691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
1907691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
1917691fed2SVaibhav Hiremath 			return count;
1927691fed2SVaibhav Hiremath 
1937691fed2SVaibhav Hiremath 		/* First we want to make sure we power off everything
1947691fed2SVaibhav Hiremath 		 * and then enter FW flashing state */
195fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
196fd60ac58SVaibhav Hiremath 
1977691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
198fd60ac58SVaibhav Hiremath 
199ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
200ad4d3f95SVaibhav Hiremath 		if (ret)
201ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
202ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
203ad4d3f95SVaibhav Hiremath 
2047691fed2SVaibhav Hiremath 		arche_platform_fw_flashing_seq(arche_pdata);
205fd60ac58SVaibhav Hiremath 
206fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
2072923c58eSVaibhav Hiremath 	} else {
2082923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
2092923c58eSVaibhav Hiremath 		ret = -EINVAL;
2102923c58eSVaibhav Hiremath 	}
2112923c58eSVaibhav Hiremath 
2122923c58eSVaibhav Hiremath 	return ret ? ret : count;
2132923c58eSVaibhav Hiremath }
2142923c58eSVaibhav Hiremath 
2152923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
2162923c58eSVaibhav Hiremath 		struct device_attribute *attr, char *buf)
2172923c58eSVaibhav Hiremath {
2182923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
2192923c58eSVaibhav Hiremath 
2202923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
2212923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
2222923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
2232923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
2242923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
2252923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
2262923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
2277691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
2287691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
2292923c58eSVaibhav Hiremath 	default:
2302923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
2312923c58eSVaibhav Hiremath 	}
2322923c58eSVaibhav Hiremath }
2332923c58eSVaibhav Hiremath 
2342923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
2352923c58eSVaibhav Hiremath 
2367fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
2377fa60654SVaibhav Hiremath {
2387fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
2397fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
2407fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
2417fa60654SVaibhav Hiremath 	int ret;
2427fa60654SVaibhav Hiremath 
2437fa60654SVaibhav Hiremath 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
2447fa60654SVaibhav Hiremath 	if (!arche_pdata)
2457fa60654SVaibhav Hiremath 		return -ENOMEM;
2467fa60654SVaibhav Hiremath 
2477fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
2487fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
2497fa60654SVaibhav Hiremath 					"svc,reset-active-high");
2507fa60654SVaibhav Hiremath 	arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
2517fa60654SVaibhav Hiremath 	if (arche_pdata->svc_reset_gpio < 0) {
2527fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get reset-gpio\n");
253f1f251b5SViresh Kumar 		return arche_pdata->svc_reset_gpio;
2547fa60654SVaibhav Hiremath 	}
2557fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
2567fa60654SVaibhav Hiremath 	if (ret) {
2577fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
2587fa60654SVaibhav Hiremath 		return ret;
2597fa60654SVaibhav Hiremath 	}
2607fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
2617fa60654SVaibhav Hiremath 					arche_pdata->is_reset_act_hi);
2627fa60654SVaibhav Hiremath 	if (ret) {
2637fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
2647fa60654SVaibhav Hiremath 		return ret;
2657fa60654SVaibhav Hiremath 	}
266e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
2677fa60654SVaibhav Hiremath 
2687fa60654SVaibhav Hiremath 	arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
2697fa60654SVaibhav Hiremath 					"svc,sysboot-gpio", 0);
2707fa60654SVaibhav Hiremath 	if (arche_pdata->svc_sysboot_gpio < 0) {
2717fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get sysboot gpio\n");
272f1f251b5SViresh Kumar 		return arche_pdata->svc_sysboot_gpio;
2737fa60654SVaibhav Hiremath 	}
2747fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
2757fa60654SVaibhav Hiremath 	if (ret) {
2767fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
2777fa60654SVaibhav Hiremath 		return ret;
2787fa60654SVaibhav Hiremath 	}
2797fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
2807fa60654SVaibhav Hiremath 	if (ret) {
2817fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
2827fa60654SVaibhav Hiremath 		return ret;
2837fa60654SVaibhav Hiremath 	}
2847fa60654SVaibhav Hiremath 
2857fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
2867fa60654SVaibhav Hiremath 	arche_pdata->svc_refclk_req = of_get_named_gpio(np,
2877fa60654SVaibhav Hiremath 					"svc,refclk-req-gpio", 0);
2887fa60654SVaibhav Hiremath 	if (arche_pdata->svc_refclk_req < 0) {
2897fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc clock-req gpio\n");
290f1f251b5SViresh Kumar 		return arche_pdata->svc_refclk_req;
2917fa60654SVaibhav Hiremath 	}
2927fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
2937fa60654SVaibhav Hiremath 	if (ret) {
2947fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
2957fa60654SVaibhav Hiremath 		return ret;
2967fa60654SVaibhav Hiremath 	}
2977fa60654SVaibhav Hiremath 	ret = gpio_direction_input(arche_pdata->svc_refclk_req);
2987fa60654SVaibhav Hiremath 	if (ret) {
2997fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
3007fa60654SVaibhav Hiremath 		return ret;
3017fa60654SVaibhav Hiremath 	}
3027fa60654SVaibhav Hiremath 
3037fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
3047fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
3057fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
3067fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
3077fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
3087fa60654SVaibhav Hiremath 		return ret;
3097fa60654SVaibhav Hiremath 	}
3107fa60654SVaibhav Hiremath 
3117fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
3127fa60654SVaibhav Hiremath 
3137fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
3147fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
3157fa60654SVaibhav Hiremath 
316a463fc15SVaibhav Hiremath 	arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
317a463fc15SVaibhav Hiremath 	if (arche_pdata->wake_detect_gpio < 0) {
318a463fc15SVaibhav Hiremath 		dev_err(dev, "failed to get wake detect gpio\n");
319a463fc15SVaibhav Hiremath 		ret = arche_pdata->wake_detect_gpio;
320758ca99dSVaibhav Hiremath 		return ret;
32172a8c24bSViresh Kumar 	}
3227fa60654SVaibhav Hiremath 
323a463fc15SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
324a463fc15SVaibhav Hiremath 	if (ret) {
325a463fc15SVaibhav Hiremath 		dev_err(dev, "Failed requesting wake_detect gpio %d\n",
326a463fc15SVaibhav Hiremath 				arche_pdata->wake_detect_gpio);
327758ca99dSVaibhav Hiremath 		return ret;
328a463fc15SVaibhav Hiremath 	}
329057aad29SMichael Scott 	/* deassert wake detect */
330057aad29SMichael Scott 	gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
331a463fc15SVaibhav Hiremath 
332a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
333a463fc15SVaibhav Hiremath 
3342923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
3352923c58eSVaibhav Hiremath 	if (ret) {
3362923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
3372923c58eSVaibhav Hiremath 		return ret;
3382923c58eSVaibhav Hiremath 	}
3392923c58eSVaibhav Hiremath 
340758ca99dSVaibhav Hiremath 	ret = arche_platform_coldboot_seq(arche_pdata);
341758ca99dSVaibhav Hiremath 	if (ret) {
342758ca99dSVaibhav Hiremath 		dev_err(dev, "Failed to cold boot svc %d\n", ret);
3436743a6fdSVaibhav Hiremath 		goto err_coldboot;
344758ca99dSVaibhav Hiremath 	}
345758ca99dSVaibhav Hiremath 
346fd60ac58SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
347fd60ac58SVaibhav Hiremath 	if (ret) {
348fd60ac58SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
3496743a6fdSVaibhav Hiremath 		goto err_populate;
350fd60ac58SVaibhav Hiremath 	}
351fd60ac58SVaibhav Hiremath 
352db5a3bcaSVaibhav Hiremath 	INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work);
353fd60ac58SVaibhav Hiremath 
3547fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
35572a8c24bSViresh Kumar 	return 0;
3566743a6fdSVaibhav Hiremath 
3576743a6fdSVaibhav Hiremath err_populate:
3586743a6fdSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
3596743a6fdSVaibhav Hiremath err_coldboot:
3606743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
3616743a6fdSVaibhav Hiremath 	return ret;
3627fa60654SVaibhav Hiremath }
3637fa60654SVaibhav Hiremath 
364bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
365bc142bbbSVaibhav Hiremath {
366bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
367bc142bbbSVaibhav Hiremath 
368bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
369bc142bbbSVaibhav Hiremath 
370bc142bbbSVaibhav Hiremath 	return 0;
371bc142bbbSVaibhav Hiremath }
372bc142bbbSVaibhav Hiremath 
3737fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
3747fa60654SVaibhav Hiremath {
3757fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
3767fa60654SVaibhav Hiremath 
3772923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
37849e6e04bSVaibhav Hiremath 	cancel_delayed_work_sync(&arche_pdata->delayed_work);
379bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
3805993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
3817fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, NULL);
3827fa60654SVaibhav Hiremath 
383ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
384ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
385ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
3867fa60654SVaibhav Hiremath 	return 0;
3877fa60654SVaibhav Hiremath }
3887fa60654SVaibhav Hiremath 
3897fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev)
3907fa60654SVaibhav Hiremath {
3917fa60654SVaibhav Hiremath 	/*
3927fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
3937fa60654SVaibhav Hiremath 	 * completely
3947fa60654SVaibhav Hiremath 	 *
3957fa60654SVaibhav Hiremath 	 * TODO: sequence ??
3967fa60654SVaibhav Hiremath 	 *
3977fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
3987fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
3997fa60654SVaibhav Hiremath 	 */
4007fa60654SVaibhav Hiremath 	return 0;
4017fa60654SVaibhav Hiremath }
4027fa60654SVaibhav Hiremath 
4037fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev)
4047fa60654SVaibhav Hiremath {
4057fa60654SVaibhav Hiremath 	/*
4067fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
4077fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
4087fa60654SVaibhav Hiremath 	 * OFF state or standby state.
4097fa60654SVaibhav Hiremath 	 *
4107fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
4117fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
4127fa60654SVaibhav Hiremath 	 *
4137fa60654SVaibhav Hiremath 	 */
4147fa60654SVaibhav Hiremath 	return 0;
4157fa60654SVaibhav Hiremath }
4167fa60654SVaibhav Hiremath 
4177fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
4187fa60654SVaibhav Hiremath 			arche_platform_suspend,
4197fa60654SVaibhav Hiremath 			arche_platform_resume);
4207fa60654SVaibhav Hiremath 
4217fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = {
4227fa60654SVaibhav Hiremath 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
4237fa60654SVaibhav Hiremath 	{ },
4247fa60654SVaibhav Hiremath };
4251e5dd1f8SGreg Kroah-Hartman 
4261e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = {
4271e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
4281e5dd1f8SGreg Kroah-Hartman 	{ },
4291e5dd1f8SGreg Kroah-Hartman };
4301e5dd1f8SGreg Kroah-Hartman 
4311e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = {
4321e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
4331e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
4341e5dd1f8SGreg Kroah-Hartman 	{ },
4351e5dd1f8SGreg Kroah-Hartman };
4361e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
4377fa60654SVaibhav Hiremath 
4387fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
4397fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
4407fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
4417fa60654SVaibhav Hiremath 	.driver		= {
4427fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
4437fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
4441e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
4457fa60654SVaibhav Hiremath 	}
4467fa60654SVaibhav Hiremath };
4477fa60654SVaibhav Hiremath 
4481e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = {
4491e5dd1f8SGreg Kroah-Hartman 	.probe		= arche_apb_ctrl_probe,
4501e5dd1f8SGreg Kroah-Hartman 	.remove		= arche_apb_ctrl_remove,
4511e5dd1f8SGreg Kroah-Hartman 	.driver		= {
4521e5dd1f8SGreg Kroah-Hartman 		.name	= "arche-apb-ctrl",
4531e5dd1f8SGreg Kroah-Hartman 		.pm	= &arche_apb_ctrl_pm_ops,
4541e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_apb_ctrl_of_match,
4551e5dd1f8SGreg Kroah-Hartman 	}
4561e5dd1f8SGreg Kroah-Hartman };
4571e5dd1f8SGreg Kroah-Hartman 
4581e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
4591e5dd1f8SGreg Kroah-Hartman {
4601e5dd1f8SGreg Kroah-Hartman 	int retval;
4611e5dd1f8SGreg Kroah-Hartman 
4621e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
4631e5dd1f8SGreg Kroah-Hartman 	if (retval)
4641e5dd1f8SGreg Kroah-Hartman 		return retval;
4651e5dd1f8SGreg Kroah-Hartman 
4661e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_apb_ctrl_device_driver);
4671e5dd1f8SGreg Kroah-Hartman 	if (retval)
4681e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
4691e5dd1f8SGreg Kroah-Hartman 
4701e5dd1f8SGreg Kroah-Hartman 	return retval;
4711e5dd1f8SGreg Kroah-Hartman }
4721e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
4731e5dd1f8SGreg Kroah-Hartman 
4741e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
4751e5dd1f8SGreg Kroah-Hartman {
4761e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_apb_ctrl_device_driver);
4771e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
4781e5dd1f8SGreg Kroah-Hartman }
4791e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
4807fa60654SVaibhav Hiremath 
4817fa60654SVaibhav Hiremath MODULE_LICENSE("GPL");
4827fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
4837fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
484