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/module.h> 117fa60654SVaibhav Hiremath #include <linux/init.h> 127fa60654SVaibhav Hiremath #include <linux/interrupt.h> 137fa60654SVaibhav Hiremath #include <linux/irq.h> 147fa60654SVaibhav Hiremath #include <linux/sched.h> 157fa60654SVaibhav Hiremath #include <linux/pm.h> 167fa60654SVaibhav Hiremath #include <linux/delay.h> 177fa60654SVaibhav Hiremath #include <linux/platform_device.h> 187fa60654SVaibhav Hiremath #include <linux/gpio.h> 197fa60654SVaibhav Hiremath #include <linux/clk.h> 207fa60654SVaibhav Hiremath #include <linux/of_platform.h> 217fa60654SVaibhav Hiremath #include <linux/of_gpio.h> 227fa60654SVaibhav Hiremath #include <linux/of_irq.h> 237fa60654SVaibhav Hiremath #include <linux/spinlock.h> 247fa60654SVaibhav Hiremath #include <linux/regulator/consumer.h> 257fa60654SVaibhav Hiremath #include <linux/pinctrl/consumer.h> 261e5dd1f8SGreg Kroah-Hartman #include "arche_platform.h" 277fa60654SVaibhav Hiremath 287fa60654SVaibhav Hiremath struct arche_platform_drvdata { 297fa60654SVaibhav Hiremath /* Control GPIO signals to and from AP <=> SVC */ 307fa60654SVaibhav Hiremath int svc_reset_gpio; 317fa60654SVaibhav Hiremath bool is_reset_act_hi; 327fa60654SVaibhav Hiremath int svc_sysboot_gpio; 337fa60654SVaibhav Hiremath 347fa60654SVaibhav Hiremath unsigned int svc_refclk_req; 357fa60654SVaibhav Hiremath struct clk *svc_ref_clk; 367fa60654SVaibhav Hiremath 377fa60654SVaibhav Hiremath struct pinctrl *pinctrl; 387fa60654SVaibhav Hiremath struct pinctrl_state *pin_default; 397fa60654SVaibhav Hiremath 407fa60654SVaibhav Hiremath int num_apbs; 417fa60654SVaibhav Hiremath }; 427fa60654SVaibhav Hiremath 437fa60654SVaibhav Hiremath static inline void svc_reset_onoff(unsigned int gpio, bool onoff) 447fa60654SVaibhav Hiremath { 457fa60654SVaibhav Hiremath gpio_set_value(gpio, onoff); 467fa60654SVaibhav Hiremath } 477fa60654SVaibhav Hiremath 486da86df3SVaibhav Hiremath /* Export gpio's to user space */ 496da86df3SVaibhav Hiremath static void export_gpios(struct arche_platform_drvdata *arche_pdata) 506da86df3SVaibhav Hiremath { 516da86df3SVaibhav Hiremath gpio_export(arche_pdata->svc_reset_gpio, false); 526da86df3SVaibhav Hiremath gpio_export(arche_pdata->svc_sysboot_gpio, false); 536da86df3SVaibhav Hiremath } 546da86df3SVaibhav Hiremath 556da86df3SVaibhav Hiremath static void unexport_gpios(struct arche_platform_drvdata *arche_pdata) 566da86df3SVaibhav Hiremath { 576da86df3SVaibhav Hiremath gpio_unexport(arche_pdata->svc_reset_gpio); 586da86df3SVaibhav Hiremath gpio_unexport(arche_pdata->svc_sysboot_gpio); 596da86df3SVaibhav Hiremath } 606da86df3SVaibhav Hiremath 617fa60654SVaibhav Hiremath static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) 627fa60654SVaibhav Hiremath { 637fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 647fa60654SVaibhav Hiremath if (gpio_is_valid(arche_pdata->svc_reset_gpio)) 657fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 667fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 677fa60654SVaibhav Hiremath } 687fa60654SVaibhav Hiremath 697fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 707fa60654SVaibhav Hiremath { 717fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 727fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 737fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 747fa60654SVaibhav Hiremath int ret; 757fa60654SVaibhav Hiremath 767fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 777fa60654SVaibhav Hiremath if (!arche_pdata) 787fa60654SVaibhav Hiremath return -ENOMEM; 797fa60654SVaibhav Hiremath 807fa60654SVaibhav Hiremath /* setup svc reset gpio */ 817fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 827fa60654SVaibhav Hiremath "svc,reset-active-high"); 837fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 847fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 857fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 867fa60654SVaibhav Hiremath return -ENODEV; 877fa60654SVaibhav Hiremath } 887fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 897fa60654SVaibhav Hiremath if (ret) { 907fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 917fa60654SVaibhav Hiremath return ret; 927fa60654SVaibhav Hiremath } 937fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 947fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 957fa60654SVaibhav Hiremath if (ret) { 967fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 977fa60654SVaibhav Hiremath return ret; 987fa60654SVaibhav Hiremath } 997fa60654SVaibhav Hiremath 1007fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 1017fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 1027fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 1037fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 1047fa60654SVaibhav Hiremath return -ENODEV; 1057fa60654SVaibhav Hiremath } 1067fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 1077fa60654SVaibhav Hiremath if (ret) { 1087fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 1097fa60654SVaibhav Hiremath return ret; 1107fa60654SVaibhav Hiremath } 1117fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 1127fa60654SVaibhav Hiremath if (ret) { 1137fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 1147fa60654SVaibhav Hiremath return ret; 1157fa60654SVaibhav Hiremath } 1167fa60654SVaibhav Hiremath 1177fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 1187fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 1197fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 1207fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 1217fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 1227fa60654SVaibhav Hiremath return -ENODEV; 1237fa60654SVaibhav Hiremath } 1247fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 1257fa60654SVaibhav Hiremath if (ret) { 1267fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 1277fa60654SVaibhav Hiremath return ret; 1287fa60654SVaibhav Hiremath } 1297fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 1307fa60654SVaibhav Hiremath if (ret) { 1317fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 1327fa60654SVaibhav Hiremath return ret; 1337fa60654SVaibhav Hiremath } 1347fa60654SVaibhav Hiremath 1357fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 1367fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 1377fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 1387fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 1397fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 1407fa60654SVaibhav Hiremath return ret; 1417fa60654SVaibhav Hiremath } 1427fa60654SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 1437fa60654SVaibhav Hiremath if (ret) { 1447fa60654SVaibhav Hiremath dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); 1457fa60654SVaibhav Hiremath return ret; 1467fa60654SVaibhav Hiremath } 1477fa60654SVaibhav Hiremath 1487fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 1497fa60654SVaibhav Hiremath 1507fa60654SVaibhav Hiremath /* bring SVC out of reset */ 1517fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1527fa60654SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 1537fa60654SVaibhav Hiremath 1547fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 1557fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 1567fa60654SVaibhav Hiremath 1577fa60654SVaibhav Hiremath /* probe all childs here */ 1587fa60654SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 1597fa60654SVaibhav Hiremath if (ret) 1607fa60654SVaibhav Hiremath dev_err(dev, "no child node found\n"); 1617fa60654SVaibhav Hiremath 1628adf71d1SViresh Kumar export_gpios(arche_pdata); 1638adf71d1SViresh Kumar 1647fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 1657fa60654SVaibhav Hiremath return ret; 1667fa60654SVaibhav Hiremath } 1677fa60654SVaibhav Hiremath 168bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 169bc142bbbSVaibhav Hiremath { 170bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 171bc142bbbSVaibhav Hiremath 172bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 173bc142bbbSVaibhav Hiremath 174bc142bbbSVaibhav Hiremath return 0; 175bc142bbbSVaibhav Hiremath } 176bc142bbbSVaibhav Hiremath 1777fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 1787fa60654SVaibhav Hiremath { 1797fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 1807fa60654SVaibhav Hiremath 181bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 1827fa60654SVaibhav Hiremath arche_platform_cleanup(arche_pdata); 1837fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 1846da86df3SVaibhav Hiremath unexport_gpios(arche_pdata); 1857fa60654SVaibhav Hiremath 1867fa60654SVaibhav Hiremath return 0; 1877fa60654SVaibhav Hiremath } 1887fa60654SVaibhav Hiremath 1897fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 1907fa60654SVaibhav Hiremath { 1917fa60654SVaibhav Hiremath /* 1927fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 1937fa60654SVaibhav Hiremath * completely 1947fa60654SVaibhav Hiremath * 1957fa60654SVaibhav Hiremath * TODO: sequence ?? 1967fa60654SVaibhav Hiremath * 1977fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 1987fa60654SVaibhav Hiremath * Precondition: Definition ??? 1997fa60654SVaibhav Hiremath */ 2007fa60654SVaibhav Hiremath return 0; 2017fa60654SVaibhav Hiremath } 2027fa60654SVaibhav Hiremath 2037fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 2047fa60654SVaibhav Hiremath { 2057fa60654SVaibhav Hiremath /* 2067fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 2077fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 2087fa60654SVaibhav Hiremath * OFF state or standby state. 2097fa60654SVaibhav Hiremath * 2107fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 2117fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 2127fa60654SVaibhav Hiremath * 2137fa60654SVaibhav Hiremath */ 2147fa60654SVaibhav Hiremath return 0; 2157fa60654SVaibhav Hiremath } 2167fa60654SVaibhav Hiremath 2177fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 2187fa60654SVaibhav Hiremath arche_platform_suspend, 2197fa60654SVaibhav Hiremath arche_platform_resume); 2207fa60654SVaibhav Hiremath 2217fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 2227fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2237fa60654SVaibhav Hiremath { }, 2247fa60654SVaibhav Hiremath }; 2251e5dd1f8SGreg Kroah-Hartman 2261e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 2271e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2281e5dd1f8SGreg Kroah-Hartman { }, 2291e5dd1f8SGreg Kroah-Hartman }; 2301e5dd1f8SGreg Kroah-Hartman 2311e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 2321e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2331e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2341e5dd1f8SGreg Kroah-Hartman { }, 2351e5dd1f8SGreg Kroah-Hartman }; 2361e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 2377fa60654SVaibhav Hiremath 2387fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 2397fa60654SVaibhav Hiremath .probe = arche_platform_probe, 2407fa60654SVaibhav Hiremath .remove = arche_platform_remove, 2417fa60654SVaibhav Hiremath .driver = { 2427fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 2437fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 2441e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 2457fa60654SVaibhav Hiremath } 2467fa60654SVaibhav Hiremath }; 2477fa60654SVaibhav Hiremath 2481e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 2491e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 2501e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 2511e5dd1f8SGreg Kroah-Hartman .driver = { 2521e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 2531e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 2541e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 2551e5dd1f8SGreg Kroah-Hartman } 2561e5dd1f8SGreg Kroah-Hartman }; 2571e5dd1f8SGreg Kroah-Hartman 2581e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 2591e5dd1f8SGreg Kroah-Hartman { 2601e5dd1f8SGreg Kroah-Hartman int retval; 2611e5dd1f8SGreg Kroah-Hartman 2621e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 2631e5dd1f8SGreg Kroah-Hartman if (retval) 2641e5dd1f8SGreg Kroah-Hartman return retval; 2651e5dd1f8SGreg Kroah-Hartman 2661e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 2671e5dd1f8SGreg Kroah-Hartman if (retval) 2681e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 2691e5dd1f8SGreg Kroah-Hartman 2701e5dd1f8SGreg Kroah-Hartman return retval; 2711e5dd1f8SGreg Kroah-Hartman } 2721e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 2731e5dd1f8SGreg Kroah-Hartman 2741e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 2751e5dd1f8SGreg Kroah-Hartman { 2761e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 2771e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 2781e5dd1f8SGreg Kroah-Hartman } 2791e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 2807fa60654SVaibhav Hiremath 2817fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 2827fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 2837fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 284