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>
229160b7c7SDavid Lin #include <linux/suspend.h>
23f760bbfbSVaibhav Hiremath #include <linux/time.h>
241e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h"
257fa60654SVaibhav Hiremath 
26ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
27ad4d3f95SVaibhav Hiremath 
28f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
29f760bbfbSVaibhav Hiremath 
30685353c1SVaibhav Hiremath enum svc_wakedetect_state {
31685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
32685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
33685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
34685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
35685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
36685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
37685353c1SVaibhav Hiremath };
38685353c1SVaibhav Hiremath 
397fa60654SVaibhav Hiremath struct arche_platform_drvdata {
407fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
417fa60654SVaibhav Hiremath 	int svc_reset_gpio;
427fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
437fa60654SVaibhav Hiremath 	int svc_sysboot_gpio;
44a463fc15SVaibhav Hiremath 	int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
457fa60654SVaibhav Hiremath 
46e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
47e74d04a5SVaibhav Hiremath 
487fe93014SDavid Lin 	int svc_refclk_req;
497fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
507fa60654SVaibhav Hiremath 
517fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
527fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
537fa60654SVaibhav Hiremath 
547fa60654SVaibhav Hiremath 	int num_apbs;
55a463fc15SVaibhav Hiremath 
56685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
57f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
586c1ca56dSVaibhav Hiremath 	spinlock_t wake_lock;			/* Protect wake_detect_state */
59886aba55SVaibhav Hiremath 	struct mutex platform_state_mutex;	/* Protect state */
60f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
619160b7c7SDavid Lin 	struct notifier_block pm_notifier;
62685353c1SVaibhav Hiremath 
63a463fc15SVaibhav Hiremath 	struct device *dev;
647fa60654SVaibhav Hiremath };
657fa60654SVaibhav Hiremath 
66c4058b79SBryan O'Donoghue /* Requires calling context to hold arche_pdata->spinlock */
67c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
68c4058b79SBryan O'Donoghue 				     enum arche_platform_state state)
69c4058b79SBryan O'Donoghue {
70c4058b79SBryan O'Donoghue 	arche_pdata->state = state;
71c4058b79SBryan O'Donoghue }
72c4058b79SBryan O'Donoghue 
73886aba55SVaibhav Hiremath /*
74886aba55SVaibhav Hiremath  * arche_platform_change_state: Change the operational state
75886aba55SVaibhav Hiremath  *
76886aba55SVaibhav Hiremath  * This exported function allows external drivers to change the state
77886aba55SVaibhav Hiremath  * of the arche-platform driver.
78886aba55SVaibhav Hiremath  * Note that this function only supports transitions between two states
79886aba55SVaibhav Hiremath  * with limited functionality.
80886aba55SVaibhav Hiremath  *
81886aba55SVaibhav Hiremath  *  - ARCHE_PLATFORM_STATE_TIME_SYNC:
82886aba55SVaibhav Hiremath  *    Once set, allows timesync operations between SVC <=> AP and makes
83886aba55SVaibhav Hiremath  *    sure that arche-platform driver ignores any subsequent events/pulses
84886aba55SVaibhav Hiremath  *    from SVC over wake/detect.
85886aba55SVaibhav Hiremath  *
86886aba55SVaibhav Hiremath  *  - ARCHE_PLATFORM_STATE_ACTIVE:
87886aba55SVaibhav Hiremath  *    Puts back driver to active state, where any pulse from SVC on wake/detect
88886aba55SVaibhav Hiremath  *    line would trigger either cold/standby boot.
89886aba55SVaibhav Hiremath  *    Note: Transition request from this function does not trigger cold/standby
90886aba55SVaibhav Hiremath  *          boot. It just puts back driver book keeping variable back to ACTIVE
91886aba55SVaibhav Hiremath  *          state and restores the interrupt.
92886aba55SVaibhav Hiremath  *
93886aba55SVaibhav Hiremath  * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
94886aba55SVaibhav Hiremath  * satisfy the requested state-transition or -EINVAL for all other
95886aba55SVaibhav Hiremath  * state-transition requests.
96886aba55SVaibhav Hiremath  */
97886aba55SVaibhav Hiremath int arche_platform_change_state(enum arche_platform_state state)
98886aba55SVaibhav Hiremath {
99886aba55SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
100886aba55SVaibhav Hiremath 	struct platform_device *pdev;
101886aba55SVaibhav Hiremath 	struct device_node *np;
102886aba55SVaibhav Hiremath 	int ret = -EAGAIN;
103886aba55SVaibhav Hiremath 	unsigned long flags;
104886aba55SVaibhav Hiremath 
105886aba55SVaibhav Hiremath 	np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
106886aba55SVaibhav Hiremath 	if (!np) {
107886aba55SVaibhav Hiremath 		pr_err("google,arche-platform device node not found\n");
108886aba55SVaibhav Hiremath 		return -ENODEV;
109886aba55SVaibhav Hiremath 	}
110886aba55SVaibhav Hiremath 
111886aba55SVaibhav Hiremath 	pdev = of_find_device_by_node(np);
112886aba55SVaibhav Hiremath 	if (!pdev) {
113886aba55SVaibhav Hiremath 		pr_err("arche-platform device not found\n");
114886aba55SVaibhav Hiremath 		return -ENODEV;
115886aba55SVaibhav Hiremath 	}
116886aba55SVaibhav Hiremath 
117886aba55SVaibhav Hiremath 	arche_pdata = platform_get_drvdata(pdev);
118886aba55SVaibhav Hiremath 
119886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
120886aba55SVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
121886aba55SVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
122886aba55SVaibhav Hiremath 		dev_err(arche_pdata->dev,
123886aba55SVaibhav Hiremath 			"driver busy with wake/detect line ops\n");
124886aba55SVaibhav Hiremath 		goto  exit;
125886aba55SVaibhav Hiremath 	}
126886aba55SVaibhav Hiremath 
127886aba55SVaibhav Hiremath 	if (arche_pdata->state == state) {
128886aba55SVaibhav Hiremath 		ret = 0;
129886aba55SVaibhav Hiremath 		goto exit;
130886aba55SVaibhav Hiremath 	}
131886aba55SVaibhav Hiremath 
132c4058b79SBryan O'Donoghue 	switch (state) {
133c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_TIME_SYNC:
134c4058b79SBryan O'Donoghue 		disable_irq(arche_pdata->wake_detect_irq);
135c4058b79SBryan O'Donoghue 		break;
136c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_ACTIVE:
137c4058b79SBryan O'Donoghue 		enable_irq(arche_pdata->wake_detect_irq);
138c4058b79SBryan O'Donoghue 		break;
139c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_OFF:
140c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_STANDBY:
141c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
142886aba55SVaibhav Hiremath 		dev_err(arche_pdata->dev, "busy, request to retry later\n");
143886aba55SVaibhav Hiremath 		goto exit;
144c4058b79SBryan O'Donoghue 	default:
145c4058b79SBryan O'Donoghue 		ret = -EINVAL;
146c4058b79SBryan O'Donoghue 		dev_err(arche_pdata->dev,
147c4058b79SBryan O'Donoghue 			"invalid state transition request\n");
148c4058b79SBryan O'Donoghue 		goto exit;
149886aba55SVaibhav Hiremath 	}
150c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, state);
151c4058b79SBryan O'Donoghue 	ret = 0;
152886aba55SVaibhav Hiremath exit:
153886aba55SVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
154886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
155886aba55SVaibhav Hiremath 	of_node_put(np);
156886aba55SVaibhav Hiremath 	return ret;
157886aba55SVaibhav Hiremath }
158886aba55SVaibhav Hiremath EXPORT_SYMBOL_GPL(arche_platform_change_state);
159886aba55SVaibhav Hiremath 
160c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */
161c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state(
162c4058b79SBryan O'Donoghue 				struct arche_platform_drvdata *arche_pdata,
163c4058b79SBryan O'Donoghue 				enum svc_wakedetect_state state)
164c4058b79SBryan O'Donoghue {
165c4058b79SBryan O'Donoghue 	arche_pdata->wake_detect_state = state;
166c4058b79SBryan O'Donoghue }
167c4058b79SBryan O'Donoghue 
1687fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
1697fa60654SVaibhav Hiremath {
1707fa60654SVaibhav Hiremath 	gpio_set_value(gpio, onoff);
1717fa60654SVaibhav Hiremath }
1727fa60654SVaibhav Hiremath 
173f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data)
174f760bbfbSVaibhav Hiremath {
175f760bbfbSVaibhav Hiremath 	int ret;
176f760bbfbSVaibhav Hiremath 
177f760bbfbSVaibhav Hiremath 	ret = apb_ctrl_coldboot(dev);
178f760bbfbSVaibhav Hiremath 	if (ret)
179f760bbfbSVaibhav Hiremath 		dev_warn(dev, "failed to coldboot\n");
180f760bbfbSVaibhav Hiremath 
181f760bbfbSVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
182f760bbfbSVaibhav Hiremath 	return 0;
183f760bbfbSVaibhav Hiremath }
184f760bbfbSVaibhav Hiremath 
185fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data)
186fd60ac58SVaibhav Hiremath {
187fd60ac58SVaibhav Hiremath 	int ret;
188fd60ac58SVaibhav Hiremath 
189fd60ac58SVaibhav Hiremath 	ret = apb_ctrl_fw_flashing(dev);
190fd60ac58SVaibhav Hiremath 	if (ret)
191fd60ac58SVaibhav Hiremath 		dev_warn(dev, "failed to switch to fw flashing state\n");
192fd60ac58SVaibhav Hiremath 
193fd60ac58SVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
194fd60ac58SVaibhav Hiremath 	return 0;
195fd60ac58SVaibhav Hiremath }
196fd60ac58SVaibhav Hiremath 
197fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data)
198fd60ac58SVaibhav Hiremath {
199fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
200fd60ac58SVaibhav Hiremath 
201e915ce48SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
202e915ce48SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
203e915ce48SVaibhav Hiremath 		dev_warn(dev, "failed to control hub device\n");
204e915ce48SVaibhav Hiremath 
205fd60ac58SVaibhav Hiremath 	return 0;
206fd60ac58SVaibhav Hiremath }
207fd60ac58SVaibhav Hiremath 
2087ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
20916fe18caSVaibhav Hiremath {
21016fe18caSVaibhav Hiremath 	/* Enable interrupt here, to read event back from SVC */
21116fe18caSVaibhav Hiremath 	gpio_direction_input(arche_pdata->wake_detect_gpio);
21216fe18caSVaibhav Hiremath 	enable_irq(arche_pdata->wake_detect_irq);
21316fe18caSVaibhav Hiremath }
21416fe18caSVaibhav Hiremath 
215f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
216f760bbfbSVaibhav Hiremath {
217f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
218f760bbfbSVaibhav Hiremath 	unsigned long flags;
219f760bbfbSVaibhav Hiremath 
2206c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
221f760bbfbSVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
222f760bbfbSVaibhav Hiremath 		/* Something is wrong */
2236c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
224f760bbfbSVaibhav Hiremath 		return IRQ_HANDLED;
225f760bbfbSVaibhav Hiremath 	}
226f760bbfbSVaibhav Hiremath 
227c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata,
228c4058b79SBryan O'Donoghue 					     WD_STATE_COLDBOOT_START);
2296c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
230f760bbfbSVaibhav Hiremath 
231ff788de0SVaibhav Hiremath 	/* It should complete power cycle, so first make sure it is poweroff */
232ff788de0SVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
233e915ce48SVaibhav Hiremath 
234f760bbfbSVaibhav Hiremath 	/* Bring APB out of reset: cold boot sequence */
235f760bbfbSVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
236f760bbfbSVaibhav Hiremath 
237a7ddda1fSVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
238a7ddda1fSVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
239a7ddda1fSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
240a7ddda1fSVaibhav Hiremath 
2416c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
242c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
2436c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
244f760bbfbSVaibhav Hiremath 
245f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
246f760bbfbSVaibhav Hiremath }
247f760bbfbSVaibhav Hiremath 
248f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
249f760bbfbSVaibhav Hiremath {
250f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
251f760bbfbSVaibhav Hiremath 	unsigned long flags;
252f760bbfbSVaibhav Hiremath 
2536c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
254f760bbfbSVaibhav Hiremath 
255f760bbfbSVaibhav Hiremath 	if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
256f760bbfbSVaibhav Hiremath 		/* wake/detect rising */
257f760bbfbSVaibhav Hiremath 
258f760bbfbSVaibhav Hiremath 		/*
259f760bbfbSVaibhav Hiremath 		 * If wake/detect line goes high after low, within less than
260f760bbfbSVaibhav Hiremath 		 * 30msec, then standby boot sequence is initiated, which is not
261f760bbfbSVaibhav Hiremath 		 * supported/implemented as of now. So ignore it.
262f760bbfbSVaibhav Hiremath 		 */
263f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
264f760bbfbSVaibhav Hiremath 			if (time_before(jiffies,
265f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_start +
266f760bbfbSVaibhav Hiremath 					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
267c4058b79SBryan O'Donoghue 				arche_platform_set_wake_detect_state(arche_pdata,
268c4058b79SBryan O'Donoghue 								     WD_STATE_IDLE);
269f760bbfbSVaibhav Hiremath 			} else {
270f760bbfbSVaibhav Hiremath 				/* Check we are not in middle of irq thread already */
271f760bbfbSVaibhav Hiremath 				if (arche_pdata->wake_detect_state !=
272f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_START) {
273c4058b79SBryan O'Donoghue 					arche_platform_set_wake_detect_state(arche_pdata,
274c4058b79SBryan O'Donoghue 									     WD_STATE_COLDBOOT_TRIG);
2756c1ca56dSVaibhav Hiremath 					spin_unlock_irqrestore(
2766c1ca56dSVaibhav Hiremath 						&arche_pdata->wake_lock,
2776c1ca56dSVaibhav Hiremath 						flags);
278f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
279f760bbfbSVaibhav Hiremath 				}
280f760bbfbSVaibhav Hiremath 			}
281f760bbfbSVaibhav Hiremath 		}
282f760bbfbSVaibhav Hiremath 	} else {
283f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
284f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
285f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
286f760bbfbSVaibhav Hiremath 			/*
287f760bbfbSVaibhav Hiremath 			 * In the begining, when wake/detect goes low (first time), we assume
288f760bbfbSVaibhav Hiremath 			 * it is meant for coldboot and set the flag. If wake/detect line stays low
289f760bbfbSVaibhav Hiremath 			 * beyond 30msec, then it is coldboot else fallback to standby boot.
290f760bbfbSVaibhav Hiremath 			 */
291c4058b79SBryan O'Donoghue 			arche_platform_set_wake_detect_state(arche_pdata,
292c4058b79SBryan O'Donoghue 							     WD_STATE_BOOT_INIT);
293f760bbfbSVaibhav Hiremath 		}
294f760bbfbSVaibhav Hiremath 	}
295f760bbfbSVaibhav Hiremath 
2966c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
297f760bbfbSVaibhav Hiremath 
298f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
299f760bbfbSVaibhav Hiremath }
300f760bbfbSVaibhav Hiremath 
301886aba55SVaibhav Hiremath /*
302886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
303886aba55SVaibhav Hiremath  */
304758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
305758ca99dSVaibhav Hiremath {
306758ca99dSVaibhav Hiremath 	int ret;
307758ca99dSVaibhav Hiremath 
308599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
309599159b6SVaibhav Hiremath 		return 0;
310599159b6SVaibhav Hiremath 
311758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
312758ca99dSVaibhav Hiremath 
313758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
314758ca99dSVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
315758ca99dSVaibhav Hiremath 
316758ca99dSVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
317758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
318758ca99dSVaibhav Hiremath 
319758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
320758ca99dSVaibhav Hiremath 	if (ret) {
321758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
322758ca99dSVaibhav Hiremath 				ret);
323758ca99dSVaibhav Hiremath 		return ret;
324758ca99dSVaibhav Hiremath 	}
325758ca99dSVaibhav Hiremath 
326758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
327758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
328758ca99dSVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
329758ca99dSVaibhav Hiremath 
330c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
331e74d04a5SVaibhav Hiremath 
332758ca99dSVaibhav Hiremath 	return 0;
333758ca99dSVaibhav Hiremath }
334758ca99dSVaibhav Hiremath 
335886aba55SVaibhav Hiremath /*
336886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
337886aba55SVaibhav Hiremath  */
3387691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
3397691fed2SVaibhav Hiremath {
340599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
341599159b6SVaibhav Hiremath 		return;
342599159b6SVaibhav Hiremath 
3437691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
3447691fed2SVaibhav Hiremath 
3457691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
3467691fed2SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
3477691fed2SVaibhav Hiremath 
3487691fed2SVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
3497691fed2SVaibhav Hiremath 
3507691fed2SVaibhav Hiremath 	usleep_range(100, 200);
3517691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
3527691fed2SVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
3537691fed2SVaibhav Hiremath 
354c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
3557691fed2SVaibhav Hiremath 
3567691fed2SVaibhav Hiremath }
3577691fed2SVaibhav Hiremath 
358886aba55SVaibhav Hiremath /*
359886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
360886aba55SVaibhav Hiremath  */
3615993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
3627fa60654SVaibhav Hiremath {
363f760bbfbSVaibhav Hiremath 	unsigned long flags;
364f760bbfbSVaibhav Hiremath 
365599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
366599159b6SVaibhav Hiremath 		return;
367599159b6SVaibhav Hiremath 
36825847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
36925847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
370d2320b2dSVaibhav Hiremath 		disable_irq(arche_pdata->wake_detect_irq);
37116fd976cSVaibhav Hiremath 
3726c1ca56dSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->wake_lock, flags);
373c4058b79SBryan O'Donoghue 		arche_platform_set_wake_detect_state(arche_pdata,
374c4058b79SBryan O'Donoghue 						     WD_STATE_IDLE);
3756c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
376b4c95fcaSVaibhav Hiremath 
377d8b16338SVaibhav Hiremath 		clk_disable_unprepare(arche_pdata->svc_ref_clk);
37825847ee7SVaibhav Hiremath 	}
37925847ee7SVaibhav Hiremath 
3807fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
3817fa60654SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
3827fa60654SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
383e74d04a5SVaibhav Hiremath 
384c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
3857fa60654SVaibhav Hiremath }
3867fa60654SVaibhav Hiremath 
3872923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
3882923c58eSVaibhav Hiremath 		struct device_attribute *attr, const char *buf, size_t count)
3892923c58eSVaibhav Hiremath {
3902923c58eSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
3912923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
3922923c58eSVaibhav Hiremath 	int ret = 0;
3932923c58eSVaibhav Hiremath 
394886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
395886aba55SVaibhav Hiremath 
3962923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
3972923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
398886aba55SVaibhav Hiremath 			goto exit;
3992923c58eSVaibhav Hiremath 
400fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
401fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
402fd60ac58SVaibhav Hiremath 
4032923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
404ad4d3f95SVaibhav Hiremath 
4052923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
4062923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
407886aba55SVaibhav Hiremath 			goto exit;
4082923c58eSVaibhav Hiremath 
4097ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
4102923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
41116fe18caSVaibhav Hiremath 
4122923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
4132923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
414886aba55SVaibhav Hiremath 			goto exit;
4152923c58eSVaibhav Hiremath 
4162923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
4177691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
4187691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
419886aba55SVaibhav Hiremath 			goto exit;
4207691fed2SVaibhav Hiremath 
4217691fed2SVaibhav Hiremath 		/* First we want to make sure we power off everything
4227691fed2SVaibhav Hiremath 		 * and then enter FW flashing state */
423fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
424fd60ac58SVaibhav Hiremath 
4257691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
426fd60ac58SVaibhav Hiremath 
4277691fed2SVaibhav Hiremath 		arche_platform_fw_flashing_seq(arche_pdata);
428fd60ac58SVaibhav Hiremath 
429fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
4302923c58eSVaibhav Hiremath 	} else {
4312923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
4322923c58eSVaibhav Hiremath 		ret = -EINVAL;
4332923c58eSVaibhav Hiremath 	}
4342923c58eSVaibhav Hiremath 
435886aba55SVaibhav Hiremath exit:
436886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
4372923c58eSVaibhav Hiremath 	return ret ? ret : count;
4382923c58eSVaibhav Hiremath }
4392923c58eSVaibhav Hiremath 
4402923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
4412923c58eSVaibhav Hiremath 		struct device_attribute *attr, char *buf)
4422923c58eSVaibhav Hiremath {
4432923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
4442923c58eSVaibhav Hiremath 
4452923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
4462923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
4472923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
4482923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
4492923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
4502923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
4512923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
4527691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
4537691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
454886aba55SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_TIME_SYNC:
455886aba55SVaibhav Hiremath 		return sprintf(buf, "time_sync\n");
4562923c58eSVaibhav Hiremath 	default:
4572923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
4582923c58eSVaibhav Hiremath 	}
4592923c58eSVaibhav Hiremath }
4602923c58eSVaibhav Hiremath 
4612923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
4622923c58eSVaibhav Hiremath 
4639160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier,
4649160b7c7SDavid Lin 				      unsigned long pm_event, void *unused)
4659160b7c7SDavid Lin {
4669160b7c7SDavid Lin 	struct arche_platform_drvdata *arche_pdata =
4679160b7c7SDavid Lin 		container_of(notifier, struct arche_platform_drvdata,
4689160b7c7SDavid Lin 			     pm_notifier);
469886aba55SVaibhav Hiremath 	int ret = NOTIFY_DONE;
4709160b7c7SDavid Lin 
471886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
4729160b7c7SDavid Lin 	switch (pm_event) {
4739160b7c7SDavid Lin 	case PM_SUSPEND_PREPARE:
474886aba55SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
475886aba55SVaibhav Hiremath 			ret = NOTIFY_STOP;
476886aba55SVaibhav Hiremath 			break;
477886aba55SVaibhav Hiremath 		}
4789160b7c7SDavid Lin 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
4799160b7c7SDavid Lin 		arche_platform_poweroff_seq(arche_pdata);
4809160b7c7SDavid Lin 		break;
4819160b7c7SDavid Lin 	case PM_POST_SUSPEND:
4827ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
48316fd976cSVaibhav Hiremath 		arche_platform_coldboot_seq(arche_pdata);
4849160b7c7SDavid Lin 		break;
4859160b7c7SDavid Lin 	default:
4869160b7c7SDavid Lin 		break;
4879160b7c7SDavid Lin 	}
488886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
4899160b7c7SDavid Lin 
490886aba55SVaibhav Hiremath 	return ret;
4919160b7c7SDavid Lin }
4929160b7c7SDavid Lin 
4937fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
4947fa60654SVaibhav Hiremath {
4957fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
4967fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
4977fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
4987fa60654SVaibhav Hiremath 	int ret;
4997fa60654SVaibhav Hiremath 
5007fa60654SVaibhav Hiremath 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
5017fa60654SVaibhav Hiremath 	if (!arche_pdata)
5027fa60654SVaibhav Hiremath 		return -ENOMEM;
5037fa60654SVaibhav Hiremath 
5047fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
5057fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
5067fa60654SVaibhav Hiremath 					"svc,reset-active-high");
5077fa60654SVaibhav Hiremath 	arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
5087fa60654SVaibhav Hiremath 	if (arche_pdata->svc_reset_gpio < 0) {
5097fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get reset-gpio\n");
510f1f251b5SViresh Kumar 		return arche_pdata->svc_reset_gpio;
5117fa60654SVaibhav Hiremath 	}
5127fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
5137fa60654SVaibhav Hiremath 	if (ret) {
5147fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
5157fa60654SVaibhav Hiremath 		return ret;
5167fa60654SVaibhav Hiremath 	}
5177fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
5187fa60654SVaibhav Hiremath 					arche_pdata->is_reset_act_hi);
5197fa60654SVaibhav Hiremath 	if (ret) {
5207fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
5217fa60654SVaibhav Hiremath 		return ret;
5227fa60654SVaibhav Hiremath 	}
523c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
5247fa60654SVaibhav Hiremath 
5257fa60654SVaibhav Hiremath 	arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
5267fa60654SVaibhav Hiremath 					"svc,sysboot-gpio", 0);
5277fa60654SVaibhav Hiremath 	if (arche_pdata->svc_sysboot_gpio < 0) {
5287fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get sysboot gpio\n");
529f1f251b5SViresh Kumar 		return arche_pdata->svc_sysboot_gpio;
5307fa60654SVaibhav Hiremath 	}
5317fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
5327fa60654SVaibhav Hiremath 	if (ret) {
5337fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
5347fa60654SVaibhav Hiremath 		return ret;
5357fa60654SVaibhav Hiremath 	}
5367fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
5377fa60654SVaibhav Hiremath 	if (ret) {
5387fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
5397fa60654SVaibhav Hiremath 		return ret;
5407fa60654SVaibhav Hiremath 	}
5417fa60654SVaibhav Hiremath 
5427fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
5437fa60654SVaibhav Hiremath 	arche_pdata->svc_refclk_req = of_get_named_gpio(np,
5447fa60654SVaibhav Hiremath 					"svc,refclk-req-gpio", 0);
5457fa60654SVaibhav Hiremath 	if (arche_pdata->svc_refclk_req < 0) {
5467fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc clock-req gpio\n");
547f1f251b5SViresh Kumar 		return arche_pdata->svc_refclk_req;
5487fa60654SVaibhav Hiremath 	}
5497fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
5507fa60654SVaibhav Hiremath 	if (ret) {
5517fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
5527fa60654SVaibhav Hiremath 		return ret;
5537fa60654SVaibhav Hiremath 	}
5547fa60654SVaibhav Hiremath 	ret = gpio_direction_input(arche_pdata->svc_refclk_req);
5557fa60654SVaibhav Hiremath 	if (ret) {
5567fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
5577fa60654SVaibhav Hiremath 		return ret;
5587fa60654SVaibhav Hiremath 	}
5597fa60654SVaibhav Hiremath 
5607fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
5617fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
5627fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
5637fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
5647fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
5657fa60654SVaibhav Hiremath 		return ret;
5667fa60654SVaibhav Hiremath 	}
5677fa60654SVaibhav Hiremath 
5687fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
5697fa60654SVaibhav Hiremath 
5707fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
5717fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
5727fa60654SVaibhav Hiremath 
573a463fc15SVaibhav Hiremath 	arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
574a463fc15SVaibhav Hiremath 	if (arche_pdata->wake_detect_gpio < 0) {
575a463fc15SVaibhav Hiremath 		dev_err(dev, "failed to get wake detect gpio\n");
576a463fc15SVaibhav Hiremath 		ret = arche_pdata->wake_detect_gpio;
577758ca99dSVaibhav Hiremath 		return ret;
57872a8c24bSViresh Kumar 	}
5797fa60654SVaibhav Hiremath 
580a463fc15SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
581a463fc15SVaibhav Hiremath 	if (ret) {
582a463fc15SVaibhav Hiremath 		dev_err(dev, "Failed requesting wake_detect gpio %d\n",
583a463fc15SVaibhav Hiremath 				arche_pdata->wake_detect_gpio);
584758ca99dSVaibhav Hiremath 		return ret;
585a463fc15SVaibhav Hiremath 	}
58616fd976cSVaibhav Hiremath 
587c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
588a463fc15SVaibhav Hiremath 
589a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
590a463fc15SVaibhav Hiremath 
5916c1ca56dSVaibhav Hiremath 	spin_lock_init(&arche_pdata->wake_lock);
592886aba55SVaibhav Hiremath 	mutex_init(&arche_pdata->platform_state_mutex);
593f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
594f760bbfbSVaibhav Hiremath 		gpio_to_irq(arche_pdata->wake_detect_gpio);
595f760bbfbSVaibhav Hiremath 
596f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
597f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq,
598f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq_thread,
599f760bbfbSVaibhav Hiremath 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
600f760bbfbSVaibhav Hiremath 			dev_name(dev), arche_pdata);
601f760bbfbSVaibhav Hiremath 	if (ret) {
602f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
603f760bbfbSVaibhav Hiremath 		return ret;
604f760bbfbSVaibhav Hiremath 	}
60516fd976cSVaibhav Hiremath 
6067ba535ecSVaibhav Hiremath 	arche_platform_wd_irq_en(arche_pdata);
607f760bbfbSVaibhav Hiremath 
6082923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
6092923c58eSVaibhav Hiremath 	if (ret) {
6102923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
6112923c58eSVaibhav Hiremath 		return ret;
6122923c58eSVaibhav Hiremath 	}
6132923c58eSVaibhav Hiremath 
614886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
615758ca99dSVaibhav Hiremath 	ret = arche_platform_coldboot_seq(arche_pdata);
616758ca99dSVaibhav Hiremath 	if (ret) {
617758ca99dSVaibhav Hiremath 		dev_err(dev, "Failed to cold boot svc %d\n", ret);
6186743a6fdSVaibhav Hiremath 		goto err_coldboot;
619758ca99dSVaibhav Hiremath 	}
620758ca99dSVaibhav Hiremath 
621fd60ac58SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
622fd60ac58SVaibhav Hiremath 	if (ret) {
623fd60ac58SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
6246743a6fdSVaibhav Hiremath 		goto err_populate;
625fd60ac58SVaibhav Hiremath 	}
626fd60ac58SVaibhav Hiremath 
6279160b7c7SDavid Lin 	arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
6289160b7c7SDavid Lin 	ret = register_pm_notifier(&arche_pdata->pm_notifier);
629886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
630886aba55SVaibhav Hiremath 
6319160b7c7SDavid Lin 	if (ret) {
6329160b7c7SDavid Lin 		dev_err(dev, "failed to register pm notifier %d\n", ret);
6339160b7c7SDavid Lin 		goto err_populate;
6349160b7c7SDavid Lin 	}
6359160b7c7SDavid Lin 
6367fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
63772a8c24bSViresh Kumar 	return 0;
6386743a6fdSVaibhav Hiremath 
6396743a6fdSVaibhav Hiremath err_populate:
6406743a6fdSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
6416743a6fdSVaibhav Hiremath err_coldboot:
642886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
6436743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
6446743a6fdSVaibhav Hiremath 	return ret;
6457fa60654SVaibhav Hiremath }
6467fa60654SVaibhav Hiremath 
647bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
648bc142bbbSVaibhav Hiremath {
649bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
650bc142bbbSVaibhav Hiremath 
651bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
652bc142bbbSVaibhav Hiremath 
653bc142bbbSVaibhav Hiremath 	return 0;
654bc142bbbSVaibhav Hiremath }
655bc142bbbSVaibhav Hiremath 
6567fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
6577fa60654SVaibhav Hiremath {
6587fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
6597fa60654SVaibhav Hiremath 
6609160b7c7SDavid Lin 	unregister_pm_notifier(&arche_pdata->pm_notifier);
6612923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
662bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
6635993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
6647fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, NULL);
6657fa60654SVaibhav Hiremath 
666ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
667ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
668ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
6697fa60654SVaibhav Hiremath 	return 0;
6707fa60654SVaibhav Hiremath }
6717fa60654SVaibhav Hiremath 
6727fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev)
6737fa60654SVaibhav Hiremath {
6747fa60654SVaibhav Hiremath 	/*
6757fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
6767fa60654SVaibhav Hiremath 	 * completely
6777fa60654SVaibhav Hiremath 	 *
6787fa60654SVaibhav Hiremath 	 * TODO: sequence ??
6797fa60654SVaibhav Hiremath 	 *
6807fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
6817fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
6827fa60654SVaibhav Hiremath 	 */
6837fa60654SVaibhav Hiremath 	return 0;
6847fa60654SVaibhav Hiremath }
6857fa60654SVaibhav Hiremath 
6867fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev)
6877fa60654SVaibhav Hiremath {
6887fa60654SVaibhav Hiremath 	/*
6897fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
6907fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
6917fa60654SVaibhav Hiremath 	 * OFF state or standby state.
6927fa60654SVaibhav Hiremath 	 *
6937fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
6947fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
6957fa60654SVaibhav Hiremath 	 *
6967fa60654SVaibhav Hiremath 	 */
6977fa60654SVaibhav Hiremath 	return 0;
6987fa60654SVaibhav Hiremath }
6997fa60654SVaibhav Hiremath 
7007fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
7017fa60654SVaibhav Hiremath 			arche_platform_suspend,
7027fa60654SVaibhav Hiremath 			arche_platform_resume);
7037fa60654SVaibhav Hiremath 
7047fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = {
7057fa60654SVaibhav Hiremath 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
7067fa60654SVaibhav Hiremath 	{ },
7077fa60654SVaibhav Hiremath };
7081e5dd1f8SGreg Kroah-Hartman 
7091e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = {
7101e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
7111e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
7121e5dd1f8SGreg Kroah-Hartman 	{ },
7131e5dd1f8SGreg Kroah-Hartman };
7141e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
7157fa60654SVaibhav Hiremath 
7167fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
7177fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
7187fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
7197fa60654SVaibhav Hiremath 	.driver		= {
7207fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
7217fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
7221e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
7237fa60654SVaibhav Hiremath 	}
7247fa60654SVaibhav Hiremath };
7257fa60654SVaibhav Hiremath 
7261e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
7271e5dd1f8SGreg Kroah-Hartman {
7281e5dd1f8SGreg Kroah-Hartman 	int retval;
7291e5dd1f8SGreg Kroah-Hartman 
7301e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
7311e5dd1f8SGreg Kroah-Hartman 	if (retval)
7321e5dd1f8SGreg Kroah-Hartman 		return retval;
7331e5dd1f8SGreg Kroah-Hartman 
7347b62b61cSViresh Kumar 	retval = arche_apb_init();
7351e5dd1f8SGreg Kroah-Hartman 	if (retval)
7361e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
7371e5dd1f8SGreg Kroah-Hartman 
7381e5dd1f8SGreg Kroah-Hartman 	return retval;
7391e5dd1f8SGreg Kroah-Hartman }
7401e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
7411e5dd1f8SGreg Kroah-Hartman 
7421e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
7431e5dd1f8SGreg Kroah-Hartman {
7447b62b61cSViresh Kumar 	arche_apb_exit();
7451e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
7461e5dd1f8SGreg Kroah-Hartman }
7471e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
7487fa60654SVaibhav Hiremath 
749a974f469SSandeep Patil MODULE_LICENSE("GPL v2");
7507fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
7517fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
752