17fa60654SVaibhav Hiremath /* 27fa60654SVaibhav Hiremath * Arche Platform driver to enable Unipro link. 37fa60654SVaibhav Hiremath * 47fa60654SVaibhav Hiremath * Copyright 2014-2015 Google Inc. 57fa60654SVaibhav Hiremath * Copyright 2014-2015 Linaro Ltd. 67fa60654SVaibhav Hiremath * 77fa60654SVaibhav Hiremath * Released under the GPLv2 only. 87fa60654SVaibhav Hiremath */ 97fa60654SVaibhav Hiremath 107fa60654SVaibhav Hiremath #include <linux/clk.h> 11a463fc15SVaibhav Hiremath #include <linux/delay.h> 123b858df0SViresh Kumar #include <linux/gpio.h> 133b858df0SViresh Kumar #include <linux/init.h> 143b858df0SViresh Kumar #include <linux/module.h> 157fa60654SVaibhav Hiremath #include <linux/of_gpio.h> 163b858df0SViresh Kumar #include <linux/of_platform.h> 177fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h> 183b858df0SViresh Kumar #include <linux/platform_device.h> 193b858df0SViresh Kumar #include <linux/pm.h> 201e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 217fa60654SVaibhav Hiremath 227fa60654SVaibhav Hiremath struct arche_platform_drvdata { 237fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 247fa60654SVaibhav Hiremath int svc_reset_gpio; 257fa60654SVaibhav Hiremath bool is_reset_act_hi; 267fa60654SVaibhav Hiremath int svc_sysboot_gpio; 27a463fc15SVaibhav Hiremath int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ 287fa60654SVaibhav Hiremath 297fa60654SVaibhav Hiremath unsigned int svc_refclk_req; 307fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 317fa60654SVaibhav Hiremath 327fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 337fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 347fa60654SVaibhav Hiremath 357fa60654SVaibhav Hiremath int num_apbs; 36a463fc15SVaibhav Hiremath 37a463fc15SVaibhav Hiremath struct delayed_work delayed_work; 38a463fc15SVaibhav Hiremath struct device *dev; 397fa60654SVaibhav Hiremath }; 407fa60654SVaibhav Hiremath 417fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 427fa60654SVaibhav Hiremath { 437fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 447fa60654SVaibhav Hiremath } 457fa60654SVaibhav Hiremath 46a463fc15SVaibhav Hiremath /** 47a463fc15SVaibhav Hiremath * svc_delayed_work - Time to give SVC to boot. 48a463fc15SVaibhav Hiremath */ 49a463fc15SVaibhav Hiremath static void svc_delayed_work(struct work_struct *work) 50a463fc15SVaibhav Hiremath { 51a463fc15SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = 52a463fc15SVaibhav Hiremath container_of(work, struct arche_platform_drvdata, delayed_work.work); 53a463fc15SVaibhav Hiremath struct device *dev = arche_pdata->dev; 54a463fc15SVaibhav Hiremath struct device_node *np = dev->of_node; 55a463fc15SVaibhav Hiremath int timeout = 10; 56a463fc15SVaibhav Hiremath int ret; 57a463fc15SVaibhav Hiremath 58a463fc15SVaibhav Hiremath /* 59a463fc15SVaibhav Hiremath * 1. SVC and AP boot independently, with AP<-->SVC wake/detect pin 60a463fc15SVaibhav Hiremath * deasserted (LOW in this case) 61a463fc15SVaibhav Hiremath * 2.1. SVC allows 360 milliseconds to elapse after switch boots to work 62a463fc15SVaibhav Hiremath * around bug described in ENG-330. 63a463fc15SVaibhav Hiremath * 2.2. AP asserts wake/detect pin (HIGH) (this can proceed in parallel with 2.1) 64a463fc15SVaibhav Hiremath * 3. SVC detects assertion of wake/detect pin, and sends "wake out" signal to AP 65a463fc15SVaibhav Hiremath * 4. AP receives "wake out" signal, takes AP Bridges through their power 66a463fc15SVaibhav Hiremath * on reset sequence as defined in the bridge ASIC reference manuals 67a463fc15SVaibhav Hiremath * 5. AP takes USB3613 through its power on reset sequence 68a463fc15SVaibhav Hiremath * 6. AP enumerates AP Bridges 69a463fc15SVaibhav Hiremath */ 70a463fc15SVaibhav Hiremath gpio_set_value(arche_pdata->wake_detect_gpio, 1); 71a463fc15SVaibhav Hiremath gpio_direction_input(arche_pdata->wake_detect_gpio); 72a463fc15SVaibhav Hiremath do { 73a463fc15SVaibhav Hiremath /* Read the wake_detect GPIO, for WAKE_OUT event from SVC */ 74a463fc15SVaibhav Hiremath if (gpio_get_value(arche_pdata->wake_detect_gpio) == 0) 75a463fc15SVaibhav Hiremath break; 76a463fc15SVaibhav Hiremath 77a463fc15SVaibhav Hiremath msleep(500); 78a463fc15SVaibhav Hiremath } while(timeout--); 79a463fc15SVaibhav Hiremath 80a463fc15SVaibhav Hiremath if (timeout >= 0) { 81a463fc15SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 821e83ee33SMichael Scott if (!ret) { 831e83ee33SMichael Scott /* re-assert wake_detect to confirm SVC WAKE_OUT */ 841e83ee33SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 1); 85a463fc15SVaibhav Hiremath return; 86a463fc15SVaibhav Hiremath } 871e83ee33SMichael Scott } 88a463fc15SVaibhav Hiremath 89a463fc15SVaibhav Hiremath /* FIXME: We may want to limit retries here */ 90a463fc15SVaibhav Hiremath gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 91a463fc15SVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 92a463fc15SVaibhav Hiremath } 93a463fc15SVaibhav Hiremath 946da86df3SVaibhav Hiremath /* Export gpio's to user space */ 956da86df3SVaibhav Hiremath static void export_gpios(struct arche_platform_drvdata *arche_pdata) 966da86df3SVaibhav Hiremath { 976da86df3SVaibhav Hiremath gpio_export(arche_pdata->svc_reset_gpio, false); 986da86df3SVaibhav Hiremath gpio_export(arche_pdata->svc_sysboot_gpio, false); 996da86df3SVaibhav Hiremath } 1006da86df3SVaibhav Hiremath 1016da86df3SVaibhav Hiremath static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) 1026da86df3SVaibhav Hiremath { 1036da86df3SVaibhav Hiremath gpio_unexport(arche_pdata->svc_reset_gpio); 1046da86df3SVaibhav Hiremath gpio_unexport(arche_pdata->svc_sysboot_gpio); 1056da86df3SVaibhav Hiremath } 1066da86df3SVaibhav Hiremath 1077fa60654SVaibhav Hiremath static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) 1087fa60654SVaibhav Hiremath { 109d8b16338SVaibhav Hiremath clk_disable_unprepare(arche_pdata->svc_ref_clk); 1107fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 1117fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1127fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 1137fa60654SVaibhav Hiremath } 1147fa60654SVaibhav Hiremath 1157fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 1167fa60654SVaibhav Hiremath { 1177fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 1187fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 1197fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 1207fa60654SVaibhav Hiremath int ret; 1217fa60654SVaibhav Hiremath 1227fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 1237fa60654SVaibhav Hiremath if (!arche_pdata) 1247fa60654SVaibhav Hiremath return -ENOMEM; 1257fa60654SVaibhav Hiremath 1267fa60654SVaibhav Hiremath /* setup svc reset gpio */ 1277fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 1287fa60654SVaibhav Hiremath "svc,reset-active-high"); 1297fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 1307fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 1317fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 132f1f251b5SViresh Kumar return arche_pdata->svc_reset_gpio; 1337fa60654SVaibhav Hiremath } 1347fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 1357fa60654SVaibhav Hiremath if (ret) { 1367fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 1377fa60654SVaibhav Hiremath return ret; 1387fa60654SVaibhav Hiremath } 1397fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 1407fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 1417fa60654SVaibhav Hiremath if (ret) { 1427fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 1437fa60654SVaibhav Hiremath return ret; 1447fa60654SVaibhav Hiremath } 1457fa60654SVaibhav Hiremath 1467fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 1477fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 1487fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 1497fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 150f1f251b5SViresh Kumar return arche_pdata->svc_sysboot_gpio; 1517fa60654SVaibhav Hiremath } 1527fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 1537fa60654SVaibhav Hiremath if (ret) { 1547fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 1557fa60654SVaibhav Hiremath return ret; 1567fa60654SVaibhav Hiremath } 1577fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 1587fa60654SVaibhav Hiremath if (ret) { 1597fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 1607fa60654SVaibhav Hiremath return ret; 1617fa60654SVaibhav Hiremath } 1627fa60654SVaibhav Hiremath 1637fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 1647fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 1657fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 1667fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 1677fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 168f1f251b5SViresh Kumar return arche_pdata->svc_refclk_req; 1697fa60654SVaibhav Hiremath } 1707fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 1717fa60654SVaibhav Hiremath if (ret) { 1727fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 1737fa60654SVaibhav Hiremath return ret; 1747fa60654SVaibhav Hiremath } 1757fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 1767fa60654SVaibhav Hiremath if (ret) { 1777fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 1787fa60654SVaibhav Hiremath return ret; 1797fa60654SVaibhav Hiremath } 1807fa60654SVaibhav Hiremath 1817fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 1827fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 1837fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 1847fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 1857fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 1867fa60654SVaibhav Hiremath return ret; 1877fa60654SVaibhav Hiremath } 1887fa60654SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 1897fa60654SVaibhav Hiremath if (ret) { 1907fa60654SVaibhav Hiremath dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); 1917fa60654SVaibhav Hiremath return ret; 1927fa60654SVaibhav Hiremath } 1937fa60654SVaibhav Hiremath 1947fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 1957fa60654SVaibhav Hiremath 1967fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 1977fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 1987fa60654SVaibhav Hiremath 199a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); 200a463fc15SVaibhav Hiremath if (arche_pdata->wake_detect_gpio < 0) { 201a463fc15SVaibhav Hiremath dev_err(dev, "failed to get wake detect gpio\n"); 202a463fc15SVaibhav Hiremath ret = arche_pdata->wake_detect_gpio; 203a463fc15SVaibhav Hiremath goto exit; 20472a8c24bSViresh Kumar } 2057fa60654SVaibhav Hiremath 206a463fc15SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); 207a463fc15SVaibhav Hiremath if (ret) { 208a463fc15SVaibhav Hiremath dev_err(dev, "Failed requesting wake_detect gpio %d\n", 209a463fc15SVaibhav Hiremath arche_pdata->wake_detect_gpio); 210a463fc15SVaibhav Hiremath goto exit; 211a463fc15SVaibhav Hiremath } 212057aad29SMichael Scott /* deassert wake detect */ 213057aad29SMichael Scott gpio_direction_output(arche_pdata->wake_detect_gpio, 0); 214a463fc15SVaibhav Hiremath 2159e1aef82SMichael Scott /* bring SVC out of reset */ 2169e1aef82SMichael Scott svc_reset_onoff(arche_pdata->svc_reset_gpio, 2179e1aef82SMichael Scott !arche_pdata->is_reset_act_hi); 2189e1aef82SMichael Scott 219a463fc15SVaibhav Hiremath arche_pdata->dev = &pdev->dev; 220a463fc15SVaibhav Hiremath INIT_DELAYED_WORK(&arche_pdata->delayed_work, svc_delayed_work); 221a463fc15SVaibhav Hiremath schedule_delayed_work(&arche_pdata->delayed_work, msecs_to_jiffies(2000)); 222a463fc15SVaibhav Hiremath 2238adf71d1SViresh Kumar export_gpios(arche_pdata); 2248adf71d1SViresh Kumar 2257fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 22672a8c24bSViresh Kumar return 0; 227a463fc15SVaibhav Hiremath 228a463fc15SVaibhav Hiremath exit: 229a463fc15SVaibhav Hiremath arche_platform_cleanup(arche_pdata); 230a463fc15SVaibhav Hiremath return ret; 2317fa60654SVaibhav Hiremath } 2327fa60654SVaibhav Hiremath 233bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 234bc142bbbSVaibhav Hiremath { 235bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 236bc142bbbSVaibhav Hiremath 237bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 238bc142bbbSVaibhav Hiremath 239bc142bbbSVaibhav Hiremath return 0; 240bc142bbbSVaibhav Hiremath } 241bc142bbbSVaibhav Hiremath 2427fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 2437fa60654SVaibhav Hiremath { 2447fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 2457fa60654SVaibhav Hiremath 246bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 2477fa60654SVaibhav Hiremath arche_platform_cleanup(arche_pdata); 2487fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 2496da86df3SVaibhav Hiremath unexport_gpios(arche_pdata); 2507fa60654SVaibhav Hiremath 2517fa60654SVaibhav Hiremath return 0; 2527fa60654SVaibhav Hiremath } 2537fa60654SVaibhav Hiremath 2547fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 2557fa60654SVaibhav Hiremath { 2567fa60654SVaibhav Hiremath /* 2577fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 2587fa60654SVaibhav Hiremath * completely 2597fa60654SVaibhav Hiremath * 2607fa60654SVaibhav Hiremath * TODO: sequence ?? 2617fa60654SVaibhav Hiremath * 2627fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 2637fa60654SVaibhav Hiremath * Precondition: Definition ??? 2647fa60654SVaibhav Hiremath */ 2657fa60654SVaibhav Hiremath return 0; 2667fa60654SVaibhav Hiremath } 2677fa60654SVaibhav Hiremath 2687fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 2697fa60654SVaibhav Hiremath { 2707fa60654SVaibhav Hiremath /* 2717fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 2727fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 2737fa60654SVaibhav Hiremath * OFF state or standby state. 2747fa60654SVaibhav Hiremath * 2757fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 2767fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 2777fa60654SVaibhav Hiremath * 2787fa60654SVaibhav Hiremath */ 2797fa60654SVaibhav Hiremath return 0; 2807fa60654SVaibhav Hiremath } 2817fa60654SVaibhav Hiremath 2827fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 2837fa60654SVaibhav Hiremath arche_platform_suspend, 2847fa60654SVaibhav Hiremath arche_platform_resume); 2857fa60654SVaibhav Hiremath 2867fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 2877fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2887fa60654SVaibhav Hiremath { }, 2897fa60654SVaibhav Hiremath }; 2901e5dd1f8SGreg Kroah-Hartman 2911e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 2921e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2931e5dd1f8SGreg Kroah-Hartman { }, 2941e5dd1f8SGreg Kroah-Hartman }; 2951e5dd1f8SGreg Kroah-Hartman 2961e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 2971e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2981e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2991e5dd1f8SGreg Kroah-Hartman { }, 3001e5dd1f8SGreg Kroah-Hartman }; 3011e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 3027fa60654SVaibhav Hiremath 3037fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 3047fa60654SVaibhav Hiremath .probe = arche_platform_probe, 3057fa60654SVaibhav Hiremath .remove = arche_platform_remove, 3067fa60654SVaibhav Hiremath .driver = { 3077fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 3087fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 3091e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 3107fa60654SVaibhav Hiremath } 3117fa60654SVaibhav Hiremath }; 3127fa60654SVaibhav Hiremath 3131e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 3141e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 3151e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 3161e5dd1f8SGreg Kroah-Hartman .driver = { 3171e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 3181e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 3191e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 3201e5dd1f8SGreg Kroah-Hartman } 3211e5dd1f8SGreg Kroah-Hartman }; 3221e5dd1f8SGreg Kroah-Hartman 3231e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 3241e5dd1f8SGreg Kroah-Hartman { 3251e5dd1f8SGreg Kroah-Hartman int retval; 3261e5dd1f8SGreg Kroah-Hartman 3271e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 3281e5dd1f8SGreg Kroah-Hartman if (retval) 3291e5dd1f8SGreg Kroah-Hartman return retval; 3301e5dd1f8SGreg Kroah-Hartman 3311e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 3321e5dd1f8SGreg Kroah-Hartman if (retval) 3331e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 3341e5dd1f8SGreg Kroah-Hartman 3351e5dd1f8SGreg Kroah-Hartman return retval; 3361e5dd1f8SGreg Kroah-Hartman } 3371e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 3381e5dd1f8SGreg Kroah-Hartman 3391e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 3401e5dd1f8SGreg Kroah-Hartman { 3411e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 3421e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 3431e5dd1f8SGreg Kroah-Hartman } 3441e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 3457fa60654SVaibhav Hiremath 3467fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 3477fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 3487fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 349