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>
20f760bbfbSVaibhav Hiremath #include <linux/interrupt.h>
21f760bbfbSVaibhav Hiremath #include <linux/irq.h>
22f760bbfbSVaibhav Hiremath #include <linux/time.h>
231e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h"
247fa60654SVaibhav Hiremath 
25ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
26ad4d3f95SVaibhav Hiremath 
27f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
28f760bbfbSVaibhav Hiremath 
29685353c1SVaibhav Hiremath enum svc_wakedetect_state {
30685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
31685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
32685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
33685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
34685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
35685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
36685353c1SVaibhav Hiremath };
37685353c1SVaibhav Hiremath 
387fa60654SVaibhav Hiremath struct arche_platform_drvdata {
397fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
407fa60654SVaibhav Hiremath 	int svc_reset_gpio;
417fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
427fa60654SVaibhav Hiremath 	int svc_sysboot_gpio;
43a463fc15SVaibhav Hiremath 	int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
447fa60654SVaibhav Hiremath 
45e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
46e74d04a5SVaibhav Hiremath 
477fa60654SVaibhav Hiremath 	unsigned int svc_refclk_req;
487fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
497fa60654SVaibhav Hiremath 
507fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
517fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
527fa60654SVaibhav Hiremath 
537fa60654SVaibhav Hiremath 	int num_apbs;
54a463fc15SVaibhav Hiremath 
55a463fc15SVaibhav Hiremath 	struct delayed_work delayed_work;
56685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
57f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
58f760bbfbSVaibhav Hiremath 	spinlock_t lock;
59f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
60685353c1SVaibhav Hiremath 
61a463fc15SVaibhav Hiremath 	struct device *dev;
627fa60654SVaibhav Hiremath };
637fa60654SVaibhav Hiremath 
647fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
657fa60654SVaibhav Hiremath {
667fa60654SVaibhav Hiremath 	gpio_set_value(gpio, onoff);
677fa60654SVaibhav Hiremath }
687fa60654SVaibhav Hiremath 
69f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data)
70f760bbfbSVaibhav Hiremath {
71f760bbfbSVaibhav Hiremath 	int ret;
72f760bbfbSVaibhav Hiremath 
73f760bbfbSVaibhav Hiremath 	ret = apb_ctrl_coldboot(dev);
74f760bbfbSVaibhav Hiremath 	if (ret)
75f760bbfbSVaibhav Hiremath 		dev_warn(dev, "failed to coldboot\n");
76f760bbfbSVaibhav Hiremath 
77f760bbfbSVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
78f760bbfbSVaibhav Hiremath 	return 0;
79f760bbfbSVaibhav Hiremath }
80f760bbfbSVaibhav Hiremath 
81fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data)
82fd60ac58SVaibhav Hiremath {
83fd60ac58SVaibhav Hiremath 	int ret;
84fd60ac58SVaibhav Hiremath 
85fd60ac58SVaibhav Hiremath 	ret = apb_ctrl_fw_flashing(dev);
86fd60ac58SVaibhav Hiremath 	if (ret)
87fd60ac58SVaibhav Hiremath 		dev_warn(dev, "failed to switch to fw flashing state\n");
88fd60ac58SVaibhav Hiremath 
89fd60ac58SVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
90fd60ac58SVaibhav Hiremath 	return 0;
91fd60ac58SVaibhav Hiremath }
92fd60ac58SVaibhav Hiremath 
93fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data)
94fd60ac58SVaibhav Hiremath {
95fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
96fd60ac58SVaibhav Hiremath 
97fd60ac58SVaibhav Hiremath 	return 0;
98fd60ac58SVaibhav Hiremath }
99fd60ac58SVaibhav Hiremath 
100a463fc15SVaibhav Hiremath /**
101db5a3bcaSVaibhav Hiremath  * hub_conf_delayed_work - Configures USB3613 device to HUB mode
102db5a3bcaSVaibhav Hiremath  *
103db5a3bcaSVaibhav Hiremath  * The idea here is to split the APB coldboot operation with slow HUB configuration,
104db5a3bcaSVaibhav Hiremath  * so that driver response to wake/detect event can be met.
105db5a3bcaSVaibhav Hiremath  * So expectation is, once code reaches here, means initial unipro linkup
106db5a3bcaSVaibhav Hiremath  * between APB<->Switch was successful, so now just take it to AP.
107a463fc15SVaibhav Hiremath  */
108db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work)
109a463fc15SVaibhav Hiremath {
110a463fc15SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata =
111a463fc15SVaibhav Hiremath 		container_of(work, struct arche_platform_drvdata, delayed_work.work);
112ad4d3f95SVaibhav Hiremath 
113ad4d3f95SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
114ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
115ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
116a463fc15SVaibhav Hiremath }
117a463fc15SVaibhav Hiremath 
118f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
119f760bbfbSVaibhav Hiremath {
120f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
121f760bbfbSVaibhav Hiremath 	unsigned long flags;
122f760bbfbSVaibhav Hiremath 
123f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
124f760bbfbSVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
125f760bbfbSVaibhav Hiremath 		/* Something is wrong */
126f760bbfbSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->lock, flags);
127f760bbfbSVaibhav Hiremath 		return IRQ_HANDLED;
128f760bbfbSVaibhav Hiremath 	}
129f760bbfbSVaibhav Hiremath 
130f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START;
131f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
132f760bbfbSVaibhav Hiremath 
133f760bbfbSVaibhav Hiremath 	/* Bring APB out of reset: cold boot sequence */
134f760bbfbSVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
135f760bbfbSVaibhav Hiremath 
136f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
137f760bbfbSVaibhav Hiremath 	/* USB HUB configuration */
138f760bbfbSVaibhav Hiremath 	schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
139f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_IDLE;
140f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
141f760bbfbSVaibhav Hiremath 
142f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
143f760bbfbSVaibhav Hiremath }
144f760bbfbSVaibhav Hiremath 
145f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
146f760bbfbSVaibhav Hiremath {
147f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
148f760bbfbSVaibhav Hiremath 	unsigned long flags;
149f760bbfbSVaibhav Hiremath 
150f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
151f760bbfbSVaibhav Hiremath 
152f760bbfbSVaibhav Hiremath 	if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
153f760bbfbSVaibhav Hiremath 		/* wake/detect rising */
154f760bbfbSVaibhav Hiremath 
155f760bbfbSVaibhav Hiremath 		/*
156f760bbfbSVaibhav Hiremath 		 * If wake/detect line goes high after low, within less than
157f760bbfbSVaibhav Hiremath 		 * 30msec, then standby boot sequence is initiated, which is not
158f760bbfbSVaibhav Hiremath 		 * supported/implemented as of now. So ignore it.
159f760bbfbSVaibhav Hiremath 		 */
160f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
161f760bbfbSVaibhav Hiremath 			if (time_before(jiffies,
162f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_start +
163f760bbfbSVaibhav Hiremath 					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
164f760bbfbSVaibhav Hiremath 				/* No harm with cancellation, even if not pending */
165f760bbfbSVaibhav Hiremath 				cancel_delayed_work(&arche_pdata->delayed_work);
166f760bbfbSVaibhav Hiremath 				arche_pdata->wake_detect_state = WD_STATE_IDLE;
167f760bbfbSVaibhav Hiremath 			} else {
168f760bbfbSVaibhav Hiremath 				/* Check we are not in middle of irq thread already */
169f760bbfbSVaibhav Hiremath 				if (arche_pdata->wake_detect_state !=
170f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_START) {
171f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_state =
172f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_TRIG;
173f760bbfbSVaibhav Hiremath 					spin_unlock_irqrestore(&arche_pdata->lock, flags);
174f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
175f760bbfbSVaibhav Hiremath 				}
176f760bbfbSVaibhav Hiremath 			}
177f760bbfbSVaibhav Hiremath 		}
178f760bbfbSVaibhav Hiremath 	} else {
179f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
180f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
181f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
182f760bbfbSVaibhav Hiremath 			/* No harm with cancellation even if it is not pending*/
183f760bbfbSVaibhav Hiremath 			cancel_delayed_work(&arche_pdata->delayed_work);
184f760bbfbSVaibhav Hiremath 			/*
185f760bbfbSVaibhav Hiremath 			 * In the begining, when wake/detect goes low (first time), we assume
186f760bbfbSVaibhav Hiremath 			 * it is meant for coldboot and set the flag. If wake/detect line stays low
187f760bbfbSVaibhav Hiremath 			 * beyond 30msec, then it is coldboot else fallback to standby boot.
188f760bbfbSVaibhav Hiremath 			 */
189f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT;
190f760bbfbSVaibhav Hiremath 		}
191f760bbfbSVaibhav Hiremath 	}
192f760bbfbSVaibhav Hiremath 
193f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
194f760bbfbSVaibhav Hiremath 
195f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
196f760bbfbSVaibhav Hiremath }
197f760bbfbSVaibhav Hiremath 
198758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
199758ca99dSVaibhav Hiremath {
200758ca99dSVaibhav Hiremath 	int ret;
201758ca99dSVaibhav Hiremath 
202599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
203599159b6SVaibhav Hiremath 		return 0;
204599159b6SVaibhav Hiremath 
205758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
206758ca99dSVaibhav Hiremath 
207758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
208758ca99dSVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
209758ca99dSVaibhav Hiremath 
210758ca99dSVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
211758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
212758ca99dSVaibhav Hiremath 
213758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
214758ca99dSVaibhav Hiremath 	if (ret) {
215758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
216758ca99dSVaibhav Hiremath 				ret);
217758ca99dSVaibhav Hiremath 		return ret;
218758ca99dSVaibhav Hiremath 	}
219758ca99dSVaibhav Hiremath 
220758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
221758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
222758ca99dSVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
223758ca99dSVaibhav Hiremath 
224e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
225e74d04a5SVaibhav Hiremath 
226758ca99dSVaibhav Hiremath 	return 0;
227758ca99dSVaibhav Hiremath }
228758ca99dSVaibhav Hiremath 
2297691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
2307691fed2SVaibhav Hiremath {
231599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
232599159b6SVaibhav Hiremath 		return;
233599159b6SVaibhav Hiremath 
2347691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
2357691fed2SVaibhav Hiremath 
2367691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2377691fed2SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
2387691fed2SVaibhav Hiremath 
2397691fed2SVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
2407691fed2SVaibhav Hiremath 
2417691fed2SVaibhav Hiremath 	usleep_range(100, 200);
2427691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2437691fed2SVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
2447691fed2SVaibhav Hiremath 
2457691fed2SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
2467691fed2SVaibhav Hiremath 
2477691fed2SVaibhav Hiremath }
2487691fed2SVaibhav Hiremath 
2495993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
2507fa60654SVaibhav Hiremath {
251f760bbfbSVaibhav Hiremath 	unsigned long flags;
252f760bbfbSVaibhav Hiremath 
253599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
254599159b6SVaibhav Hiremath 		return;
255599159b6SVaibhav Hiremath 
25625847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
25725847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
258b4c95fcaSVaibhav Hiremath 		/* Send disconnect/detach event to SVC */
259b4c95fcaSVaibhav Hiremath 		gpio_set_value(arche_pdata->wake_detect_gpio, 0);
260b4c95fcaSVaibhav Hiremath 		usleep_range(100, 200);
261f760bbfbSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->lock, flags);
262685353c1SVaibhav Hiremath 		arche_pdata->wake_detect_state = WD_STATE_IDLE;
263f760bbfbSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->lock, flags);
264b4c95fcaSVaibhav Hiremath 
265d8b16338SVaibhav Hiremath 		clk_disable_unprepare(arche_pdata->svc_ref_clk);
26625847ee7SVaibhav Hiremath 	}
26725847ee7SVaibhav Hiremath 
2687fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
2697fa60654SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2707fa60654SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
271e74d04a5SVaibhav Hiremath 
272e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
2737fa60654SVaibhav Hiremath }
2747fa60654SVaibhav Hiremath 
2752923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
2762923c58eSVaibhav Hiremath 		struct device_attribute *attr, const char *buf, size_t count)
2772923c58eSVaibhav Hiremath {
2782923c58eSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
2792923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
2802923c58eSVaibhav Hiremath 	int ret = 0;
2812923c58eSVaibhav Hiremath 
2822923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
2832923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
2842923c58eSVaibhav Hiremath 			return count;
2852923c58eSVaibhav Hiremath 
286fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
287fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
288fd60ac58SVaibhav Hiremath 
2892923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
290ad4d3f95SVaibhav Hiremath 
291ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
292ad4d3f95SVaibhav Hiremath 		if (ret)
293ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
294ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
2952923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
2962923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
2972923c58eSVaibhav Hiremath 			return count;
2982923c58eSVaibhav Hiremath 
2992923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
3002923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
3012923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
3022923c58eSVaibhav Hiremath 			return count;
3032923c58eSVaibhav Hiremath 
3042923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
3057691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
3067691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
3077691fed2SVaibhav Hiremath 			return count;
3087691fed2SVaibhav Hiremath 
3097691fed2SVaibhav Hiremath 		/* First we want to make sure we power off everything
3107691fed2SVaibhav Hiremath 		 * and then enter FW flashing state */
311fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
312fd60ac58SVaibhav Hiremath 
3137691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
314fd60ac58SVaibhav Hiremath 
315ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
316ad4d3f95SVaibhav Hiremath 		if (ret)
317ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
318ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
319ad4d3f95SVaibhav Hiremath 
3207691fed2SVaibhav Hiremath 		arche_platform_fw_flashing_seq(arche_pdata);
321fd60ac58SVaibhav Hiremath 
322fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
3232923c58eSVaibhav Hiremath 	} else {
3242923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
3252923c58eSVaibhav Hiremath 		ret = -EINVAL;
3262923c58eSVaibhav Hiremath 	}
3272923c58eSVaibhav Hiremath 
3282923c58eSVaibhav Hiremath 	return ret ? ret : count;
3292923c58eSVaibhav Hiremath }
3302923c58eSVaibhav Hiremath 
3312923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
3322923c58eSVaibhav Hiremath 		struct device_attribute *attr, char *buf)
3332923c58eSVaibhav Hiremath {
3342923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3352923c58eSVaibhav Hiremath 
3362923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
3372923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
3382923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
3392923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
3402923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
3412923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
3422923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
3437691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
3447691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
3452923c58eSVaibhav Hiremath 	default:
3462923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
3472923c58eSVaibhav Hiremath 	}
3482923c58eSVaibhav Hiremath }
3492923c58eSVaibhav Hiremath 
3502923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
3512923c58eSVaibhav Hiremath 
3527fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
3537fa60654SVaibhav Hiremath {
3547fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
3557fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
3567fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
3577fa60654SVaibhav Hiremath 	int ret;
3587fa60654SVaibhav Hiremath 
3597fa60654SVaibhav Hiremath 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
3607fa60654SVaibhav Hiremath 	if (!arche_pdata)
3617fa60654SVaibhav Hiremath 		return -ENOMEM;
3627fa60654SVaibhav Hiremath 
3637fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
3647fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
3657fa60654SVaibhav Hiremath 					"svc,reset-active-high");
3667fa60654SVaibhav Hiremath 	arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
3677fa60654SVaibhav Hiremath 	if (arche_pdata->svc_reset_gpio < 0) {
3687fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get reset-gpio\n");
369f1f251b5SViresh Kumar 		return arche_pdata->svc_reset_gpio;
3707fa60654SVaibhav Hiremath 	}
3717fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
3727fa60654SVaibhav Hiremath 	if (ret) {
3737fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
3747fa60654SVaibhav Hiremath 		return ret;
3757fa60654SVaibhav Hiremath 	}
3767fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
3777fa60654SVaibhav Hiremath 					arche_pdata->is_reset_act_hi);
3787fa60654SVaibhav Hiremath 	if (ret) {
3797fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
3807fa60654SVaibhav Hiremath 		return ret;
3817fa60654SVaibhav Hiremath 	}
382e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
3837fa60654SVaibhav Hiremath 
3847fa60654SVaibhav Hiremath 	arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
3857fa60654SVaibhav Hiremath 					"svc,sysboot-gpio", 0);
3867fa60654SVaibhav Hiremath 	if (arche_pdata->svc_sysboot_gpio < 0) {
3877fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get sysboot gpio\n");
388f1f251b5SViresh Kumar 		return arche_pdata->svc_sysboot_gpio;
3897fa60654SVaibhav Hiremath 	}
3907fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
3917fa60654SVaibhav Hiremath 	if (ret) {
3927fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
3937fa60654SVaibhav Hiremath 		return ret;
3947fa60654SVaibhav Hiremath 	}
3957fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
3967fa60654SVaibhav Hiremath 	if (ret) {
3977fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
3987fa60654SVaibhav Hiremath 		return ret;
3997fa60654SVaibhav Hiremath 	}
4007fa60654SVaibhav Hiremath 
4017fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
4027fa60654SVaibhav Hiremath 	arche_pdata->svc_refclk_req = of_get_named_gpio(np,
4037fa60654SVaibhav Hiremath 					"svc,refclk-req-gpio", 0);
4047fa60654SVaibhav Hiremath 	if (arche_pdata->svc_refclk_req < 0) {
4057fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc clock-req gpio\n");
406f1f251b5SViresh Kumar 		return arche_pdata->svc_refclk_req;
4077fa60654SVaibhav Hiremath 	}
4087fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
4097fa60654SVaibhav Hiremath 	if (ret) {
4107fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
4117fa60654SVaibhav Hiremath 		return ret;
4127fa60654SVaibhav Hiremath 	}
4137fa60654SVaibhav Hiremath 	ret = gpio_direction_input(arche_pdata->svc_refclk_req);
4147fa60654SVaibhav Hiremath 	if (ret) {
4157fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
4167fa60654SVaibhav Hiremath 		return ret;
4177fa60654SVaibhav Hiremath 	}
4187fa60654SVaibhav Hiremath 
4197fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
4207fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
4217fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
4227fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
4237fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
4247fa60654SVaibhav Hiremath 		return ret;
4257fa60654SVaibhav Hiremath 	}
4267fa60654SVaibhav Hiremath 
4277fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
4287fa60654SVaibhav Hiremath 
4297fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
4307fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
4317fa60654SVaibhav Hiremath 
432a463fc15SVaibhav Hiremath 	arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
433a463fc15SVaibhav Hiremath 	if (arche_pdata->wake_detect_gpio < 0) {
434a463fc15SVaibhav Hiremath 		dev_err(dev, "failed to get wake detect gpio\n");
435a463fc15SVaibhav Hiremath 		ret = arche_pdata->wake_detect_gpio;
436758ca99dSVaibhav Hiremath 		return ret;
43772a8c24bSViresh Kumar 	}
4387fa60654SVaibhav Hiremath 
439a463fc15SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
440a463fc15SVaibhav Hiremath 	if (ret) {
441a463fc15SVaibhav Hiremath 		dev_err(dev, "Failed requesting wake_detect gpio %d\n",
442a463fc15SVaibhav Hiremath 				arche_pdata->wake_detect_gpio);
443758ca99dSVaibhav Hiremath 		return ret;
444a463fc15SVaibhav Hiremath 	}
445057aad29SMichael Scott 	/* deassert wake detect */
446057aad29SMichael Scott 	gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
447685353c1SVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_IDLE;
448a463fc15SVaibhav Hiremath 
449a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
450a463fc15SVaibhav Hiremath 
451f760bbfbSVaibhav Hiremath 	spin_lock_init(&arche_pdata->lock);
452f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
453f760bbfbSVaibhav Hiremath 		gpio_to_irq(arche_pdata->wake_detect_gpio);
454f760bbfbSVaibhav Hiremath 
455f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
456f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq,
457f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq_thread,
458f760bbfbSVaibhav Hiremath 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
459f760bbfbSVaibhav Hiremath 			dev_name(dev), arche_pdata);
460f760bbfbSVaibhav Hiremath 	if (ret) {
461f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
462f760bbfbSVaibhav Hiremath 		return ret;
463f760bbfbSVaibhav Hiremath 	}
464f760bbfbSVaibhav Hiremath 	/* Enable it only after  sending wake/detect event */
465f760bbfbSVaibhav Hiremath 	disable_irq(arche_pdata->wake_detect_irq);
466f760bbfbSVaibhav Hiremath 
4672923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
4682923c58eSVaibhav Hiremath 	if (ret) {
4692923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
4702923c58eSVaibhav Hiremath 		return ret;
4712923c58eSVaibhav Hiremath 	}
4722923c58eSVaibhav Hiremath 
473758ca99dSVaibhav Hiremath 	ret = arche_platform_coldboot_seq(arche_pdata);
474758ca99dSVaibhav Hiremath 	if (ret) {
475758ca99dSVaibhav Hiremath 		dev_err(dev, "Failed to cold boot svc %d\n", ret);
4766743a6fdSVaibhav Hiremath 		goto err_coldboot;
477758ca99dSVaibhav Hiremath 	}
478758ca99dSVaibhav Hiremath 
479fd60ac58SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
480fd60ac58SVaibhav Hiremath 	if (ret) {
481fd60ac58SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
4826743a6fdSVaibhav Hiremath 		goto err_populate;
483fd60ac58SVaibhav Hiremath 	}
484fd60ac58SVaibhav Hiremath 
485db5a3bcaSVaibhav Hiremath 	INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work);
486fd60ac58SVaibhav Hiremath 
4877fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
48872a8c24bSViresh Kumar 	return 0;
4896743a6fdSVaibhav Hiremath 
4906743a6fdSVaibhav Hiremath err_populate:
4916743a6fdSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
4926743a6fdSVaibhav Hiremath err_coldboot:
4936743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
4946743a6fdSVaibhav Hiremath 	return ret;
4957fa60654SVaibhav Hiremath }
4967fa60654SVaibhav Hiremath 
497bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
498bc142bbbSVaibhav Hiremath {
499bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
500bc142bbbSVaibhav Hiremath 
501bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
502bc142bbbSVaibhav Hiremath 
503bc142bbbSVaibhav Hiremath 	return 0;
504bc142bbbSVaibhav Hiremath }
505bc142bbbSVaibhav Hiremath 
5067fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
5077fa60654SVaibhav Hiremath {
5087fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
5097fa60654SVaibhav Hiremath 
5102923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
51149e6e04bSVaibhav Hiremath 	cancel_delayed_work_sync(&arche_pdata->delayed_work);
512bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
5135993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
5147fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, NULL);
5157fa60654SVaibhav Hiremath 
516ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
517ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
518ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
5197fa60654SVaibhav Hiremath 	return 0;
5207fa60654SVaibhav Hiremath }
5217fa60654SVaibhav Hiremath 
5227fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev)
5237fa60654SVaibhav Hiremath {
5247fa60654SVaibhav Hiremath 	/*
5257fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
5267fa60654SVaibhav Hiremath 	 * completely
5277fa60654SVaibhav Hiremath 	 *
5287fa60654SVaibhav Hiremath 	 * TODO: sequence ??
5297fa60654SVaibhav Hiremath 	 *
5307fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
5317fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
5327fa60654SVaibhav Hiremath 	 */
5337fa60654SVaibhav Hiremath 	return 0;
5347fa60654SVaibhav Hiremath }
5357fa60654SVaibhav Hiremath 
5367fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev)
5377fa60654SVaibhav Hiremath {
5387fa60654SVaibhav Hiremath 	/*
5397fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
5407fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
5417fa60654SVaibhav Hiremath 	 * OFF state or standby state.
5427fa60654SVaibhav Hiremath 	 *
5437fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
5447fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
5457fa60654SVaibhav Hiremath 	 *
5467fa60654SVaibhav Hiremath 	 */
5477fa60654SVaibhav Hiremath 	return 0;
5487fa60654SVaibhav Hiremath }
5497fa60654SVaibhav Hiremath 
5507fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
5517fa60654SVaibhav Hiremath 			arche_platform_suspend,
5527fa60654SVaibhav Hiremath 			arche_platform_resume);
5537fa60654SVaibhav Hiremath 
5547fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = {
5557fa60654SVaibhav Hiremath 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
5567fa60654SVaibhav Hiremath 	{ },
5577fa60654SVaibhav Hiremath };
5581e5dd1f8SGreg Kroah-Hartman 
5591e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = {
5601e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
5611e5dd1f8SGreg Kroah-Hartman 	{ },
5621e5dd1f8SGreg Kroah-Hartman };
5631e5dd1f8SGreg Kroah-Hartman 
5641e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = {
5651e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
5661e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
5671e5dd1f8SGreg Kroah-Hartman 	{ },
5681e5dd1f8SGreg Kroah-Hartman };
5691e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
5707fa60654SVaibhav Hiremath 
5717fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
5727fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
5737fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
5747fa60654SVaibhav Hiremath 	.driver		= {
5757fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
5767fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
5771e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
5787fa60654SVaibhav Hiremath 	}
5797fa60654SVaibhav Hiremath };
5807fa60654SVaibhav Hiremath 
5811e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = {
5821e5dd1f8SGreg Kroah-Hartman 	.probe		= arche_apb_ctrl_probe,
5831e5dd1f8SGreg Kroah-Hartman 	.remove		= arche_apb_ctrl_remove,
5841e5dd1f8SGreg Kroah-Hartman 	.driver		= {
5851e5dd1f8SGreg Kroah-Hartman 		.name	= "arche-apb-ctrl",
5861e5dd1f8SGreg Kroah-Hartman 		.pm	= &arche_apb_ctrl_pm_ops,
5871e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_apb_ctrl_of_match,
5881e5dd1f8SGreg Kroah-Hartman 	}
5891e5dd1f8SGreg Kroah-Hartman };
5901e5dd1f8SGreg Kroah-Hartman 
5911e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
5921e5dd1f8SGreg Kroah-Hartman {
5931e5dd1f8SGreg Kroah-Hartman 	int retval;
5941e5dd1f8SGreg Kroah-Hartman 
5951e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
5961e5dd1f8SGreg Kroah-Hartman 	if (retval)
5971e5dd1f8SGreg Kroah-Hartman 		return retval;
5981e5dd1f8SGreg Kroah-Hartman 
5991e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_apb_ctrl_device_driver);
6001e5dd1f8SGreg Kroah-Hartman 	if (retval)
6011e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
6021e5dd1f8SGreg Kroah-Hartman 
6031e5dd1f8SGreg Kroah-Hartman 	return retval;
6041e5dd1f8SGreg Kroah-Hartman }
6051e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
6061e5dd1f8SGreg Kroah-Hartman 
6071e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
6081e5dd1f8SGreg Kroah-Hartman {
6091e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_apb_ctrl_device_driver);
6101e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
6111e5dd1f8SGreg Kroah-Hartman }
6121e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
6137fa60654SVaibhav Hiremath 
6147fa60654SVaibhav Hiremath MODULE_LICENSE("GPL");
6157fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
6167fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
617