1eb50fd3aSGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
27fa60654SVaibhav Hiremath /*
37fa60654SVaibhav Hiremath  * Arche Platform driver to enable Unipro link.
47fa60654SVaibhav Hiremath  *
57fa60654SVaibhav Hiremath  * Copyright 2014-2015 Google Inc.
67fa60654SVaibhav Hiremath  * Copyright 2014-2015 Linaro Ltd.
77fa60654SVaibhav Hiremath  */
87fa60654SVaibhav Hiremath 
97fa60654SVaibhav Hiremath #include <linux/clk.h>
10a463fc15SVaibhav Hiremath #include <linux/delay.h>
114207b587SNishad Kamdar #include <linux/gpio/consumer.h>
123b858df0SViresh Kumar #include <linux/init.h>
133b858df0SViresh Kumar #include <linux/module.h>
143b858df0SViresh Kumar #include <linux/of_platform.h>
157fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h>
163b858df0SViresh Kumar #include <linux/platform_device.h>
173b858df0SViresh Kumar #include <linux/pm.h>
18f760bbfbSVaibhav Hiremath #include <linux/interrupt.h>
19f760bbfbSVaibhav Hiremath #include <linux/irq.h>
209160b7c7SDavid Lin #include <linux/suspend.h>
21f760bbfbSVaibhav Hiremath #include <linux/time.h>
221e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h"
23970dc85bSBryan O'Donoghue #include "greybus.h"
247fa60654SVaibhav Hiremath 
252eccd4aaSJohan Hovold #if IS_ENABLED(CONFIG_USB_HSIC_USB3613)
26ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
272eccd4aaSJohan Hovold #else
282eccd4aaSJohan Hovold static inline int usb3613_hub_mode_ctrl(bool unused)
292eccd4aaSJohan Hovold {
302eccd4aaSJohan Hovold 	return 0;
312eccd4aaSJohan Hovold }
322eccd4aaSJohan Hovold #endif
33ad4d3f95SVaibhav Hiremath 
34f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
35f760bbfbSVaibhav Hiremath 
36685353c1SVaibhav Hiremath enum svc_wakedetect_state {
37685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
38685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
39685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
40685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
41685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
42685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
43685353c1SVaibhav Hiremath };
44685353c1SVaibhav Hiremath 
457fa60654SVaibhav Hiremath struct arche_platform_drvdata {
467fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
474207b587SNishad Kamdar 	struct gpio_desc *svc_reset;
487fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
494207b587SNishad Kamdar 	struct gpio_desc *svc_sysboot;
504207b587SNishad Kamdar 	struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
517fa60654SVaibhav Hiremath 
52e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
53e74d04a5SVaibhav Hiremath 
544207b587SNishad Kamdar 	struct gpio_desc *svc_refclk_req;
557fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
567fa60654SVaibhav Hiremath 
577fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
587fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
597fa60654SVaibhav Hiremath 
607fa60654SVaibhav Hiremath 	int num_apbs;
61a463fc15SVaibhav Hiremath 
62685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
63f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
646c1ca56dSVaibhav Hiremath 	spinlock_t wake_lock;			/* Protect wake_detect_state */
65886aba55SVaibhav Hiremath 	struct mutex platform_state_mutex;	/* Protect state */
66f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
679160b7c7SDavid Lin 	struct notifier_block pm_notifier;
68685353c1SVaibhav Hiremath 
69a463fc15SVaibhav Hiremath 	struct device *dev;
707fa60654SVaibhav Hiremath };
717fa60654SVaibhav Hiremath 
72970dc85bSBryan O'Donoghue /* Requires calling context to hold arche_pdata->platform_state_mutex */
73c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
74c4058b79SBryan O'Donoghue 				     enum arche_platform_state state)
75c4058b79SBryan O'Donoghue {
76c4058b79SBryan O'Donoghue 	arche_pdata->state = state;
77c4058b79SBryan O'Donoghue }
78c4058b79SBryan O'Donoghue 
79c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */
80c4058b79SBryan O'Donoghue static void arche_platform_set_wake_detect_state(
81c4058b79SBryan O'Donoghue 				struct arche_platform_drvdata *arche_pdata,
82c4058b79SBryan O'Donoghue 				enum svc_wakedetect_state state)
83c4058b79SBryan O'Donoghue {
84c4058b79SBryan O'Donoghue 	arche_pdata->wake_detect_state = state;
85c4058b79SBryan O'Donoghue }
86c4058b79SBryan O'Donoghue 
874207b587SNishad Kamdar static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff)
887fa60654SVaibhav Hiremath {
894207b587SNishad Kamdar 	gpiod_set_raw_value(gpio, onoff);
907fa60654SVaibhav Hiremath }
917fa60654SVaibhav Hiremath 
92f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data)
93f760bbfbSVaibhav Hiremath {
94f760bbfbSVaibhav Hiremath 	int ret;
95f760bbfbSVaibhav Hiremath 
96f760bbfbSVaibhav Hiremath 	ret = apb_ctrl_coldboot(dev);
97f760bbfbSVaibhav Hiremath 	if (ret)
98f760bbfbSVaibhav Hiremath 		dev_warn(dev, "failed to coldboot\n");
99f760bbfbSVaibhav Hiremath 
100f760bbfbSVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
101f760bbfbSVaibhav Hiremath 	return 0;
102f760bbfbSVaibhav Hiremath }
103f760bbfbSVaibhav Hiremath 
104fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data)
105fd60ac58SVaibhav Hiremath {
106fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
107fd60ac58SVaibhav Hiremath 
108e915ce48SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
109e915ce48SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
110e915ce48SVaibhav Hiremath 		dev_warn(dev, "failed to control hub device\n");
111e915ce48SVaibhav Hiremath 
112fd60ac58SVaibhav Hiremath 	return 0;
113fd60ac58SVaibhav Hiremath }
114fd60ac58SVaibhav Hiremath 
1157ba535ecSVaibhav Hiremath static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
11616fe18caSVaibhav Hiremath {
11716fe18caSVaibhav Hiremath 	/* Enable interrupt here, to read event back from SVC */
11816fe18caSVaibhav Hiremath 	enable_irq(arche_pdata->wake_detect_irq);
11916fe18caSVaibhav Hiremath }
12016fe18caSVaibhav Hiremath 
121f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
122f760bbfbSVaibhav Hiremath {
123f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
124f760bbfbSVaibhav Hiremath 	unsigned long flags;
125f760bbfbSVaibhav Hiremath 
1266c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
127f760bbfbSVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
128f760bbfbSVaibhav Hiremath 		/* Something is wrong */
1296c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
130f760bbfbSVaibhav Hiremath 		return IRQ_HANDLED;
131f760bbfbSVaibhav Hiremath 	}
132f760bbfbSVaibhav Hiremath 
133c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata,
134c4058b79SBryan O'Donoghue 					     WD_STATE_COLDBOOT_START);
1356c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
136f760bbfbSVaibhav Hiremath 
137ff788de0SVaibhav Hiremath 	/* It should complete power cycle, so first make sure it is poweroff */
138ff788de0SVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
139e915ce48SVaibhav Hiremath 
140f760bbfbSVaibhav Hiremath 	/* Bring APB out of reset: cold boot sequence */
141f760bbfbSVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
142f760bbfbSVaibhav Hiremath 
143a7ddda1fSVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
144a7ddda1fSVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
145a7ddda1fSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
146a7ddda1fSVaibhav Hiremath 
1476c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
148c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
1496c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
150f760bbfbSVaibhav Hiremath 
151f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
152f760bbfbSVaibhav Hiremath }
153f760bbfbSVaibhav Hiremath 
154f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
155f760bbfbSVaibhav Hiremath {
156f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
157f760bbfbSVaibhav Hiremath 	unsigned long flags;
158f760bbfbSVaibhav Hiremath 
1596c1ca56dSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->wake_lock, flags);
160f760bbfbSVaibhav Hiremath 
1614207b587SNishad Kamdar 	if (gpiod_get_value(arche_pdata->wake_detect)) {
162f760bbfbSVaibhav Hiremath 		/* wake/detect rising */
163f760bbfbSVaibhav Hiremath 
164f760bbfbSVaibhav Hiremath 		/*
165f760bbfbSVaibhav Hiremath 		 * If wake/detect line goes high after low, within less than
166f760bbfbSVaibhav Hiremath 		 * 30msec, then standby boot sequence is initiated, which is not
167f760bbfbSVaibhav Hiremath 		 * supported/implemented as of now. So ignore it.
168f760bbfbSVaibhav Hiremath 		 */
169f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
170f760bbfbSVaibhav Hiremath 			if (time_before(jiffies,
171f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_start +
172f760bbfbSVaibhav Hiremath 					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
173c4058b79SBryan O'Donoghue 				arche_platform_set_wake_detect_state(arche_pdata,
174c4058b79SBryan O'Donoghue 								     WD_STATE_IDLE);
175f760bbfbSVaibhav Hiremath 			} else {
176bf766625SMitchell Tasman 				/*
177bf766625SMitchell Tasman 				 * Check we are not in middle of irq thread
178bf766625SMitchell Tasman 				 * already
179bf766625SMitchell Tasman 				 */
180f760bbfbSVaibhav Hiremath 				if (arche_pdata->wake_detect_state !=
181f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_START) {
182c4058b79SBryan O'Donoghue 					arche_platform_set_wake_detect_state(arche_pdata,
183c4058b79SBryan O'Donoghue 									     WD_STATE_COLDBOOT_TRIG);
1846c1ca56dSVaibhav Hiremath 					spin_unlock_irqrestore(
1856c1ca56dSVaibhav Hiremath 						&arche_pdata->wake_lock,
1866c1ca56dSVaibhav Hiremath 						flags);
187f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
188f760bbfbSVaibhav Hiremath 				}
189f760bbfbSVaibhav Hiremath 			}
190f760bbfbSVaibhav Hiremath 		}
191f760bbfbSVaibhav Hiremath 	} else {
192f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
193f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
194f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
195f760bbfbSVaibhav Hiremath 			/*
1964a27e3e0SEames Trinh 			 * In the beginning, when wake/detect goes low
197461ab807SGioh Kim 			 * (first time), we assume it is meant for coldboot
198461ab807SGioh Kim 			 * and set the flag. If wake/detect line stays low
199461ab807SGioh Kim 			 * beyond 30msec, then it is coldboot else fallback
200461ab807SGioh Kim 			 * to standby boot.
201f760bbfbSVaibhav Hiremath 			 */
202c4058b79SBryan O'Donoghue 			arche_platform_set_wake_detect_state(arche_pdata,
203c4058b79SBryan O'Donoghue 							     WD_STATE_BOOT_INIT);
204f760bbfbSVaibhav Hiremath 		}
205f760bbfbSVaibhav Hiremath 	}
206f760bbfbSVaibhav Hiremath 
2076c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
208f760bbfbSVaibhav Hiremath 
209f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
210f760bbfbSVaibhav Hiremath }
211f760bbfbSVaibhav Hiremath 
212886aba55SVaibhav Hiremath /*
213886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
214886aba55SVaibhav Hiremath  */
215461ab807SGioh Kim static int
216461ab807SGioh Kim arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
217758ca99dSVaibhav Hiremath {
218758ca99dSVaibhav Hiremath 	int ret;
219758ca99dSVaibhav Hiremath 
220599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
221599159b6SVaibhav Hiremath 		return 0;
222599159b6SVaibhav Hiremath 
223758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
224758ca99dSVaibhav Hiremath 
2254207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
226758ca99dSVaibhav Hiremath 
2274207b587SNishad Kamdar 	gpiod_set_value(arche_pdata->svc_sysboot, 0);
228758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
229758ca99dSVaibhav Hiremath 
230758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
231758ca99dSVaibhav Hiremath 	if (ret) {
232758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
233758ca99dSVaibhav Hiremath 			ret);
234758ca99dSVaibhav Hiremath 		return ret;
235758ca99dSVaibhav Hiremath 	}
236758ca99dSVaibhav Hiremath 
237758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
2384207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi);
239758ca99dSVaibhav Hiremath 
240c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
241e74d04a5SVaibhav Hiremath 
242758ca99dSVaibhav Hiremath 	return 0;
243758ca99dSVaibhav Hiremath }
244758ca99dSVaibhav Hiremath 
245886aba55SVaibhav Hiremath /*
246886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
247886aba55SVaibhav Hiremath  */
248461ab807SGioh Kim static int
249461ab807SGioh Kim arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
2507691fed2SVaibhav Hiremath {
2517a867d14SVaibhav Hiremath 	int ret;
2527a867d14SVaibhav Hiremath 
253599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
254c61a8b49SVaibhav Hiremath 		return 0;
255599159b6SVaibhav Hiremath 
2567691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
2577691fed2SVaibhav Hiremath 
2584207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
2597691fed2SVaibhav Hiremath 
2604207b587SNishad Kamdar 	gpiod_set_value(arche_pdata->svc_sysboot, 1);
2617691fed2SVaibhav Hiremath 
2627691fed2SVaibhav Hiremath 	usleep_range(100, 200);
2637a867d14SVaibhav Hiremath 
2647a867d14SVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
2657a867d14SVaibhav Hiremath 	if (ret) {
2667a867d14SVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
2677a867d14SVaibhav Hiremath 			ret);
2687a867d14SVaibhav Hiremath 		return ret;
2697a867d14SVaibhav Hiremath 	}
2707a867d14SVaibhav Hiremath 
2714207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset,	!arche_pdata->is_reset_act_hi);
2727691fed2SVaibhav Hiremath 
273c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
2747691fed2SVaibhav Hiremath 
275c61a8b49SVaibhav Hiremath 	return 0;
2767691fed2SVaibhav Hiremath }
2777691fed2SVaibhav Hiremath 
278886aba55SVaibhav Hiremath /*
279886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
280886aba55SVaibhav Hiremath  */
281461ab807SGioh Kim static void
282461ab807SGioh Kim arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
2837fa60654SVaibhav Hiremath {
284f760bbfbSVaibhav Hiremath 	unsigned long flags;
285f760bbfbSVaibhav Hiremath 
286599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
287599159b6SVaibhav Hiremath 		return;
288599159b6SVaibhav Hiremath 
28925847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
29025847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
291d2320b2dSVaibhav Hiremath 		disable_irq(arche_pdata->wake_detect_irq);
29216fd976cSVaibhav Hiremath 
2936c1ca56dSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->wake_lock, flags);
294c4058b79SBryan O'Donoghue 		arche_platform_set_wake_detect_state(arche_pdata,
295c4058b79SBryan O'Donoghue 						     WD_STATE_IDLE);
2966c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
2977a867d14SVaibhav Hiremath 	}
298b4c95fcaSVaibhav Hiremath 
299d8b16338SVaibhav Hiremath 	clk_disable_unprepare(arche_pdata->svc_ref_clk);
30025847ee7SVaibhav Hiremath 
3017fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
3024207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset,	arche_pdata->is_reset_act_hi);
303e74d04a5SVaibhav Hiremath 
304c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
3057fa60654SVaibhav Hiremath }
3067fa60654SVaibhav Hiremath 
3072923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
30807df5b7cSKamal Heib 			   struct device_attribute *attr,
30907df5b7cSKamal Heib 			   const char *buf, size_t count)
3102923c58eSVaibhav Hiremath {
3111dab154eSWolfram Sang 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3122923c58eSVaibhav Hiremath 	int ret = 0;
3132923c58eSVaibhav Hiremath 
314886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
315886aba55SVaibhav Hiremath 
3162923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
3172923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
318886aba55SVaibhav Hiremath 			goto exit;
3192923c58eSVaibhav Hiremath 
320fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
321fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
322fd60ac58SVaibhav Hiremath 
3232923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
324ad4d3f95SVaibhav Hiremath 
3252923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
3262923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
327886aba55SVaibhav Hiremath 			goto exit;
3282923c58eSVaibhav Hiremath 
3290b1283e3SVaibhav Hiremath 		/* First we want to make sure we power off everything
330cf3ba55dSElise Lennion 		 * and then activate back again
331cf3ba55dSElise Lennion 		 */
3320b1283e3SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
3330b1283e3SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
3340b1283e3SVaibhav Hiremath 
3357ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
3362923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
3370b1283e3SVaibhav Hiremath 		if (ret)
3380b1283e3SVaibhav Hiremath 			goto exit;
33916fe18caSVaibhav Hiremath 
3402923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
3412923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
342886aba55SVaibhav Hiremath 			goto exit;
3432923c58eSVaibhav Hiremath 
3442923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
3457691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
3467691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
347886aba55SVaibhav Hiremath 			goto exit;
3487691fed2SVaibhav Hiremath 
349921dbe52SVaibhav Hiremath 		/*
350921dbe52SVaibhav Hiremath 		 * Here we only control SVC.
351921dbe52SVaibhav Hiremath 		 *
352921dbe52SVaibhav Hiremath 		 * In case of FW_FLASHING mode we do not want to control
353921dbe52SVaibhav Hiremath 		 * APBs, as in case of V2, SPI bus is shared between both
354921dbe52SVaibhav Hiremath 		 * the APBs. So let user chose which APB he wants to flash.
355921dbe52SVaibhav Hiremath 		 */
3567691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
357fd60ac58SVaibhav Hiremath 
358c61a8b49SVaibhav Hiremath 		ret = arche_platform_fw_flashing_seq(arche_pdata);
359c61a8b49SVaibhav Hiremath 		if (ret)
360c61a8b49SVaibhav Hiremath 			goto exit;
3612923c58eSVaibhav Hiremath 	} else {
3622923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
3632923c58eSVaibhav Hiremath 		ret = -EINVAL;
3642923c58eSVaibhav Hiremath 	}
3652923c58eSVaibhav Hiremath 
366886aba55SVaibhav Hiremath exit:
367886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
3682923c58eSVaibhav Hiremath 	return ret ? ret : count;
3692923c58eSVaibhav Hiremath }
3702923c58eSVaibhav Hiremath 
3712923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
3722923c58eSVaibhav Hiremath 			  struct device_attribute *attr, char *buf)
3732923c58eSVaibhav Hiremath {
3742923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3752923c58eSVaibhav Hiremath 
3762923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
3772923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
3782923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
3792923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
3802923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
3812923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
3822923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
3837691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
3847691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
3852923c58eSVaibhav Hiremath 	default:
3862923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
3872923c58eSVaibhav Hiremath 	}
3882923c58eSVaibhav Hiremath }
3892923c58eSVaibhav Hiremath 
3902923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
3912923c58eSVaibhav Hiremath 
3929160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier,
3939160b7c7SDavid Lin 				      unsigned long pm_event, void *unused)
3949160b7c7SDavid Lin {
3959160b7c7SDavid Lin 	struct arche_platform_drvdata *arche_pdata =
3969160b7c7SDavid Lin 		container_of(notifier, struct arche_platform_drvdata,
3979160b7c7SDavid Lin 			     pm_notifier);
398886aba55SVaibhav Hiremath 	int ret = NOTIFY_DONE;
3999160b7c7SDavid Lin 
400886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
4019160b7c7SDavid Lin 	switch (pm_event) {
4029160b7c7SDavid Lin 	case PM_SUSPEND_PREPARE:
403886aba55SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
404886aba55SVaibhav Hiremath 			ret = NOTIFY_STOP;
405886aba55SVaibhav Hiremath 			break;
406886aba55SVaibhav Hiremath 		}
4079160b7c7SDavid Lin 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
4089160b7c7SDavid Lin 		arche_platform_poweroff_seq(arche_pdata);
4099160b7c7SDavid Lin 		break;
4109160b7c7SDavid Lin 	case PM_POST_SUSPEND:
4118ef0b538SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
4128ef0b538SVaibhav Hiremath 			break;
4138ef0b538SVaibhav Hiremath 
4147ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
41516fd976cSVaibhav Hiremath 		arche_platform_coldboot_seq(arche_pdata);
4169160b7c7SDavid Lin 		break;
4179160b7c7SDavid Lin 	default:
4189160b7c7SDavid Lin 		break;
4199160b7c7SDavid Lin 	}
420886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
4219160b7c7SDavid Lin 
422886aba55SVaibhav Hiremath 	return ret;
4239160b7c7SDavid Lin }
4249160b7c7SDavid Lin 
4257fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
4267fa60654SVaibhav Hiremath {
4277fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
4287fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
4297fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
4307fa60654SVaibhav Hiremath 	int ret;
4314207b587SNishad Kamdar 	unsigned int flags;
4327fa60654SVaibhav Hiremath 
433461ab807SGioh Kim 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata),
434461ab807SGioh Kim 				   GFP_KERNEL);
4357fa60654SVaibhav Hiremath 	if (!arche_pdata)
4367fa60654SVaibhav Hiremath 		return -ENOMEM;
4377fa60654SVaibhav Hiremath 
4387fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
4397fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
4407fa60654SVaibhav Hiremath 							     "svc,reset-active-high");
4414207b587SNishad Kamdar 	if (arche_pdata->is_reset_act_hi)
4424207b587SNishad Kamdar 		flags = GPIOD_OUT_HIGH;
4434207b587SNishad Kamdar 	else
4444207b587SNishad Kamdar 		flags = GPIOD_OUT_LOW;
4454207b587SNishad Kamdar 
4464207b587SNishad Kamdar 	arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags);
4474207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_reset)) {
4484207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_reset);
4494207b587SNishad Kamdar 		dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret);
4507fa60654SVaibhav Hiremath 		return ret;
4517fa60654SVaibhav Hiremath 	}
452c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
4537fa60654SVaibhav Hiremath 
4544207b587SNishad Kamdar 	arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot",
4554207b587SNishad Kamdar 						  GPIOD_OUT_LOW);
4564207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_sysboot)) {
4574207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_sysboot);
4584207b587SNishad Kamdar 		dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret);
4597fa60654SVaibhav Hiremath 		return ret;
4607fa60654SVaibhav Hiremath 	}
4617fa60654SVaibhav Hiremath 
4627fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
4634207b587SNishad Kamdar 	arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req",
4644207b587SNishad Kamdar 						     GPIOD_IN);
4654207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_refclk_req)) {
4664207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_refclk_req);
4674207b587SNishad Kamdar 		dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret);
4687fa60654SVaibhav Hiremath 		return ret;
4697fa60654SVaibhav Hiremath 	}
4707fa60654SVaibhav Hiremath 
4717fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
4727fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
4737fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
4747fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
4757fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
4767fa60654SVaibhav Hiremath 		return ret;
4777fa60654SVaibhav Hiremath 	}
4787fa60654SVaibhav Hiremath 
4797fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
4807fa60654SVaibhav Hiremath 
4817fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
4827fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
4837fa60654SVaibhav Hiremath 
4844207b587SNishad Kamdar 	arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect",
4854207b587SNishad Kamdar 						  GPIOD_IN);
4864207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->wake_detect)) {
4874207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->wake_detect);
4884207b587SNishad Kamdar 		dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret);
489758ca99dSVaibhav Hiremath 		return ret;
490a463fc15SVaibhav Hiremath 	}
49116fd976cSVaibhav Hiremath 
492c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
493a463fc15SVaibhav Hiremath 
494a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
495a463fc15SVaibhav Hiremath 
4966c1ca56dSVaibhav Hiremath 	spin_lock_init(&arche_pdata->wake_lock);
497886aba55SVaibhav Hiremath 	mutex_init(&arche_pdata->platform_state_mutex);
498f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
4994207b587SNishad Kamdar 		gpiod_to_irq(arche_pdata->wake_detect);
500f760bbfbSVaibhav Hiremath 
501f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
502f760bbfbSVaibhav Hiremath 					arche_platform_wd_irq,
503f760bbfbSVaibhav Hiremath 					arche_platform_wd_irq_thread,
504461ab807SGioh Kim 					IRQF_TRIGGER_FALLING |
505461ab807SGioh Kim 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
506f760bbfbSVaibhav Hiremath 					dev_name(dev), arche_pdata);
507f760bbfbSVaibhav Hiremath 	if (ret) {
508f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
509f760bbfbSVaibhav Hiremath 		return ret;
510f760bbfbSVaibhav Hiremath 	}
511d29b67d4SVaibhav Hiremath 	disable_irq(arche_pdata->wake_detect_irq);
512f760bbfbSVaibhav Hiremath 
5132923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
5142923c58eSVaibhav Hiremath 	if (ret) {
5152923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
5162923c58eSVaibhav Hiremath 		return ret;
5172923c58eSVaibhav Hiremath 	}
5182923c58eSVaibhav Hiremath 
519d29b67d4SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
520d29b67d4SVaibhav Hiremath 	if (ret) {
521d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
522d29b67d4SVaibhav Hiremath 		goto err_device_remove;
523d29b67d4SVaibhav Hiremath 	}
524d29b67d4SVaibhav Hiremath 
525d29b67d4SVaibhav Hiremath 	arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
526d29b67d4SVaibhav Hiremath 	ret = register_pm_notifier(&arche_pdata->pm_notifier);
527d29b67d4SVaibhav Hiremath 
528d29b67d4SVaibhav Hiremath 	if (ret) {
529d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to register pm notifier %d\n", ret);
530d29b67d4SVaibhav Hiremath 		goto err_device_remove;
531d29b67d4SVaibhav Hiremath 	}
532d29b67d4SVaibhav Hiremath 
533d29b67d4SVaibhav Hiremath 	/* Explicitly power off if requested */
534d29b67d4SVaibhav Hiremath 	if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
535886aba55SVaibhav Hiremath 		mutex_lock(&arche_pdata->platform_state_mutex);
536758ca99dSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
537758ca99dSVaibhav Hiremath 		if (ret) {
538758ca99dSVaibhav Hiremath 			dev_err(dev, "Failed to cold boot svc %d\n", ret);
5396743a6fdSVaibhav Hiremath 			goto err_coldboot;
540758ca99dSVaibhav Hiremath 		}
541d29b67d4SVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
542886aba55SVaibhav Hiremath 		mutex_unlock(&arche_pdata->platform_state_mutex);
5439160b7c7SDavid Lin 	}
5449160b7c7SDavid Lin 
5457fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
54672a8c24bSViresh Kumar 	return 0;
5476743a6fdSVaibhav Hiremath 
5486743a6fdSVaibhav Hiremath err_coldboot:
549886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
550d29b67d4SVaibhav Hiremath err_device_remove:
5516743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
5526743a6fdSVaibhav Hiremath 	return ret;
5537fa60654SVaibhav Hiremath }
5547fa60654SVaibhav Hiremath 
555bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
556bc142bbbSVaibhav Hiremath {
557bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
558bc142bbbSVaibhav Hiremath 
559bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
560bc142bbbSVaibhav Hiremath 
561bc142bbbSVaibhav Hiremath 	return 0;
562bc142bbbSVaibhav Hiremath }
563bc142bbbSVaibhav Hiremath 
5647fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
5657fa60654SVaibhav Hiremath {
5667fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
5677fa60654SVaibhav Hiremath 
5689160b7c7SDavid Lin 	unregister_pm_notifier(&arche_pdata->pm_notifier);
5692923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
570bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
5715993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
5727fa60654SVaibhav Hiremath 
573ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
574ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
575ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
5767fa60654SVaibhav Hiremath 	return 0;
5777fa60654SVaibhav Hiremath }
5787fa60654SVaibhav Hiremath 
5790687090aSArnd Bergmann static __maybe_unused int arche_platform_suspend(struct device *dev)
5807fa60654SVaibhav Hiremath {
5817fa60654SVaibhav Hiremath 	/*
5827fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
5837fa60654SVaibhav Hiremath 	 * completely
5847fa60654SVaibhav Hiremath 	 *
5857fa60654SVaibhav Hiremath 	 * TODO: sequence ??
5867fa60654SVaibhav Hiremath 	 *
5877fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
5887fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
5897fa60654SVaibhav Hiremath 	 */
5907fa60654SVaibhav Hiremath 	return 0;
5917fa60654SVaibhav Hiremath }
5927fa60654SVaibhav Hiremath 
5930687090aSArnd Bergmann static __maybe_unused int arche_platform_resume(struct device *dev)
5947fa60654SVaibhav Hiremath {
5957fa60654SVaibhav Hiremath 	/*
5967fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
5977fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
5987fa60654SVaibhav Hiremath 	 * OFF state or standby state.
5997fa60654SVaibhav Hiremath 	 *
6007fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
6017fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
6027fa60654SVaibhav Hiremath 	 *
6037fa60654SVaibhav Hiremath 	 */
6047fa60654SVaibhav Hiremath 	return 0;
6057fa60654SVaibhav Hiremath }
6067fa60654SVaibhav Hiremath 
6071f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev)
6081f77b363SDavid Lin {
6091f77b363SDavid Lin 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
6101f77b363SDavid Lin 
6111f77b363SDavid Lin 	arche_platform_poweroff_seq(arche_pdata);
6121f77b363SDavid Lin 
6131f77b363SDavid Lin 	usb3613_hub_mode_ctrl(false);
6141f77b363SDavid Lin }
6151f77b363SDavid Lin 
6167fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
6177fa60654SVaibhav Hiremath 			arche_platform_suspend,
6187fa60654SVaibhav Hiremath 			arche_platform_resume);
6197fa60654SVaibhav Hiremath 
620cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_platform_of_match[] = {
621bf766625SMitchell Tasman 	/* Use PID/VID of SVC device */
622bf766625SMitchell Tasman 	{ .compatible = "google,arche-platform", },
6237fa60654SVaibhav Hiremath 	{ },
6247fa60654SVaibhav Hiremath };
6251e5dd1f8SGreg Kroah-Hartman 
626cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_combined_id[] = {
627bf766625SMitchell Tasman 	/* Use PID/VID of SVC device */
628bf766625SMitchell Tasman 	{ .compatible = "google,arche-platform", },
6291e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
6301e5dd1f8SGreg Kroah-Hartman 	{ },
6311e5dd1f8SGreg Kroah-Hartman };
6321e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
6337fa60654SVaibhav Hiremath 
6347fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
6357fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
6367fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
6371f77b363SDavid Lin 	.shutdown	= arche_platform_shutdown,
6387fa60654SVaibhav Hiremath 	.driver		= {
6397fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
6407fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
6411e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
6427fa60654SVaibhav Hiremath 	}
6437fa60654SVaibhav Hiremath };
6447fa60654SVaibhav Hiremath 
6451e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
6461e5dd1f8SGreg Kroah-Hartman {
6471e5dd1f8SGreg Kroah-Hartman 	int retval;
6481e5dd1f8SGreg Kroah-Hartman 
6491e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
6501e5dd1f8SGreg Kroah-Hartman 	if (retval)
6511e5dd1f8SGreg Kroah-Hartman 		return retval;
6521e5dd1f8SGreg Kroah-Hartman 
6537b62b61cSViresh Kumar 	retval = arche_apb_init();
6541e5dd1f8SGreg Kroah-Hartman 	if (retval)
6551e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
6561e5dd1f8SGreg Kroah-Hartman 
6571e5dd1f8SGreg Kroah-Hartman 	return retval;
6581e5dd1f8SGreg Kroah-Hartman }
6591e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
6601e5dd1f8SGreg Kroah-Hartman 
6611e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
6621e5dd1f8SGreg Kroah-Hartman {
6637b62b61cSViresh Kumar 	arche_apb_exit();
6641e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
6651e5dd1f8SGreg Kroah-Hartman }
6661e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
6677fa60654SVaibhav Hiremath 
668a974f469SSandeep Patil MODULE_LICENSE("GPL v2");
6697fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
6707fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
671