1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Arche Platform driver to enable Unipro link. 4 * 5 * Copyright 2014-2015 Google Inc. 6 * Copyright 2014-2015 Linaro Ltd. 7 */ 8 9 #include <linux/clk.h> 10 #include <linux/delay.h> 11 #include <linux/gpio/consumer.h> 12 #include <linux/init.h> 13 #include <linux/module.h> 14 #include <linux/of_platform.h> 15 #include <linux/pinctrl/consumer.h> 16 #include <linux/platform_device.h> 17 #include <linux/pm.h> 18 #include <linux/interrupt.h> 19 #include <linux/irq.h> 20 #include <linux/suspend.h> 21 #include <linux/time.h> 22 #include <linux/greybus.h> 23 #include "arche_platform.h" 24 25 #if IS_ENABLED(CONFIG_USB_HSIC_USB3613) 26 #include <linux/usb/usb3613.h> 27 #else 28 static inline int usb3613_hub_mode_ctrl(bool unused) 29 { 30 return 0; 31 } 32 #endif 33 34 #define WD_COLDBOOT_PULSE_WIDTH_MS 30 35 36 enum svc_wakedetect_state { 37 WD_STATE_IDLE, /* Default state = pulled high/low */ 38 WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ 39 WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ 40 WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ 41 WD_STATE_COLDBOOT_START, /* Cold boot process started */ 42 WD_STATE_STANDBYBOOT_START, /* Not used */ 43 }; 44 45 struct arche_platform_drvdata { 46 /* Control GPIO signals to and from AP <=> SVC */ 47 struct gpio_desc *svc_reset; 48 bool is_reset_act_hi; 49 struct gpio_desc *svc_sysboot; 50 struct gpio_desc *wake_detect; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 51 52 enum arche_platform_state state; 53 54 struct gpio_desc *svc_refclk_req; 55 struct clk *svc_ref_clk; 56 57 struct pinctrl *pinctrl; 58 struct pinctrl_state *pin_default; 59 60 int num_apbs; 61 62 enum svc_wakedetect_state wake_detect_state; 63 int wake_detect_irq; 64 spinlock_t wake_lock; /* Protect wake_detect_state */ 65 struct mutex platform_state_mutex; /* Protect state */ 66 unsigned long wake_detect_start; 67 struct notifier_block pm_notifier; 68 69 struct device *dev; 70 }; 71 72 /* Requires calling context to hold arche_pdata->platform_state_mutex */ 73 static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, 74 enum arche_platform_state state) 75 { 76 arche_pdata->state = state; 77 } 78 79 /* Requires arche_pdata->wake_lock is held by calling context */ 80 static void arche_platform_set_wake_detect_state( 81 struct arche_platform_drvdata *arche_pdata, 82 enum svc_wakedetect_state state) 83 { 84 arche_pdata->wake_detect_state = state; 85 } 86 87 static inline void svc_reset_onoff(struct gpio_desc *gpio, bool onoff) 88 { 89 gpiod_set_raw_value(gpio, onoff); 90 } 91 92 static int apb_cold_boot(struct device *dev, void *data) 93 { 94 int ret; 95 96 ret = apb_ctrl_coldboot(dev); 97 if (ret) 98 dev_warn(dev, "failed to coldboot\n"); 99 100 /*Child nodes are independent, so do not exit coldboot operation */ 101 return 0; 102 } 103 104 static int apb_poweroff(struct device *dev, void *data) 105 { 106 apb_ctrl_poweroff(dev); 107 108 /* Enable HUB3613 into HUB mode. */ 109 if (usb3613_hub_mode_ctrl(false)) 110 dev_warn(dev, "failed to control hub device\n"); 111 112 return 0; 113 } 114 115 static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) 116 { 117 /* Enable interrupt here, to read event back from SVC */ 118 enable_irq(arche_pdata->wake_detect_irq); 119 } 120 121 static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) 122 { 123 struct arche_platform_drvdata *arche_pdata = devid; 124 unsigned long flags; 125 126 spin_lock_irqsave(&arche_pdata->wake_lock, flags); 127 if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { 128 /* Something is wrong */ 129 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 130 return IRQ_HANDLED; 131 } 132 133 arche_platform_set_wake_detect_state(arche_pdata, 134 WD_STATE_COLDBOOT_START); 135 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 136 137 /* It should complete power cycle, so first make sure it is poweroff */ 138 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 139 140 /* Bring APB out of reset: cold boot sequence */ 141 device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); 142 143 /* Enable HUB3613 into HUB mode. */ 144 if (usb3613_hub_mode_ctrl(true)) 145 dev_warn(arche_pdata->dev, "failed to control hub device\n"); 146 147 spin_lock_irqsave(&arche_pdata->wake_lock, flags); 148 arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 149 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 150 151 return IRQ_HANDLED; 152 } 153 154 static irqreturn_t arche_platform_wd_irq(int irq, void *devid) 155 { 156 struct arche_platform_drvdata *arche_pdata = devid; 157 unsigned long flags; 158 159 spin_lock_irqsave(&arche_pdata->wake_lock, flags); 160 161 if (gpiod_get_value(arche_pdata->wake_detect)) { 162 /* wake/detect rising */ 163 164 /* 165 * If wake/detect line goes high after low, within less than 166 * 30msec, then standby boot sequence is initiated, which is not 167 * supported/implemented as of now. So ignore it. 168 */ 169 if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { 170 if (time_before(jiffies, 171 arche_pdata->wake_detect_start + 172 msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { 173 arche_platform_set_wake_detect_state(arche_pdata, 174 WD_STATE_IDLE); 175 } else { 176 /* 177 * Check we are not in middle of irq thread 178 * already 179 */ 180 if (arche_pdata->wake_detect_state != 181 WD_STATE_COLDBOOT_START) { 182 arche_platform_set_wake_detect_state(arche_pdata, 183 WD_STATE_COLDBOOT_TRIG); 184 spin_unlock_irqrestore( 185 &arche_pdata->wake_lock, 186 flags); 187 return IRQ_WAKE_THREAD; 188 } 189 } 190 } 191 } else { 192 /* wake/detect falling */ 193 if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { 194 arche_pdata->wake_detect_start = jiffies; 195 /* 196 * In the beginning, when wake/detect goes low 197 * (first time), we assume it is meant for coldboot 198 * and set the flag. If wake/detect line stays low 199 * beyond 30msec, then it is coldboot else fallback 200 * to standby boot. 201 */ 202 arche_platform_set_wake_detect_state(arche_pdata, 203 WD_STATE_BOOT_INIT); 204 } 205 } 206 207 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 208 209 return IRQ_HANDLED; 210 } 211 212 /* 213 * Requires arche_pdata->platform_state_mutex to be held 214 */ 215 static int 216 arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) 217 { 218 int ret; 219 220 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 221 return 0; 222 223 dev_info(arche_pdata->dev, "Booting from cold boot state\n"); 224 225 svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 226 227 gpiod_set_value(arche_pdata->svc_sysboot, 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, !arche_pdata->is_reset_act_hi); 239 240 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); 241 242 return 0; 243 } 244 245 /* 246 * Requires arche_pdata->platform_state_mutex to be held 247 */ 248 static int 249 arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) 250 { 251 int ret; 252 253 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 254 return 0; 255 256 dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); 257 258 svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 259 260 gpiod_set_value(arche_pdata->svc_sysboot, 1); 261 262 usleep_range(100, 200); 263 264 ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 265 if (ret) { 266 dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", 267 ret); 268 return ret; 269 } 270 271 svc_reset_onoff(arche_pdata->svc_reset, !arche_pdata->is_reset_act_hi); 272 273 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); 274 275 return 0; 276 } 277 278 /* 279 * Requires arche_pdata->platform_state_mutex to be held 280 */ 281 static void 282 arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) 283 { 284 unsigned long flags; 285 286 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 287 return; 288 289 /* If in fw_flashing mode, then no need to repeate things again */ 290 if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { 291 disable_irq(arche_pdata->wake_detect_irq); 292 293 spin_lock_irqsave(&arche_pdata->wake_lock, flags); 294 arche_platform_set_wake_detect_state(arche_pdata, 295 WD_STATE_IDLE); 296 spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); 297 } 298 299 clk_disable_unprepare(arche_pdata->svc_ref_clk); 300 301 /* As part of exit, put APB back in reset state */ 302 svc_reset_onoff(arche_pdata->svc_reset, arche_pdata->is_reset_act_hi); 303 304 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 305 } 306 307 static ssize_t state_store(struct device *dev, 308 struct device_attribute *attr, 309 const char *buf, size_t count) 310 { 311 struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 312 int ret = 0; 313 314 mutex_lock(&arche_pdata->platform_state_mutex); 315 316 if (sysfs_streq(buf, "off")) { 317 if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) 318 goto exit; 319 320 /* If SVC goes down, bring down APB's as well */ 321 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 322 323 arche_platform_poweroff_seq(arche_pdata); 324 325 } else if (sysfs_streq(buf, "active")) { 326 if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) 327 goto exit; 328 329 /* First we want to make sure we power off everything 330 * and then activate back again 331 */ 332 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 333 arche_platform_poweroff_seq(arche_pdata); 334 335 arche_platform_wd_irq_en(arche_pdata); 336 ret = arche_platform_coldboot_seq(arche_pdata); 337 if (ret) 338 goto exit; 339 340 } else if (sysfs_streq(buf, "standby")) { 341 if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) 342 goto exit; 343 344 dev_warn(arche_pdata->dev, "standby state not supported\n"); 345 } else if (sysfs_streq(buf, "fw_flashing")) { 346 if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) 347 goto exit; 348 349 /* 350 * Here we only control SVC. 351 * 352 * In case of FW_FLASHING mode we do not want to control 353 * APBs, as in case of V2, SPI bus is shared between both 354 * the APBs. So let user chose which APB he wants to flash. 355 */ 356 arche_platform_poweroff_seq(arche_pdata); 357 358 ret = arche_platform_fw_flashing_seq(arche_pdata); 359 if (ret) 360 goto exit; 361 } else { 362 dev_err(arche_pdata->dev, "unknown state\n"); 363 ret = -EINVAL; 364 } 365 366 exit: 367 mutex_unlock(&arche_pdata->platform_state_mutex); 368 return ret ? ret : count; 369 } 370 371 static ssize_t state_show(struct device *dev, 372 struct device_attribute *attr, char *buf) 373 { 374 struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); 375 376 switch (arche_pdata->state) { 377 case ARCHE_PLATFORM_STATE_OFF: 378 return sprintf(buf, "off\n"); 379 case ARCHE_PLATFORM_STATE_ACTIVE: 380 return sprintf(buf, "active\n"); 381 case ARCHE_PLATFORM_STATE_STANDBY: 382 return sprintf(buf, "standby\n"); 383 case ARCHE_PLATFORM_STATE_FW_FLASHING: 384 return sprintf(buf, "fw_flashing\n"); 385 default: 386 return sprintf(buf, "unknown state\n"); 387 } 388 } 389 390 static DEVICE_ATTR_RW(state); 391 392 static int arche_platform_pm_notifier(struct notifier_block *notifier, 393 unsigned long pm_event, void *unused) 394 { 395 struct arche_platform_drvdata *arche_pdata = 396 container_of(notifier, struct arche_platform_drvdata, 397 pm_notifier); 398 int ret = NOTIFY_DONE; 399 400 mutex_lock(&arche_pdata->platform_state_mutex); 401 switch (pm_event) { 402 case PM_SUSPEND_PREPARE: 403 if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { 404 ret = NOTIFY_STOP; 405 break; 406 } 407 device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); 408 arche_platform_poweroff_seq(arche_pdata); 409 break; 410 case PM_POST_SUSPEND: 411 if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) 412 break; 413 414 arche_platform_wd_irq_en(arche_pdata); 415 arche_platform_coldboot_seq(arche_pdata); 416 break; 417 default: 418 break; 419 } 420 mutex_unlock(&arche_pdata->platform_state_mutex); 421 422 return ret; 423 } 424 425 static int arche_platform_probe(struct platform_device *pdev) 426 { 427 struct arche_platform_drvdata *arche_pdata; 428 struct device *dev = &pdev->dev; 429 struct device_node *np = dev->of_node; 430 int ret; 431 unsigned int flags; 432 433 arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), 434 GFP_KERNEL); 435 if (!arche_pdata) 436 return -ENOMEM; 437 438 /* setup svc reset gpio */ 439 arche_pdata->is_reset_act_hi = of_property_read_bool(np, 440 "svc,reset-active-high"); 441 if (arche_pdata->is_reset_act_hi) 442 flags = GPIOD_OUT_HIGH; 443 else 444 flags = GPIOD_OUT_LOW; 445 446 arche_pdata->svc_reset = devm_gpiod_get(dev, "svc,reset", flags); 447 if (IS_ERR(arche_pdata->svc_reset)) { 448 ret = PTR_ERR(arche_pdata->svc_reset); 449 dev_err(dev, "failed to request svc-reset GPIO: %d\n", ret); 450 return ret; 451 } 452 arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); 453 454 arche_pdata->svc_sysboot = devm_gpiod_get(dev, "svc,sysboot", 455 GPIOD_OUT_LOW); 456 if (IS_ERR(arche_pdata->svc_sysboot)) { 457 ret = PTR_ERR(arche_pdata->svc_sysboot); 458 dev_err(dev, "failed to request sysboot0 GPIO: %d\n", ret); 459 return ret; 460 } 461 462 /* setup the clock request gpio first */ 463 arche_pdata->svc_refclk_req = devm_gpiod_get(dev, "svc,refclk-req", 464 GPIOD_IN); 465 if (IS_ERR(arche_pdata->svc_refclk_req)) { 466 ret = PTR_ERR(arche_pdata->svc_refclk_req); 467 dev_err(dev, "failed to request svc-clk-req GPIO: %d\n", ret); 468 return ret; 469 } 470 471 /* setup refclk2 to follow the pin */ 472 arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 473 if (IS_ERR(arche_pdata->svc_ref_clk)) { 474 ret = PTR_ERR(arche_pdata->svc_ref_clk); 475 dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 476 return ret; 477 } 478 479 platform_set_drvdata(pdev, arche_pdata); 480 481 arche_pdata->num_apbs = of_get_child_count(np); 482 dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 483 484 arche_pdata->wake_detect = devm_gpiod_get(dev, "svc,wake-detect", 485 GPIOD_IN); 486 if (IS_ERR(arche_pdata->wake_detect)) { 487 ret = PTR_ERR(arche_pdata->wake_detect); 488 dev_err(dev, "Failed requesting wake_detect GPIO: %d\n", ret); 489 return ret; 490 } 491 492 arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); 493 494 arche_pdata->dev = &pdev->dev; 495 496 spin_lock_init(&arche_pdata->wake_lock); 497 mutex_init(&arche_pdata->platform_state_mutex); 498 arche_pdata->wake_detect_irq = 499 gpiod_to_irq(arche_pdata->wake_detect); 500 501 ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, 502 arche_platform_wd_irq, 503 arche_platform_wd_irq_thread, 504 IRQF_TRIGGER_FALLING | 505 IRQF_TRIGGER_RISING | IRQF_ONESHOT, 506 dev_name(dev), arche_pdata); 507 if (ret) { 508 dev_err(dev, "failed to request wake detect IRQ %d\n", ret); 509 return ret; 510 } 511 disable_irq(arche_pdata->wake_detect_irq); 512 513 ret = device_create_file(dev, &dev_attr_state); 514 if (ret) { 515 dev_err(dev, "failed to create state file in sysfs\n"); 516 return ret; 517 } 518 519 ret = of_platform_populate(np, NULL, NULL, dev); 520 if (ret) { 521 dev_err(dev, "failed to populate child nodes %d\n", ret); 522 goto err_device_remove; 523 } 524 525 arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; 526 ret = register_pm_notifier(&arche_pdata->pm_notifier); 527 528 if (ret) { 529 dev_err(dev, "failed to register pm notifier %d\n", ret); 530 goto err_device_remove; 531 } 532 533 /* Explicitly power off if requested */ 534 if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { 535 mutex_lock(&arche_pdata->platform_state_mutex); 536 ret = arche_platform_coldboot_seq(arche_pdata); 537 if (ret) { 538 dev_err(dev, "Failed to cold boot svc %d\n", ret); 539 goto err_coldboot; 540 } 541 arche_platform_wd_irq_en(arche_pdata); 542 mutex_unlock(&arche_pdata->platform_state_mutex); 543 } 544 545 dev_info(dev, "Device registered successfully\n"); 546 return 0; 547 548 err_coldboot: 549 mutex_unlock(&arche_pdata->platform_state_mutex); 550 err_device_remove: 551 device_remove_file(&pdev->dev, &dev_attr_state); 552 return ret; 553 } 554 555 static int arche_remove_child(struct device *dev, void *unused) 556 { 557 struct platform_device *pdev = to_platform_device(dev); 558 559 platform_device_unregister(pdev); 560 561 return 0; 562 } 563 564 static int arche_platform_remove(struct platform_device *pdev) 565 { 566 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 567 568 unregister_pm_notifier(&arche_pdata->pm_notifier); 569 device_remove_file(&pdev->dev, &dev_attr_state); 570 device_for_each_child(&pdev->dev, NULL, arche_remove_child); 571 arche_platform_poweroff_seq(arche_pdata); 572 573 if (usb3613_hub_mode_ctrl(false)) 574 dev_warn(arche_pdata->dev, "failed to control hub device\n"); 575 /* TODO: Should we do anything more here ?? */ 576 return 0; 577 } 578 579 static __maybe_unused int arche_platform_suspend(struct device *dev) 580 { 581 /* 582 * If timing profile premits, we may shutdown bridge 583 * completely 584 * 585 * TODO: sequence ?? 586 * 587 * Also, need to make sure we meet precondition for unipro suspend 588 * Precondition: Definition ??? 589 */ 590 return 0; 591 } 592 593 static __maybe_unused int arche_platform_resume(struct device *dev) 594 { 595 /* 596 * Atleast for ES2 we have to meet the delay requirement between 597 * unipro switch and AP bridge init, depending on whether bridge is in 598 * OFF state or standby state. 599 * 600 * Based on whether bridge is in standby or OFF state we may have to 601 * assert multiple signals. Please refer to WDM spec, for more info. 602 * 603 */ 604 return 0; 605 } 606 607 static void arche_platform_shutdown(struct platform_device *pdev) 608 { 609 struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 610 611 arche_platform_poweroff_seq(arche_pdata); 612 613 usb3613_hub_mode_ctrl(false); 614 } 615 616 static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 617 arche_platform_suspend, 618 arche_platform_resume); 619 620 static const struct of_device_id arche_platform_of_match[] = { 621 /* Use PID/VID of SVC device */ 622 { .compatible = "google,arche-platform", }, 623 { }, 624 }; 625 626 static const struct of_device_id arche_combined_id[] = { 627 /* Use PID/VID of SVC device */ 628 { .compatible = "google,arche-platform", }, 629 { .compatible = "usbffff,2", }, 630 { }, 631 }; 632 MODULE_DEVICE_TABLE(of, arche_combined_id); 633 634 static struct platform_driver arche_platform_device_driver = { 635 .probe = arche_platform_probe, 636 .remove = arche_platform_remove, 637 .shutdown = arche_platform_shutdown, 638 .driver = { 639 .name = "arche-platform-ctrl", 640 .pm = &arche_platform_pm_ops, 641 .of_match_table = arche_platform_of_match, 642 } 643 }; 644 645 static int __init arche_init(void) 646 { 647 int retval; 648 649 retval = platform_driver_register(&arche_platform_device_driver); 650 if (retval) 651 return retval; 652 653 retval = arche_apb_init(); 654 if (retval) 655 platform_driver_unregister(&arche_platform_device_driver); 656 657 return retval; 658 } 659 module_init(arche_init); 660 661 static void __exit arche_exit(void) 662 { 663 arche_apb_exit(); 664 platform_driver_unregister(&arche_platform_device_driver); 665 } 666 module_exit(arche_exit); 667 668 MODULE_LICENSE("GPL v2"); 669 MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 670 MODULE_DESCRIPTION("Arche Platform Driver"); 671