blob: 3293661d876a19bff26fc4273a4d3a2d0402276a [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>
22#include <linux/time.h>
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -080023#include "arche_platform.h"
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053024
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +053025#include <linux/usb/usb3613.h>
26
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053027#define WD_COLDBOOT_PULSE_WIDTH_MS 30
28
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053029enum svc_wakedetect_state {
30 WD_STATE_IDLE, /* Default state = pulled high/low */
31 WD_STATE_BOOT_INIT, /* WD = falling edge (low) */
32 WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */
33 WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */
34 WD_STATE_COLDBOOT_START, /* Cold boot process started */
35 WD_STATE_STANDBYBOOT_START, /* Not used */
36};
37
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053038struct arche_platform_drvdata {
39 /* Control GPIO signals to and from AP <=> SVC */
40 int svc_reset_gpio;
41 bool is_reset_act_hi;
42 int svc_sysboot_gpio;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053043 int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053044
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +053045 enum arche_platform_state state;
46
David Lin7fe93012016-03-07 21:52:54 -080047 int svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053048 struct clk *svc_ref_clk;
49
50 struct pinctrl *pinctrl;
51 struct pinctrl_state *pin_default;
52
53 int num_apbs;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053054
55 struct delayed_work delayed_work;
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053056 enum svc_wakedetect_state wake_detect_state;
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053057 int wake_detect_irq;
58 spinlock_t lock;
59 unsigned long wake_detect_start;
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053060
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053061 struct device *dev;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053062};
63
64static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
65{
66 gpio_set_value(gpio, onoff);
67}
68
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053069static int apb_cold_boot(struct device *dev, void *data)
70{
71 int ret;
72
73 ret = apb_ctrl_coldboot(dev);
74 if (ret)
75 dev_warn(dev, "failed to coldboot\n");
76
77 /*Child nodes are independent, so do not exit coldboot operation */
78 return 0;
79}
80
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +053081static int apb_fw_flashing_state(struct device *dev, void *data)
82{
83 int ret;
84
85 ret = apb_ctrl_fw_flashing(dev);
86 if (ret)
87 dev_warn(dev, "failed to switch to fw flashing state\n");
88
89 /*Child nodes are independent, so do not exit coldboot operation */
90 return 0;
91}
92
93static int apb_poweroff(struct device *dev, void *data)
94{
95 apb_ctrl_poweroff(dev);
96
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +053097 /* Enable HUB3613 into HUB mode. */
98 if (usb3613_hub_mode_ctrl(false))
99 dev_warn(dev, "failed to control hub device\n");
100
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530101 return 0;
102}
103
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530104/**
Vaibhav Hiremathdb5a3bc2016-02-25 04:37:34 +0530105 * hub_conf_delayed_work - Configures USB3613 device to HUB mode
106 *
107 * The idea here is to split the APB coldboot operation with slow HUB configuration,
108 * so that driver response to wake/detect event can be met.
109 * So expectation is, once code reaches here, means initial unipro linkup
110 * between APB<->Switch was successful, so now just take it to AP.
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530111 */
Vaibhav Hiremathdb5a3bc2016-02-25 04:37:34 +0530112static void hub_conf_delayed_work(struct work_struct *work)
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530113{
114 struct arche_platform_drvdata *arche_pdata =
115 container_of(work, struct arche_platform_drvdata, delayed_work.work);
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530116
117 /* Enable HUB3613 into HUB mode. */
118 if (usb3613_hub_mode_ctrl(true))
119 dev_warn(arche_pdata->dev, "failed to control hub device\n");
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530120}
121
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530122static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata)
123{
124 /* Assert wake/detect = Detect event from AP */
125 gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
126
127 /* Enable interrupt here, to read event back from SVC */
128 gpio_direction_input(arche_pdata->wake_detect_gpio);
129 enable_irq(arche_pdata->wake_detect_irq);
130}
131
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530132static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
133{
134 struct arche_platform_drvdata *arche_pdata = devid;
135 unsigned long flags;
136
137 spin_lock_irqsave(&arche_pdata->lock, flags);
138 if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
139 /* Something is wrong */
140 spin_unlock_irqrestore(&arche_pdata->lock, flags);
141 return IRQ_HANDLED;
142 }
143
144 arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START;
145 spin_unlock_irqrestore(&arche_pdata->lock, flags);
146
Vaibhav Hiremathff788de2016-02-25 16:45:44 +0530147 /* It should complete power cycle, so first make sure it is poweroff */
148 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +0530149
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530150 /* Bring APB out of reset: cold boot sequence */
151 device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
152
153 spin_lock_irqsave(&arche_pdata->lock, flags);
154 /* USB HUB configuration */
155 schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000));
156 arche_pdata->wake_detect_state = WD_STATE_IDLE;
157 spin_unlock_irqrestore(&arche_pdata->lock, flags);
158
159 return IRQ_HANDLED;
160}
161
162static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
163{
164 struct arche_platform_drvdata *arche_pdata = devid;
165 unsigned long flags;
166
167 spin_lock_irqsave(&arche_pdata->lock, flags);
168
169 if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
170 /* wake/detect rising */
171
172 /*
173 * If wake/detect line goes high after low, within less than
174 * 30msec, then standby boot sequence is initiated, which is not
175 * supported/implemented as of now. So ignore it.
176 */
177 if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
178 if (time_before(jiffies,
179 arche_pdata->wake_detect_start +
180 msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
181 /* No harm with cancellation, even if not pending */
182 cancel_delayed_work(&arche_pdata->delayed_work);
183 arche_pdata->wake_detect_state = WD_STATE_IDLE;
184 } else {
185 /* Check we are not in middle of irq thread already */
186 if (arche_pdata->wake_detect_state !=
187 WD_STATE_COLDBOOT_START) {
188 arche_pdata->wake_detect_state =
189 WD_STATE_COLDBOOT_TRIG;
190 spin_unlock_irqrestore(&arche_pdata->lock, flags);
191 return IRQ_WAKE_THREAD;
192 }
193 }
194 }
195 } else {
196 /* wake/detect falling */
197 if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
198 arche_pdata->wake_detect_start = jiffies;
199 /* No harm with cancellation even if it is not pending*/
200 cancel_delayed_work(&arche_pdata->delayed_work);
201 /*
202 * In the begining, when wake/detect goes low (first time), we assume
203 * it is meant for coldboot and set the flag. If wake/detect line stays low
204 * beyond 30msec, then it is coldboot else fallback to standby boot.
205 */
206 arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT;
207 }
208 }
209
210 spin_unlock_irqrestore(&arche_pdata->lock, flags);
211
212 return IRQ_HANDLED;
213}
214
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530215static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
216{
217 int ret;
218
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530219 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
220 return 0;
221
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530222 dev_info(arche_pdata->dev, "Booting from cold boot state\n");
223
224 svc_reset_onoff(arche_pdata->svc_reset_gpio,
225 arche_pdata->is_reset_act_hi);
226
227 gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
228 usleep_range(100, 200);
229
230 ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
231 if (ret) {
232 dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
233 ret);
234 return ret;
235 }
236
237 /* bring SVC out of reset */
238 svc_reset_onoff(arche_pdata->svc_reset_gpio,
239 !arche_pdata->is_reset_act_hi);
240
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530241 arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
242
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530243 return 0;
244}
245
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530246static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
247{
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530248 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
249 return;
250
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530251 dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
252
253 svc_reset_onoff(arche_pdata->svc_reset_gpio,
254 arche_pdata->is_reset_act_hi);
255
256 gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
257
258 usleep_range(100, 200);
259 svc_reset_onoff(arche_pdata->svc_reset_gpio,
260 !arche_pdata->is_reset_act_hi);
261
262 arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
263
264}
265
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530266static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530267{
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530268 unsigned long flags;
269
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530270 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
271 return;
272
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530273 /* If in fw_flashing mode, then no need to repeate things again */
274 if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
Vaibhav Hiremathd2320b22016-02-25 04:37:39 +0530275 disable_irq(arche_pdata->wake_detect_irq);
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530276 /* Send disconnect/detach event to SVC */
Vaibhav Hiremath07862122016-02-25 04:37:38 +0530277 gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530278 usleep_range(100, 200);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530279 spin_lock_irqsave(&arche_pdata->lock, flags);
Vaibhav Hiremath685353c2016-02-25 04:37:35 +0530280 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530281 spin_unlock_irqrestore(&arche_pdata->lock, flags);
Vaibhav Hiremathb4c95fc2016-02-13 02:04:06 +0530282
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530283 clk_disable_unprepare(arche_pdata->svc_ref_clk);
284 }
285
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530286 /* As part of exit, put APB back in reset state */
Viresh Kumar140741e2016-01-11 11:29:06 +0530287 svc_reset_onoff(arche_pdata->svc_reset_gpio,
288 arche_pdata->is_reset_act_hi);
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530289
290 arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530291}
292
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530293static ssize_t state_store(struct device *dev,
294 struct device_attribute *attr, const char *buf, size_t count)
295{
296 struct platform_device *pdev = to_platform_device(dev);
297 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
298 int ret = 0;
299
300 if (sysfs_streq(buf, "off")) {
301 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
302 return count;
303
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530304 /* If SVC goes down, bring down APB's as well */
305 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
306
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530307 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530308
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530309 } else if (sysfs_streq(buf, "active")) {
310 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
311 return count;
312
313 ret = arche_platform_coldboot_seq(arche_pdata);
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530314
315 assert_wakedetect(arche_pdata);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530316 } else if (sysfs_streq(buf, "standby")) {
317 if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
318 return count;
319
320 dev_warn(arche_pdata->dev, "standby state not supported\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530321 } else if (sysfs_streq(buf, "fw_flashing")) {
322 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
323 return count;
324
325 /* First we want to make sure we power off everything
326 * and then enter FW flashing state */
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530327 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
328
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530329 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530330
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530331 arche_platform_fw_flashing_seq(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530332
333 device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530334 } else {
335 dev_err(arche_pdata->dev, "unknown state\n");
336 ret = -EINVAL;
337 }
338
339 return ret ? ret : count;
340}
341
342static ssize_t state_show(struct device *dev,
343 struct device_attribute *attr, char *buf)
344{
345 struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
346
347 switch (arche_pdata->state) {
348 case ARCHE_PLATFORM_STATE_OFF:
349 return sprintf(buf, "off\n");
350 case ARCHE_PLATFORM_STATE_ACTIVE:
351 return sprintf(buf, "active\n");
352 case ARCHE_PLATFORM_STATE_STANDBY:
353 return sprintf(buf, "standby\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530354 case ARCHE_PLATFORM_STATE_FW_FLASHING:
355 return sprintf(buf, "fw_flashing\n");
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530356 default:
357 return sprintf(buf, "unknown state\n");
358 }
359}
360
361static DEVICE_ATTR_RW(state);
362
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530363static int arche_platform_probe(struct platform_device *pdev)
364{
365 struct arche_platform_drvdata *arche_pdata;
366 struct device *dev = &pdev->dev;
367 struct device_node *np = dev->of_node;
368 int ret;
369
370 arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
371 if (!arche_pdata)
372 return -ENOMEM;
373
374 /* setup svc reset gpio */
375 arche_pdata->is_reset_act_hi = of_property_read_bool(np,
376 "svc,reset-active-high");
377 arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
378 if (arche_pdata->svc_reset_gpio < 0) {
379 dev_err(dev, "failed to get reset-gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530380 return arche_pdata->svc_reset_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530381 }
382 ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
383 if (ret) {
384 dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
385 return ret;
386 }
387 ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
388 arche_pdata->is_reset_act_hi);
389 if (ret) {
390 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
391 return ret;
392 }
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530393 arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530394
395 arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
396 "svc,sysboot-gpio", 0);
397 if (arche_pdata->svc_sysboot_gpio < 0) {
398 dev_err(dev, "failed to get sysboot gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530399 return arche_pdata->svc_sysboot_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530400 }
401 ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
402 if (ret) {
403 dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
404 return ret;
405 }
406 ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
407 if (ret) {
408 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
409 return ret;
410 }
411
412 /* setup the clock request gpio first */
413 arche_pdata->svc_refclk_req = of_get_named_gpio(np,
414 "svc,refclk-req-gpio", 0);
415 if (arche_pdata->svc_refclk_req < 0) {
416 dev_err(dev, "failed to get svc clock-req gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530417 return arche_pdata->svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530418 }
419 ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
420 if (ret) {
421 dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
422 return ret;
423 }
424 ret = gpio_direction_input(arche_pdata->svc_refclk_req);
425 if (ret) {
426 dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
427 return ret;
428 }
429
430 /* setup refclk2 to follow the pin */
431 arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
432 if (IS_ERR(arche_pdata->svc_ref_clk)) {
433 ret = PTR_ERR(arche_pdata->svc_ref_clk);
434 dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
435 return ret;
436 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530437
438 platform_set_drvdata(pdev, arche_pdata);
439
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530440 arche_pdata->num_apbs = of_get_child_count(np);
441 dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
442
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530443 arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
444 if (arche_pdata->wake_detect_gpio < 0) {
445 dev_err(dev, "failed to get wake detect gpio\n");
446 ret = arche_pdata->wake_detect_gpio;
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530447 return ret;
Viresh Kumar72a8c242016-01-11 11:29:05 +0530448 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530449
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530450 ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
451 if (ret) {
452 dev_err(dev, "Failed requesting wake_detect gpio %d\n",
453 arche_pdata->wake_detect_gpio);
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530454 return ret;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530455 }
Michael Scott057aad22016-01-27 16:40:58 -0800456 /* deassert wake detect */
457 gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
Vaibhav Hiremath685353c2016-02-25 04:37:35 +0530458 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530459
460 arche_pdata->dev = &pdev->dev;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530461
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530462 spin_lock_init(&arche_pdata->lock);
463 arche_pdata->wake_detect_irq =
464 gpio_to_irq(arche_pdata->wake_detect_gpio);
465
466 ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
467 arche_platform_wd_irq,
468 arche_platform_wd_irq_thread,
469 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
470 dev_name(dev), arche_pdata);
471 if (ret) {
472 dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
473 return ret;
474 }
475 /* Enable it only after sending wake/detect event */
476 disable_irq(arche_pdata->wake_detect_irq);
477
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530478 ret = device_create_file(dev, &dev_attr_state);
479 if (ret) {
480 dev_err(dev, "failed to create state file in sysfs\n");
481 return ret;
482 }
483
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530484 ret = arche_platform_coldboot_seq(arche_pdata);
485 if (ret) {
486 dev_err(dev, "Failed to cold boot svc %d\n", ret);
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530487 goto err_coldboot;
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530488 }
489
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530490 ret = of_platform_populate(np, NULL, NULL, dev);
491 if (ret) {
492 dev_err(dev, "failed to populate child nodes %d\n", ret);
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530493 goto err_populate;
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530494 }
495
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530496 assert_wakedetect(arche_pdata);
Vaibhav Hiremathdb5a3bc2016-02-25 04:37:34 +0530497 INIT_DELAYED_WORK(&arche_pdata->delayed_work, hub_conf_delayed_work);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530498
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530499 dev_info(dev, "Device registered successfully\n");
Viresh Kumar72a8c242016-01-11 11:29:05 +0530500 return 0;
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530501
502err_populate:
503 arche_platform_poweroff_seq(arche_pdata);
504err_coldboot:
505 device_remove_file(&pdev->dev, &dev_attr_state);
506 return ret;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530507}
508
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530509static int arche_remove_child(struct device *dev, void *unused)
510{
511 struct platform_device *pdev = to_platform_device(dev);
512
513 platform_device_unregister(pdev);
514
515 return 0;
516}
517
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530518static int arche_platform_remove(struct platform_device *pdev)
519{
520 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
521
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530522 device_remove_file(&pdev->dev, &dev_attr_state);
Vaibhav Hiremath49e6e042016-02-13 01:15:11 +0530523 cancel_delayed_work_sync(&arche_pdata->delayed_work);
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530524 device_for_each_child(&pdev->dev, NULL, arche_remove_child);
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530525 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530526 platform_set_drvdata(pdev, NULL);
527
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530528 if (usb3613_hub_mode_ctrl(false))
529 dev_warn(arche_pdata->dev, "failed to control hub device\n");
530 /* TODO: Should we do anything more here ?? */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530531 return 0;
532}
533
534static int arche_platform_suspend(struct device *dev)
535{
536 /*
537 * If timing profile premits, we may shutdown bridge
538 * completely
539 *
540 * TODO: sequence ??
541 *
542 * Also, need to make sure we meet precondition for unipro suspend
543 * Precondition: Definition ???
544 */
545 return 0;
546}
547
548static int arche_platform_resume(struct device *dev)
549{
550 /*
551 * Atleast for ES2 we have to meet the delay requirement between
552 * unipro switch and AP bridge init, depending on whether bridge is in
553 * OFF state or standby state.
554 *
555 * Based on whether bridge is in standby or OFF state we may have to
556 * assert multiple signals. Please refer to WDM spec, for more info.
557 *
558 */
559 return 0;
560}
561
562static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
563 arche_platform_suspend,
564 arche_platform_resume);
565
566static struct of_device_id arche_platform_of_match[] = {
567 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
568 { },
569};
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800570
571static struct of_device_id arche_apb_ctrl_of_match[] = {
572 { .compatible = "usbffff,2", },
573 { },
574};
575
576static struct of_device_id arche_combined_id[] = {
577 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
578 { .compatible = "usbffff,2", },
579 { },
580};
581MODULE_DEVICE_TABLE(of, arche_combined_id);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530582
583static struct platform_driver arche_platform_device_driver = {
584 .probe = arche_platform_probe,
585 .remove = arche_platform_remove,
586 .driver = {
587 .name = "arche-platform-ctrl",
588 .pm = &arche_platform_pm_ops,
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800589 .of_match_table = arche_platform_of_match,
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530590 }
591};
592
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800593static struct platform_driver arche_apb_ctrl_device_driver = {
594 .probe = arche_apb_ctrl_probe,
595 .remove = arche_apb_ctrl_remove,
596 .driver = {
597 .name = "arche-apb-ctrl",
598 .pm = &arche_apb_ctrl_pm_ops,
599 .of_match_table = arche_apb_ctrl_of_match,
600 }
601};
602
603static int __init arche_init(void)
604{
605 int retval;
606
607 retval = platform_driver_register(&arche_platform_device_driver);
608 if (retval)
609 return retval;
610
611 retval = platform_driver_register(&arche_apb_ctrl_device_driver);
612 if (retval)
613 platform_driver_unregister(&arche_platform_device_driver);
614
615 return retval;
616}
617module_init(arche_init);
618
619static void __exit arche_exit(void)
620{
621 platform_driver_unregister(&arche_apb_ctrl_device_driver);
622 platform_driver_unregister(&arche_platform_device_driver);
623}
624module_exit(arche_exit);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530625
626MODULE_LICENSE("GPL");
627MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
628MODULE_DESCRIPTION("Arche Platform Driver");