17fa60654SVaibhav Hiremath /*
27fa60654SVaibhav Hiremath  * Arche Platform driver to enable Unipro link.
37fa60654SVaibhav Hiremath  *
47fa60654SVaibhav Hiremath  * Copyright 2014-2015 Google Inc.
57fa60654SVaibhav Hiremath  * Copyright 2014-2015 Linaro Ltd.
67fa60654SVaibhav Hiremath  *
77fa60654SVaibhav Hiremath  * Released under the GPLv2 only.
87fa60654SVaibhav Hiremath  */
97fa60654SVaibhav Hiremath 
107fa60654SVaibhav Hiremath #include <linux/clk.h>
11a463fc15SVaibhav Hiremath #include <linux/delay.h>
123b858df0SViresh Kumar #include <linux/gpio.h>
133b858df0SViresh Kumar #include <linux/init.h>
143b858df0SViresh Kumar #include <linux/module.h>
157fa60654SVaibhav Hiremath #include <linux/of_gpio.h>
163b858df0SViresh Kumar #include <linux/of_platform.h>
177fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h>
183b858df0SViresh Kumar #include <linux/platform_device.h>
193b858df0SViresh Kumar #include <linux/pm.h>
20f760bbfbSVaibhav Hiremath #include <linux/interrupt.h>
21f760bbfbSVaibhav Hiremath #include <linux/irq.h>
22f760bbfbSVaibhav Hiremath #include <linux/time.h>
231e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h"
247fa60654SVaibhav Hiremath 
25ad4d3f95SVaibhav Hiremath #include <linux/usb/usb3613.h>
26ad4d3f95SVaibhav Hiremath 
27f760bbfbSVaibhav Hiremath #define WD_COLDBOOT_PULSE_WIDTH_MS	30
28f760bbfbSVaibhav Hiremath 
29685353c1SVaibhav Hiremath enum svc_wakedetect_state {
30685353c1SVaibhav Hiremath 	WD_STATE_IDLE,			/* Default state = pulled high/low */
31685353c1SVaibhav Hiremath 	WD_STATE_BOOT_INIT,		/* WD = falling edge (low) */
32685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_TRIG,		/* WD = rising edge (high), > 30msec */
33685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_TRIG,	/* As of now not used ?? */
34685353c1SVaibhav Hiremath 	WD_STATE_COLDBOOT_START,	/* Cold boot process started */
35685353c1SVaibhav Hiremath 	WD_STATE_STANDBYBOOT_START,	/* Not used */
36685353c1SVaibhav Hiremath };
37685353c1SVaibhav Hiremath 
387fa60654SVaibhav Hiremath struct arche_platform_drvdata {
397fa60654SVaibhav Hiremath 	/* Control GPIO signals to and from AP <=> SVC */
407fa60654SVaibhav Hiremath 	int svc_reset_gpio;
417fa60654SVaibhav Hiremath 	bool is_reset_act_hi;
427fa60654SVaibhav Hiremath 	int svc_sysboot_gpio;
43a463fc15SVaibhav Hiremath 	int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
447fa60654SVaibhav Hiremath 
45e74d04a5SVaibhav Hiremath 	enum arche_platform_state state;
46e74d04a5SVaibhav Hiremath 
477fa60654SVaibhav Hiremath 	unsigned int svc_refclk_req;
487fa60654SVaibhav Hiremath 	struct clk *svc_ref_clk;
497fa60654SVaibhav Hiremath 
507fa60654SVaibhav Hiremath 	struct pinctrl *pinctrl;
517fa60654SVaibhav Hiremath 	struct pinctrl_state *pin_default;
527fa60654SVaibhav Hiremath 
537fa60654SVaibhav Hiremath 	int num_apbs;
54a463fc15SVaibhav Hiremath 
55a463fc15SVaibhav Hiremath 	struct delayed_work delayed_work;
56685353c1SVaibhav Hiremath 	enum svc_wakedetect_state wake_detect_state;
57f760bbfbSVaibhav Hiremath 	int wake_detect_irq;
58f760bbfbSVaibhav Hiremath 	spinlock_t lock;
59f760bbfbSVaibhav Hiremath 	unsigned long wake_detect_start;
60685353c1SVaibhav Hiremath 
61a463fc15SVaibhav Hiremath 	struct device *dev;
627fa60654SVaibhav Hiremath };
637fa60654SVaibhav Hiremath 
647fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
657fa60654SVaibhav Hiremath {
667fa60654SVaibhav Hiremath 	gpio_set_value(gpio, onoff);
677fa60654SVaibhav Hiremath }
687fa60654SVaibhav Hiremath 
69f760bbfbSVaibhav Hiremath static int apb_cold_boot(struct device *dev, void *data)
70f760bbfbSVaibhav Hiremath {
71f760bbfbSVaibhav Hiremath 	int ret;
72f760bbfbSVaibhav Hiremath 
73f760bbfbSVaibhav Hiremath 	ret = apb_ctrl_coldboot(dev);
74f760bbfbSVaibhav Hiremath 	if (ret)
75f760bbfbSVaibhav Hiremath 		dev_warn(dev, "failed to coldboot\n");
76f760bbfbSVaibhav Hiremath 
77f760bbfbSVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
78f760bbfbSVaibhav Hiremath 	return 0;
79f760bbfbSVaibhav Hiremath }
80f760bbfbSVaibhav Hiremath 
81fd60ac58SVaibhav Hiremath static int apb_fw_flashing_state(struct device *dev, void *data)
82fd60ac58SVaibhav Hiremath {
83fd60ac58SVaibhav Hiremath 	int ret;
84fd60ac58SVaibhav Hiremath 
85fd60ac58SVaibhav Hiremath 	ret = apb_ctrl_fw_flashing(dev);
86fd60ac58SVaibhav Hiremath 	if (ret)
87fd60ac58SVaibhav Hiremath 		dev_warn(dev, "failed to switch to fw flashing state\n");
88fd60ac58SVaibhav Hiremath 
89fd60ac58SVaibhav Hiremath 	/*Child nodes are independent, so do not exit coldboot operation */
90fd60ac58SVaibhav Hiremath 	return 0;
91fd60ac58SVaibhav Hiremath }
92fd60ac58SVaibhav Hiremath 
93fd60ac58SVaibhav Hiremath static int apb_poweroff(struct device *dev, void *data)
94fd60ac58SVaibhav Hiremath {
95fd60ac58SVaibhav Hiremath 	apb_ctrl_poweroff(dev);
96fd60ac58SVaibhav Hiremath 
97fd60ac58SVaibhav Hiremath 	return 0;
98fd60ac58SVaibhav Hiremath }
99fd60ac58SVaibhav Hiremath 
100a463fc15SVaibhav Hiremath /**
101db5a3bcaSVaibhav Hiremath  * hub_conf_delayed_work - Configures USB3613 device to HUB mode
102db5a3bcaSVaibhav Hiremath  *
103db5a3bcaSVaibhav Hiremath  * The idea here is to split the APB coldboot operation with slow HUB configuration,
104db5a3bcaSVaibhav Hiremath  * so that driver response to wake/detect event can be met.
105db5a3bcaSVaibhav Hiremath  * So expectation is, once code reaches here, means initial unipro linkup
106db5a3bcaSVaibhav Hiremath  * between APB<->Switch was successful, so now just take it to AP.
107a463fc15SVaibhav Hiremath  */
108db5a3bcaSVaibhav Hiremath static void hub_conf_delayed_work(struct work_struct *work)
109a463fc15SVaibhav Hiremath {
110a463fc15SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata =
111a463fc15SVaibhav Hiremath 		container_of(work, struct arche_platform_drvdata, delayed_work.work);
112ad4d3f95SVaibhav Hiremath 
113ad4d3f95SVaibhav Hiremath 	/* Enable HUB3613 into HUB mode. */
114ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(true))
115ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
116a463fc15SVaibhav Hiremath }
117a463fc15SVaibhav Hiremath 
11816fe18caSVaibhav Hiremath static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata)
11916fe18caSVaibhav Hiremath {
12016fe18caSVaibhav Hiremath 	/* Assert wake/detect = Detect event from AP */
12116fe18caSVaibhav Hiremath 	gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
12216fe18caSVaibhav Hiremath 
12316fe18caSVaibhav Hiremath 	/* Enable interrupt here, to read event back from SVC */
12416fe18caSVaibhav Hiremath 	gpio_direction_input(arche_pdata->wake_detect_gpio);
12516fe18caSVaibhav Hiremath 	enable_irq(arche_pdata->wake_detect_irq);
12616fe18caSVaibhav Hiremath }
12716fe18caSVaibhav Hiremath 
128f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
129f760bbfbSVaibhav Hiremath {
130f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
131f760bbfbSVaibhav Hiremath 	unsigned long flags;
132f760bbfbSVaibhav Hiremath 
133f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
134f760bbfbSVaibhav Hiremath 	if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
135f760bbfbSVaibhav Hiremath 		/* Something is wrong */
136f760bbfbSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->lock, flags);
137f760bbfbSVaibhav Hiremath 		return IRQ_HANDLED;
138f760bbfbSVaibhav Hiremath 	}
139f760bbfbSVaibhav Hiremath 
140f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START;
141f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
142f760bbfbSVaibhav Hiremath 
143f760bbfbSVaibhav Hiremath 	/* Bring APB out of reset: cold boot sequence */
144f760bbfbSVaibhav Hiremath 	device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
145f760bbfbSVaibhav Hiremath 
146f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
147f760bbfbSVaibhav Hiremath 	/* USB HUB configuration */
148f760bbfbSVaibhav Hiremath 	schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
149f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_IDLE;
150f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
151f760bbfbSVaibhav Hiremath 
152f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
153f760bbfbSVaibhav Hiremath }
154f760bbfbSVaibhav Hiremath 
155f760bbfbSVaibhav Hiremath static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
156f760bbfbSVaibhav Hiremath {
157f760bbfbSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = devid;
158f760bbfbSVaibhav Hiremath 	unsigned long flags;
159f760bbfbSVaibhav Hiremath 
160f760bbfbSVaibhav Hiremath 	spin_lock_irqsave(&arche_pdata->lock, flags);
161f760bbfbSVaibhav Hiremath 
162f760bbfbSVaibhav Hiremath 	if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
163f760bbfbSVaibhav Hiremath 		/* wake/detect rising */
164f760bbfbSVaibhav Hiremath 
165f760bbfbSVaibhav Hiremath 		/*
166f760bbfbSVaibhav Hiremath 		 * If wake/detect line goes high after low, within less than
167f760bbfbSVaibhav Hiremath 		 * 30msec, then standby boot sequence is initiated, which is not
168f760bbfbSVaibhav Hiremath 		 * supported/implemented as of now. So ignore it.
169f760bbfbSVaibhav Hiremath 		 */
170f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
171f760bbfbSVaibhav Hiremath 			if (time_before(jiffies,
172f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_start +
173f760bbfbSVaibhav Hiremath 					msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
174f760bbfbSVaibhav Hiremath 				/* No harm with cancellation, even if not pending */
175f760bbfbSVaibhav Hiremath 				cancel_delayed_work(&arche_pdata->delayed_work);
176f760bbfbSVaibhav Hiremath 				arche_pdata->wake_detect_state = WD_STATE_IDLE;
177f760bbfbSVaibhav Hiremath 			} else {
178f760bbfbSVaibhav Hiremath 				/* Check we are not in middle of irq thread already */
179f760bbfbSVaibhav Hiremath 				if (arche_pdata->wake_detect_state !=
180f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_START) {
181f760bbfbSVaibhav Hiremath 					arche_pdata->wake_detect_state =
182f760bbfbSVaibhav Hiremath 						WD_STATE_COLDBOOT_TRIG;
183f760bbfbSVaibhav Hiremath 					spin_unlock_irqrestore(&arche_pdata->lock, flags);
184f760bbfbSVaibhav Hiremath 					return IRQ_WAKE_THREAD;
185f760bbfbSVaibhav Hiremath 				}
186f760bbfbSVaibhav Hiremath 			}
187f760bbfbSVaibhav Hiremath 		}
188f760bbfbSVaibhav Hiremath 	} else {
189f760bbfbSVaibhav Hiremath 		/* wake/detect falling */
190f760bbfbSVaibhav Hiremath 		if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
191f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_start = jiffies;
192f760bbfbSVaibhav Hiremath 			/* No harm with cancellation even if it is not pending*/
193f760bbfbSVaibhav Hiremath 			cancel_delayed_work(&arche_pdata->delayed_work);
194f760bbfbSVaibhav Hiremath 			/*
195f760bbfbSVaibhav Hiremath 			 * In the begining, when wake/detect goes low (first time), we assume
196f760bbfbSVaibhav Hiremath 			 * it is meant for coldboot and set the flag. If wake/detect line stays low
197f760bbfbSVaibhav Hiremath 			 * beyond 30msec, then it is coldboot else fallback to standby boot.
198f760bbfbSVaibhav Hiremath 			 */
199f760bbfbSVaibhav Hiremath 			arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT;
200f760bbfbSVaibhav Hiremath 		}
201f760bbfbSVaibhav Hiremath 	}
202f760bbfbSVaibhav Hiremath 
203f760bbfbSVaibhav Hiremath 	spin_unlock_irqrestore(&arche_pdata->lock, flags);
204f760bbfbSVaibhav Hiremath 
205f760bbfbSVaibhav Hiremath 	return IRQ_HANDLED;
206f760bbfbSVaibhav Hiremath }
207f760bbfbSVaibhav Hiremath 
208758ca99dSVaibhav Hiremath static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
209758ca99dSVaibhav Hiremath {
210758ca99dSVaibhav Hiremath 	int ret;
211758ca99dSVaibhav Hiremath 
212599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
213599159b6SVaibhav Hiremath 		return 0;
214599159b6SVaibhav Hiremath 
215758ca99dSVaibhav Hiremath 	dev_info(arche_pdata->dev, "Booting from cold boot state\n");
216758ca99dSVaibhav Hiremath 
217758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
218758ca99dSVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
219758ca99dSVaibhav Hiremath 
220758ca99dSVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
221758ca99dSVaibhav Hiremath 	usleep_range(100, 200);
222758ca99dSVaibhav Hiremath 
223758ca99dSVaibhav Hiremath 	ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
224758ca99dSVaibhav Hiremath 	if (ret) {
225758ca99dSVaibhav Hiremath 		dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
226758ca99dSVaibhav Hiremath 				ret);
227758ca99dSVaibhav Hiremath 		return ret;
228758ca99dSVaibhav Hiremath 	}
229758ca99dSVaibhav Hiremath 
230758ca99dSVaibhav Hiremath 	/* bring SVC out of reset */
231758ca99dSVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
232758ca99dSVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
233758ca99dSVaibhav Hiremath 
234e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
235e74d04a5SVaibhav Hiremath 
236758ca99dSVaibhav Hiremath 	return 0;
237758ca99dSVaibhav Hiremath }
238758ca99dSVaibhav Hiremath 
2397691fed2SVaibhav Hiremath static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
2407691fed2SVaibhav Hiremath {
241599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
242599159b6SVaibhav Hiremath 		return;
243599159b6SVaibhav Hiremath 
2447691fed2SVaibhav Hiremath 	dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
2457691fed2SVaibhav Hiremath 
2467691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2477691fed2SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
2487691fed2SVaibhav Hiremath 
2497691fed2SVaibhav Hiremath 	gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
2507691fed2SVaibhav Hiremath 
2517691fed2SVaibhav Hiremath 	usleep_range(100, 200);
2527691fed2SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2537691fed2SVaibhav Hiremath 			!arche_pdata->is_reset_act_hi);
2547691fed2SVaibhav Hiremath 
2557691fed2SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
2567691fed2SVaibhav Hiremath 
2577691fed2SVaibhav Hiremath }
2587691fed2SVaibhav Hiremath 
2595993e2bfSVaibhav Hiremath static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
2607fa60654SVaibhav Hiremath {
261f760bbfbSVaibhav Hiremath 	unsigned long flags;
262f760bbfbSVaibhav Hiremath 
263599159b6SVaibhav Hiremath 	if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
264599159b6SVaibhav Hiremath 		return;
265599159b6SVaibhav Hiremath 
26625847ee7SVaibhav Hiremath 	/* If in fw_flashing mode, then no need to repeate things again */
26725847ee7SVaibhav Hiremath 	if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
268b4c95fcaSVaibhav Hiremath 		/* Send disconnect/detach event to SVC */
2690786212dSVaibhav Hiremath 		gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
270b4c95fcaSVaibhav Hiremath 		usleep_range(100, 200);
271f760bbfbSVaibhav Hiremath 		spin_lock_irqsave(&arche_pdata->lock, flags);
272685353c1SVaibhav Hiremath 		arche_pdata->wake_detect_state = WD_STATE_IDLE;
273f760bbfbSVaibhav Hiremath 		spin_unlock_irqrestore(&arche_pdata->lock, flags);
274b4c95fcaSVaibhav Hiremath 
275d8b16338SVaibhav Hiremath 		clk_disable_unprepare(arche_pdata->svc_ref_clk);
27625847ee7SVaibhav Hiremath 	}
27725847ee7SVaibhav Hiremath 
2787fa60654SVaibhav Hiremath 	/* As part of exit, put APB back in reset state */
2797fa60654SVaibhav Hiremath 	svc_reset_onoff(arche_pdata->svc_reset_gpio,
2807fa60654SVaibhav Hiremath 			arche_pdata->is_reset_act_hi);
281e74d04a5SVaibhav Hiremath 
282e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
2837fa60654SVaibhav Hiremath }
2847fa60654SVaibhav Hiremath 
2852923c58eSVaibhav Hiremath static ssize_t state_store(struct device *dev,
2862923c58eSVaibhav Hiremath 		struct device_attribute *attr, const char *buf, size_t count)
2872923c58eSVaibhav Hiremath {
2882923c58eSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
2892923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
2902923c58eSVaibhav Hiremath 	int ret = 0;
2912923c58eSVaibhav Hiremath 
2922923c58eSVaibhav Hiremath 	if (sysfs_streq(buf, "off")) {
2932923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
2942923c58eSVaibhav Hiremath 			return count;
2952923c58eSVaibhav Hiremath 
296fd60ac58SVaibhav Hiremath 		/*  If SVC goes down, bring down APB's as well */
297fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
298fd60ac58SVaibhav Hiremath 
2992923c58eSVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
300ad4d3f95SVaibhav Hiremath 
301ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
302ad4d3f95SVaibhav Hiremath 		if (ret)
303ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
304ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
3052923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "active")) {
3062923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
3072923c58eSVaibhav Hiremath 			return count;
3082923c58eSVaibhav Hiremath 
3092923c58eSVaibhav Hiremath 		ret = arche_platform_coldboot_seq(arche_pdata);
31016fe18caSVaibhav Hiremath 
31116fe18caSVaibhav Hiremath 		assert_wakedetect(arche_pdata);
3122923c58eSVaibhav Hiremath 	} else if (sysfs_streq(buf, "standby")) {
3132923c58eSVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
3142923c58eSVaibhav Hiremath 			return count;
3152923c58eSVaibhav Hiremath 
3162923c58eSVaibhav Hiremath 		dev_warn(arche_pdata->dev, "standby state not supported\n");
3177691fed2SVaibhav Hiremath 	} else if (sysfs_streq(buf, "fw_flashing")) {
3187691fed2SVaibhav Hiremath 		if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
3197691fed2SVaibhav Hiremath 			return count;
3207691fed2SVaibhav Hiremath 
3217691fed2SVaibhav Hiremath 		/* First we want to make sure we power off everything
3227691fed2SVaibhav Hiremath 		 * and then enter FW flashing state */
323fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
324fd60ac58SVaibhav Hiremath 
3257691fed2SVaibhav Hiremath 		arche_platform_poweroff_seq(arche_pdata);
326fd60ac58SVaibhav Hiremath 
327ad4d3f95SVaibhav Hiremath 		ret = usb3613_hub_mode_ctrl(false);
328ad4d3f95SVaibhav Hiremath 		if (ret)
329ad4d3f95SVaibhav Hiremath 			dev_warn(arche_pdata->dev, "failed to control hub device\n");
330ad4d3f95SVaibhav Hiremath 			/* TODO: Should we do anything more here ?? */
331ad4d3f95SVaibhav Hiremath 
3327691fed2SVaibhav Hiremath 		arche_platform_fw_flashing_seq(arche_pdata);
333fd60ac58SVaibhav Hiremath 
334fd60ac58SVaibhav Hiremath 		device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
3352923c58eSVaibhav Hiremath 	} else {
3362923c58eSVaibhav Hiremath 		dev_err(arche_pdata->dev, "unknown state\n");
3372923c58eSVaibhav Hiremath 		ret = -EINVAL;
3382923c58eSVaibhav Hiremath 	}
3392923c58eSVaibhav Hiremath 
3402923c58eSVaibhav Hiremath 	return ret ? ret : count;
3412923c58eSVaibhav Hiremath }
3422923c58eSVaibhav Hiremath 
3432923c58eSVaibhav Hiremath static ssize_t state_show(struct device *dev,
3442923c58eSVaibhav Hiremath 		struct device_attribute *attr, char *buf)
3452923c58eSVaibhav Hiremath {
3462923c58eSVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
3472923c58eSVaibhav Hiremath 
3482923c58eSVaibhav Hiremath 	switch (arche_pdata->state) {
3492923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_OFF:
3502923c58eSVaibhav Hiremath 		return sprintf(buf, "off\n");
3512923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_ACTIVE:
3522923c58eSVaibhav Hiremath 		return sprintf(buf, "active\n");
3532923c58eSVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_STANDBY:
3542923c58eSVaibhav Hiremath 		return sprintf(buf, "standby\n");
3557691fed2SVaibhav Hiremath 	case ARCHE_PLATFORM_STATE_FW_FLASHING:
3567691fed2SVaibhav Hiremath 		return sprintf(buf, "fw_flashing\n");
3572923c58eSVaibhav Hiremath 	default:
3582923c58eSVaibhav Hiremath 		return sprintf(buf, "unknown state\n");
3592923c58eSVaibhav Hiremath 	}
3602923c58eSVaibhav Hiremath }
3612923c58eSVaibhav Hiremath 
3622923c58eSVaibhav Hiremath static DEVICE_ATTR_RW(state);
3632923c58eSVaibhav Hiremath 
3647fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev)
3657fa60654SVaibhav Hiremath {
3667fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata;
3677fa60654SVaibhav Hiremath 	struct device *dev = &pdev->dev;
3687fa60654SVaibhav Hiremath 	struct device_node *np = dev->of_node;
3697fa60654SVaibhav Hiremath 	int ret;
3707fa60654SVaibhav Hiremath 
3717fa60654SVaibhav Hiremath 	arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
3727fa60654SVaibhav Hiremath 	if (!arche_pdata)
3737fa60654SVaibhav Hiremath 		return -ENOMEM;
3747fa60654SVaibhav Hiremath 
3757fa60654SVaibhav Hiremath 	/* setup svc reset gpio */
3767fa60654SVaibhav Hiremath 	arche_pdata->is_reset_act_hi = of_property_read_bool(np,
3777fa60654SVaibhav Hiremath 					"svc,reset-active-high");
3787fa60654SVaibhav Hiremath 	arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
3797fa60654SVaibhav Hiremath 	if (arche_pdata->svc_reset_gpio < 0) {
3807fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get reset-gpio\n");
381f1f251b5SViresh Kumar 		return arche_pdata->svc_reset_gpio;
3827fa60654SVaibhav Hiremath 	}
3837fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
3847fa60654SVaibhav Hiremath 	if (ret) {
3857fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
3867fa60654SVaibhav Hiremath 		return ret;
3877fa60654SVaibhav Hiremath 	}
3887fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
3897fa60654SVaibhav Hiremath 					arche_pdata->is_reset_act_hi);
3907fa60654SVaibhav Hiremath 	if (ret) {
3917fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
3927fa60654SVaibhav Hiremath 		return ret;
3937fa60654SVaibhav Hiremath 	}
394e74d04a5SVaibhav Hiremath 	arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
3957fa60654SVaibhav Hiremath 
3967fa60654SVaibhav Hiremath 	arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
3977fa60654SVaibhav Hiremath 					"svc,sysboot-gpio", 0);
3987fa60654SVaibhav Hiremath 	if (arche_pdata->svc_sysboot_gpio < 0) {
3997fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get sysboot gpio\n");
400f1f251b5SViresh Kumar 		return arche_pdata->svc_sysboot_gpio;
4017fa60654SVaibhav Hiremath 	}
4027fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
4037fa60654SVaibhav Hiremath 	if (ret) {
4047fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
4057fa60654SVaibhav Hiremath 		return ret;
4067fa60654SVaibhav Hiremath 	}
4077fa60654SVaibhav Hiremath 	ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
4087fa60654SVaibhav Hiremath 	if (ret) {
4097fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
4107fa60654SVaibhav Hiremath 		return ret;
4117fa60654SVaibhav Hiremath 	}
4127fa60654SVaibhav Hiremath 
4137fa60654SVaibhav Hiremath 	/* setup the clock request gpio first */
4147fa60654SVaibhav Hiremath 	arche_pdata->svc_refclk_req = of_get_named_gpio(np,
4157fa60654SVaibhav Hiremath 					"svc,refclk-req-gpio", 0);
4167fa60654SVaibhav Hiremath 	if (arche_pdata->svc_refclk_req < 0) {
4177fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc clock-req gpio\n");
418f1f251b5SViresh Kumar 		return arche_pdata->svc_refclk_req;
4197fa60654SVaibhav Hiremath 	}
4207fa60654SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
4217fa60654SVaibhav Hiremath 	if (ret) {
4227fa60654SVaibhav Hiremath 		dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
4237fa60654SVaibhav Hiremath 		return ret;
4247fa60654SVaibhav Hiremath 	}
4257fa60654SVaibhav Hiremath 	ret = gpio_direction_input(arche_pdata->svc_refclk_req);
4267fa60654SVaibhav Hiremath 	if (ret) {
4277fa60654SVaibhav Hiremath 		dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
4287fa60654SVaibhav Hiremath 		return ret;
4297fa60654SVaibhav Hiremath 	}
4307fa60654SVaibhav Hiremath 
4317fa60654SVaibhav Hiremath 	/* setup refclk2 to follow the pin */
4327fa60654SVaibhav Hiremath 	arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
4337fa60654SVaibhav Hiremath 	if (IS_ERR(arche_pdata->svc_ref_clk)) {
4347fa60654SVaibhav Hiremath 		ret = PTR_ERR(arche_pdata->svc_ref_clk);
4357fa60654SVaibhav Hiremath 		dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
4367fa60654SVaibhav Hiremath 		return ret;
4377fa60654SVaibhav Hiremath 	}
4387fa60654SVaibhav Hiremath 
4397fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, arche_pdata);
4407fa60654SVaibhav Hiremath 
4417fa60654SVaibhav Hiremath 	arche_pdata->num_apbs = of_get_child_count(np);
4427fa60654SVaibhav Hiremath 	dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
4437fa60654SVaibhav Hiremath 
444a463fc15SVaibhav Hiremath 	arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
445a463fc15SVaibhav Hiremath 	if (arche_pdata->wake_detect_gpio < 0) {
446a463fc15SVaibhav Hiremath 		dev_err(dev, "failed to get wake detect gpio\n");
447a463fc15SVaibhav Hiremath 		ret = arche_pdata->wake_detect_gpio;
448758ca99dSVaibhav Hiremath 		return ret;
44972a8c24bSViresh Kumar 	}
4507fa60654SVaibhav Hiremath 
451a463fc15SVaibhav Hiremath 	ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
452a463fc15SVaibhav Hiremath 	if (ret) {
453a463fc15SVaibhav Hiremath 		dev_err(dev, "Failed requesting wake_detect gpio %d\n",
454a463fc15SVaibhav Hiremath 				arche_pdata->wake_detect_gpio);
455758ca99dSVaibhav Hiremath 		return ret;
456a463fc15SVaibhav Hiremath 	}
457057aad29SMichael Scott 	/* deassert wake detect */
458057aad29SMichael Scott 	gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
459685353c1SVaibhav Hiremath 	arche_pdata->wake_detect_state = WD_STATE_IDLE;
460a463fc15SVaibhav Hiremath 
461a463fc15SVaibhav Hiremath 	arche_pdata->dev = &pdev->dev;
462a463fc15SVaibhav Hiremath 
463f760bbfbSVaibhav Hiremath 	spin_lock_init(&arche_pdata->lock);
464f760bbfbSVaibhav Hiremath 	arche_pdata->wake_detect_irq =
465f760bbfbSVaibhav Hiremath 		gpio_to_irq(arche_pdata->wake_detect_gpio);
466f760bbfbSVaibhav Hiremath 
467f760bbfbSVaibhav Hiremath 	ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
468f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq,
469f760bbfbSVaibhav Hiremath 			arche_platform_wd_irq_thread,
470f760bbfbSVaibhav Hiremath 			IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
471f760bbfbSVaibhav Hiremath 			dev_name(dev), arche_pdata);
472f760bbfbSVaibhav Hiremath 	if (ret) {
473f760bbfbSVaibhav Hiremath 		dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
474f760bbfbSVaibhav Hiremath 		return ret;
475f760bbfbSVaibhav Hiremath 	}
476f760bbfbSVaibhav Hiremath 	/* Enable it only after  sending wake/detect event */
477f760bbfbSVaibhav Hiremath 	disable_irq(arche_pdata->wake_detect_irq);
478f760bbfbSVaibhav Hiremath 
4792923c58eSVaibhav Hiremath 	ret = device_create_file(dev, &dev_attr_state);
4802923c58eSVaibhav Hiremath 	if (ret) {
4812923c58eSVaibhav Hiremath 		dev_err(dev, "failed to create state file in sysfs\n");
4822923c58eSVaibhav Hiremath 		return ret;
4832923c58eSVaibhav Hiremath 	}
4842923c58eSVaibhav Hiremath 
485758ca99dSVaibhav Hiremath 	ret = arche_platform_coldboot_seq(arche_pdata);
486758ca99dSVaibhav Hiremath 	if (ret) {
487758ca99dSVaibhav Hiremath 		dev_err(dev, "Failed to cold boot svc %d\n", ret);
4886743a6fdSVaibhav Hiremath 		goto err_coldboot;
489758ca99dSVaibhav Hiremath 	}
490758ca99dSVaibhav Hiremath 
491fd60ac58SVaibhav Hiremath 	ret = of_platform_populate(np, NULL, NULL, dev);
492fd60ac58SVaibhav Hiremath 	if (ret) {
493fd60ac58SVaibhav Hiremath 		dev_err(dev, "failed to populate child nodes %d\n", ret);
4946743a6fdSVaibhav Hiremath 		goto err_populate;
495fd60ac58SVaibhav Hiremath 	}
496fd60ac58SVaibhav Hiremath 
49716fe18caSVaibhav Hiremath 	assert_wakedetect(arche_pdata);
498db5a3bcaSVaibhav Hiremath 	INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work);
499fd60ac58SVaibhav Hiremath 
5007fa60654SVaibhav Hiremath 	dev_info(dev, "Device registered successfully\n");
50172a8c24bSViresh Kumar 	return 0;
5026743a6fdSVaibhav Hiremath 
5036743a6fdSVaibhav Hiremath err_populate:
5046743a6fdSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
5056743a6fdSVaibhav Hiremath err_coldboot:
5066743a6fdSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
5076743a6fdSVaibhav Hiremath 	return ret;
5087fa60654SVaibhav Hiremath }
5097fa60654SVaibhav Hiremath 
510bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused)
511bc142bbbSVaibhav Hiremath {
512bc142bbbSVaibhav Hiremath 	struct platform_device *pdev = to_platform_device(dev);
513bc142bbbSVaibhav Hiremath 
514bc142bbbSVaibhav Hiremath 	platform_device_unregister(pdev);
515bc142bbbSVaibhav Hiremath 
516bc142bbbSVaibhav Hiremath 	return 0;
517bc142bbbSVaibhav Hiremath }
518bc142bbbSVaibhav Hiremath 
5197fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev)
5207fa60654SVaibhav Hiremath {
5217fa60654SVaibhav Hiremath 	struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
5227fa60654SVaibhav Hiremath 
5232923c58eSVaibhav Hiremath 	device_remove_file(&pdev->dev, &dev_attr_state);
52449e6e04bSVaibhav Hiremath 	cancel_delayed_work_sync(&arche_pdata->delayed_work);
525bc142bbbSVaibhav Hiremath 	device_for_each_child(&pdev->dev, NULL, arche_remove_child);
5265993e2bfSVaibhav Hiremath 	arche_platform_poweroff_seq(arche_pdata);
5277fa60654SVaibhav Hiremath 	platform_set_drvdata(pdev, NULL);
5287fa60654SVaibhav Hiremath 
529ad4d3f95SVaibhav Hiremath 	if (usb3613_hub_mode_ctrl(false))
530ad4d3f95SVaibhav Hiremath 		dev_warn(arche_pdata->dev, "failed to control hub device\n");
531ad4d3f95SVaibhav Hiremath 		/* TODO: Should we do anything more here ?? */
5327fa60654SVaibhav Hiremath 	return 0;
5337fa60654SVaibhav Hiremath }
5347fa60654SVaibhav Hiremath 
5357fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev)
5367fa60654SVaibhav Hiremath {
5377fa60654SVaibhav Hiremath 	/*
5387fa60654SVaibhav Hiremath 	 * If timing profile premits, we may shutdown bridge
5397fa60654SVaibhav Hiremath 	 * completely
5407fa60654SVaibhav Hiremath 	 *
5417fa60654SVaibhav Hiremath 	 * TODO: sequence ??
5427fa60654SVaibhav Hiremath 	 *
5437fa60654SVaibhav Hiremath 	 * Also, need to make sure we meet precondition for unipro suspend
5447fa60654SVaibhav Hiremath 	 * Precondition: Definition ???
5457fa60654SVaibhav Hiremath 	 */
5467fa60654SVaibhav Hiremath 	return 0;
5477fa60654SVaibhav Hiremath }
5487fa60654SVaibhav Hiremath 
5497fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev)
5507fa60654SVaibhav Hiremath {
5517fa60654SVaibhav Hiremath 	/*
5527fa60654SVaibhav Hiremath 	 * Atleast for ES2 we have to meet the delay requirement between
5537fa60654SVaibhav Hiremath 	 * unipro switch and AP bridge init, depending on whether bridge is in
5547fa60654SVaibhav Hiremath 	 * OFF state or standby state.
5557fa60654SVaibhav Hiremath 	 *
5567fa60654SVaibhav Hiremath 	 * Based on whether bridge is in standby or OFF state we may have to
5577fa60654SVaibhav Hiremath 	 * assert multiple signals. Please refer to WDM spec, for more info.
5587fa60654SVaibhav Hiremath 	 *
5597fa60654SVaibhav Hiremath 	 */
5607fa60654SVaibhav Hiremath 	return 0;
5617fa60654SVaibhav Hiremath }
5627fa60654SVaibhav Hiremath 
5637fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
5647fa60654SVaibhav Hiremath 			arche_platform_suspend,
5657fa60654SVaibhav Hiremath 			arche_platform_resume);
5667fa60654SVaibhav Hiremath 
5677fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = {
5687fa60654SVaibhav Hiremath 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
5697fa60654SVaibhav Hiremath 	{ },
5707fa60654SVaibhav Hiremath };
5711e5dd1f8SGreg Kroah-Hartman 
5721e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = {
5731e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
5741e5dd1f8SGreg Kroah-Hartman 	{ },
5751e5dd1f8SGreg Kroah-Hartman };
5761e5dd1f8SGreg Kroah-Hartman 
5771e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = {
5781e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
5791e5dd1f8SGreg Kroah-Hartman 	{ .compatible = "usbffff,2", },
5801e5dd1f8SGreg Kroah-Hartman 	{ },
5811e5dd1f8SGreg Kroah-Hartman };
5821e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id);
5837fa60654SVaibhav Hiremath 
5847fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = {
5857fa60654SVaibhav Hiremath 	.probe		= arche_platform_probe,
5867fa60654SVaibhav Hiremath 	.remove		= arche_platform_remove,
5877fa60654SVaibhav Hiremath 	.driver		= {
5887fa60654SVaibhav Hiremath 		.name	= "arche-platform-ctrl",
5897fa60654SVaibhav Hiremath 		.pm	= &arche_platform_pm_ops,
5901e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_platform_of_match,
5917fa60654SVaibhav Hiremath 	}
5927fa60654SVaibhav Hiremath };
5937fa60654SVaibhav Hiremath 
5941e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = {
5951e5dd1f8SGreg Kroah-Hartman 	.probe		= arche_apb_ctrl_probe,
5961e5dd1f8SGreg Kroah-Hartman 	.remove		= arche_apb_ctrl_remove,
5971e5dd1f8SGreg Kroah-Hartman 	.driver		= {
5981e5dd1f8SGreg Kroah-Hartman 		.name	= "arche-apb-ctrl",
5991e5dd1f8SGreg Kroah-Hartman 		.pm	= &arche_apb_ctrl_pm_ops,
6001e5dd1f8SGreg Kroah-Hartman 		.of_match_table = arche_apb_ctrl_of_match,
6011e5dd1f8SGreg Kroah-Hartman 	}
6021e5dd1f8SGreg Kroah-Hartman };
6031e5dd1f8SGreg Kroah-Hartman 
6041e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void)
6051e5dd1f8SGreg Kroah-Hartman {
6061e5dd1f8SGreg Kroah-Hartman 	int retval;
6071e5dd1f8SGreg Kroah-Hartman 
6081e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_platform_device_driver);
6091e5dd1f8SGreg Kroah-Hartman 	if (retval)
6101e5dd1f8SGreg Kroah-Hartman 		return retval;
6111e5dd1f8SGreg Kroah-Hartman 
6121e5dd1f8SGreg Kroah-Hartman 	retval = platform_driver_register(&arche_apb_ctrl_device_driver);
6131e5dd1f8SGreg Kroah-Hartman 	if (retval)
6141e5dd1f8SGreg Kroah-Hartman 		platform_driver_unregister(&arche_platform_device_driver);
6151e5dd1f8SGreg Kroah-Hartman 
6161e5dd1f8SGreg Kroah-Hartman 	return retval;
6171e5dd1f8SGreg Kroah-Hartman }
6181e5dd1f8SGreg Kroah-Hartman module_init(arche_init);
6191e5dd1f8SGreg Kroah-Hartman 
6201e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void)
6211e5dd1f8SGreg Kroah-Hartman {
6221e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_apb_ctrl_device_driver);
6231e5dd1f8SGreg Kroah-Hartman 	platform_driver_unregister(&arche_platform_device_driver);
6241e5dd1f8SGreg Kroah-Hartman }
6251e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit);
6267fa60654SVaibhav Hiremath 
6277fa60654SVaibhav Hiremath MODULE_LICENSE("GPL");
6287fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
6297fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver");
630