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 487fa60654SVaibhav Hiremath static void arche_platform_cleanup(struct arche_platform_drvdata *arche_pdata) 497fa60654SVaibhav Hiremath { 507fa60654SVaibhav Hiremath /* As part of exit, put APB back in reset state */ 517fa60654SVaibhav Hiremath if (gpio_is_valid(arche_pdata->svc_reset_gpio)) 527fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 537fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 547fa60654SVaibhav Hiremath } 557fa60654SVaibhav Hiremath 567fa60654SVaibhav Hiremath static int arche_platform_probe(struct platform_device *pdev) 577fa60654SVaibhav Hiremath { 587fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata; 597fa60654SVaibhav Hiremath struct device *dev = &pdev->dev; 607fa60654SVaibhav Hiremath struct device_node *np = dev->of_node; 617fa60654SVaibhav Hiremath int ret; 627fa60654SVaibhav Hiremath 637fa60654SVaibhav Hiremath arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); 647fa60654SVaibhav Hiremath if (!arche_pdata) 657fa60654SVaibhav Hiremath return -ENOMEM; 667fa60654SVaibhav Hiremath 677fa60654SVaibhav Hiremath /* setup svc reset gpio */ 687fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi = of_property_read_bool(np, 697fa60654SVaibhav Hiremath "svc,reset-active-high"); 707fa60654SVaibhav Hiremath arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); 717fa60654SVaibhav Hiremath if (arche_pdata->svc_reset_gpio < 0) { 727fa60654SVaibhav Hiremath dev_err(dev, "failed to get reset-gpio\n"); 737fa60654SVaibhav Hiremath return -ENODEV; 747fa60654SVaibhav Hiremath } 757fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); 767fa60654SVaibhav Hiremath if (ret) { 777fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); 787fa60654SVaibhav Hiremath return ret; 797fa60654SVaibhav Hiremath } 807fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_reset_gpio, 817fa60654SVaibhav Hiremath arche_pdata->is_reset_act_hi); 827fa60654SVaibhav Hiremath if (ret) { 837fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 847fa60654SVaibhav Hiremath return ret; 857fa60654SVaibhav Hiremath } 867fa60654SVaibhav Hiremath 877fa60654SVaibhav Hiremath arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, 887fa60654SVaibhav Hiremath "svc,sysboot-gpio", 0); 897fa60654SVaibhav Hiremath if (arche_pdata->svc_sysboot_gpio < 0) { 907fa60654SVaibhav Hiremath dev_err(dev, "failed to get sysboot gpio\n"); 917fa60654SVaibhav Hiremath return -ENODEV; 927fa60654SVaibhav Hiremath } 937fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); 947fa60654SVaibhav Hiremath if (ret) { 957fa60654SVaibhav Hiremath dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); 967fa60654SVaibhav Hiremath return ret; 977fa60654SVaibhav Hiremath } 987fa60654SVaibhav Hiremath ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); 997fa60654SVaibhav Hiremath if (ret) { 1007fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); 1017fa60654SVaibhav Hiremath return ret; 1027fa60654SVaibhav Hiremath } 1037fa60654SVaibhav Hiremath 1047fa60654SVaibhav Hiremath /* setup the clock request gpio first */ 1057fa60654SVaibhav Hiremath arche_pdata->svc_refclk_req = of_get_named_gpio(np, 1067fa60654SVaibhav Hiremath "svc,refclk-req-gpio", 0); 1077fa60654SVaibhav Hiremath if (arche_pdata->svc_refclk_req < 0) { 1087fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc clock-req gpio\n"); 1097fa60654SVaibhav Hiremath return -ENODEV; 1107fa60654SVaibhav Hiremath } 1117fa60654SVaibhav Hiremath ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); 1127fa60654SVaibhav Hiremath if (ret) { 1137fa60654SVaibhav Hiremath dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); 1147fa60654SVaibhav Hiremath return ret; 1157fa60654SVaibhav Hiremath } 1167fa60654SVaibhav Hiremath ret = gpio_direction_input(arche_pdata->svc_refclk_req); 1177fa60654SVaibhav Hiremath if (ret) { 1187fa60654SVaibhav Hiremath dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); 1197fa60654SVaibhav Hiremath return ret; 1207fa60654SVaibhav Hiremath } 1217fa60654SVaibhav Hiremath 1227fa60654SVaibhav Hiremath /* setup refclk2 to follow the pin */ 1237fa60654SVaibhav Hiremath arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); 1247fa60654SVaibhav Hiremath if (IS_ERR(arche_pdata->svc_ref_clk)) { 1257fa60654SVaibhav Hiremath ret = PTR_ERR(arche_pdata->svc_ref_clk); 1267fa60654SVaibhav Hiremath dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); 1277fa60654SVaibhav Hiremath return ret; 1287fa60654SVaibhav Hiremath } 1297fa60654SVaibhav Hiremath ret = clk_prepare_enable(arche_pdata->svc_ref_clk); 1307fa60654SVaibhav Hiremath if (ret) { 1317fa60654SVaibhav Hiremath dev_err(dev, "failed to enable svc_ref_clk: %d\n", ret); 1327fa60654SVaibhav Hiremath return ret; 1337fa60654SVaibhav Hiremath } 1347fa60654SVaibhav Hiremath 1357fa60654SVaibhav Hiremath platform_set_drvdata(pdev, arche_pdata); 1367fa60654SVaibhav Hiremath 1377fa60654SVaibhav Hiremath /* bring SVC out of reset */ 1387fa60654SVaibhav Hiremath svc_reset_onoff(arche_pdata->svc_reset_gpio, 1397fa60654SVaibhav Hiremath !arche_pdata->is_reset_act_hi); 1407fa60654SVaibhav Hiremath 1417fa60654SVaibhav Hiremath arche_pdata->num_apbs = of_get_child_count(np); 1427fa60654SVaibhav Hiremath dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); 1437fa60654SVaibhav Hiremath 1447fa60654SVaibhav Hiremath /* probe all childs here */ 1457fa60654SVaibhav Hiremath ret = of_platform_populate(np, NULL, NULL, dev); 1467fa60654SVaibhav Hiremath if (ret) 1477fa60654SVaibhav Hiremath dev_err(dev, "no child node found\n"); 1487fa60654SVaibhav Hiremath 1497fa60654SVaibhav Hiremath dev_info(dev, "Device registered successfully\n"); 1507fa60654SVaibhav Hiremath return ret; 1517fa60654SVaibhav Hiremath } 1527fa60654SVaibhav Hiremath 153bc142bbbSVaibhav Hiremath static int arche_remove_child(struct device *dev, void *unused) 154bc142bbbSVaibhav Hiremath { 155bc142bbbSVaibhav Hiremath struct platform_device *pdev = to_platform_device(dev); 156bc142bbbSVaibhav Hiremath 157bc142bbbSVaibhav Hiremath platform_device_unregister(pdev); 158bc142bbbSVaibhav Hiremath 159bc142bbbSVaibhav Hiremath return 0; 160bc142bbbSVaibhav Hiremath } 161bc142bbbSVaibhav Hiremath 1627fa60654SVaibhav Hiremath static int arche_platform_remove(struct platform_device *pdev) 1637fa60654SVaibhav Hiremath { 1647fa60654SVaibhav Hiremath struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); 1657fa60654SVaibhav Hiremath 166bc142bbbSVaibhav Hiremath device_for_each_child(&pdev->dev, NULL, arche_remove_child); 167bc142bbbSVaibhav Hiremath 1687fa60654SVaibhav Hiremath if (arche_pdata) 1697fa60654SVaibhav Hiremath arche_platform_cleanup(arche_pdata); 1707fa60654SVaibhav Hiremath 1717fa60654SVaibhav Hiremath platform_set_drvdata(pdev, NULL); 1727fa60654SVaibhav Hiremath 1737fa60654SVaibhav Hiremath return 0; 1747fa60654SVaibhav Hiremath } 1757fa60654SVaibhav Hiremath 1767fa60654SVaibhav Hiremath static int arche_platform_suspend(struct device *dev) 1777fa60654SVaibhav Hiremath { 1787fa60654SVaibhav Hiremath /* 1797fa60654SVaibhav Hiremath * If timing profile premits, we may shutdown bridge 1807fa60654SVaibhav Hiremath * completely 1817fa60654SVaibhav Hiremath * 1827fa60654SVaibhav Hiremath * TODO: sequence ?? 1837fa60654SVaibhav Hiremath * 1847fa60654SVaibhav Hiremath * Also, need to make sure we meet precondition for unipro suspend 1857fa60654SVaibhav Hiremath * Precondition: Definition ??? 1867fa60654SVaibhav Hiremath */ 1877fa60654SVaibhav Hiremath return 0; 1887fa60654SVaibhav Hiremath } 1897fa60654SVaibhav Hiremath 1907fa60654SVaibhav Hiremath static int arche_platform_resume(struct device *dev) 1917fa60654SVaibhav Hiremath { 1927fa60654SVaibhav Hiremath /* 1937fa60654SVaibhav Hiremath * Atleast for ES2 we have to meet the delay requirement between 1947fa60654SVaibhav Hiremath * unipro switch and AP bridge init, depending on whether bridge is in 1957fa60654SVaibhav Hiremath * OFF state or standby state. 1967fa60654SVaibhav Hiremath * 1977fa60654SVaibhav Hiremath * Based on whether bridge is in standby or OFF state we may have to 1987fa60654SVaibhav Hiremath * assert multiple signals. Please refer to WDM spec, for more info. 1997fa60654SVaibhav Hiremath * 2007fa60654SVaibhav Hiremath */ 2017fa60654SVaibhav Hiremath return 0; 2027fa60654SVaibhav Hiremath } 2037fa60654SVaibhav Hiremath 2047fa60654SVaibhav Hiremath static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, 2057fa60654SVaibhav Hiremath arche_platform_suspend, 2067fa60654SVaibhav Hiremath arche_platform_resume); 2077fa60654SVaibhav Hiremath 2087fa60654SVaibhav Hiremath static struct of_device_id arche_platform_of_match[] = { 2097fa60654SVaibhav Hiremath { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2107fa60654SVaibhav Hiremath { }, 2117fa60654SVaibhav Hiremath }; 2121e5dd1f8SGreg Kroah-Hartman 2131e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_apb_ctrl_of_match[] = { 2141e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2151e5dd1f8SGreg Kroah-Hartman { }, 2161e5dd1f8SGreg Kroah-Hartman }; 2171e5dd1f8SGreg Kroah-Hartman 2181e5dd1f8SGreg Kroah-Hartman static struct of_device_id arche_combined_id[] = { 2191e5dd1f8SGreg Kroah-Hartman { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ 2201e5dd1f8SGreg Kroah-Hartman { .compatible = "usbffff,2", }, 2211e5dd1f8SGreg Kroah-Hartman { }, 2221e5dd1f8SGreg Kroah-Hartman }; 2231e5dd1f8SGreg Kroah-Hartman MODULE_DEVICE_TABLE(of, arche_combined_id); 2247fa60654SVaibhav Hiremath 2257fa60654SVaibhav Hiremath static struct platform_driver arche_platform_device_driver = { 2267fa60654SVaibhav Hiremath .probe = arche_platform_probe, 2277fa60654SVaibhav Hiremath .remove = arche_platform_remove, 2287fa60654SVaibhav Hiremath .driver = { 2297fa60654SVaibhav Hiremath .name = "arche-platform-ctrl", 2307fa60654SVaibhav Hiremath .pm = &arche_platform_pm_ops, 2311e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_platform_of_match, 2327fa60654SVaibhav Hiremath } 2337fa60654SVaibhav Hiremath }; 2347fa60654SVaibhav Hiremath 2351e5dd1f8SGreg Kroah-Hartman static struct platform_driver arche_apb_ctrl_device_driver = { 2361e5dd1f8SGreg Kroah-Hartman .probe = arche_apb_ctrl_probe, 2371e5dd1f8SGreg Kroah-Hartman .remove = arche_apb_ctrl_remove, 2381e5dd1f8SGreg Kroah-Hartman .driver = { 2391e5dd1f8SGreg Kroah-Hartman .name = "arche-apb-ctrl", 2401e5dd1f8SGreg Kroah-Hartman .pm = &arche_apb_ctrl_pm_ops, 2411e5dd1f8SGreg Kroah-Hartman .of_match_table = arche_apb_ctrl_of_match, 2421e5dd1f8SGreg Kroah-Hartman } 2431e5dd1f8SGreg Kroah-Hartman }; 2441e5dd1f8SGreg Kroah-Hartman 2451e5dd1f8SGreg Kroah-Hartman static int __init arche_init(void) 2461e5dd1f8SGreg Kroah-Hartman { 2471e5dd1f8SGreg Kroah-Hartman int retval; 2481e5dd1f8SGreg Kroah-Hartman 2491e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_platform_device_driver); 2501e5dd1f8SGreg Kroah-Hartman if (retval) 2511e5dd1f8SGreg Kroah-Hartman return retval; 2521e5dd1f8SGreg Kroah-Hartman 2531e5dd1f8SGreg Kroah-Hartman retval = platform_driver_register(&arche_apb_ctrl_device_driver); 2541e5dd1f8SGreg Kroah-Hartman if (retval) 2551e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 2561e5dd1f8SGreg Kroah-Hartman 2571e5dd1f8SGreg Kroah-Hartman return retval; 2581e5dd1f8SGreg Kroah-Hartman } 2591e5dd1f8SGreg Kroah-Hartman module_init(arche_init); 2601e5dd1f8SGreg Kroah-Hartman 2611e5dd1f8SGreg Kroah-Hartman static void __exit arche_exit(void) 2621e5dd1f8SGreg Kroah-Hartman { 2631e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_apb_ctrl_device_driver); 2641e5dd1f8SGreg Kroah-Hartman platform_driver_unregister(&arche_platform_device_driver); 2651e5dd1f8SGreg Kroah-Hartman } 2661e5dd1f8SGreg Kroah-Hartman module_exit(arche_exit); 2677fa60654SVaibhav Hiremath 2687fa60654SVaibhav Hiremath MODULE_LICENSE("GPL"); 2697fa60654SVaibhav Hiremath MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); 2707fa60654SVaibhav Hiremath MODULE_DESCRIPTION("Arche Platform Driver"); 271