Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Vaibhav Hiremath | 2182 | 83.44% | 33 | 58.93% |
David Lin | 170 | 6.50% | 2 | 3.57% |
Greg Kroah-Hartman | 95 | 3.63% | 3 | 5.36% |
Bryan O'Donoghue | 93 | 3.56% | 3 | 5.36% |
Viresh Kumar | 26 | 0.99% | 4 | 7.14% |
Johan Hovold | 24 | 0.92% | 2 | 3.57% |
Arvind Yadav | 12 | 0.46% | 1 | 1.79% |
Mitchell Tasman | 3 | 0.11% | 1 | 1.79% |
Eva Rachel Retuya | 3 | 0.11% | 2 | 3.57% |
Wolfram Sang | 2 | 0.08% | 1 | 1.79% |
Arnd Bergmann | 2 | 0.08% | 1 | 1.79% |
Eames Trinh | 1 | 0.04% | 1 | 1.79% |
Sandeep Patil | 1 | 0.04% | 1 | 1.79% |
Elise Lennion | 1 | 0.04% | 1 | 1.79% |
Total | 2615 | 56 |
// SPDX-License-Identifier: GPL-2.0 /* * Arche Platform driver to enable Unipro link. * * Copyright 2014-2015 Google Inc. * Copyright 2014-2015 Linaro Ltd. */ #include <linux/clk.h> #include <linux/delay.h> #include <linux/gpio.h> #include <linux/init.h> #include <linux/module.h> #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/pinctrl/consumer.h> #include <linux/platform_device.h> #include <linux/pm.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/suspend.h> #include <linux/time.h> #include "arche_platform.h" #include "greybus.h" #if IS_ENABLED(CONFIG_USB_HSIC_USB3613) #include <linux/usb/usb3613.h> #else static inline int usb3613_hub_mode_ctrl(bool unused) { return 0; } #endif #define WD_COLDBOOT_PULSE_WIDTH_MS 30 enum svc_wakedetect_state { WD_STATE_IDLE, /* Default state = pulled high/low */ WD_STATE_BOOT_INIT, /* WD = falling edge (low) */ WD_STATE_COLDBOOT_TRIG, /* WD = rising edge (high), > 30msec */ WD_STATE_STANDBYBOOT_TRIG, /* As of now not used ?? */ WD_STATE_COLDBOOT_START, /* Cold boot process started */ WD_STATE_STANDBYBOOT_START, /* Not used */ }; struct arche_platform_drvdata { /* Control GPIO signals to and from AP <=> SVC */ int svc_reset_gpio; bool is_reset_act_hi; int svc_sysboot_gpio; int wake_detect_gpio; /* bi-dir,maps to WAKE_MOD & WAKE_FRAME signals */ enum arche_platform_state state; int svc_refclk_req; struct clk *svc_ref_clk; struct pinctrl *pinctrl; struct pinctrl_state *pin_default; int num_apbs; enum svc_wakedetect_state wake_detect_state; int wake_detect_irq; spinlock_t wake_lock; /* Protect wake_detect_state */ struct mutex platform_state_mutex; /* Protect state */ unsigned long wake_detect_start; struct notifier_block pm_notifier; struct device *dev; }; /* Requires calling context to hold arche_pdata->platform_state_mutex */ static void arche_platform_set_state(struct arche_platform_drvdata *arche_pdata, enum arche_platform_state state) { arche_pdata->state = state; } /* Requires arche_pdata->wake_lock is held by calling context */ static void arche_platform_set_wake_detect_state( struct arche_platform_drvdata *arche_pdata, enum svc_wakedetect_state state) { arche_pdata->wake_detect_state = state; } static inline void svc_reset_onoff(unsigned int gpio, bool onoff) { gpio_set_value(gpio, onoff); } static int apb_cold_boot(struct device *dev, void *data) { int ret; ret = apb_ctrl_coldboot(dev); if (ret) dev_warn(dev, "failed to coldboot\n"); /*Child nodes are independent, so do not exit coldboot operation */ return 0; } static int apb_poweroff(struct device *dev, void *data) { apb_ctrl_poweroff(dev); /* Enable HUB3613 into HUB mode. */ if (usb3613_hub_mode_ctrl(false)) dev_warn(dev, "failed to control hub device\n"); return 0; } static void arche_platform_wd_irq_en(struct arche_platform_drvdata *arche_pdata) { /* Enable interrupt here, to read event back from SVC */ gpio_direction_input(arche_pdata->wake_detect_gpio); enable_irq(arche_pdata->wake_detect_irq); } static irqreturn_t arche_platform_wd_irq_thread(int irq, void *devid) { struct arche_platform_drvdata *arche_pdata = devid; unsigned long flags; spin_lock_irqsave(&arche_pdata->wake_lock, flags); if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_TRIG) { /* Something is wrong */ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_COLDBOOT_START); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); /* It should complete power cycle, so first make sure it is poweroff */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); /* Bring APB out of reset: cold boot sequence */ device_for_each_child(arche_pdata->dev, NULL, apb_cold_boot); /* Enable HUB3613 into HUB mode. */ if (usb3613_hub_mode_ctrl(true)) dev_warn(arche_pdata->dev, "failed to control hub device\n"); spin_lock_irqsave(&arche_pdata->wake_lock, flags); arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } static irqreturn_t arche_platform_wd_irq(int irq, void *devid) { struct arche_platform_drvdata *arche_pdata = devid; unsigned long flags; spin_lock_irqsave(&arche_pdata->wake_lock, flags); if (gpio_get_value(arche_pdata->wake_detect_gpio)) { /* wake/detect rising */ /* * If wake/detect line goes high after low, within less than * 30msec, then standby boot sequence is initiated, which is not * supported/implemented as of now. So ignore it. */ if (arche_pdata->wake_detect_state == WD_STATE_BOOT_INIT) { if (time_before(jiffies, arche_pdata->wake_detect_start + msecs_to_jiffies(WD_COLDBOOT_PULSE_WIDTH_MS))) { arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); } else { /* * Check we are not in middle of irq thread * already */ if (arche_pdata->wake_detect_state != WD_STATE_COLDBOOT_START) { arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_COLDBOOT_TRIG); spin_unlock_irqrestore( &arche_pdata->wake_lock, flags); return IRQ_WAKE_THREAD; } } } } else { /* wake/detect falling */ if (arche_pdata->wake_detect_state == WD_STATE_IDLE) { arche_pdata->wake_detect_start = jiffies; /* * In the beginning, when wake/detect goes low * (first time), we assume it is meant for coldboot * and set the flag. If wake/detect line stays low * beyond 30msec, then it is coldboot else fallback * to standby boot. */ arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_BOOT_INIT); } } spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); return IRQ_HANDLED; } /* * Requires arche_pdata->platform_state_mutex to be held */ static int arche_platform_coldboot_seq(struct arche_platform_drvdata *arche_pdata) { int ret; if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) return 0; dev_info(arche_pdata->dev, "Booting from cold boot state\n"); svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); gpio_set_value(arche_pdata->svc_sysboot_gpio, 0); usleep_range(100, 200); ret = clk_prepare_enable(arche_pdata->svc_ref_clk); if (ret) { dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", ret); return ret; } /* bring SVC out of reset */ svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_ACTIVE); return 0; } /* * Requires arche_pdata->platform_state_mutex to be held */ static int arche_platform_fw_flashing_seq(struct arche_platform_drvdata *arche_pdata) { int ret; if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) return 0; dev_info(arche_pdata->dev, "Switching to FW flashing state\n"); svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); gpio_set_value(arche_pdata->svc_sysboot_gpio, 1); usleep_range(100, 200); ret = clk_prepare_enable(arche_pdata->svc_ref_clk); if (ret) { dev_err(arche_pdata->dev, "failed to enable svc_ref_clk: %d\n", ret); return ret; } svc_reset_onoff(arche_pdata->svc_reset_gpio, !arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_FW_FLASHING); return 0; } /* * Requires arche_pdata->platform_state_mutex to be held */ static void arche_platform_poweroff_seq(struct arche_platform_drvdata *arche_pdata) { unsigned long flags; if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) return; /* If in fw_flashing mode, then no need to repeate things again */ if (arche_pdata->state != ARCHE_PLATFORM_STATE_FW_FLASHING) { disable_irq(arche_pdata->wake_detect_irq); spin_lock_irqsave(&arche_pdata->wake_lock, flags); arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); } clk_disable_unprepare(arche_pdata->svc_ref_clk); /* As part of exit, put APB back in reset state */ svc_reset_onoff(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); } static ssize_t state_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); int ret = 0; mutex_lock(&arche_pdata->platform_state_mutex); if (sysfs_streq(buf, "off")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_OFF) goto exit; /* If SVC goes down, bring down APB's as well */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); arche_platform_poweroff_seq(arche_pdata); } else if (sysfs_streq(buf, "active")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_ACTIVE) goto exit; /* First we want to make sure we power off everything * and then activate back again */ device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); arche_platform_poweroff_seq(arche_pdata); arche_platform_wd_irq_en(arche_pdata); ret = arche_platform_coldboot_seq(arche_pdata); if (ret) goto exit; } else if (sysfs_streq(buf, "standby")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_STANDBY) goto exit; dev_warn(arche_pdata->dev, "standby state not supported\n"); } else if (sysfs_streq(buf, "fw_flashing")) { if (arche_pdata->state == ARCHE_PLATFORM_STATE_FW_FLASHING) goto exit; /* * Here we only control SVC. * * In case of FW_FLASHING mode we do not want to control * APBs, as in case of V2, SPI bus is shared between both * the APBs. So let user chose which APB he wants to flash. */ arche_platform_poweroff_seq(arche_pdata); ret = arche_platform_fw_flashing_seq(arche_pdata); if (ret) goto exit; } else { dev_err(arche_pdata->dev, "unknown state\n"); ret = -EINVAL; } exit: mutex_unlock(&arche_pdata->platform_state_mutex); return ret ? ret : count; } static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) { struct arche_platform_drvdata *arche_pdata = dev_get_drvdata(dev); switch (arche_pdata->state) { case ARCHE_PLATFORM_STATE_OFF: return sprintf(buf, "off\n"); case ARCHE_PLATFORM_STATE_ACTIVE: return sprintf(buf, "active\n"); case ARCHE_PLATFORM_STATE_STANDBY: return sprintf(buf, "standby\n"); case ARCHE_PLATFORM_STATE_FW_FLASHING: return sprintf(buf, "fw_flashing\n"); default: return sprintf(buf, "unknown state\n"); } } static DEVICE_ATTR_RW(state); static int arche_platform_pm_notifier(struct notifier_block *notifier, unsigned long pm_event, void *unused) { struct arche_platform_drvdata *arche_pdata = container_of(notifier, struct arche_platform_drvdata, pm_notifier); int ret = NOTIFY_DONE; mutex_lock(&arche_pdata->platform_state_mutex); switch (pm_event) { case PM_SUSPEND_PREPARE: if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { ret = NOTIFY_STOP; break; } device_for_each_child(arche_pdata->dev, NULL, apb_poweroff); arche_platform_poweroff_seq(arche_pdata); break; case PM_POST_SUSPEND: if (arche_pdata->state != ARCHE_PLATFORM_STATE_OFF) break; arche_platform_wd_irq_en(arche_pdata); arche_platform_coldboot_seq(arche_pdata); break; default: break; } mutex_unlock(&arche_pdata->platform_state_mutex); return ret; } static int arche_platform_probe(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int ret; arche_pdata = devm_kzalloc(&pdev->dev, sizeof(*arche_pdata), GFP_KERNEL); if (!arche_pdata) return -ENOMEM; /* setup svc reset gpio */ arche_pdata->is_reset_act_hi = of_property_read_bool(np, "svc,reset-active-high"); arche_pdata->svc_reset_gpio = of_get_named_gpio(np, "svc,reset-gpio", 0); if (!gpio_is_valid(arche_pdata->svc_reset_gpio)) { dev_err(dev, "failed to get reset-gpio\n"); return arche_pdata->svc_reset_gpio; } ret = devm_gpio_request(dev, arche_pdata->svc_reset_gpio, "svc-reset"); if (ret) { dev_err(dev, "failed to request svc-reset gpio:%d\n", ret); return ret; } ret = gpio_direction_output(arche_pdata->svc_reset_gpio, arche_pdata->is_reset_act_hi); if (ret) { dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); return ret; } arche_platform_set_state(arche_pdata, ARCHE_PLATFORM_STATE_OFF); arche_pdata->svc_sysboot_gpio = of_get_named_gpio(np, "svc,sysboot-gpio", 0); if (!gpio_is_valid(arche_pdata->svc_sysboot_gpio)) { dev_err(dev, "failed to get sysboot gpio\n"); return arche_pdata->svc_sysboot_gpio; } ret = devm_gpio_request(dev, arche_pdata->svc_sysboot_gpio, "sysboot0"); if (ret) { dev_err(dev, "failed to request sysboot0 gpio:%d\n", ret); return ret; } ret = gpio_direction_output(arche_pdata->svc_sysboot_gpio, 0); if (ret) { dev_err(dev, "failed to set svc-reset gpio dir:%d\n", ret); return ret; } /* setup the clock request gpio first */ arche_pdata->svc_refclk_req = of_get_named_gpio(np, "svc,refclk-req-gpio", 0); if (!gpio_is_valid(arche_pdata->svc_refclk_req)) { dev_err(dev, "failed to get svc clock-req gpio\n"); return arche_pdata->svc_refclk_req; } ret = devm_gpio_request(dev, arche_pdata->svc_refclk_req, "svc-clk-req"); if (ret) { dev_err(dev, "failed to request svc-clk-req gpio: %d\n", ret); return ret; } ret = gpio_direction_input(arche_pdata->svc_refclk_req); if (ret) { dev_err(dev, "failed to set svc-clk-req gpio dir :%d\n", ret); return ret; } /* setup refclk2 to follow the pin */ arche_pdata->svc_ref_clk = devm_clk_get(dev, "svc_ref_clk"); if (IS_ERR(arche_pdata->svc_ref_clk)) { ret = PTR_ERR(arche_pdata->svc_ref_clk); dev_err(dev, "failed to get svc_ref_clk: %d\n", ret); return ret; } platform_set_drvdata(pdev, arche_pdata); arche_pdata->num_apbs = of_get_child_count(np); dev_dbg(dev, "Number of APB's available - %d\n", arche_pdata->num_apbs); arche_pdata->wake_detect_gpio = of_get_named_gpio(np, "svc,wake-detect-gpio", 0); if (arche_pdata->wake_detect_gpio < 0) { dev_err(dev, "failed to get wake detect gpio\n"); return arche_pdata->wake_detect_gpio; } ret = devm_gpio_request(dev, arche_pdata->wake_detect_gpio, "wake detect"); if (ret) { dev_err(dev, "Failed requesting wake_detect gpio %d\n", arche_pdata->wake_detect_gpio); return ret; } arche_platform_set_wake_detect_state(arche_pdata, WD_STATE_IDLE); arche_pdata->dev = &pdev->dev; spin_lock_init(&arche_pdata->wake_lock); mutex_init(&arche_pdata->platform_state_mutex); arche_pdata->wake_detect_irq = gpio_to_irq(arche_pdata->wake_detect_gpio); ret = devm_request_threaded_irq(dev, arche_pdata->wake_detect_irq, arche_platform_wd_irq, arche_platform_wd_irq_thread, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING | IRQF_ONESHOT, dev_name(dev), arche_pdata); if (ret) { dev_err(dev, "failed to request wake detect IRQ %d\n", ret); return ret; } disable_irq(arche_pdata->wake_detect_irq); ret = device_create_file(dev, &dev_attr_state); if (ret) { dev_err(dev, "failed to create state file in sysfs\n"); return ret; } ret = of_platform_populate(np, NULL, NULL, dev); if (ret) { dev_err(dev, "failed to populate child nodes %d\n", ret); goto err_device_remove; } arche_pdata->pm_notifier.notifier_call = arche_platform_pm_notifier; ret = register_pm_notifier(&arche_pdata->pm_notifier); if (ret) { dev_err(dev, "failed to register pm notifier %d\n", ret); goto err_device_remove; } /* Explicitly power off if requested */ if (!of_property_read_bool(pdev->dev.of_node, "arche,init-off")) { mutex_lock(&arche_pdata->platform_state_mutex); ret = arche_platform_coldboot_seq(arche_pdata); if (ret) { dev_err(dev, "Failed to cold boot svc %d\n", ret); goto err_coldboot; } arche_platform_wd_irq_en(arche_pdata); mutex_unlock(&arche_pdata->platform_state_mutex); } dev_info(dev, "Device registered successfully\n"); return 0; err_coldboot: mutex_unlock(&arche_pdata->platform_state_mutex); err_device_remove: device_remove_file(&pdev->dev, &dev_attr_state); return ret; } static int arche_remove_child(struct device *dev, void *unused) { struct platform_device *pdev = to_platform_device(dev); platform_device_unregister(pdev); return 0; } static int arche_platform_remove(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); unregister_pm_notifier(&arche_pdata->pm_notifier); device_remove_file(&pdev->dev, &dev_attr_state); device_for_each_child(&pdev->dev, NULL, arche_remove_child); arche_platform_poweroff_seq(arche_pdata); if (usb3613_hub_mode_ctrl(false)) dev_warn(arche_pdata->dev, "failed to control hub device\n"); /* TODO: Should we do anything more here ?? */ return 0; } static __maybe_unused int arche_platform_suspend(struct device *dev) { /* * If timing profile premits, we may shutdown bridge * completely * * TODO: sequence ?? * * Also, need to make sure we meet precondition for unipro suspend * Precondition: Definition ??? */ return 0; } static __maybe_unused int arche_platform_resume(struct device *dev) { /* * Atleast for ES2 we have to meet the delay requirement between * unipro switch and AP bridge init, depending on whether bridge is in * OFF state or standby state. * * Based on whether bridge is in standby or OFF state we may have to * assert multiple signals. Please refer to WDM spec, for more info. * */ return 0; } static void arche_platform_shutdown(struct platform_device *pdev) { struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); arche_platform_poweroff_seq(arche_pdata); usb3613_hub_mode_ctrl(false); } static SIMPLE_DEV_PM_OPS(arche_platform_pm_ops, arche_platform_suspend, arche_platform_resume); static const struct of_device_id arche_platform_of_match[] = { /* Use PID/VID of SVC device */ { .compatible = "google,arche-platform", }, { }, }; static const struct of_device_id arche_combined_id[] = { /* Use PID/VID of SVC device */ { .compatible = "google,arche-platform", }, { .compatible = "usbffff,2", }, { }, }; MODULE_DEVICE_TABLE(of, arche_combined_id); static struct platform_driver arche_platform_device_driver = { .probe = arche_platform_probe, .remove = arche_platform_remove, .shutdown = arche_platform_shutdown, .driver = { .name = "arche-platform-ctrl", .pm = &arche_platform_pm_ops, .of_match_table = arche_platform_of_match, } }; static int __init arche_init(void) { int retval; retval = platform_driver_register(&arche_platform_device_driver); if (retval) return retval; retval = arche_apb_init(); if (retval) platform_driver_unregister(&arche_platform_device_driver); return retval; } module_init(arche_init); static void __exit arche_exit(void) { arche_apb_exit(); platform_driver_unregister(&arche_platform_device_driver); } module_exit(arche_exit); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Vaibhav Hiremath <vaibhav.hiremath@linaro.org>"); MODULE_DESCRIPTION("Arche Platform Driver");
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1