blob: d33d6fe078ad730ae3cdb4c7d9291c23d3711397 [file] [log] [blame]
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +05301/*
2 * Arche Platform driver to enable Unipro link.
3 *
4 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
9
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053010#include <linux/clk.h>
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053011#include <linux/delay.h>
Viresh Kumar3b858df2016-01-11 11:29:17 +053012#include <linux/gpio.h>
13#include <linux/init.h>
14#include <linux/module.h>
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053015#include <linux/of_gpio.h>
Viresh Kumar3b858df2016-01-11 11:29:17 +053016#include <linux/of_platform.h>
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053017#include <linux/pinctrl/consumer.h>
Viresh Kumar3b858df2016-01-11 11:29:17 +053018#include <linux/platform_device.h>
19#include <linux/pm.h>
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053020#include <linux/interrupt.h>
21#include <linux/irq.h>
David Lin9160b7c2016-04-16 01:15:15 +053022#include <linux/suspend.h>
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053023#include <linux/time.h>
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -080024#include "arche_platform.h"
Bryan O'Donoghue970dc852016-06-05 14:03:26 +010025#include "greybus.h"
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053026
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +053027#include <linux/usb/usb3613.h>
28
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053029#define WD_COLDBOOT_PULSE_WIDTH_MS 30
30
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053031enum svc_wakedetect_state {
32 WD_STATE_IDLE, /* Default state = pulled high/low */
33 WD_STATE_BOOT_INIT, /* WD = falling edge (low) */
34 WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */
35 WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */
36 WD_STATE_COLDBOOT_START, /* Cold boot process started */
37 WD_STATE_STANDBYBOOT_START, /* Not used */
Bryan O'Donoghue970dc852016-06-05 14:03:26 +010038 WD_STATE_TIMESYNC,
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053039};
40
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053041struct arche_platform_drvdata {
42 /* Control GPIO signals to and from AP <=> SVC */
43 int svc_reset_gpio;
44 bool is_reset_act_hi;
45 int svc_sysboot_gpio;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053046 int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053047
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +053048 enum arche_platform_state state;
49
David Lin7fe93012016-03-07 21:52:54 -080050 int svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053051 struct clk *svc_ref_clk;
52
53 struct pinctrl *pinctrl;
54 struct pinctrl_state *pin_default;
55
56 int num_apbs;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053057
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053058 enum svc_wakedetect_state wake_detect_state;
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053059 int wake_detect_irq;
Vaibhav Hiremath886aba52016-05-05 15:34:58 +010060 spinlock_t wake_lock; /* Protect wake_detect_state */
61 struct mutex platform_state_mutex; /* Protect state */
Bryan O'Donoghue970dc852016-06-05 14:03:26 +010062 wait_queue_head_t wq; /* WQ for arche_pdata->state */
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053063 unsigned long wake_detect_start;
David Lin9160b7c2016-04-16 01:15:15 +053064 struct notifier_block pm_notifier;
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053065
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053066 struct device *dev;
Bryan O'Donoghue970dc852016-06-05 14:03:26 +010067 struct gb_timesync_svc *timesync_svc_pdata;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053068};
69
Bryan O'Donoghue970dc852016-06-05 14:03:26 +010070static int arche_apb_bootret_assert(struct device *dev, void *data)
71{
72 apb_bootret_assert(dev);
73 return 0;
74}
75
76static int arche_apb_bootret_deassert(struct device *dev, void *data)
77{
78 apb_bootret_deassert(dev);
79 return 0;
80}
81
82/* Requires calling context to hold arche_pdata->platform_state_mutex */
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +010083static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata,
84 enum arche_platform_state state)
85{
86 arche_pdata->state = state;
87}
88
Vaibhav Hiremath886aba52016-05-05 15:34:58 +010089/*
90 * arche_platform_change_state: Change the operational state
91 *
92 * This exported function allows external drivers to change the state
93 * of the arche-platform driver.
94 * Note that this function only supports transitions between two states
95 * with limited functionality.
96 *
97 * - ARCHE_PLATFORM_STATE_TIME_SYNC:
98 * Once set, allows timesync operations between SVC <=> AP and makes
99 * sure that arche-platform driver ignores any subsequent events/pulses
100 * from SVC over wake/detect.
101 *
102 * - ARCHE_PLATFORM_STATE_ACTIVE:
103 * Puts back driver to active state, where any pulse from SVC on wake/detect
104 * line would trigger either cold/standby boot.
105 * Note: Transition request from this function does not trigger cold/standby
106 * boot. It just puts back driver book keeping variable back to ACTIVE
107 * state and restores the interrupt.
108 *
109 * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently
110 * satisfy the requested state-transition or -EINVAL for all other
111 * state-transition requests.
112 */
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100113int arche_platform_change_state(enum arche_platform_state state,
114 struct gb_timesync_svc *timesync_svc_pdata)
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100115{
116 struct arche_platform_drvdata *arche_pdata;
117 struct platform_device *pdev;
118 struct device_node *np;
119 int ret = -EAGAIN;
120 unsigned long flags;
121
122 np = of_find_compatible_node(NULL, NULL, "google,arche-platform");
123 if (!np) {
124 pr_err("google,arche-platform device node not found\n");
125 return -ENODEV;
126 }
127
128 pdev = of_find_device_by_node(np);
129 if (!pdev) {
130 pr_err("arche-platform device not found\n");
Wei Yongjun25633d12016-10-17 16:37:20 +0000131 of_node_put(np);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100132 return -ENODEV;
133 }
134
135 arche_pdata = platform_get_drvdata(pdev);
136
137 mutex_lock(&arche_pdata->platform_state_mutex);
138 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100139
140 if (arche_pdata->state == state) {
141 ret = 0;
142 goto exit;
143 }
144
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100145 switch (state) {
146 case ARCHE_PLATFORM_STATE_TIME_SYNC:
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100147 if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
148 ret = -EINVAL;
149 goto exit;
150 }
151 if (arche_pdata->wake_detect_state != WD_STATE_IDLE) {
152 dev_err(arche_pdata->dev,
153 "driver busy with wake/detect line ops\n");
154 goto exit;
155 }
156 device_for_each_child(arche_pdata->dev, NULL,
157 arche_apb_bootret_assert);
158 arche_pdata->wake_detect_state = WD_STATE_TIMESYNC;
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100159 break;
160 case ARCHE_PLATFORM_STATE_ACTIVE:
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100161 if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) {
162 ret = -EINVAL;
163 goto exit;
164 }
165 device_for_each_child(arche_pdata->dev, NULL,
166 arche_apb_bootret_deassert);
167 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100168 break;
169 case ARCHE_PLATFORM_STATE_OFF:
170 case ARCHE_PLATFORM_STATE_STANDBY:
171 case ARCHE_PLATFORM_STATE_FW_FLASHING:
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100172 dev_err(arche_pdata->dev, "busy, request to retry later\n");
173 goto exit;
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100174 default:
175 ret = -EINVAL;
176 dev_err(arche_pdata->dev,
177 "invalid state transition request\n");
178 goto exit;
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100179 }
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100180 arche_pdata->timesync_svc_pdata = timesync_svc_pdata;
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100181 arche_platform_set_state(arche_pdata, state);
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100182 if (state == ARCHE_PLATFORM_STATE_ACTIVE)
183 wake_up(&arche_pdata->wq);
184
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100185 ret = 0;
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100186exit:
187 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
188 mutex_unlock(&arche_pdata->platform_state_mutex);
Johan Hovoldd9966f12016-11-01 11:55:02 +0100189 put_device(&pdev->dev);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100190 of_node_put(np);
191 return ret;
192}
193EXPORT_SYMBOL_GPL(arche_platform_change_state);
194
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100195/* Requires arche_pdata->wake_lock is held by calling context */
196static void arche_platform_set_wake_detect_state(
197 struct arche_platform_drvdata *arche_pdata,
198 enum svc_wakedetect_state state)
199{
200 arche_pdata->wake_detect_state = state;
201}
202
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530203static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
204{
205 gpio_set_value(gpio, onoff);
206}
207
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530208static int apb_cold_boot(struct device *dev, void *data)
209{
210 int ret;
211
212 ret = apb_ctrl_coldboot(dev);
213 if (ret)
214 dev_warn(dev, "failed to coldboot\n");
215
216 /*Child nodes are independent, so do not exit coldboot operation */
217 return 0;
218}
219
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530220static int apb_poweroff(struct device *dev, void *data)
221{
222 apb_ctrl_poweroff(dev);
223
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +0530224 /* Enable HUB3613 into HUB mode. */
225 if (usb3613_hub_mode_ctrl(false))
226 dev_warn(dev, "failed to control hub device\n");
227
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530228 return 0;
229}
230
Vaibhav Hiremath7ba535e2016-05-17 22:39:47 +0530231static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata)
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530232{
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530233 /* Enable interrupt here, to read event back from SVC */
234 gpio_direction_input(arche_pdata->wake_detect_gpio);
235 enable_irq(arche_pdata->wake_detect_irq);
236}
237
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530238static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
239{
240 struct arche_platform_drvdata *arche_pdata = devid;
241 unsigned long flags;
242
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100243 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530244 if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
245 /* Something is wrong */
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100246 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530247 return IRQ_HANDLED;
248 }
249
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100250 arche_platform_set_wake_detect_state(arche_pdata,
251 WD_STATE_COLDBOOT_START);
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100252 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530253
Vaibhav Hiremathff788de2016-02-25 16:45:44 +0530254 /* It should complete power cycle, so first make sure it is poweroff */
255 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +0530256
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530257 /* Bring APB out of reset: cold boot sequence */
258 device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
259
Vaibhav Hirematha7ddda12016-04-12 17:18:47 +0530260 /* Enable HUB3613 into HUB mode. */
261 if (usb3613_hub_mode_ctrl(true))
262 dev_warn(arche_pdata->dev, "failed to control hub device\n");
263
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100264 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100265 arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100266 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530267
268 return IRQ_HANDLED;
269}
270
271static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
272{
273 struct arche_platform_drvdata *arche_pdata = devid;
274 unsigned long flags;
275
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100276 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530277
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100278 if (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) {
279 gb_timesync_irq(arche_pdata->timesync_svc_pdata);
280 goto exit;
281 }
282
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530283 if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
284 /* wake/detect rising */
285
286 /*
287 * If wake/detect line goes high after low, within less than
288 * 30msec, then standby boot sequence is initiated, which is not
289 * supported/implemented as of now. So ignore it.
290 */
291 if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
292 if (time_before(jiffies,
293 arche_pdata->wake_detect_start +
294 msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100295 arche_platform_set_wake_detect_state(arche_pdata,
296 WD_STATE_IDLE);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530297 } else {
298 /* Check we are not in middle of irq thread already */
299 if (arche_pdata->wake_detect_state !=
300 WD_STATE_COLDBOOT_START) {
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100301 arche_platform_set_wake_detect_state(arche_pdata,
302 WD_STATE_COLDBOOT_TRIG);
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100303 spin_unlock_irqrestore(
304 &arche_pdata->wake_lock,
305 flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530306 return IRQ_WAKE_THREAD;
307 }
308 }
309 }
310 } else {
311 /* wake/detect falling */
312 if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
313 arche_pdata->wake_detect_start = jiffies;
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530314 /*
315 * In the begining, when wake/detect goes low (first time), we assume
316 * it is meant for coldboot and set the flag. If wake/detect line stays low
317 * beyond 30msec, then it is coldboot else fallback to standby boot.
318 */
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100319 arche_platform_set_wake_detect_state(arche_pdata,
320 WD_STATE_BOOT_INIT);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530321 }
322 }
323
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100324exit:
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100325 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530326
327 return IRQ_HANDLED;
328}
329
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100330/*
331 * Requires arche_pdata->platform_state_mutex to be held
332 */
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530333static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
334{
335 int ret;
336
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530337 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
338 return 0;
339
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530340 dev_info(arche_pdata->dev, "Booting from cold boot state\n");
341
342 svc_reset_onoff(arche_pdata->svc_reset_gpio,
343 arche_pdata->is_reset_act_hi);
344
345 gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
346 usleep_range(100, 200);
347
348 ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
349 if (ret) {
350 dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
351 ret);
352 return ret;
353 }
354
355 /* bring SVC out of reset */
356 svc_reset_onoff(arche_pdata->svc_reset_gpio,
357 !arche_pdata->is_reset_act_hi);
358
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100359 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE);
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530360
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530361 return 0;
362}
363
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100364/*
365 * Requires arche_pdata->platform_state_mutex to be held
366 */
Vaibhav Hiremathc61a8b42016-05-24 18:32:02 +0530367static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530368{
Vaibhav Hiremath7a867d12016-05-24 18:32:04 +0530369 int ret;
370
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530371 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
Vaibhav Hiremathc61a8b42016-05-24 18:32:02 +0530372 return 0;
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530373
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530374 dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
375
376 svc_reset_onoff(arche_pdata->svc_reset_gpio,
377 arche_pdata->is_reset_act_hi);
378
379 gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
380
381 usleep_range(100, 200);
Vaibhav Hiremath7a867d12016-05-24 18:32:04 +0530382
383 ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
384 if (ret) {
385 dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
386 ret);
387 return ret;
388 }
389
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530390 svc_reset_onoff(arche_pdata->svc_reset_gpio,
391 !arche_pdata->is_reset_act_hi);
392
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100393 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING);
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530394
Vaibhav Hiremathc61a8b42016-05-24 18:32:02 +0530395 return 0;
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530396}
397
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100398/*
399 * Requires arche_pdata->platform_state_mutex to be held
400 */
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530401static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530402{
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530403 unsigned long flags;
404
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530405 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
406 return;
407
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530408 /* If in fw_flashing mode, then no need to repeate things again */
409 if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
Vaibhav Hiremathd2320b22016-02-25 04:37:39 +0530410 disable_irq(arche_pdata->wake_detect_irq);
Vaibhav Hiremath16fd9762016-05-17 22:39:46 +0530411
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100412 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100413 arche_platform_set_wake_detect_state(arche_pdata,
414 WD_STATE_IDLE);
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100415 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530416 }
417
Vaibhav Hiremath7a867d12016-05-24 18:32:04 +0530418 clk_disable_unprepare(arche_pdata->svc_ref_clk);
419
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530420 /* As part of exit, put APB back in reset state */
Viresh Kumar140741e2016-01-11 11:29:06 +0530421 svc_reset_onoff(arche_pdata->svc_reset_gpio,
422 arche_pdata->is_reset_act_hi);
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530423
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100424 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530425}
426
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530427static ssize_t state_store(struct device *dev,
428 struct device_attribute *attr, const char *buf, size_t count)
429{
430 struct platform_device *pdev = to_platform_device(dev);
431 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
432 int ret = 0;
433
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100434retry:
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100435 mutex_lock(&arche_pdata->platform_state_mutex);
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100436 if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) {
437 mutex_unlock(&arche_pdata->platform_state_mutex);
438 ret = wait_event_interruptible(
439 arche_pdata->wq,
440 arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC);
441 if (ret)
442 return ret;
443 goto retry;
444 }
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100445
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530446 if (sysfs_streq(buf, "off")) {
447 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100448 goto exit;
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530449
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530450 /* If SVC goes down, bring down APB's as well */
451 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
452
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530453 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530454
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530455 } else if (sysfs_streq(buf, "active")) {
456 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100457 goto exit;
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530458
Vaibhav Hiremath0b1283e2016-05-24 18:32:03 +0530459 /* First we want to make sure we power off everything
460 * and then activate back again */
461 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
462 arche_platform_poweroff_seq(arche_pdata);
463
Vaibhav Hiremath7ba535e2016-05-17 22:39:47 +0530464 arche_platform_wd_irq_en(arche_pdata);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530465 ret = arche_platform_coldboot_seq(arche_pdata);
Vaibhav Hiremath0b1283e2016-05-24 18:32:03 +0530466 if (ret)
467 goto exit;
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530468
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530469 } else if (sysfs_streq(buf, "standby")) {
470 if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100471 goto exit;
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530472
473 dev_warn(arche_pdata->dev, "standby state not supported\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530474 } else if (sysfs_streq(buf, "fw_flashing")) {
475 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100476 goto exit;
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530477
Vaibhav Hiremath921dbe52016-07-28 13:47:37 +0530478 /*
479 * Here we only control SVC.
480 *
481 * In case of FW_FLASHING mode we do not want to control
482 * APBs, as in case of V2, SPI bus is shared between both
483 * the APBs. So let user chose which APB he wants to flash.
484 */
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530485 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530486
Vaibhav Hiremathc61a8b42016-05-24 18:32:02 +0530487 ret = arche_platform_fw_flashing_seq(arche_pdata);
488 if (ret)
489 goto exit;
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530490 } else {
491 dev_err(arche_pdata->dev, "unknown state\n");
492 ret = -EINVAL;
493 }
494
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100495exit:
496 mutex_unlock(&arche_pdata->platform_state_mutex);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530497 return ret ? ret : count;
498}
499
500static ssize_t state_show(struct device *dev,
501 struct device_attribute *attr, char *buf)
502{
503 struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
504
505 switch (arche_pdata->state) {
506 case ARCHE_PLATFORM_STATE_OFF:
507 return sprintf(buf, "off\n");
508 case ARCHE_PLATFORM_STATE_ACTIVE:
509 return sprintf(buf, "active\n");
510 case ARCHE_PLATFORM_STATE_STANDBY:
511 return sprintf(buf, "standby\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530512 case ARCHE_PLATFORM_STATE_FW_FLASHING:
513 return sprintf(buf, "fw_flashing\n");
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100514 case ARCHE_PLATFORM_STATE_TIME_SYNC:
515 return sprintf(buf, "time_sync\n");
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530516 default:
517 return sprintf(buf, "unknown state\n");
518 }
519}
520
521static DEVICE_ATTR_RW(state);
522
David Lin9160b7c2016-04-16 01:15:15 +0530523static int arche_platform_pm_notifier(struct notifier_block *notifier,
524 unsigned long pm_event, void *unused)
525{
526 struct arche_platform_drvdata *arche_pdata =
527 container_of(notifier, struct arche_platform_drvdata,
528 pm_notifier);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100529 int ret = NOTIFY_DONE;
David Lin9160b7c2016-04-16 01:15:15 +0530530
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100531 mutex_lock(&arche_pdata->platform_state_mutex);
David Lin9160b7c2016-04-16 01:15:15 +0530532 switch (pm_event) {
533 case PM_SUSPEND_PREPARE:
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100534 if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) {
535 ret = NOTIFY_STOP;
536 break;
537 }
David Lin9160b7c2016-04-16 01:15:15 +0530538 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
539 arche_platform_poweroff_seq(arche_pdata);
540 break;
541 case PM_POST_SUSPEND:
Vaibhav Hiremath8ef0b532016-08-03 17:15:38 +0530542 if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF)
543 break;
544
Vaibhav Hiremath7ba535e2016-05-17 22:39:47 +0530545 arche_platform_wd_irq_en(arche_pdata);
Vaibhav Hiremath16fd9762016-05-17 22:39:46 +0530546 arche_platform_coldboot_seq(arche_pdata);
David Lin9160b7c2016-04-16 01:15:15 +0530547 break;
548 default:
549 break;
550 }
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100551 mutex_unlock(&arche_pdata->platform_state_mutex);
David Lin9160b7c2016-04-16 01:15:15 +0530552
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100553 return ret;
David Lin9160b7c2016-04-16 01:15:15 +0530554}
555
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530556static int arche_platform_probe(struct platform_device *pdev)
557{
558 struct arche_platform_drvdata *arche_pdata;
559 struct device *dev = &pdev->dev;
560 struct device_node *np = dev->of_node;
561 int ret;
562
563 arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
564 if (!arche_pdata)
565 return -ENOMEM;
566
567 /* setup svc reset gpio */
568 arche_pdata->is_reset_act_hi = of_property_read_bool(np,
569 "svc,reset-active-high");
570 arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
571 if (arche_pdata->svc_reset_gpio < 0) {
572 dev_err(dev, "failed to get reset-gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530573 return arche_pdata->svc_reset_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530574 }
575 ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
576 if (ret) {
577 dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
578 return ret;
579 }
580 ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
581 arche_pdata->is_reset_act_hi);
582 if (ret) {
583 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
584 return ret;
585 }
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100586 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530587
588 arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
589 "svc,sysboot-gpio", 0);
590 if (arche_pdata->svc_sysboot_gpio < 0) {
591 dev_err(dev, "failed to get sysboot gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530592 return arche_pdata->svc_sysboot_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530593 }
594 ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
595 if (ret) {
596 dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
597 return ret;
598 }
599 ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
600 if (ret) {
601 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
602 return ret;
603 }
604
605 /* setup the clock request gpio first */
606 arche_pdata->svc_refclk_req = of_get_named_gpio(np,
607 "svc,refclk-req-gpio", 0);
608 if (arche_pdata->svc_refclk_req < 0) {
609 dev_err(dev, "failed to get svc clock-req gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530610 return arche_pdata->svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530611 }
612 ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
613 if (ret) {
614 dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
615 return ret;
616 }
617 ret = gpio_direction_input(arche_pdata->svc_refclk_req);
618 if (ret) {
619 dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
620 return ret;
621 }
622
623 /* setup refclk2 to follow the pin */
624 arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
625 if (IS_ERR(arche_pdata->svc_ref_clk)) {
626 ret = PTR_ERR(arche_pdata->svc_ref_clk);
627 dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
628 return ret;
629 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530630
631 platform_set_drvdata(pdev, arche_pdata);
632
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530633 arche_pdata->num_apbs = of_get_child_count(np);
634 dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
635
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530636 arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
637 if (arche_pdata->wake_detect_gpio < 0) {
638 dev_err(dev, "failed to get wake detect gpio\n");
Eva Rachel Retuya5a420d12016-09-21 13:07:04 +0800639 return arche_pdata->wake_detect_gpio;
Viresh Kumar72a8c242016-01-11 11:29:05 +0530640 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530641
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530642 ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
643 if (ret) {
644 dev_err(dev, "Failed requesting wake_detect gpio %d\n",
645 arche_pdata->wake_detect_gpio);
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530646 return ret;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530647 }
Vaibhav Hiremath16fd9762016-05-17 22:39:46 +0530648
Bryan O'Donoghuec4058b72016-05-05 15:34:59 +0100649 arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE);
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530650
651 arche_pdata->dev = &pdev->dev;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530652
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100653 spin_lock_init(&arche_pdata->wake_lock);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100654 mutex_init(&arche_pdata->platform_state_mutex);
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100655 init_waitqueue_head(&arche_pdata->wq);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530656 arche_pdata->wake_detect_irq =
657 gpio_to_irq(arche_pdata->wake_detect_gpio);
658
659 ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
660 arche_platform_wd_irq,
661 arche_platform_wd_irq_thread,
662 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
663 dev_name(dev), arche_pdata);
664 if (ret) {
665 dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
666 return ret;
667 }
Vaibhav Hiremathd29b67d2016-07-19 13:31:46 +0530668 disable_irq(arche_pdata->wake_detect_irq);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530669
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530670 ret = device_create_file(dev, &dev_attr_state);
671 if (ret) {
672 dev_err(dev, "failed to create state file in sysfs\n");
673 return ret;
674 }
675
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530676 ret = of_platform_populate(np, NULL, NULL, dev);
677 if (ret) {
678 dev_err(dev, "failed to populate child nodes %d\n", ret);
Vaibhav Hiremathd29b67d2016-07-19 13:31:46 +0530679 goto err_device_remove;
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530680 }
681
David Lin9160b7c2016-04-16 01:15:15 +0530682 arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
683 ret = register_pm_notifier(&arche_pdata->pm_notifier);
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100684
David Lin9160b7c2016-04-16 01:15:15 +0530685 if (ret) {
686 dev_err(dev, "failed to register pm notifier %d\n", ret);
Vaibhav Hiremathd29b67d2016-07-19 13:31:46 +0530687 goto err_device_remove;
David Lin9160b7c2016-04-16 01:15:15 +0530688 }
689
Bryan O'Donoghue970dc852016-06-05 14:03:26 +0100690 /* Register callback pointer */
691 arche_platform_change_state_cb = arche_platform_change_state;
692
Vaibhav Hiremathd29b67d2016-07-19 13:31:46 +0530693 /* Explicitly power off if requested */
694 if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) {
695 mutex_lock(&arche_pdata->platform_state_mutex);
696 ret = arche_platform_coldboot_seq(arche_pdata);
697 if (ret) {
698 dev_err(dev, "Failed to cold boot svc %d\n", ret);
699 goto err_coldboot;
700 }
701 arche_platform_wd_irq_en(arche_pdata);
702 mutex_unlock(&arche_pdata->platform_state_mutex);
703 }
704
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530705 dev_info(dev, "Device registered successfully\n");
Viresh Kumar72a8c242016-01-11 11:29:05 +0530706 return 0;
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530707
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530708err_coldboot:
Vaibhav Hiremath886aba52016-05-05 15:34:58 +0100709 mutex_unlock(&arche_pdata->platform_state_mutex);
Vaibhav Hiremathd29b67d2016-07-19 13:31:46 +0530710err_device_remove:
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530711 device_remove_file(&pdev->dev, &dev_attr_state);
712 return ret;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530713}
714
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530715static int arche_remove_child(struct device *dev, void *unused)
716{
717 struct platform_device *pdev = to_platform_device(dev);
718
719 platform_device_unregister(pdev);
720
721 return 0;
722}
723
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530724static int arche_platform_remove(struct platform_device *pdev)
725{
726 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
727
David Lin9160b7c2016-04-16 01:15:15 +0530728 unregister_pm_notifier(&arche_pdata->pm_notifier);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530729 device_remove_file(&pdev->dev, &dev_attr_state);
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530730 device_for_each_child(&pdev->dev, NULL, arche_remove_child);
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530731 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530732 platform_set_drvdata(pdev, NULL);
733
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530734 if (usb3613_hub_mode_ctrl(false))
735 dev_warn(arche_pdata->dev, "failed to control hub device\n");
736 /* TODO: Should we do anything more here ?? */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530737 return 0;
738}
739
740static int arche_platform_suspend(struct device *dev)
741{
742 /*
743 * If timing profile premits, we may shutdown bridge
744 * completely
745 *
746 * TODO: sequence ??
747 *
748 * Also, need to make sure we meet precondition for unipro suspend
749 * Precondition: Definition ???
750 */
751 return 0;
752}
753
754static int arche_platform_resume(struct device *dev)
755{
756 /*
757 * Atleast for ES2 we have to meet the delay requirement between
758 * unipro switch and AP bridge init, depending on whether bridge is in
759 * OFF state or standby state.
760 *
761 * Based on whether bridge is in standby or OFF state we may have to
762 * assert multiple signals. Please refer to WDM spec, for more info.
763 *
764 */
765 return 0;
766}
767
David Lin1f77b362016-07-12 17:41:21 -0700768static void arche_platform_shutdown(struct platform_device *pdev)
769{
770 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
771
772 arche_platform_poweroff_seq(arche_pdata);
773
774 usb3613_hub_mode_ctrl(false);
775}
776
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530777static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
778 arche_platform_suspend,
779 arche_platform_resume);
780
Eva Rachel Retuyacdcffc02016-09-22 23:33:34 +0800781static const struct of_device_id arche_platform_of_match[] = {
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530782 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
783 { },
784};
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800785
Eva Rachel Retuyacdcffc02016-09-22 23:33:34 +0800786static const struct of_device_id arche_combined_id[] = {
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800787 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
788 { .compatible = "usbffff,2", },
789 { },
790};
791MODULE_DEVICE_TABLE(of, arche_combined_id);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530792
793static struct platform_driver arche_platform_device_driver = {
794 .probe = arche_platform_probe,
795 .remove = arche_platform_remove,
David Lin1f77b362016-07-12 17:41:21 -0700796 .shutdown = arche_platform_shutdown,
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530797 .driver = {
798 .name = "arche-platform-ctrl",
799 .pm = &arche_platform_pm_ops,
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800800 .of_match_table = arche_platform_of_match,
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530801 }
802};
803
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800804static int __init arche_init(void)
805{
806 int retval;
807
808 retval = platform_driver_register(&arche_platform_device_driver);
809 if (retval)
810 return retval;
811
Viresh Kumar7b62b612016-04-20 11:48:37 +0530812 retval = arche_apb_init();
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800813 if (retval)
814 platform_driver_unregister(&arche_platform_device_driver);
815
816 return retval;
817}
818module_init(arche_init);
819
820static void __exit arche_exit(void)
821{
Viresh Kumar7b62b612016-04-20 11:48:37 +0530822 arche_apb_exit();
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800823 platform_driver_unregister(&arche_platform_device_driver);
824}
825module_exit(arche_exit);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530826
Sandeep Patila974f462016-05-13 12:00:56 -0700827MODULE_LICENSE("GPL v2");
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530828MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
829MODULE_DESCRIPTION("Arche Platform Driver");