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>
22ec0ad868SGreg Kroah-Hartman #include <linux/greybus.h>
23decb929fSRob Herring #include <linux/of.h>
241e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h"
257fa60654SVaibhav Hiremath 
262eccd4aaSJohan Hovold #if IS_ENABLED(CONFIG_USB_HSIC_USB3613)
27ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
282eccd4aaSJohan Hovold #else
usb3613_hub_mode_ctrl(bool unused)292eccd4aaSJohan Hovold static inline int usb3613_hub_mode_ctrl(bool unused)
302eccd4aaSJohan Hovold {
312eccd4aaSJohan Hovold 	return 0;
322eccd4aaSJohan Hovold }
332eccd4aaSJohan Hovold #endif
34ad4d3f95SVaibhav Hiremath 
35f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
36f760bbfbSVaibhav Hiremath 
37685353c1SVaibhav Hiremath enum svc_wakedetect_state {
38685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
39685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
40685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
41685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
42685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
43685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
44685353c1SVaibhav Hiremath };
45685353c1SVaibhav Hiremath 
467fa60654SVaibhav Hiremath struct arche_platform_drvdata {
477fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
484207b587SNishad Kamdar 	struct gpio_desc *svc_reset;
497fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
504207b587SNishad Kamdar 	struct gpio_desc *svc_sysboot;
514207b587SNishad Kamdar 	struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
527fa60654SVaibhav Hiremath 
53e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
54e74d04a5SVaibhav Hiremath 
554207b587SNishad Kamdar 	struct gpio_desc *svc_refclk_req;
567fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
577fa60654SVaibhav Hiremath 
587fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
597fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
607fa60654SVaibhav Hiremath 
617fa60654SVaibhav Hiremath 	int num_apbs;
62a463fc15SVaibhav Hiremath 
63685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
64f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
656c1ca56dSVaibhav Hiremath 	spinlock_t wake_lock;			/* Protect wake_detect_state */
66886aba55SVaibhav Hiremath 	struct mutex platform_state_mutex;	/* Protect state */
67f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
689160b7c7SDavid Lin 	struct notifier_block pm_notifier;
69685353c1SVaibhav Hiremath 
70a463fc15SVaibhav Hiremath 	struct device *dev;
717fa60654SVaibhav Hiremath };
727fa60654SVaibhav Hiremath 
73970dc85bSBryan O'Donoghue /* Requires calling context to hold arche_pdata->platform_state_mutex */
arche_platform_set_state(struct arche_platform_drvdata * arche_pdata,enum arche_platform_state state)74c4058b79SBryan O'Donoghue static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
75c4058b79SBryan O'Donoghue 				     enum arche_platform_state state)
76c4058b79SBryan O'Donoghue {
77c4058b79SBryan O'Donoghue 	arche_pdata->state = state;
78c4058b79SBryan O'Donoghue }
79c4058b79SBryan O'Donoghue 
80c4058b79SBryan O'Donoghue /* Requires arche_pdata->wake_lock is held by calling context */
arche_platform_set_wake_detect_state(struct arche_platform_drvdata * arche_pdata,enum svc_wakedetect_state state)810778f04bSBeatriz Martins de Carvalho static void arche_platform_set_wake_detect_state(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 
svc_reset_onoff(struct gpio_desc * gpio,bool onoff)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 
apb_cold_boot(struct device * dev,void * data)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 
apb_poweroff(struct device * dev,void * data)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 
arche_platform_wd_irq_en(struct arche_platform_drvdata * arche_pdata)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 
arche_platform_wd_irq_thread(int irq,void * devid)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 
arche_platform_wd_irq(int irq,void * devid)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);
1840778f04bSBeatriz Martins de Carvalho 					spin_unlock_irqrestore(&arche_pdata->wake_lock,
1856c1ca56dSVaibhav Hiremath 							       flags);
186f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
187f760bbfbSVaibhav Hiremath 				}
188f760bbfbSVaibhav Hiremath 			}
189f760bbfbSVaibhav Hiremath 		}
190f760bbfbSVaibhav Hiremath 	} else {
191f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
192f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
193f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
194f760bbfbSVaibhav Hiremath 			/*
1954a27e3e0SEames Trinh 			 * In the beginning, when wake/detect goes low
196461ab807SGioh Kim 			 * (first time), we assume it is meant for coldboot
197461ab807SGioh Kim 			 * and set the flag. If wake/detect line stays low
198461ab807SGioh Kim 			 * beyond 30msec, then it is coldboot else fallback
199461ab807SGioh Kim 			 * to standby boot.
200f760bbfbSVaibhav Hiremath 			 */
201c4058b79SBryan O'Donoghue 			arche_platform_set_wake_detect_state(arche_pdata,
202c4058b79SBryan O'Donoghue 							     WD_STATE_BOOT_INIT);
203f760bbfbSVaibhav Hiremath 		}
204f760bbfbSVaibhav Hiremath 	}
205f760bbfbSVaibhav Hiremath 
2066c1ca56dSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
207f760bbfbSVaibhav Hiremath 
208f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
209f760bbfbSVaibhav Hiremath }
210f760bbfbSVaibhav Hiremath 
211886aba55SVaibhav Hiremath /*
212886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
213886aba55SVaibhav Hiremath  */
214461ab807SGioh Kim static int
arche_platform_coldboot_seq(struct arche_platform_drvdata * arche_pdata)215461ab807SGioh Kim arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
216758ca99dSVaibhav Hiremath {
217758ca99dSVaibhav Hiremath 	int ret;
218758ca99dSVaibhav Hiremath 
219599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
220599159b6SVaibhav Hiremath 		return 0;
221599159b6SVaibhav Hiremath 
222758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
223758ca99dSVaibhav Hiremath 
2244207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
225758ca99dSVaibhav Hiremath 
2264207b587SNishad Kamdar 	gpiod_set_value(arche_pdata->svc_sysboot, 0);
227758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
228758ca99dSVaibhav Hiremath 
229758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
230758ca99dSVaibhav Hiremath 	if (ret) {
231758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
232758ca99dSVaibhav Hiremath 			ret);
233758ca99dSVaibhav Hiremath 		return ret;
234758ca99dSVaibhav Hiremath 	}
235758ca99dSVaibhav Hiremath 
236758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
2374207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi);
238758ca99dSVaibhav Hiremath 
239c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
240e74d04a5SVaibhav Hiremath 
241758ca99dSVaibhav Hiremath 	return 0;
242758ca99dSVaibhav Hiremath }
243758ca99dSVaibhav Hiremath 
244886aba55SVaibhav Hiremath /*
245886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
246886aba55SVaibhav Hiremath  */
247461ab807SGioh Kim static int
arche_platform_fw_flashing_seq(struct arche_platform_drvdata * arche_pdata)248461ab807SGioh Kim arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
2497691fed2SVaibhav Hiremath {
2507a867d14SVaibhav Hiremath 	int ret;
2517a867d14SVaibhav Hiremath 
252599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
253c61a8b49SVaibhav Hiremath 		return 0;
254599159b6SVaibhav Hiremath 
2557691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
2567691fed2SVaibhav Hiremath 
2574207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi);
2587691fed2SVaibhav Hiremath 
2594207b587SNishad Kamdar 	gpiod_set_value(arche_pdata->svc_sysboot, 1);
2607691fed2SVaibhav Hiremath 
2617691fed2SVaibhav Hiremath 	usleep_range(100, 200);
2627a867d14SVaibhav Hiremath 
2637a867d14SVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
2647a867d14SVaibhav Hiremath 	if (ret) {
2657a867d14SVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
2667a867d14SVaibhav Hiremath 			ret);
2677a867d14SVaibhav Hiremath 		return ret;
2687a867d14SVaibhav Hiremath 	}
2697a867d14SVaibhav Hiremath 
2704207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset,	!arche_pdata->is_reset_act_hi);
2717691fed2SVaibhav Hiremath 
272c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
2737691fed2SVaibhav Hiremath 
274c61a8b49SVaibhav Hiremath 	return 0;
2757691fed2SVaibhav Hiremath }
2767691fed2SVaibhav Hiremath 
277886aba55SVaibhav Hiremath /*
278886aba55SVaibhav Hiremath  * Requires arche_pdata->platform_state_mutex to be held
279886aba55SVaibhav Hiremath  */
280461ab807SGioh Kim static void
arche_platform_poweroff_seq(struct arche_platform_drvdata * arche_pdata)281461ab807SGioh Kim arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
2827fa60654SVaibhav Hiremath {
283f760bbfbSVaibhav Hiremath 	unsigned long flags;
284f760bbfbSVaibhav Hiremath 
285599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
286599159b6SVaibhav Hiremath 		return;
287599159b6SVaibhav Hiremath 
28825847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
28925847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
290d2320b2dSVaibhav Hiremath 		disable_irq(arche_pdata->wake_detect_irq);
29116fd976cSVaibhav Hiremath 
2926c1ca56dSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->wake_lock, flags);
293c4058b79SBryan O'Donoghue 		arche_platform_set_wake_detect_state(arche_pdata,
294c4058b79SBryan O'Donoghue 						     WD_STATE_IDLE);
2956c1ca56dSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
2967a867d14SVaibhav Hiremath 	}
297b4c95fcaSVaibhav Hiremath 
298d8b16338SVaibhav Hiremath 	clk_disable_unprepare(arche_pdata->svc_ref_clk);
29925847ee7SVaibhav Hiremath 
3007fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
3014207b587SNishad Kamdar 	svc_reset_onoff(arche_pdata->svc_reset,	arche_pdata->is_reset_act_hi);
302e74d04a5SVaibhav Hiremath 
303c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
3047fa60654SVaibhav Hiremath }
3057fa60654SVaibhav Hiremath 
state_store(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)3062923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
30707df5b7cSKamal Heib 			   struct device_attribute *attr,
30807df5b7cSKamal Heib 			   const char *buf, size_t count)
3092923c58eSVaibhav Hiremath {
3101dab154eSWolfram Sang 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3112923c58eSVaibhav Hiremath 	int ret = 0;
3122923c58eSVaibhav Hiremath 
313886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
314886aba55SVaibhav Hiremath 
3152923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
3162923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
317886aba55SVaibhav Hiremath 			goto exit;
3182923c58eSVaibhav Hiremath 
319fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
320fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
321fd60ac58SVaibhav Hiremath 
3222923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
323ad4d3f95SVaibhav Hiremath 
3242923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
3252923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
326886aba55SVaibhav Hiremath 			goto exit;
3272923c58eSVaibhav Hiremath 
3280b1283e3SVaibhav Hiremath 		/* First we want to make sure we power off everything
329cf3ba55dSElise Lennion 		 * and then activate back again
330cf3ba55dSElise Lennion 		 */
3310b1283e3SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
3320b1283e3SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
3330b1283e3SVaibhav Hiremath 
3347ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
3352923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
3360b1283e3SVaibhav Hiremath 		if (ret)
3370b1283e3SVaibhav Hiremath 			goto exit;
33816fe18caSVaibhav Hiremath 
3392923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
3402923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
341886aba55SVaibhav Hiremath 			goto exit;
3422923c58eSVaibhav Hiremath 
3432923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
3447691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
3457691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
346886aba55SVaibhav Hiremath 			goto exit;
3477691fed2SVaibhav Hiremath 
348921dbe52SVaibhav Hiremath 		/*
349921dbe52SVaibhav Hiremath 		 * Here we only control SVC.
350921dbe52SVaibhav Hiremath 		 *
351921dbe52SVaibhav Hiremath 		 * In case of FW_FLASHING mode we do not want to control
352921dbe52SVaibhav Hiremath 		 * APBs, as in case of V2, SPI bus is shared between both
353921dbe52SVaibhav Hiremath 		 * the APBs. So let user chose which APB he wants to flash.
354921dbe52SVaibhav Hiremath 		 */
3557691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
356fd60ac58SVaibhav Hiremath 
357c61a8b49SVaibhav Hiremath 		ret = arche_platform_fw_flashing_seq(arche_pdata);
358c61a8b49SVaibhav Hiremath 		if (ret)
359c61a8b49SVaibhav Hiremath 			goto exit;
3602923c58eSVaibhav Hiremath 	} else {
3612923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
3622923c58eSVaibhav Hiremath 		ret = -EINVAL;
3632923c58eSVaibhav Hiremath 	}
3642923c58eSVaibhav Hiremath 
365886aba55SVaibhav Hiremath exit:
366886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
3672923c58eSVaibhav Hiremath 	return ret ? ret : count;
3682923c58eSVaibhav Hiremath }
3692923c58eSVaibhav Hiremath 
state_show(struct device * dev,struct device_attribute * attr,char * buf)3702923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
3712923c58eSVaibhav Hiremath 			  struct device_attribute *attr, char *buf)
3722923c58eSVaibhav Hiremath {
3732923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3742923c58eSVaibhav Hiremath 
3752923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
3762923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
3772923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
3782923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
3792923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
3802923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
3812923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
3827691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
3837691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
3842923c58eSVaibhav Hiremath 	default:
3852923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
3862923c58eSVaibhav Hiremath 	}
3872923c58eSVaibhav Hiremath }
3882923c58eSVaibhav Hiremath 
3892923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
3902923c58eSVaibhav Hiremath 
arche_platform_pm_notifier(struct notifier_block * notifier,unsigned long pm_event,void * unused)3919160b7c7SDavid Lin static int arche_platform_pm_notifier(struct notifier_block *notifier,
3929160b7c7SDavid Lin 				      unsigned long pm_event, void *unused)
3939160b7c7SDavid Lin {
3949160b7c7SDavid Lin 	struct arche_platform_drvdata *arche_pdata =
3959160b7c7SDavid Lin 		container_of(notifier, struct arche_platform_drvdata,
3969160b7c7SDavid Lin 			     pm_notifier);
397886aba55SVaibhav Hiremath 	int ret = NOTIFY_DONE;
3989160b7c7SDavid Lin 
399886aba55SVaibhav Hiremath 	mutex_lock(&arche_pdata->platform_state_mutex);
4009160b7c7SDavid Lin 	switch (pm_event) {
4019160b7c7SDavid Lin 	case PM_SUSPEND_PREPARE:
402886aba55SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
403886aba55SVaibhav Hiremath 			ret = NOTIFY_STOP;
404886aba55SVaibhav Hiremath 			break;
405886aba55SVaibhav Hiremath 		}
4069160b7c7SDavid Lin 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
4079160b7c7SDavid Lin 		arche_platform_poweroff_seq(arche_pdata);
4089160b7c7SDavid Lin 		break;
4099160b7c7SDavid Lin 	case PM_POST_SUSPEND:
4108ef0b538SVaibhav Hiremath 		if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
4118ef0b538SVaibhav Hiremath 			break;
4128ef0b538SVaibhav Hiremath 
4137ba535ecSVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
41416fd976cSVaibhav Hiremath 		arche_platform_coldboot_seq(arche_pdata);
4159160b7c7SDavid Lin 		break;
4169160b7c7SDavid Lin 	default:
4179160b7c7SDavid Lin 		break;
4189160b7c7SDavid Lin 	}
419886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
4209160b7c7SDavid Lin 
421886aba55SVaibhav Hiremath 	return ret;
4229160b7c7SDavid Lin }
4239160b7c7SDavid Lin 
arche_platform_probe(struct platform_device * pdev)4247fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
4257fa60654SVaibhav Hiremath {
4267fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
4277fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
4287fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
4297fa60654SVaibhav Hiremath 	int ret;
4304207b587SNishad Kamdar 	unsigned int flags;
4317fa60654SVaibhav Hiremath 
432461ab807SGioh Kim 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata),
433461ab807SGioh Kim 				   GFP_KERNEL);
4347fa60654SVaibhav Hiremath 	if (!arche_pdata)
4357fa60654SVaibhav Hiremath 		return -ENOMEM;
4367fa60654SVaibhav Hiremath 
4377fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
4387fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
4397fa60654SVaibhav Hiremath 							     "svc,reset-active-high");
4404207b587SNishad Kamdar 	if (arche_pdata->is_reset_act_hi)
4414207b587SNishad Kamdar 		flags = GPIOD_OUT_HIGH;
4424207b587SNishad Kamdar 	else
4434207b587SNishad Kamdar 		flags = GPIOD_OUT_LOW;
4444207b587SNishad Kamdar 
4454207b587SNishad Kamdar 	arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags);
4464207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_reset)) {
4474207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_reset);
4484207b587SNishad Kamdar 		dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret);
4497fa60654SVaibhav Hiremath 		return ret;
4507fa60654SVaibhav Hiremath 	}
451c4058b79SBryan O'Donoghue 	arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
4527fa60654SVaibhav Hiremath 
4534207b587SNishad Kamdar 	arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot",
4544207b587SNishad Kamdar 						  GPIOD_OUT_LOW);
4554207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_sysboot)) {
4564207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_sysboot);
4574207b587SNishad Kamdar 		dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret);
4587fa60654SVaibhav Hiremath 		return ret;
4597fa60654SVaibhav Hiremath 	}
4607fa60654SVaibhav Hiremath 
4617fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
4624207b587SNishad Kamdar 	arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req",
4634207b587SNishad Kamdar 						     GPIOD_IN);
4644207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->svc_refclk_req)) {
4654207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->svc_refclk_req);
4664207b587SNishad Kamdar 		dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret);
4677fa60654SVaibhav Hiremath 		return ret;
4687fa60654SVaibhav Hiremath 	}
4697fa60654SVaibhav Hiremath 
4707fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
4717fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
4727fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
4737fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
4747fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
4757fa60654SVaibhav Hiremath 		return ret;
4767fa60654SVaibhav Hiremath 	}
4777fa60654SVaibhav Hiremath 
4787fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
4797fa60654SVaibhav Hiremath 
4807fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
4817fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
4827fa60654SVaibhav Hiremath 
4834207b587SNishad Kamdar 	arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect",
4844207b587SNishad Kamdar 						  GPIOD_IN);
4854207b587SNishad Kamdar 	if (IS_ERR(arche_pdata->wake_detect)) {
4864207b587SNishad Kamdar 		ret = PTR_ERR(arche_pdata->wake_detect);
4874207b587SNishad Kamdar 		dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret);
488758ca99dSVaibhav Hiremath 		return ret;
489a463fc15SVaibhav Hiremath 	}
49016fd976cSVaibhav Hiremath 
491c4058b79SBryan O'Donoghue 	arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
492a463fc15SVaibhav Hiremath 
493a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
494a463fc15SVaibhav Hiremath 
4956c1ca56dSVaibhav Hiremath 	spin_lock_init(&arche_pdata->wake_lock);
496886aba55SVaibhav Hiremath 	mutex_init(&arche_pdata->platform_state_mutex);
497f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
4984207b587SNishad Kamdar 		gpiod_to_irq(arche_pdata->wake_detect);
499f760bbfbSVaibhav Hiremath 
500f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
501f760bbfbSVaibhav Hiremath 					arche_platform_wd_irq,
502f760bbfbSVaibhav Hiremath 					arche_platform_wd_irq_thread,
503461ab807SGioh Kim 					IRQF_TRIGGER_FALLING |
504461ab807SGioh Kim 					IRQF_TRIGGER_RISING | IRQF_ONESHOT,
505f760bbfbSVaibhav Hiremath 					dev_name(dev), arche_pdata);
506f760bbfbSVaibhav Hiremath 	if (ret) {
507f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
508f760bbfbSVaibhav Hiremath 		return ret;
509f760bbfbSVaibhav Hiremath 	}
510d29b67d4SVaibhav Hiremath 	disable_irq(arche_pdata->wake_detect_irq);
511f760bbfbSVaibhav Hiremath 
5122923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
5132923c58eSVaibhav Hiremath 	if (ret) {
5142923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
5152923c58eSVaibhav Hiremath 		return ret;
5162923c58eSVaibhav Hiremath 	}
5172923c58eSVaibhav Hiremath 
518d29b67d4SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
519d29b67d4SVaibhav Hiremath 	if (ret) {
520d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
521d29b67d4SVaibhav Hiremath 		goto err_device_remove;
522d29b67d4SVaibhav Hiremath 	}
523d29b67d4SVaibhav Hiremath 
524d29b67d4SVaibhav Hiremath 	arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
525d29b67d4SVaibhav Hiremath 	ret = register_pm_notifier(&arche_pdata->pm_notifier);
526d29b67d4SVaibhav Hiremath 
527d29b67d4SVaibhav Hiremath 	if (ret) {
528d29b67d4SVaibhav Hiremath 		dev_err(dev, "failed to register pm notifier %d\n", ret);
529d29b67d4SVaibhav Hiremath 		goto err_device_remove;
530d29b67d4SVaibhav Hiremath 	}
531d29b67d4SVaibhav Hiremath 
532d29b67d4SVaibhav Hiremath 	/* Explicitly power off if requested */
533d29b67d4SVaibhav Hiremath 	if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
534886aba55SVaibhav Hiremath 		mutex_lock(&arche_pdata->platform_state_mutex);
535758ca99dSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
536758ca99dSVaibhav Hiremath 		if (ret) {
537758ca99dSVaibhav Hiremath 			dev_err(dev, "Failed to cold boot svc %d\n", ret);
5386743a6fdSVaibhav Hiremath 			goto err_coldboot;
539758ca99dSVaibhav Hiremath 		}
540d29b67d4SVaibhav Hiremath 		arche_platform_wd_irq_en(arche_pdata);
541886aba55SVaibhav Hiremath 		mutex_unlock(&arche_pdata->platform_state_mutex);
5429160b7c7SDavid Lin 	}
5439160b7c7SDavid Lin 
5447fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
54572a8c24bSViresh Kumar 	return 0;
5466743a6fdSVaibhav Hiremath 
5476743a6fdSVaibhav Hiremath err_coldboot:
548886aba55SVaibhav Hiremath 	mutex_unlock(&arche_pdata->platform_state_mutex);
549d29b67d4SVaibhav Hiremath err_device_remove:
5506743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
5516743a6fdSVaibhav Hiremath 	return ret;
5527fa60654SVaibhav Hiremath }
5537fa60654SVaibhav Hiremath 
arche_remove_child(struct device * dev,void * unused)554bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
555bc142bbbSVaibhav Hiremath {
556bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
557bc142bbbSVaibhav Hiremath 
558bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
559bc142bbbSVaibhav Hiremath 
560bc142bbbSVaibhav Hiremath 	return 0;
561bc142bbbSVaibhav Hiremath }
562bc142bbbSVaibhav Hiremath 
arche_platform_remove(struct platform_device * pdev)5637846a2aeSUwe Kleine-König static void arche_platform_remove(struct platform_device *pdev)
5647fa60654SVaibhav Hiremath {
5657fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
5667fa60654SVaibhav Hiremath 
5679160b7c7SDavid Lin 	unregister_pm_notifier(&arche_pdata->pm_notifier);
5682923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
569bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
5705993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
5717fa60654SVaibhav Hiremath 
572ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
573ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
5747fa60654SVaibhav Hiremath }
5757fa60654SVaibhav Hiremath 
arche_platform_suspend(struct device * dev)5760687090aSArnd Bergmann static __maybe_unused int arche_platform_suspend(struct device *dev)
5777fa60654SVaibhav Hiremath {
5787fa60654SVaibhav Hiremath 	/*
5797fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
5807fa60654SVaibhav Hiremath 	 * completely
5817fa60654SVaibhav Hiremath 	 *
5827fa60654SVaibhav Hiremath 	 * TODO: sequence ??
5837fa60654SVaibhav Hiremath 	 *
5847fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
5857fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
5867fa60654SVaibhav Hiremath 	 */
5877fa60654SVaibhav Hiremath 	return 0;
5887fa60654SVaibhav Hiremath }
5897fa60654SVaibhav Hiremath 
arche_platform_resume(struct device * dev)5900687090aSArnd Bergmann static __maybe_unused int arche_platform_resume(struct device *dev)
5917fa60654SVaibhav Hiremath {
5927fa60654SVaibhav Hiremath 	/*
5937fa60654SVaibhav Hiremath 	 * At least for ES2 we have to meet the delay requirement between
5947fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
5957fa60654SVaibhav Hiremath 	 * OFF state or standby state.
5967fa60654SVaibhav Hiremath 	 *
5977fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
5987fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
5997fa60654SVaibhav Hiremath 	 *
6007fa60654SVaibhav Hiremath 	 */
6017fa60654SVaibhav Hiremath 	return 0;
6027fa60654SVaibhav Hiremath }
6037fa60654SVaibhav Hiremath 
arche_platform_shutdown(struct platform_device * pdev)6041f77b363SDavid Lin static void arche_platform_shutdown(struct platform_device *pdev)
6051f77b363SDavid Lin {
6061f77b363SDavid Lin 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
6071f77b363SDavid Lin 
6081f77b363SDavid Lin 	arche_platform_poweroff_seq(arche_pdata);
6091f77b363SDavid Lin 
6101f77b363SDavid Lin 	usb3613_hub_mode_ctrl(false);
6111f77b363SDavid Lin }
6121f77b363SDavid Lin 
6137fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
6147fa60654SVaibhav Hiremath 			arche_platform_suspend,
6157fa60654SVaibhav Hiremath 			arche_platform_resume);
6167fa60654SVaibhav Hiremath 
617cdcffc0cSEva Rachel Retuya static const struct of_device_id arche_platform_of_match[] = {
618bf766625SMitchell Tasman 	/* Use PID/VID of SVC device */
619bf766625SMitchell Tasman 	{ .compatible = "google,arche-platform", },
6207fa60654SVaibhav Hiremath 	{ },
6217fa60654SVaibhav Hiremath };
62246c6fc13SArnd Bergmann MODULE_DEVICE_TABLE(of, arche_platform_of_match);
6237fa60654SVaibhav Hiremath 
6247fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
6257fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
6267846a2aeSUwe Kleine-König 	.remove_new	= arche_platform_remove,
6271f77b363SDavid Lin 	.shutdown	= arche_platform_shutdown,
6287fa60654SVaibhav Hiremath 	.driver		= {
6297fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
6307fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
6311e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
6327fa60654SVaibhav Hiremath 	}
6337fa60654SVaibhav Hiremath };
6347fa60654SVaibhav Hiremath 
arche_init(void)6351e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
6361e5dd1f8SGreg Kroah-Hartman {
6371e5dd1f8SGreg Kroah-Hartman 	int retval;
6381e5dd1f8SGreg Kroah-Hartman 
6391e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
6401e5dd1f8SGreg Kroah-Hartman 	if (retval)
6411e5dd1f8SGreg Kroah-Hartman 		return retval;
6421e5dd1f8SGreg Kroah-Hartman 
6437b62b61cSViresh Kumar 	retval = arche_apb_init();
6441e5dd1f8SGreg Kroah-Hartman 	if (retval)
6451e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
6461e5dd1f8SGreg Kroah-Hartman 
6471e5dd1f8SGreg Kroah-Hartman 	return retval;
6481e5dd1f8SGreg Kroah-Hartman }
6491e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
6501e5dd1f8SGreg Kroah-Hartman 
arche_exit(void)6511e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
6521e5dd1f8SGreg Kroah-Hartman {
6537b62b61cSViresh Kumar 	arche_apb_exit();
6541e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
6551e5dd1f8SGreg Kroah-Hartman }
6561e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
6577fa60654SVaibhav Hiremath 
658a974f469SSandeep Patil MODULE_LICENSE("GPL v2");
6597fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
6607fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
661