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"
25970dc85bSBryan O'Donoghue #include "greybus.h"
267fa60654SVaibhav Hiremath 
27ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
28ad4d3f95SVaibhav Hiremath 
29f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
30f760bbfbSVaibhav Hiremath 
31685353c1SVaibhav Hiremath enum svc_wakedetect_state {
32685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
33685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
34685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
35685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
36685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
37685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
38970dc85bSBryan O'Donoghue 	WD_STATE_TIMESYNC,
39685353c1SVaibhav Hiremath };
40685353c1SVaibhav Hiremath 
417fa60654SVaibhav Hiremath struct arche_platform_drvdata {
427fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
437fa60654SVaibhav Hiremath 	int svc_reset_gpio;
447fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
457fa60654SVaibhav Hiremath 	int svc_sysboot_gpio;
46a463fc15SVaibhav Hiremath 	int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
477fa60654SVaibhav Hiremath 
48e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
49e74d04a5SVaibhav Hiremath 
507fe93014SDavid Lin 	int svc_refclk_req;
517fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
527fa60654SVaibhav Hiremath 
537fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
547fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
557fa60654SVaibhav Hiremath 
567fa60654SVaibhav Hiremath 	int num_apbs;
57a463fc15SVaibhav Hiremath 
58685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
59f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
606c1ca56dSVaibhav Hiremath 	spinlock_t wake_lock;			/* Protect wake_detect_state */
61886aba55SVaibhav Hiremath 	struct mutex platform_state_mutex;	/* Protect state */
62970dc85bSBryan O'Donoghue 	wait_queue_head_t wq;			/* WQ for arche_pdata->state */
63f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
649160b7c7SDavid Lin 	struct notifier_block pm_notifier;
65685353c1SVaibhav Hiremath 
66a463fc15SVaibhav Hiremath 	struct device *dev;
67970dc85bSBryan O'Donoghue 	struct gb_timesync_svc *timesync_svc_pdata;
687fa60654SVaibhav Hiremath };
697fa60654SVaibhav Hiremath 
70970dc85bSBryan O'Donoghue static int arche_apb_bootret_assert(struct device *dev, void *data)
71970dc85bSBryan O'Donoghue {
72970dc85bSBryan O'Donoghue 	apb_bootret_assert(dev);
73970dc85bSBryan O'Donoghue 	return 0;
74970dc85bSBryan O'Donoghue }
75970dc85bSBryan O'Donoghue 
76970dc85bSBryan O'Donoghue static int arche_apb_bootret_deassert(struct device *dev, void *data)
77970dc85bSBryan O'Donoghue {
78970dc85bSBryan O'Donoghue 	apb_bootret_deassert(dev);
79970dc85bSBryan O'Donoghue 	return 0;
80970dc85bSBryan O'Donoghue }
81970dc85bSBryan O'Donoghue 
82970dc85bSBryan O'Donoghue /* Requires calling context to hold arche_pdata->platform_state_mutex */
83c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
84c4058b79SBryan O'Donoghue 				     enum arche_platform_state state)
85c4058b79SBryan O'Donoghue {
86c4058b79SBryan O'Donoghue 	arche_pdata->state = state;
87c4058b79SBryan O'Donoghue }
88c4058b79SBryan O'Donoghue 
89886aba55SVaibhav Hiremath /*
90886aba55SVaibhav Hiremath  * arche_platform_change_state: Change the operational state
91886aba55SVaibhav Hiremath  *
92886aba55SVaibhav Hiremath  * This exported function allows external drivers to change the state
93886aba55SVaibhav Hiremath  * of the arche-platform driver.
94886aba55SVaibhav Hiremath  * Note that this function only supports transitions between two states
95886aba55SVaibhav Hiremath  * with limited functionality.
96886aba55SVaibhav Hiremath  *
97886aba55SVaibhav Hiremath  *  - ARCHE_PLATFORM_STATE_TIME_SYNC:
98886aba55SVaibhav Hiremath  *    Once set, allows timesync operations between SVC <=> AP and makes
99886aba55SVaibhav Hiremath  *    sure that arche-platform driver ignores any subsequent events/pulses
100886aba55SVaibhav Hiremath  *    from SVC over wake/detect.
101886aba55SVaibhav Hiremath  *
102886aba55SVaibhav Hiremath  *  - ARCHE_PLATFORM_STATE_ACTIVE:
103886aba55SVaibhav Hiremath  *    Puts back driver to active state, where any pulse from SVC on wake/detect
104886aba55SVaibhav Hiremath  *    line would trigger either cold/standby boot.
105886aba55SVaibhav Hiremath  *    Note: Transition request from this function does not trigger cold/standby
106886aba55SVaibhav Hiremath  *          boot. It just puts back driver book keeping variable back to ACTIVE
107886aba55SVaibhav Hiremath  *          state and restores the interrupt.
108886aba55SVaibhav Hiremath  *
109886aba55SVaibhav Hiremath  * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
110886aba55SVaibhav Hiremath  * satisfy the requested state-transition or -EINVAL for all other
111886aba55SVaibhav Hiremath  * state-transition requests.
112886aba55SVaibhav Hiremath  */
113970dc85bSBryan O'Donoghue int arche_platform_change_state(enum arche_platform_state state,
114970dc85bSBryan O'Donoghue 				struct gb_timesync_svc *timesync_svc_pdata)
115886aba55SVaibhav Hiremath {
116886aba55SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
117886aba55SVaibhav Hiremath 	struct platform_device *pdev;
118886aba55SVaibhav Hiremath 	struct device_node *np;
119886aba55SVaibhav Hiremath 	int ret = -EAGAIN;
120886aba55SVaibhav Hiremath 	unsigned long flags;
121886aba55SVaibhav Hiremath 
122886aba55SVaibhav Hiremath 	np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
123886aba55SVaibhav Hiremath 	if (!np) {
124886aba55SVaibhav Hiremath 		pr_err("google,arche-platform device node not found\n");
125886aba55SVaibhav Hiremath 		return -ENODEV;
126886aba55SVaibhav Hiremath 	}
127886aba55SVaibhav Hiremath 
128886aba55SVaibhav Hiremath 	pdev = of_find_device_by_node(np);
129886aba55SVaibhav Hiremath 	if (!pdev) {
130886aba55SVaibhav Hiremath 		pr_err("arche-platform device not found\n");
131886aba55SVaibhav Hiremath 		return -ENODEV;
132886aba55SVaibhav Hiremath 	}
133886aba55SVaibhav Hiremath 
134886aba55SVaibhav Hiremath 	arche_pdata = platform_get_drvdata(pdev);
135886aba55SVaibhav Hiremath 
136886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
137886aba55SVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
138886aba55SVaibhav Hiremath 
139886aba55SVaibhav Hiremath 	if (arche_pdata->state == state) {
140886aba55SVaibhav Hiremath 		ret = 0;
141886aba55SVaibhav Hiremath 		goto exit;
142886aba55SVaibhav Hiremath 	}
143886aba55SVaibhav Hiremath 
144c4058b79SBryan O'Donoghue 	switch (state) {
145c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_TIME_SYNC:
146970dc85bSBryan O'Donoghue 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
147970dc85bSBryan O'Donoghue 			ret = -EINVAL;
148970dc85bSBryan O'Donoghue 			goto exit;
149970dc85bSBryan O'Donoghue 		}
150970dc85bSBryan O'Donoghue 		if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
151970dc85bSBryan O'Donoghue 			dev_err(arche_pdata->dev,
152970dc85bSBryan O'Donoghue 				"driver busy with wake/detect line ops\n");
153970dc85bSBryan O'Donoghue 			goto  exit;
154970dc85bSBryan O'Donoghue 		}
155970dc85bSBryan O'Donoghue 		device_for_each_child(arche_pdata->dev, NULL,
156970dc85bSBryan O'Donoghue 				      arche_apb_bootret_assert);
157970dc85bSBryan O'Donoghue 		arche_pdata->wake_detect_state = WD_STATE_TIMESYNC;
158c4058b79SBryan O'Donoghue 		break;
159c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_ACTIVE:
160970dc85bSBryan O'Donoghue 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) {
161970dc85bSBryan O'Donoghue 			ret = -EINVAL;
162970dc85bSBryan O'Donoghue 			goto exit;
163970dc85bSBryan O'Donoghue 		}
164970dc85bSBryan O'Donoghue 		device_for_each_child(arche_pdata->dev, NULL,
165970dc85bSBryan O'Donoghue 				      arche_apb_bootret_deassert);
166970dc85bSBryan O'Donoghue 		arche_pdata->wake_detect_state = WD_STATE_IDLE;
167c4058b79SBryan O'Donoghue 		break;
168c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_OFF:
169c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_STANDBY:
170c4058b79SBryan O'Donoghue 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
171886aba55SVaibhav Hiremath 		dev_err(arche_pdata->dev, "busy, request to retry later\n");
172886aba55SVaibhav Hiremath 		goto exit;
173c4058b79SBryan O'Donoghue 	default:
174c4058b79SBryan O'Donoghue 		ret = -EINVAL;
175c4058b79SBryan O'Donoghue 		dev_err(arche_pdata->dev,
176c4058b79SBryan O'Donoghue 			"invalid state transition request\n");
177c4058b79SBryan O'Donoghue 		goto exit;
178886aba55SVaibhav Hiremath 	}
179970dc85bSBryan O'Donoghue 	arche_pdata->timesync_svc_pdata = timesync_svc_pdata;
180c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, state);
181970dc85bSBryan O'Donoghue 	if (state == ARCHE_PLATFORM_STATE_ACTIVE)
182970dc85bSBryan O'Donoghue 		wake_up(&arche_pdata->wq);
183970dc85bSBryan O'Donoghue 
184c4058b79SBryan O'Donoghue 	ret = 0;
185886aba55SVaibhav Hiremath exit:
186886aba55SVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
187886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
188886aba55SVaibhav Hiremath 	of_node_put(np);
189886aba55SVaibhav Hiremath 	return ret;
190886aba55SVaibhav Hiremath }
191886aba55SVaibhav Hiremath EXPORT_SYMBOL_GPL(arche_platform_change_state);
192886aba55SVaibhav Hiremath 
193c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */
194c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state(
195c4058b79SBryan O'Donoghue 				struct arche_platform_drvdata *arche_pdata,
196c4058b79SBryan O'Donoghue 				enum svc_wakedetect_state state)
197c4058b79SBryan O'Donoghue {
198c4058b79SBryan O'Donoghue 	arche_pdata->wake_detect_state = state;
199c4058b79SBryan O'Donoghue }
200c4058b79SBryan O'Donoghue 
2017fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
2027fa60654SVaibhav Hiremath {
2037fa60654SVaibhav Hiremath 	gpio_set_value(gpio, onoff);
2047fa60654SVaibhav Hiremath }
2057fa60654SVaibhav Hiremath 
206f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data)
207f760bbfbSVaibhav Hiremath {
208f760bbfbSVaibhav Hiremath 	int ret;
209f760bbfbSVaibhav Hiremath 
210f760bbfbSVaibhav Hiremath 	ret = apb_ctrl_coldboot(dev);
211f760bbfbSVaibhav Hiremath 	if (ret)
212f760bbfbSVaibhav Hiremath 		dev_warn(dev, "failed to coldboot\n");
213f760bbfbSVaibhav Hiremath 
214f760bbfbSVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
215f760bbfbSVaibhav Hiremath 	return 0;
216f760bbfbSVaibhav Hiremath }
217f760bbfbSVaibhav Hiremath 
218fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data)
219fd60ac58SVaibhav Hiremath {
220fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
221fd60ac58SVaibhav Hiremath 
222e915ce48SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
223e915ce48SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
224e915ce48SVaibhav Hiremath 		dev_warn(dev, "failed to control hub device\n");
225e915ce48SVaibhav Hiremath 
226fd60ac58SVaibhav Hiremath 	return 0;
227fd60ac58SVaibhav Hiremath }
228fd60ac58SVaibhav Hiremath 
2297ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
23016fe18caSVaibhav Hiremath {
23116fe18caSVaibhav Hiremath 	/* Enable interrupt here, to read event back from SVC */
23216fe18caSVaibhav Hiremath 	gpio_direction_input(arche_pdata->wake_detect_gpio);
23316fe18caSVaibhav Hiremath 	enable_irq(arche_pdata->wake_detect_irq);
23416fe18caSVaibhav Hiremath }
23516fe18caSVaibhav Hiremath 
236f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
237f760bbfbSVaibhav Hiremath {
238f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
239f760bbfbSVaibhav Hiremath 	unsigned long flags;
240f760bbfbSVaibhav Hiremath 
2416c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
242f760bbfbSVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
243f760bbfbSVaibhav Hiremath 		/* Something is wrong */
2446c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
245f760bbfbSVaibhav Hiremath 		return IRQ_HANDLED;
246f760bbfbSVaibhav Hiremath 	}
247f760bbfbSVaibhav Hiremath 
248c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata,
249c4058b79SBryan O'Donoghue 					     WD_STATE_COLDBOOT_START);
2506c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
251f760bbfbSVaibhav Hiremath 
252ff788de0SVaibhav Hiremath 	/* It should complete power cycle, so first make sure it is poweroff */
253ff788de0SVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
254e915ce48SVaibhav Hiremath 
255f760bbfbSVaibhav Hiremath 	/* Bring APB out of reset: cold boot sequence */
256f760bbfbSVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
257f760bbfbSVaibhav Hiremath 
258a7ddda1fSVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
259a7ddda1fSVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
260a7ddda1fSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
261a7ddda1fSVaibhav Hiremath 
2626c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
263c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
2646c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
265f760bbfbSVaibhav Hiremath 
266f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
267f760bbfbSVaibhav Hiremath }
268f760bbfbSVaibhav Hiremath 
269f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
270f760bbfbSVaibhav Hiremath {
271f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
272f760bbfbSVaibhav Hiremath 	unsigned long flags;
273f760bbfbSVaibhav Hiremath 
2746c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
275f760bbfbSVaibhav Hiremath 
276970dc85bSBryan O'Donoghue 	if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) {
277970dc85bSBryan O'Donoghue 		gb_timesync_irq(arche_pdata->timesync_svc_pdata);
278970dc85bSBryan O'Donoghue 		goto exit;
279970dc85bSBryan O'Donoghue 	}
280970dc85bSBryan O'Donoghue 
281f760bbfbSVaibhav Hiremath 	if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
282f760bbfbSVaibhav Hiremath 		/* wake/detect rising */
283f760bbfbSVaibhav Hiremath 
284f760bbfbSVaibhav Hiremath 		/*
285f760bbfbSVaibhav Hiremath 		 * If wake/detect line goes high after low, within less than
286f760bbfbSVaibhav Hiremath 		 * 30msec, then standby boot sequence is initiated, which is not
287f760bbfbSVaibhav Hiremath 		 * supported/implemented as of now. So ignore it.
288f760bbfbSVaibhav Hiremath 		 */
289f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
290f760bbfbSVaibhav Hiremath 			if (time_before(jiffies,
291f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_start +
292f760bbfbSVaibhav Hiremath 					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
293c4058b79SBryan O'Donoghue 				arche_platform_set_wake_detect_state(arche_pdata,
294c4058b79SBryan O'Donoghue 								     WD_STATE_IDLE);
295f760bbfbSVaibhav Hiremath 			} else {
296f760bbfbSVaibhav Hiremath 				/* Check we are not in middle of irq thread already */
297f760bbfbSVaibhav Hiremath 				if (arche_pdata->wake_detect_state !=
298f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_START) {
299c4058b79SBryan O'Donoghue 					arche_platform_set_wake_detect_state(arche_pdata,
300c4058b79SBryan O'Donoghue 									     WD_STATE_COLDBOOT_TRIG);
3016c1ca56dSVaibhav Hiremath 					spin_unlock_irqrestore(
3026c1ca56dSVaibhav Hiremath 						&arche_pdata->wake_lock,
3036c1ca56dSVaibhav Hiremath 						flags);
304f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
305f760bbfbSVaibhav Hiremath 				}
306f760bbfbSVaibhav Hiremath 			}
307f760bbfbSVaibhav Hiremath 		}
308f760bbfbSVaibhav Hiremath 	} else {
309f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
310f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
311f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
312f760bbfbSVaibhav Hiremath 			/*
313f760bbfbSVaibhav Hiremath 			 * In the begining, when wake/detect goes low (first time), we assume
314f760bbfbSVaibhav Hiremath 			 * it is meant for coldboot and set the flag. If wake/detect line stays low
315f760bbfbSVaibhav Hiremath 			 * beyond 30msec, then it is coldboot else fallback to standby boot.
316f760bbfbSVaibhav Hiremath 			 */
317c4058b79SBryan O'Donoghue 			arche_platform_set_wake_detect_state(arche_pdata,
318c4058b79SBryan O'Donoghue 							     WD_STATE_BOOT_INIT);
319f760bbfbSVaibhav Hiremath 		}
320f760bbfbSVaibhav Hiremath 	}
321f760bbfbSVaibhav Hiremath 
322970dc85bSBryan O'Donoghue exit:
3236c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
324f760bbfbSVaibhav Hiremath 
325f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
326f760bbfbSVaibhav Hiremath }
327f760bbfbSVaibhav Hiremath 
328886aba55SVaibhav Hiremath /*
329886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
330886aba55SVaibhav Hiremath  */
331758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
332758ca99dSVaibhav Hiremath {
333758ca99dSVaibhav Hiremath 	int ret;
334758ca99dSVaibhav Hiremath 
335599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
336599159b6SVaibhav Hiremath 		return 0;
337599159b6SVaibhav Hiremath 
338758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
339758ca99dSVaibhav Hiremath 
340758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
341758ca99dSVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
342758ca99dSVaibhav Hiremath 
343758ca99dSVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
344758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
345758ca99dSVaibhav Hiremath 
346758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
347758ca99dSVaibhav Hiremath 	if (ret) {
348758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
349758ca99dSVaibhav Hiremath 				ret);
350758ca99dSVaibhav Hiremath 		return ret;
351758ca99dSVaibhav Hiremath 	}
352758ca99dSVaibhav Hiremath 
353758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
354758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
355758ca99dSVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
356758ca99dSVaibhav Hiremath 
357c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
358e74d04a5SVaibhav Hiremath 
359758ca99dSVaibhav Hiremath 	return 0;
360758ca99dSVaibhav Hiremath }
361758ca99dSVaibhav Hiremath 
362886aba55SVaibhav Hiremath /*
363886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
364886aba55SVaibhav Hiremath  */
365c61a8b49SVaibhav Hiremath static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
3667691fed2SVaibhav Hiremath {
3677a867d14SVaibhav Hiremath 	int ret;
3687a867d14SVaibhav Hiremath 
369599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
370c61a8b49SVaibhav Hiremath 		return 0;
371599159b6SVaibhav Hiremath 
3727691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
3737691fed2SVaibhav Hiremath 
3747691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
3757691fed2SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
3767691fed2SVaibhav Hiremath 
3777691fed2SVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
3787691fed2SVaibhav Hiremath 
3797691fed2SVaibhav Hiremath 	usleep_range(100, 200);
3807a867d14SVaibhav Hiremath 
3817a867d14SVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
3827a867d14SVaibhav Hiremath 	if (ret) {
3837a867d14SVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
3847a867d14SVaibhav Hiremath 				ret);
3857a867d14SVaibhav Hiremath 		return ret;
3867a867d14SVaibhav Hiremath 	}
3877a867d14SVaibhav Hiremath 
3887691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
3897691fed2SVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
3907691fed2SVaibhav Hiremath 
391c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
3927691fed2SVaibhav Hiremath 
393c61a8b49SVaibhav Hiremath 	return 0;
3947691fed2SVaibhav Hiremath }
3957691fed2SVaibhav Hiremath 
396886aba55SVaibhav Hiremath /*
397886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
398886aba55SVaibhav Hiremath  */
3995993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
4007fa60654SVaibhav Hiremath {
401f760bbfbSVaibhav Hiremath 	unsigned long flags;
402f760bbfbSVaibhav Hiremath 
403599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
404599159b6SVaibhav Hiremath 		return;
405599159b6SVaibhav Hiremath 
40625847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
40725847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
408d2320b2dSVaibhav Hiremath 		disable_irq(arche_pdata->wake_detect_irq);
40916fd976cSVaibhav Hiremath 
4106c1ca56dSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->wake_lock, flags);
411c4058b79SBryan O'Donoghue 		arche_platform_set_wake_detect_state(arche_pdata,
412c4058b79SBryan O'Donoghue 						     WD_STATE_IDLE);
4136c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
4147a867d14SVaibhav Hiremath 	}
415b4c95fcaSVaibhav Hiremath 
416d8b16338SVaibhav Hiremath 	clk_disable_unprepare(arche_pdata->svc_ref_clk);
41725847ee7SVaibhav Hiremath 
4187fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
4197fa60654SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
4207fa60654SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
421e74d04a5SVaibhav Hiremath 
422c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
4237fa60654SVaibhav Hiremath }
4247fa60654SVaibhav Hiremath 
4252923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
4262923c58eSVaibhav Hiremath 		struct device_attribute *attr, const char *buf, size_t count)
4272923c58eSVaibhav Hiremath {
4282923c58eSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
4292923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
4302923c58eSVaibhav Hiremath 	int ret = 0;
4312923c58eSVaibhav Hiremath 
432970dc85bSBryan O'Donoghue retry:
433886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
434970dc85bSBryan O'Donoghue 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
435970dc85bSBryan O'Donoghue 		mutex_unlock(&arche_pdata->platform_state_mutex);
436970dc85bSBryan O'Donoghue 		ret = wait_event_interruptible(
437970dc85bSBryan O'Donoghue 			arche_pdata->wq,
438970dc85bSBryan O'Donoghue 			arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC);
439970dc85bSBryan O'Donoghue 		if (ret)
440970dc85bSBryan O'Donoghue 			return ret;
441970dc85bSBryan O'Donoghue 		goto retry;
442970dc85bSBryan O'Donoghue 	}
443886aba55SVaibhav Hiremath 
4442923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
4452923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
446886aba55SVaibhav Hiremath 			goto exit;
4472923c58eSVaibhav Hiremath 
448fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
449fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
450fd60ac58SVaibhav Hiremath 
4512923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
452ad4d3f95SVaibhav Hiremath 
4532923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
4542923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
455886aba55SVaibhav Hiremath 			goto exit;
4562923c58eSVaibhav Hiremath 
4570b1283e3SVaibhav Hiremath 		/* First we want to make sure we power off everything
4580b1283e3SVaibhav Hiremath 		 * and then activate back again */
4590b1283e3SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
4600b1283e3SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
4610b1283e3SVaibhav Hiremath 
4627ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
4632923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
4640b1283e3SVaibhav Hiremath 		if (ret)
4650b1283e3SVaibhav Hiremath 			goto exit;
46616fe18caSVaibhav Hiremath 
4672923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
4682923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
469886aba55SVaibhav Hiremath 			goto exit;
4702923c58eSVaibhav Hiremath 
4712923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
4727691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
4737691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
474886aba55SVaibhav Hiremath 			goto exit;
4757691fed2SVaibhav Hiremath 
476921dbe52SVaibhav Hiremath 		/*
477921dbe52SVaibhav Hiremath 		 * Here we only control SVC.
478921dbe52SVaibhav Hiremath 		 *
479921dbe52SVaibhav Hiremath 		 * In case of FW_FLASHING mode we do not want to control
480921dbe52SVaibhav Hiremath 		 * APBs, as in case of V2, SPI bus is shared between both
481921dbe52SVaibhav Hiremath 		 * the APBs. So let user chose which APB he wants to flash.
482921dbe52SVaibhav Hiremath 		 */
4837691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
484fd60ac58SVaibhav Hiremath 
485c61a8b49SVaibhav Hiremath 		ret = arche_platform_fw_flashing_seq(arche_pdata);
486c61a8b49SVaibhav Hiremath 		if (ret)
487c61a8b49SVaibhav Hiremath 			goto exit;
4882923c58eSVaibhav Hiremath 	} else {
4892923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
4902923c58eSVaibhav Hiremath 		ret = -EINVAL;
4912923c58eSVaibhav Hiremath 	}
4922923c58eSVaibhav Hiremath 
493886aba55SVaibhav Hiremath exit:
494886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
4952923c58eSVaibhav Hiremath 	return ret ? ret : count;
4962923c58eSVaibhav Hiremath }
4972923c58eSVaibhav Hiremath 
4982923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
4992923c58eSVaibhav Hiremath 		struct device_attribute *attr, char *buf)
5002923c58eSVaibhav Hiremath {
5012923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
5022923c58eSVaibhav Hiremath 
5032923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
5042923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
5052923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
5062923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
5072923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
5082923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
5092923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
5107691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
5117691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
512886aba55SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_TIME_SYNC:
513886aba55SVaibhav Hiremath 		return sprintf(buf, "time_sync\n");
5142923c58eSVaibhav Hiremath 	default:
5152923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
5162923c58eSVaibhav Hiremath 	}
5172923c58eSVaibhav Hiremath }
5182923c58eSVaibhav Hiremath 
5192923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
5202923c58eSVaibhav Hiremath 
5219160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier,
5229160b7c7SDavid Lin 				      unsigned long pm_event, void *unused)
5239160b7c7SDavid Lin {
5249160b7c7SDavid Lin 	struct arche_platform_drvdata *arche_pdata =
5259160b7c7SDavid Lin 		container_of(notifier, struct arche_platform_drvdata,
5269160b7c7SDavid Lin 			     pm_notifier);
527886aba55SVaibhav Hiremath 	int ret = NOTIFY_DONE;
5289160b7c7SDavid Lin 
529886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
5309160b7c7SDavid Lin 	switch (pm_event) {
5319160b7c7SDavid Lin 	case PM_SUSPEND_PREPARE:
532886aba55SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
533886aba55SVaibhav Hiremath 			ret = NOTIFY_STOP;
534886aba55SVaibhav Hiremath 			break;
535886aba55SVaibhav Hiremath 		}
5369160b7c7SDavid Lin 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
5379160b7c7SDavid Lin 		arche_platform_poweroff_seq(arche_pdata);
5389160b7c7SDavid Lin 		break;
5399160b7c7SDavid Lin 	case PM_POST_SUSPEND:
5408ef0b538SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
5418ef0b538SVaibhav Hiremath 			break;
5428ef0b538SVaibhav Hiremath 
5437ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
54416fd976cSVaibhav Hiremath 		arche_platform_coldboot_seq(arche_pdata);
5459160b7c7SDavid Lin 		break;
5469160b7c7SDavid Lin 	default:
5479160b7c7SDavid Lin 		break;
5489160b7c7SDavid Lin 	}
549886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
5509160b7c7SDavid Lin 
551886aba55SVaibhav Hiremath 	return ret;
5529160b7c7SDavid Lin }
5539160b7c7SDavid Lin 
5547fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
5557fa60654SVaibhav Hiremath {
5567fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
5577fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
5587fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
5597fa60654SVaibhav Hiremath 	int ret;
5607fa60654SVaibhav Hiremath 
5617fa60654SVaibhav Hiremath 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
5627fa60654SVaibhav Hiremath 	if (!arche_pdata)
5637fa60654SVaibhav Hiremath 		return -ENOMEM;
5647fa60654SVaibhav Hiremath 
5657fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
5667fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
5677fa60654SVaibhav Hiremath 					"svc,reset-active-high");
5687fa60654SVaibhav Hiremath 	arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
5697fa60654SVaibhav Hiremath 	if (arche_pdata->svc_reset_gpio < 0) {
5707fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get reset-gpio\n");
571f1f251b5SViresh Kumar 		return arche_pdata->svc_reset_gpio;
5727fa60654SVaibhav Hiremath 	}
5737fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
5747fa60654SVaibhav Hiremath 	if (ret) {
5757fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
5767fa60654SVaibhav Hiremath 		return ret;
5777fa60654SVaibhav Hiremath 	}
5787fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
5797fa60654SVaibhav Hiremath 					arche_pdata->is_reset_act_hi);
5807fa60654SVaibhav Hiremath 	if (ret) {
5817fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
5827fa60654SVaibhav Hiremath 		return ret;
5837fa60654SVaibhav Hiremath 	}
584c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
5857fa60654SVaibhav Hiremath 
5867fa60654SVaibhav Hiremath 	arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
5877fa60654SVaibhav Hiremath 					"svc,sysboot-gpio", 0);
5887fa60654SVaibhav Hiremath 	if (arche_pdata->svc_sysboot_gpio < 0) {
5897fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get sysboot gpio\n");
590f1f251b5SViresh Kumar 		return arche_pdata->svc_sysboot_gpio;
5917fa60654SVaibhav Hiremath 	}
5927fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
5937fa60654SVaibhav Hiremath 	if (ret) {
5947fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
5957fa60654SVaibhav Hiremath 		return ret;
5967fa60654SVaibhav Hiremath 	}
5977fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
5987fa60654SVaibhav Hiremath 	if (ret) {
5997fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
6007fa60654SVaibhav Hiremath 		return ret;
6017fa60654SVaibhav Hiremath 	}
6027fa60654SVaibhav Hiremath 
6037fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
6047fa60654SVaibhav Hiremath 	arche_pdata->svc_refclk_req = of_get_named_gpio(np,
6057fa60654SVaibhav Hiremath 					"svc,refclk-req-gpio", 0);
6067fa60654SVaibhav Hiremath 	if (arche_pdata->svc_refclk_req < 0) {
6077fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc clock-req gpio\n");
608f1f251b5SViresh Kumar 		return arche_pdata->svc_refclk_req;
6097fa60654SVaibhav Hiremath 	}
6107fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
6117fa60654SVaibhav Hiremath 	if (ret) {
6127fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
6137fa60654SVaibhav Hiremath 		return ret;
6147fa60654SVaibhav Hiremath 	}
6157fa60654SVaibhav Hiremath 	ret = gpio_direction_input(arche_pdata->svc_refclk_req);
6167fa60654SVaibhav Hiremath 	if (ret) {
6177fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
6187fa60654SVaibhav Hiremath 		return ret;
6197fa60654SVaibhav Hiremath 	}
6207fa60654SVaibhav Hiremath 
6217fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
6227fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
6237fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
6247fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
6257fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
6267fa60654SVaibhav Hiremath 		return ret;
6277fa60654SVaibhav Hiremath 	}
6287fa60654SVaibhav Hiremath 
6297fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
6307fa60654SVaibhav Hiremath 
6317fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
6327fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
6337fa60654SVaibhav Hiremath 
634a463fc15SVaibhav Hiremath 	arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
635a463fc15SVaibhav Hiremath 	if (arche_pdata->wake_detect_gpio < 0) {
636a463fc15SVaibhav Hiremath 		dev_err(dev, "failed to get wake detect gpio\n");
6375a420d15SEva Rachel Retuya 		return arche_pdata->wake_detect_gpio;
63872a8c24bSViresh Kumar 	}
6397fa60654SVaibhav Hiremath 
640a463fc15SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
641a463fc15SVaibhav Hiremath 	if (ret) {
642a463fc15SVaibhav Hiremath 		dev_err(dev, "Failed requesting wake_detect gpio %d\n",
643a463fc15SVaibhav Hiremath 				arche_pdata->wake_detect_gpio);
644758ca99dSVaibhav Hiremath 		return ret;
645a463fc15SVaibhav Hiremath 	}
64616fd976cSVaibhav Hiremath 
647c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
648a463fc15SVaibhav Hiremath 
649a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
650a463fc15SVaibhav Hiremath 
6516c1ca56dSVaibhav Hiremath 	spin_lock_init(&arche_pdata->wake_lock);
652886aba55SVaibhav Hiremath 	mutex_init(&arche_pdata->platform_state_mutex);
653970dc85bSBryan O'Donoghue 	init_waitqueue_head(&arche_pdata->wq);
654f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
655f760bbfbSVaibhav Hiremath 		gpio_to_irq(arche_pdata->wake_detect_gpio);
656f760bbfbSVaibhav Hiremath 
657f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
658f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq,
659f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq_thread,
660f760bbfbSVaibhav Hiremath 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
661f760bbfbSVaibhav Hiremath 			dev_name(dev), arche_pdata);
662f760bbfbSVaibhav Hiremath 	if (ret) {
663f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
664f760bbfbSVaibhav Hiremath 		return ret;
665f760bbfbSVaibhav Hiremath 	}
666d29b67d4SVaibhav Hiremath 	disable_irq(arche_pdata->wake_detect_irq);
667f760bbfbSVaibhav Hiremath 
6682923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
6692923c58eSVaibhav Hiremath 	if (ret) {
6702923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
6712923c58eSVaibhav Hiremath 		return ret;
6722923c58eSVaibhav Hiremath 	}
6732923c58eSVaibhav Hiremath 
674d29b67d4SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
675d29b67d4SVaibhav Hiremath 	if (ret) {
676d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
677d29b67d4SVaibhav Hiremath 		goto err_device_remove;
678d29b67d4SVaibhav Hiremath 	}
679d29b67d4SVaibhav Hiremath 
680d29b67d4SVaibhav Hiremath 	arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
681d29b67d4SVaibhav Hiremath 	ret = register_pm_notifier(&arche_pdata->pm_notifier);
682d29b67d4SVaibhav Hiremath 
683d29b67d4SVaibhav Hiremath 	if (ret) {
684d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to register pm notifier %d\n", ret);
685d29b67d4SVaibhav Hiremath 		goto err_device_remove;
686d29b67d4SVaibhav Hiremath 	}
687d29b67d4SVaibhav Hiremath 
688d29b67d4SVaibhav Hiremath 	/* Register callback pointer */
689d29b67d4SVaibhav Hiremath 	arche_platform_change_state_cb = arche_platform_change_state;
690d29b67d4SVaibhav Hiremath 
691d29b67d4SVaibhav Hiremath 	/* Explicitly power off if requested */
692d29b67d4SVaibhav Hiremath 	if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
693886aba55SVaibhav Hiremath 		mutex_lock(&arche_pdata->platform_state_mutex);
694758ca99dSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
695758ca99dSVaibhav Hiremath 		if (ret) {
696758ca99dSVaibhav Hiremath 			dev_err(dev, "Failed to cold boot svc %d\n", ret);
6976743a6fdSVaibhav Hiremath 			goto err_coldboot;
698758ca99dSVaibhav Hiremath 		}
699d29b67d4SVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
700886aba55SVaibhav Hiremath 		mutex_unlock(&arche_pdata->platform_state_mutex);
7019160b7c7SDavid Lin 	}
7029160b7c7SDavid Lin 
7037fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
70472a8c24bSViresh Kumar 	return 0;
7056743a6fdSVaibhav Hiremath 
7066743a6fdSVaibhav Hiremath err_coldboot:
707886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
708d29b67d4SVaibhav Hiremath err_device_remove:
7096743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
7106743a6fdSVaibhav Hiremath 	return ret;
7117fa60654SVaibhav Hiremath }
7127fa60654SVaibhav Hiremath 
713bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
714bc142bbbSVaibhav Hiremath {
715bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
716bc142bbbSVaibhav Hiremath 
717bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
718bc142bbbSVaibhav Hiremath 
719bc142bbbSVaibhav Hiremath 	return 0;
720bc142bbbSVaibhav Hiremath }
721bc142bbbSVaibhav Hiremath 
7227fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
7237fa60654SVaibhav Hiremath {
7247fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
7257fa60654SVaibhav Hiremath 
7269160b7c7SDavid Lin 	unregister_pm_notifier(&arche_pdata->pm_notifier);
7272923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
728bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
7295993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
7307fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, NULL);
7317fa60654SVaibhav Hiremath 
732ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
733ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
734ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
7357fa60654SVaibhav Hiremath 	return 0;
7367fa60654SVaibhav Hiremath }
7377fa60654SVaibhav Hiremath 
7387fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev)
7397fa60654SVaibhav Hiremath {
7407fa60654SVaibhav Hiremath 	/*
7417fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
7427fa60654SVaibhav Hiremath 	 * completely
7437fa60654SVaibhav Hiremath 	 *
7447fa60654SVaibhav Hiremath 	 * TODO: sequence ??
7457fa60654SVaibhav Hiremath 	 *
7467fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
7477fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
7487fa60654SVaibhav Hiremath 	 */
7497fa60654SVaibhav Hiremath 	return 0;
7507fa60654SVaibhav Hiremath }
7517fa60654SVaibhav Hiremath 
7527fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev)
7537fa60654SVaibhav Hiremath {
7547fa60654SVaibhav Hiremath 	/*
7557fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
7567fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
7577fa60654SVaibhav Hiremath 	 * OFF state or standby state.
7587fa60654SVaibhav Hiremath 	 *
7597fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
7607fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
7617fa60654SVaibhav Hiremath 	 *
7627fa60654SVaibhav Hiremath 	 */
7637fa60654SVaibhav Hiremath 	return 0;
7647fa60654SVaibhav Hiremath }
7657fa60654SVaibhav Hiremath 
7661f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev)
7671f77b363SDavid Lin {
7681f77b363SDavid Lin 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
7691f77b363SDavid Lin 
7701f77b363SDavid Lin 	arche_platform_poweroff_seq(arche_pdata);
7711f77b363SDavid Lin 
7721f77b363SDavid Lin 	usb3613_hub_mode_ctrl(false);
7731f77b363SDavid Lin }
7741f77b363SDavid Lin 
7757fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
7767fa60654SVaibhav Hiremath 			arche_platform_suspend,
7777fa60654SVaibhav Hiremath 			arche_platform_resume);
7787fa60654SVaibhav Hiremath 
779cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_platform_of_match[] = {
7807fa60654SVaibhav Hiremath 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
7817fa60654SVaibhav Hiremath 	{ },
7827fa60654SVaibhav Hiremath };
7831e5dd1f8SGreg Kroah-Hartman 
784cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_combined_id[] = {
7851e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
7861e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
7871e5dd1f8SGreg Kroah-Hartman 	{ },
7881e5dd1f8SGreg Kroah-Hartman };
7891e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
7907fa60654SVaibhav Hiremath 
7917fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
7927fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
7937fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
7941f77b363SDavid Lin 	.shutdown	= arche_platform_shutdown,
7957fa60654SVaibhav Hiremath 	.driver		= {
7967fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
7977fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
7981e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
7997fa60654SVaibhav Hiremath 	}
8007fa60654SVaibhav Hiremath };
8017fa60654SVaibhav Hiremath 
8021e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
8031e5dd1f8SGreg Kroah-Hartman {
8041e5dd1f8SGreg Kroah-Hartman 	int retval;
8051e5dd1f8SGreg Kroah-Hartman 
8061e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
8071e5dd1f8SGreg Kroah-Hartman 	if (retval)
8081e5dd1f8SGreg Kroah-Hartman 		return retval;
8091e5dd1f8SGreg Kroah-Hartman 
8107b62b61cSViresh Kumar 	retval = arche_apb_init();
8111e5dd1f8SGreg Kroah-Hartman 	if (retval)
8121e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
8131e5dd1f8SGreg Kroah-Hartman 
8141e5dd1f8SGreg Kroah-Hartman 	return retval;
8151e5dd1f8SGreg Kroah-Hartman }
8161e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
8171e5dd1f8SGreg Kroah-Hartman 
8181e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
8191e5dd1f8SGreg Kroah-Hartman {
8207b62b61cSViresh Kumar 	arche_apb_exit();
8211e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
8221e5dd1f8SGreg Kroah-Hartman }
8231e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
8247fa60654SVaibhav Hiremath 
825a974f469SSandeep Patil MODULE_LICENSE("GPL v2");
8267fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
8277fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
828