blob: 9a117176ad50c960b62aeefbae1ff39a6cb2cf4a [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"
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053025
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +053026#include <linux/usb/usb3613.h>
27
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053028#define WD_COLDBOOT_PULSE_WIDTH_MS 30
29
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053030enum svc_wakedetect_state {
31 WD_STATE_IDLE, /* Default state = pulled high/low */
32 WD_STATE_BOOT_INIT, /* WD = falling edge (low) */
33 WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */
34 WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */
35 WD_STATE_COLDBOOT_START, /* Cold boot process started */
36 WD_STATE_STANDBYBOOT_START, /* Not used */
37};
38
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053039struct arche_platform_drvdata {
40 /* Control GPIO signals to and from AP <=> SVC */
41 int svc_reset_gpio;
42 bool is_reset_act_hi;
43 int svc_sysboot_gpio;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053044 int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053045
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +053046 enum arche_platform_state state;
47
David Lin7fe93012016-03-07 21:52:54 -080048 int svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053049 struct clk *svc_ref_clk;
50
51 struct pinctrl *pinctrl;
52 struct pinctrl_state *pin_default;
53
54 int num_apbs;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053055
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;
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +010058 spinlock_t wake_lock; /* Protect wake_detect_state */
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053059 unsigned long wake_detect_start;
David Lin9160b7c2016-04-16 01:15:15 +053060 struct notifier_block pm_notifier;
Vaibhav Hiremath685353c2016-02-25 04:37:35 +053061
Vaibhav Hirematha463fc12016-01-11 17:41:24 +053062 struct device *dev;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +053063};
64
65static inline void svc_reset_onoff(unsigned int gpio, bool onoff)
66{
67 gpio_set_value(gpio, onoff);
68}
69
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +053070static int apb_cold_boot(struct device *dev, void *data)
71{
72 int ret;
73
74 ret = apb_ctrl_coldboot(dev);
75 if (ret)
76 dev_warn(dev, "failed to coldboot\n");
77
78 /*Child nodes are independent, so do not exit coldboot operation */
79 return 0;
80}
81
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +053082static int apb_fw_flashing_state(struct device *dev, void *data)
83{
84 int ret;
85
86 ret = apb_ctrl_fw_flashing(dev);
87 if (ret)
88 dev_warn(dev, "failed to switch to fw flashing state\n");
89
90 /*Child nodes are independent, so do not exit coldboot operation */
91 return 0;
92}
93
94static int apb_poweroff(struct device *dev, void *data)
95{
96 apb_ctrl_poweroff(dev);
97
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +053098 /* Enable HUB3613 into HUB mode. */
99 if (usb3613_hub_mode_ctrl(false))
100 dev_warn(dev, "failed to control hub device\n");
101
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530102 return 0;
103}
104
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530105static void assert_wakedetect(struct arche_platform_drvdata *arche_pdata)
106{
107 /* Assert wake/detect = Detect event from AP */
108 gpio_direction_output(arche_pdata->wake_detect_gpio, 1);
109
110 /* Enable interrupt here, to read event back from SVC */
111 gpio_direction_input(arche_pdata->wake_detect_gpio);
112 enable_irq(arche_pdata->wake_detect_irq);
113}
114
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530115static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid)
116{
117 struct arche_platform_drvdata *arche_pdata = devid;
118 unsigned long flags;
119
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100120 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530121 if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) {
122 /* Something is wrong */
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100123 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530124 return IRQ_HANDLED;
125 }
126
127 arche_pdata->wake_detect_state = WD_STATE_COLDBOOT_START;
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100128 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530129
Vaibhav Hiremathff788de2016-02-25 16:45:44 +0530130 /* It should complete power cycle, so first make sure it is poweroff */
131 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
Vaibhav Hiremathe915ce42016-02-25 16:45:45 +0530132
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530133 /* Bring APB out of reset: cold boot sequence */
134 device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot);
135
Vaibhav Hirematha7ddda12016-04-12 17:18:47 +0530136 /* Enable HUB3613 into HUB mode. */
137 if (usb3613_hub_mode_ctrl(true))
138 dev_warn(arche_pdata->dev, "failed to control hub device\n");
139
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100140 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530141 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100142 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530143
144 return IRQ_HANDLED;
145}
146
147static irqreturn_t arche_platform_wd_irq(int irq, void *devid)
148{
149 struct arche_platform_drvdata *arche_pdata = devid;
150 unsigned long flags;
151
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100152 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530153
154 if (gpio_get_value(arche_pdata->wake_detect_gpio)) {
155 /* wake/detect rising */
156
157 /*
158 * If wake/detect line goes high after low, within less than
159 * 30msec, then standby boot sequence is initiated, which is not
160 * supported/implemented as of now. So ignore it.
161 */
162 if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) {
163 if (time_before(jiffies,
164 arche_pdata->wake_detect_start +
165 msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) {
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530166 arche_pdata->wake_detect_state = WD_STATE_IDLE;
167 } else {
168 /* Check we are not in middle of irq thread already */
169 if (arche_pdata->wake_detect_state !=
170 WD_STATE_COLDBOOT_START) {
171 arche_pdata->wake_detect_state =
172 WD_STATE_COLDBOOT_TRIG;
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100173 spin_unlock_irqrestore(
174 &arche_pdata->wake_lock,
175 flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530176 return IRQ_WAKE_THREAD;
177 }
178 }
179 }
180 } else {
181 /* wake/detect falling */
182 if (arche_pdata->wake_detect_state == WD_STATE_IDLE) {
183 arche_pdata->wake_detect_start = jiffies;
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530184 /*
185 * In the begining, when wake/detect goes low (first time), we assume
186 * it is meant for coldboot and set the flag. If wake/detect line stays low
187 * beyond 30msec, then it is coldboot else fallback to standby boot.
188 */
189 arche_pdata->wake_detect_state = WD_STATE_BOOT_INIT;
190 }
191 }
192
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100193 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530194
195 return IRQ_HANDLED;
196}
197
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530198static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata)
199{
200 int ret;
201
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530202 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
203 return 0;
204
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530205 dev_info(arche_pdata->dev, "Booting from cold boot state\n");
206
207 svc_reset_onoff(arche_pdata->svc_reset_gpio,
208 arche_pdata->is_reset_act_hi);
209
210 gpio_set_value(arche_pdata->svc_sysboot_gpio, 0);
211 usleep_range(100, 200);
212
213 ret = clk_prepare_enable(arche_pdata->svc_ref_clk);
214 if (ret) {
215 dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n",
216 ret);
217 return ret;
218 }
219
220 /* bring SVC out of reset */
221 svc_reset_onoff(arche_pdata->svc_reset_gpio,
222 !arche_pdata->is_reset_act_hi);
223
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530224 arche_pdata->state = ARCHE_PLATFORM_STATE_ACTIVE;
225
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530226 return 0;
227}
228
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530229static void arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata)
230{
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530231 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
232 return;
233
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530234 dev_info(arche_pdata->dev, "Switching to FW flashing state\n");
235
236 svc_reset_onoff(arche_pdata->svc_reset_gpio,
237 arche_pdata->is_reset_act_hi);
238
239 gpio_set_value(arche_pdata->svc_sysboot_gpio, 1);
240
241 usleep_range(100, 200);
242 svc_reset_onoff(arche_pdata->svc_reset_gpio,
243 !arche_pdata->is_reset_act_hi);
244
245 arche_pdata->state = ARCHE_PLATFORM_STATE_FW_FLASHING;
246
247}
248
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530249static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata)
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530250{
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530251 unsigned long flags;
252
Vaibhav Hiremath599159b2016-02-22 17:27:24 +0530253 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
254 return;
255
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530256 /* If in fw_flashing mode, then no need to repeate things again */
257 if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) {
Vaibhav Hiremathd2320b22016-02-25 04:37:39 +0530258 disable_irq(arche_pdata->wake_detect_irq);
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530259 /* Send disconnect/detach event to SVC */
Vaibhav Hiremath07862122016-02-25 04:37:38 +0530260 gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530261 usleep_range(100, 200);
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100262 spin_lock_irqsave(&arche_pdata->wake_lock, flags);
Vaibhav Hiremath685353c2016-02-25 04:37:35 +0530263 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100264 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags);
Vaibhav Hiremathb4c95fc2016-02-13 02:04:06 +0530265
Vaibhav Hiremath25847ee2016-02-22 17:27:25 +0530266 clk_disable_unprepare(arche_pdata->svc_ref_clk);
267 }
268
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530269 /* As part of exit, put APB back in reset state */
Viresh Kumar140741e2016-01-11 11:29:06 +0530270 svc_reset_onoff(arche_pdata->svc_reset_gpio,
271 arche_pdata->is_reset_act_hi);
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530272
273 arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530274}
275
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530276static ssize_t state_store(struct device *dev,
277 struct device_attribute *attr, const char *buf, size_t count)
278{
279 struct platform_device *pdev = to_platform_device(dev);
280 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
281 int ret = 0;
282
283 if (sysfs_streq(buf, "off")) {
284 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF)
285 return count;
286
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530287 /* If SVC goes down, bring down APB's as well */
288 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
289
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530290 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530291
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530292 } else if (sysfs_streq(buf, "active")) {
293 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE)
294 return count;
295
296 ret = arche_platform_coldboot_seq(arche_pdata);
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530297
298 assert_wakedetect(arche_pdata);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530299 } else if (sysfs_streq(buf, "standby")) {
300 if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY)
301 return count;
302
303 dev_warn(arche_pdata->dev, "standby state not supported\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530304 } else if (sysfs_streq(buf, "fw_flashing")) {
305 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING)
306 return count;
307
308 /* First we want to make sure we power off everything
309 * and then enter FW flashing state */
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530310 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
311
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530312 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530313
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530314 arche_platform_fw_flashing_seq(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530315
316 device_for_each_child(arche_pdata->dev, NULL, apb_fw_flashing_state);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530317 } else {
318 dev_err(arche_pdata->dev, "unknown state\n");
319 ret = -EINVAL;
320 }
321
322 return ret ? ret : count;
323}
324
325static ssize_t state_show(struct device *dev,
326 struct device_attribute *attr, char *buf)
327{
328 struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev);
329
330 switch (arche_pdata->state) {
331 case ARCHE_PLATFORM_STATE_OFF:
332 return sprintf(buf, "off\n");
333 case ARCHE_PLATFORM_STATE_ACTIVE:
334 return sprintf(buf, "active\n");
335 case ARCHE_PLATFORM_STATE_STANDBY:
336 return sprintf(buf, "standby\n");
Vaibhav Hiremath7691fed2016-02-13 02:04:08 +0530337 case ARCHE_PLATFORM_STATE_FW_FLASHING:
338 return sprintf(buf, "fw_flashing\n");
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530339 default:
340 return sprintf(buf, "unknown state\n");
341 }
342}
343
344static DEVICE_ATTR_RW(state);
345
David Lin9160b7c2016-04-16 01:15:15 +0530346static int arche_platform_pm_notifier(struct notifier_block *notifier,
347 unsigned long pm_event, void *unused)
348{
349 struct arche_platform_drvdata *arche_pdata =
350 container_of(notifier, struct arche_platform_drvdata,
351 pm_notifier);
352
353 switch (pm_event) {
354 case PM_SUSPEND_PREPARE:
355 if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE)
356 return NOTIFY_STOP;
357 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff);
358 arche_platform_poweroff_seq(arche_pdata);
359 break;
360 case PM_POST_SUSPEND:
361 arche_platform_coldboot_seq(arche_pdata);
362 assert_wakedetect(arche_pdata);
363 break;
364 default:
365 break;
366 }
367
368 return NOTIFY_DONE;
369}
370
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530371static int arche_platform_probe(struct platform_device *pdev)
372{
373 struct arche_platform_drvdata *arche_pdata;
374 struct device *dev = &pdev->dev;
375 struct device_node *np = dev->of_node;
376 int ret;
377
378 arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL);
379 if (!arche_pdata)
380 return -ENOMEM;
381
382 /* setup svc reset gpio */
383 arche_pdata->is_reset_act_hi = of_property_read_bool(np,
384 "svc,reset-active-high");
385 arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0);
386 if (arche_pdata->svc_reset_gpio < 0) {
387 dev_err(dev, "failed to get reset-gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530388 return arche_pdata->svc_reset_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530389 }
390 ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset");
391 if (ret) {
392 dev_err(dev, "failed to request svc-reset gpio:%d\n", ret);
393 return ret;
394 }
395 ret = gpio_direction_output(arche_pdata->svc_reset_gpio,
396 arche_pdata->is_reset_act_hi);
397 if (ret) {
398 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
399 return ret;
400 }
Vaibhav Hiremathe74d04a2016-02-13 02:04:05 +0530401 arche_pdata->state = ARCHE_PLATFORM_STATE_OFF;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530402
403 arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np,
404 "svc,sysboot-gpio", 0);
405 if (arche_pdata->svc_sysboot_gpio < 0) {
406 dev_err(dev, "failed to get sysboot gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530407 return arche_pdata->svc_sysboot_gpio;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530408 }
409 ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0");
410 if (ret) {
411 dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret);
412 return ret;
413 }
414 ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0);
415 if (ret) {
416 dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret);
417 return ret;
418 }
419
420 /* setup the clock request gpio first */
421 arche_pdata->svc_refclk_req = of_get_named_gpio(np,
422 "svc,refclk-req-gpio", 0);
423 if (arche_pdata->svc_refclk_req < 0) {
424 dev_err(dev, "failed to get svc clock-req gpio\n");
Viresh Kumarf1f251b2016-01-11 11:29:07 +0530425 return arche_pdata->svc_refclk_req;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530426 }
427 ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req");
428 if (ret) {
429 dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret);
430 return ret;
431 }
432 ret = gpio_direction_input(arche_pdata->svc_refclk_req);
433 if (ret) {
434 dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret);
435 return ret;
436 }
437
438 /* setup refclk2 to follow the pin */
439 arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk");
440 if (IS_ERR(arche_pdata->svc_ref_clk)) {
441 ret = PTR_ERR(arche_pdata->svc_ref_clk);
442 dev_err(dev, "failed to get svc_ref_clk: %d\n", ret);
443 return ret;
444 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530445
446 platform_set_drvdata(pdev, arche_pdata);
447
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530448 arche_pdata->num_apbs = of_get_child_count(np);
449 dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs);
450
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530451 arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0);
452 if (arche_pdata->wake_detect_gpio < 0) {
453 dev_err(dev, "failed to get wake detect gpio\n");
454 ret = arche_pdata->wake_detect_gpio;
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530455 return ret;
Viresh Kumar72a8c242016-01-11 11:29:05 +0530456 }
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530457
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530458 ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect");
459 if (ret) {
460 dev_err(dev, "Failed requesting wake_detect gpio %d\n",
461 arche_pdata->wake_detect_gpio);
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530462 return ret;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530463 }
Michael Scott057aad22016-01-27 16:40:58 -0800464 /* deassert wake detect */
465 gpio_direction_output(arche_pdata->wake_detect_gpio, 0);
Vaibhav Hiremath685353c2016-02-25 04:37:35 +0530466 arche_pdata->wake_detect_state = WD_STATE_IDLE;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530467
468 arche_pdata->dev = &pdev->dev;
Vaibhav Hirematha463fc12016-01-11 17:41:24 +0530469
Vaibhav Hiremath6c1ca562016-05-05 15:34:57 +0100470 spin_lock_init(&arche_pdata->wake_lock);
Vaibhav Hiremathf760bbf2016-02-25 04:37:36 +0530471 arche_pdata->wake_detect_irq =
472 gpio_to_irq(arche_pdata->wake_detect_gpio);
473
474 ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq,
475 arche_platform_wd_irq,
476 arche_platform_wd_irq_thread,
477 IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT,
478 dev_name(dev), arche_pdata);
479 if (ret) {
480 dev_err(dev, "failed to request wake detect IRQ %d\n", ret);
481 return ret;
482 }
483 /* Enable it only after sending wake/detect event */
484 disable_irq(arche_pdata->wake_detect_irq);
485
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530486 ret = device_create_file(dev, &dev_attr_state);
487 if (ret) {
488 dev_err(dev, "failed to create state file in sysfs\n");
489 return ret;
490 }
491
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530492 ret = arche_platform_coldboot_seq(arche_pdata);
493 if (ret) {
494 dev_err(dev, "Failed to cold boot svc %d\n", ret);
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530495 goto err_coldboot;
Vaibhav Hiremath758ca992016-02-13 02:04:03 +0530496 }
497
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530498 ret = of_platform_populate(np, NULL, NULL, dev);
499 if (ret) {
500 dev_err(dev, "failed to populate child nodes %d\n", ret);
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530501 goto err_populate;
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530502 }
503
Vaibhav Hiremath16fe18c2016-02-25 04:37:37 +0530504 assert_wakedetect(arche_pdata);
Vaibhav Hiremathfd60ac52016-02-13 02:04:17 +0530505
David Lin9160b7c2016-04-16 01:15:15 +0530506 arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier;
507 ret = register_pm_notifier(&arche_pdata->pm_notifier);
508 if (ret) {
509 dev_err(dev, "failed to register pm notifier %d\n", ret);
510 goto err_populate;
511 }
512
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530513 dev_info(dev, "Device registered successfully\n");
Viresh Kumar72a8c242016-01-11 11:29:05 +0530514 return 0;
Vaibhav Hiremath6743a6f2016-02-25 02:27:36 +0530515
516err_populate:
517 arche_platform_poweroff_seq(arche_pdata);
518err_coldboot:
519 device_remove_file(&pdev->dev, &dev_attr_state);
520 return ret;
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530521}
522
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530523static int arche_remove_child(struct device *dev, void *unused)
524{
525 struct platform_device *pdev = to_platform_device(dev);
526
527 platform_device_unregister(pdev);
528
529 return 0;
530}
531
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530532static int arche_platform_remove(struct platform_device *pdev)
533{
534 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev);
535
David Lin9160b7c2016-04-16 01:15:15 +0530536 unregister_pm_notifier(&arche_pdata->pm_notifier);
Vaibhav Hiremath2923c582016-02-13 02:04:07 +0530537 device_remove_file(&pdev->dev, &dev_attr_state);
Vaibhav Hiremathbc142bb2015-12-28 20:06:32 +0530538 device_for_each_child(&pdev->dev, NULL, arche_remove_child);
Vaibhav Hiremath5993e2b2016-02-13 02:04:04 +0530539 arche_platform_poweroff_seq(arche_pdata);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530540 platform_set_drvdata(pdev, NULL);
541
Vaibhav Hiremathad4d3f92016-02-13 02:04:20 +0530542 if (usb3613_hub_mode_ctrl(false))
543 dev_warn(arche_pdata->dev, "failed to control hub device\n");
544 /* TODO: Should we do anything more here ?? */
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530545 return 0;
546}
547
548static int arche_platform_suspend(struct device *dev)
549{
550 /*
551 * If timing profile premits, we may shutdown bridge
552 * completely
553 *
554 * TODO: sequence ??
555 *
556 * Also, need to make sure we meet precondition for unipro suspend
557 * Precondition: Definition ???
558 */
559 return 0;
560}
561
562static int arche_platform_resume(struct device *dev)
563{
564 /*
565 * Atleast for ES2 we have to meet the delay requirement between
566 * unipro switch and AP bridge init, depending on whether bridge is in
567 * OFF state or standby state.
568 *
569 * Based on whether bridge is in standby or OFF state we may have to
570 * assert multiple signals. Please refer to WDM spec, for more info.
571 *
572 */
573 return 0;
574}
575
576static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops,
577 arche_platform_suspend,
578 arche_platform_resume);
579
580static struct of_device_id arche_platform_of_match[] = {
581 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
582 { },
583};
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800584
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800585static struct of_device_id arche_combined_id[] = {
586 { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */
587 { .compatible = "usbffff,2", },
588 { },
589};
590MODULE_DEVICE_TABLE(of, arche_combined_id);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530591
592static struct platform_driver arche_platform_device_driver = {
593 .probe = arche_platform_probe,
594 .remove = arche_platform_remove,
595 .driver = {
596 .name = "arche-platform-ctrl",
597 .pm = &arche_platform_pm_ops,
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800598 .of_match_table = arche_platform_of_match,
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530599 }
600};
601
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800602static int __init arche_init(void)
603{
604 int retval;
605
606 retval = platform_driver_register(&arche_platform_device_driver);
607 if (retval)
608 return retval;
609
Viresh Kumar7b62b612016-04-20 11:48:37 +0530610 retval = arche_apb_init();
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800611 if (retval)
612 platform_driver_unregister(&arche_platform_device_driver);
613
614 return retval;
615}
616module_init(arche_init);
617
618static void __exit arche_exit(void)
619{
Viresh Kumar7b62b612016-04-20 11:48:37 +0530620 arche_apb_exit();
Greg Kroah-Hartman1e5dd1f2015-12-30 13:38:33 -0800621 platform_driver_unregister(&arche_platform_device_driver);
622}
623module_exit(arche_exit);
Vaibhav Hiremath7fa60652015-12-16 16:29:18 +0530624
625MODULE_LICENSE("GPL");
626MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>");
627MODULE_DESCRIPTION("Arche Platform Driver");