diff options
author | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-15 13:54:11 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-09-15 13:54:11 +0200 |
commit | c1aa01c96e590714d99c5f17cfae1b14dec8bdee (patch) | |
tree | 5811422f65efdff3fd89fdaf9a8c6d7d1cc4d06b | |
parent | 4758ee8bc89a86c2110b9e85878538ced8045ef5 (diff) | |
download | patches-c1aa01c96e590714d99c5f17cfae1b14dec8bdee.tar.gz |
greybus patches
33 files changed, 38163 insertions, 57 deletions
diff --git a/greybus_arche.patch b/greybus_arche.patch new file mode 100644 index 00000000000000..e4222119051d8a --- /dev/null +++ b/greybus_arche.patch @@ -0,0 +1,1404 @@ +--- + drivers/greybus/arche-apb-ctrl.c | 522 ++++++++++++++++++++++++ + drivers/greybus/arche-platform.c | 828 +++++++++++++++++++++++++++++++++++++++ + drivers/greybus/arche_platform.h | 39 + + 3 files changed, 1389 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/arche-apb-ctrl.c +@@ -0,0 +1,522 @@ ++/* ++ * Arche Platform driver to control APB. ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/clk.h> ++#include <linux/delay.h> ++#include <linux/gpio.h> ++#include <linux/interrupt.h> ++#include <linux/of_gpio.h> ++#include <linux/of_irq.h> ++#include <linux/module.h> ++#include <linux/pinctrl/consumer.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/regulator/consumer.h> ++#include <linux/spinlock.h> ++#include "arche_platform.h" ++ ++ ++struct arche_apb_ctrl_drvdata { ++ /* Control GPIO signals to and from AP <=> AP Bridges */ ++ int resetn_gpio; ++ int boot_ret_gpio; ++ int pwroff_gpio; ++ int wake_in_gpio; ++ int wake_out_gpio; ++ int pwrdn_gpio; ++ ++ enum arche_platform_state state; ++ bool init_disabled; ++ ++ struct regulator *vcore; ++ struct regulator *vio; ++ ++ int clk_en_gpio; ++ struct clk *clk; ++ ++ struct pinctrl *pinctrl; ++ struct pinctrl_state *pin_default; ++ ++ /* V2: SPI Bus control */ ++ int spi_en_gpio; ++ bool spi_en_polarity_high; ++}; ++ ++/* ++ * Note that these low level api's are active high ++ */ ++static inline void deassert_reset(unsigned int gpio) ++{ ++ gpio_set_value(gpio, 1); ++} ++ ++static inline void assert_reset(unsigned int gpio) ++{ ++ gpio_set_value(gpio, 0); ++} ++ ++/* ++ * Note: Please do not modify the below sequence, as it is as per the spec ++ */ ++static int coldboot_seq(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); ++ int ret; ++ ++ if (apb->init_disabled || ++ apb->state == ARCHE_PLATFORM_STATE_ACTIVE) ++ return 0; ++ ++ /* Hold APB in reset state */ ++ assert_reset(apb->resetn_gpio); ++ ++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && ++ gpio_is_valid(apb->spi_en_gpio)) ++ devm_gpio_free(dev, apb->spi_en_gpio); ++ ++ /* Enable power to APB */ ++ if (!IS_ERR(apb->vcore)) { ++ ret = regulator_enable(apb->vcore); ++ if (ret) { ++ dev_err(dev, "failed to enable core regulator\n"); ++ return ret; ++ } ++ } ++ ++ if (!IS_ERR(apb->vio)) { ++ ret = regulator_enable(apb->vio); ++ if (ret) { ++ dev_err(dev, "failed to enable IO regulator\n"); ++ return ret; ++ } ++ } ++ ++ apb_bootret_deassert(dev); ++ ++ /* On DB3 clock was not mandatory */ ++ if (gpio_is_valid(apb->clk_en_gpio)) ++ gpio_set_value(apb->clk_en_gpio, 1); ++ ++ usleep_range(100, 200); ++ ++ /* deassert reset to APB : Active-low signal */ ++ deassert_reset(apb->resetn_gpio); ++ ++ apb->state = ARCHE_PLATFORM_STATE_ACTIVE; ++ ++ return 0; ++} ++ ++static int fw_flashing_seq(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); ++ int ret; ++ ++ if (apb->init_disabled || ++ apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) ++ return 0; ++ ++ ret = regulator_enable(apb->vcore); ++ if (ret) { ++ dev_err(dev, "failed to enable core regulator\n"); ++ return ret; ++ } ++ ++ ret = regulator_enable(apb->vio); ++ if (ret) { ++ dev_err(dev, "failed to enable IO regulator\n"); ++ return ret; ++ } ++ ++ if (gpio_is_valid(apb->spi_en_gpio)) { ++ unsigned long flags; ++ ++ if (apb->spi_en_polarity_high) ++ flags = GPIOF_OUT_INIT_HIGH; ++ else ++ flags = GPIOF_OUT_INIT_LOW; ++ ++ ret = devm_gpio_request_one(dev, apb->spi_en_gpio, ++ flags, "apb_spi_en"); ++ if (ret) { ++ dev_err(dev, "Failed requesting SPI bus en gpio %d\n", ++ apb->spi_en_gpio); ++ return ret; ++ } ++ } ++ ++ /* for flashing device should be in reset state */ ++ assert_reset(apb->resetn_gpio); ++ apb->state = ARCHE_PLATFORM_STATE_FW_FLASHING; ++ ++ return 0; ++} ++ ++static int standby_boot_seq(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); ++ ++ if (apb->init_disabled) ++ return 0; ++ ++ /* Even if it is in OFF state, then we do not want to change the state */ ++ if (apb->state == ARCHE_PLATFORM_STATE_STANDBY || ++ apb->state == ARCHE_PLATFORM_STATE_OFF) ++ return 0; ++ ++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && ++ gpio_is_valid(apb->spi_en_gpio)) ++ devm_gpio_free(dev, apb->spi_en_gpio); ++ ++ /* ++ * As per WDM spec, do nothing ++ * ++ * Pasted from WDM spec, ++ * - A falling edge on POWEROFF_L is detected (a) ++ * - WDM enters standby mode, but no output signals are changed ++ * */ ++ ++ /* TODO: POWEROFF_L is input to WDM module */ ++ apb->state = ARCHE_PLATFORM_STATE_STANDBY; ++ return 0; ++} ++ ++static void poweroff_seq(struct platform_device *pdev) ++{ ++ struct device *dev = &pdev->dev; ++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); ++ ++ if (apb->init_disabled || apb->state == ARCHE_PLATFORM_STATE_OFF) ++ return; ++ ++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING && ++ gpio_is_valid(apb->spi_en_gpio)) ++ devm_gpio_free(dev, apb->spi_en_gpio); ++ ++ /* disable the clock */ ++ if (gpio_is_valid(apb->clk_en_gpio)) ++ gpio_set_value(apb->clk_en_gpio, 0); ++ ++ if (!IS_ERR(apb->vcore) && regulator_is_enabled(apb->vcore) > 0) ++ regulator_disable(apb->vcore); ++ ++ if (!IS_ERR(apb->vio) && regulator_is_enabled(apb->vio) > 0) ++ regulator_disable(apb->vio); ++ ++ /* As part of exit, put APB back in reset state */ ++ assert_reset(apb->resetn_gpio); ++ apb->state = ARCHE_PLATFORM_STATE_OFF; ++ ++ /* TODO: May have to send an event to SVC about this exit */ ++} ++ ++void apb_bootret_assert(struct device *dev) ++{ ++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); ++ ++ gpio_set_value(apb->boot_ret_gpio, 1); ++} ++ ++void apb_bootret_deassert(struct device *dev) ++{ ++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); ++ ++ gpio_set_value(apb->boot_ret_gpio, 0); ++} ++ ++int apb_ctrl_coldboot(struct device *dev) ++{ ++ return coldboot_seq(to_platform_device(dev)); ++} ++ ++int apb_ctrl_fw_flashing(struct device *dev) ++{ ++ return fw_flashing_seq(to_platform_device(dev)); ++} ++ ++int apb_ctrl_standby_boot(struct device *dev) ++{ ++ return standby_boot_seq(to_platform_device(dev)); ++} ++ ++void apb_ctrl_poweroff(struct device *dev) ++{ ++ poweroff_seq(to_platform_device(dev)); ++} ++ ++static ssize_t state_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, size_t count) ++{ ++ struct platform_device *pdev = to_platform_device(dev); ++ struct arche_apb_ctrl_drvdata *apb = platform_get_drvdata(pdev); ++ int ret = 0; ++ bool is_disabled; ++ ++ if (sysfs_streq(buf, "off")) { ++ if (apb->state == ARCHE_PLATFORM_STATE_OFF) ++ return count; ++ ++ poweroff_seq(pdev); ++ } else if (sysfs_streq(buf, "active")) { ++ if (apb->state == ARCHE_PLATFORM_STATE_ACTIVE) ++ return count; ++ ++ poweroff_seq(pdev); ++ is_disabled = apb->init_disabled; ++ apb->init_disabled = false; ++ ret = coldboot_seq(pdev); ++ if (ret) ++ apb->init_disabled = is_disabled; ++ } else if (sysfs_streq(buf, "standby")) { ++ if (apb->state == ARCHE_PLATFORM_STATE_STANDBY) ++ return count; ++ ++ ret = standby_boot_seq(pdev); ++ } else if (sysfs_streq(buf, "fw_flashing")) { ++ if (apb->state == ARCHE_PLATFORM_STATE_FW_FLASHING) ++ return count; ++ ++ /* First we want to make sure we power off everything ++ * and then enter FW flashing state */ ++ poweroff_seq(pdev); ++ ret = fw_flashing_seq(pdev); ++ } else { ++ dev_err(dev, "unknown state\n"); ++ ret = -EINVAL; ++ } ++ ++ return ret ? ret : count; ++} ++ ++static ssize_t state_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct arche_apb_ctrl_drvdata *apb = dev_get_drvdata(dev); ++ ++ switch (apb->state) { ++ case ARCHE_PLATFORM_STATE_OFF: ++ return sprintf(buf, "off%s\n", ++ apb->init_disabled ? ",disabled" : ""); ++ 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 apb_ctrl_get_devtree_data(struct platform_device *pdev, ++ struct arche_apb_ctrl_drvdata *apb) ++{ ++ struct device *dev = &pdev->dev; ++ struct device_node *np = dev->of_node; ++ int ret; ++ ++ apb->resetn_gpio = of_get_named_gpio(np, "reset-gpios", 0); ++ if (apb->resetn_gpio < 0) { ++ dev_err(dev, "failed to get reset gpio\n"); ++ return apb->resetn_gpio; ++ } ++ ret = devm_gpio_request_one(dev, apb->resetn_gpio, ++ GPIOF_OUT_INIT_LOW, "apb-reset"); ++ if (ret) { ++ dev_err(dev, "Failed requesting reset gpio %d\n", ++ apb->resetn_gpio); ++ return ret; ++ } ++ ++ apb->boot_ret_gpio = of_get_named_gpio(np, "boot-ret-gpios", 0); ++ if (apb->boot_ret_gpio < 0) { ++ dev_err(dev, "failed to get boot retention gpio\n"); ++ return apb->boot_ret_gpio; ++ } ++ ret = devm_gpio_request_one(dev, apb->boot_ret_gpio, ++ GPIOF_OUT_INIT_LOW, "boot retention"); ++ if (ret) { ++ dev_err(dev, "Failed requesting bootret gpio %d\n", ++ apb->boot_ret_gpio); ++ return ret; ++ } ++ ++ /* It's not mandatory to support power management interface */ ++ apb->pwroff_gpio = of_get_named_gpio(np, "pwr-off-gpios", 0); ++ if (apb->pwroff_gpio < 0) { ++ dev_err(dev, "failed to get power off gpio\n"); ++ return apb->pwroff_gpio; ++ } ++ ret = devm_gpio_request_one(dev, apb->pwroff_gpio, ++ GPIOF_IN, "pwroff_n"); ++ if (ret) { ++ dev_err(dev, "Failed requesting pwroff_n gpio %d\n", ++ apb->pwroff_gpio); ++ return ret; ++ } ++ ++ /* Do not make clock mandatory as of now (for DB3) */ ++ apb->clk_en_gpio = of_get_named_gpio(np, "clock-en-gpio", 0); ++ if (apb->clk_en_gpio < 0) { ++ dev_warn(dev, "failed to get clock en gpio\n"); ++ } else if (gpio_is_valid(apb->clk_en_gpio)) { ++ ret = devm_gpio_request_one(dev, apb->clk_en_gpio, ++ GPIOF_OUT_INIT_LOW, "apb_clk_en"); ++ if (ret) { ++ dev_warn(dev, "Failed requesting APB clock en gpio %d\n", ++ apb->clk_en_gpio); ++ return ret; ++ } ++ } ++ ++ apb->pwrdn_gpio = of_get_named_gpio(np, "pwr-down-gpios", 0); ++ if (apb->pwrdn_gpio < 0) ++ dev_warn(dev, "failed to get power down gpio\n"); ++ ++ /* Regulators are optional, as we may have fixed supply coming in */ ++ apb->vcore = devm_regulator_get(dev, "vcore"); ++ if (IS_ERR(apb->vcore)) ++ dev_warn(dev, "no core regulator found\n"); ++ ++ apb->vio = devm_regulator_get(dev, "vio"); ++ if (IS_ERR(apb->vio)) ++ dev_warn(dev, "no IO regulator found\n"); ++ ++ apb->pinctrl = devm_pinctrl_get(&pdev->dev); ++ if (IS_ERR(apb->pinctrl)) { ++ dev_err(&pdev->dev, "could not get pinctrl handle\n"); ++ return PTR_ERR(apb->pinctrl); ++ } ++ apb->pin_default = pinctrl_lookup_state(apb->pinctrl, "default"); ++ if (IS_ERR(apb->pin_default)) { ++ dev_err(&pdev->dev, "could not get default pin state\n"); ++ return PTR_ERR(apb->pin_default); ++ } ++ ++ /* Only applicable for platform >= V2 */ ++ apb->spi_en_gpio = of_get_named_gpio(np, "spi-en-gpio", 0); ++ if (apb->spi_en_gpio >= 0) { ++ if (of_property_read_bool(pdev->dev.of_node, ++ "spi-en-active-high")) ++ apb->spi_en_polarity_high = true; ++ } ++ ++ return 0; ++} ++ ++static int arche_apb_ctrl_probe(struct platform_device *pdev) ++{ ++ int ret; ++ struct arche_apb_ctrl_drvdata *apb; ++ struct device *dev = &pdev->dev; ++ ++ apb = devm_kzalloc(&pdev->dev, sizeof(*apb), GFP_KERNEL); ++ if (!apb) ++ return -ENOMEM; ++ ++ ret = apb_ctrl_get_devtree_data(pdev, apb); ++ if (ret) { ++ dev_err(dev, "failed to get apb devicetree data %d\n", ret); ++ return ret; ++ } ++ ++ /* Initially set APB to OFF state */ ++ apb->state = ARCHE_PLATFORM_STATE_OFF; ++ /* Check whether device needs to be enabled on boot */ ++ if (of_property_read_bool(pdev->dev.of_node, "arche,init-disable")) ++ apb->init_disabled = true; ++ ++ platform_set_drvdata(pdev, apb); ++ ++ /* Create sysfs interface to allow user to change state dynamically */ ++ ret = device_create_file(dev, &dev_attr_state); ++ if (ret) { ++ dev_err(dev, "failed to create state file in sysfs\n"); ++ return ret; ++ } ++ ++ dev_info(&pdev->dev, "Device registered successfully\n"); ++ return 0; ++} ++ ++static int arche_apb_ctrl_remove(struct platform_device *pdev) ++{ ++ device_remove_file(&pdev->dev, &dev_attr_state); ++ poweroff_seq(pdev); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static int arche_apb_ctrl_suspend(struct device *dev) ++{ ++ /* ++ * If timing profile permits, we may shutdown bridge ++ * completely ++ * ++ * TODO: sequence ?? ++ * ++ * Also, need to make sure we meet precondition for unipro suspend ++ * Precondition: Definition ??? ++ */ ++ return 0; ++} ++ ++static int arche_apb_ctrl_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_apb_ctrl_shutdown(struct platform_device *pdev) ++{ ++ apb_ctrl_poweroff(&pdev->dev); ++} ++ ++static SIMPLE_DEV_PM_OPS(arche_apb_ctrl_pm_ops, arche_apb_ctrl_suspend, ++ arche_apb_ctrl_resume); ++ ++static struct of_device_id arche_apb_ctrl_of_match[] = { ++ { .compatible = "usbffff,2", }, ++ { }, ++}; ++ ++static struct platform_driver arche_apb_ctrl_device_driver = { ++ .probe = arche_apb_ctrl_probe, ++ .remove = arche_apb_ctrl_remove, ++ .shutdown = arche_apb_ctrl_shutdown, ++ .driver = { ++ .name = "arche-apb-ctrl", ++ .pm = &arche_apb_ctrl_pm_ops, ++ .of_match_table = arche_apb_ctrl_of_match, ++ } ++}; ++ ++int __init arche_apb_init(void) ++{ ++ return platform_driver_register(&arche_apb_ctrl_device_driver); ++} ++ ++void __exit arche_apb_exit(void) ++{ ++ platform_driver_unregister(&arche_apb_ctrl_device_driver); ++} +--- /dev/null ++++ b/drivers/greybus/arche-platform.c +@@ -0,0 +1,828 @@ ++/* ++ * Arche Platform driver to enable Unipro link. ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#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" ++ ++#include <linux/usb/usb3613.h> ++ ++#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 */ ++ WD_STATE_TIMESYNC, ++}; ++ ++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 */ ++ wait_queue_head_t wq; /* WQ for arche_pdata->state */ ++ unsigned long wake_detect_start; ++ struct notifier_block pm_notifier; ++ ++ struct device *dev; ++ struct gb_timesync_svc *timesync_svc_pdata; ++}; ++ ++static int arche_apb_bootret_assert(struct device *dev, void *data) ++{ ++ apb_bootret_assert(dev); ++ return 0; ++} ++ ++static int arche_apb_bootret_deassert(struct device *dev, void *data) ++{ ++ apb_bootret_deassert(dev); ++ return 0; ++} ++ ++/* 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; ++} ++ ++/* ++ * arche_platform_change_state: Change the operational state ++ * ++ * This exported function allows external drivers to change the state ++ * of the arche-platform driver. ++ * Note that this function only supports transitions between two states ++ * with limited functionality. ++ * ++ * - ARCHE_PLATFORM_STATE_TIME_SYNC: ++ * Once set, allows timesync operations between SVC <=> AP and makes ++ * sure that arche-platform driver ignores any subsequent events/pulses ++ * from SVC over wake/detect. ++ * ++ * - ARCHE_PLATFORM_STATE_ACTIVE: ++ * Puts back driver to active state, where any pulse from SVC on wake/detect ++ * line would trigger either cold/standby boot. ++ * Note: Transition request from this function does not trigger cold/standby ++ * boot. It just puts back driver book keeping variable back to ACTIVE ++ * state and restores the interrupt. ++ * ++ * Returns -ENODEV if device not found, -EAGAIN if the driver cannot currently ++ * satisfy the requested state-transition or -EINVAL for all other ++ * state-transition requests. ++ */ ++int arche_platform_change_state(enum arche_platform_state state, ++ struct gb_timesync_svc *timesync_svc_pdata) ++{ ++ struct arche_platform_drvdata *arche_pdata; ++ struct platform_device *pdev; ++ struct device_node *np; ++ int ret = -EAGAIN; ++ unsigned long flags; ++ ++ np = of_find_compatible_node(NULL, NULL, "google,arche-platform"); ++ if (!np) { ++ pr_err("google,arche-platform device node not found\n"); ++ return -ENODEV; ++ } ++ ++ pdev = of_find_device_by_node(np); ++ if (!pdev) { ++ pr_err("arche-platform device not found\n"); ++ return -ENODEV; ++ } ++ ++ arche_pdata = platform_get_drvdata(pdev); ++ ++ mutex_lock(&arche_pdata->platform_state_mutex); ++ spin_lock_irqsave(&arche_pdata->wake_lock, flags); ++ ++ if (arche_pdata->state == state) { ++ ret = 0; ++ goto exit; ++ } ++ ++ switch (state) { ++ case ARCHE_PLATFORM_STATE_TIME_SYNC: ++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_ACTIVE) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ if (arche_pdata->wake_detect_state != WD_STATE_IDLE) { ++ dev_err(arche_pdata->dev, ++ "driver busy with wake/detect line ops\n"); ++ goto exit; ++ } ++ device_for_each_child(arche_pdata->dev, NULL, ++ arche_apb_bootret_assert); ++ arche_pdata->wake_detect_state = WD_STATE_TIMESYNC; ++ break; ++ case ARCHE_PLATFORM_STATE_ACTIVE: ++ if (arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC) { ++ ret = -EINVAL; ++ goto exit; ++ } ++ device_for_each_child(arche_pdata->dev, NULL, ++ arche_apb_bootret_deassert); ++ arche_pdata->wake_detect_state = WD_STATE_IDLE; ++ break; ++ case ARCHE_PLATFORM_STATE_OFF: ++ case ARCHE_PLATFORM_STATE_STANDBY: ++ case ARCHE_PLATFORM_STATE_FW_FLASHING: ++ dev_err(arche_pdata->dev, "busy, request to retry later\n"); ++ goto exit; ++ default: ++ ret = -EINVAL; ++ dev_err(arche_pdata->dev, ++ "invalid state transition request\n"); ++ goto exit; ++ } ++ arche_pdata->timesync_svc_pdata = timesync_svc_pdata; ++ arche_platform_set_state(arche_pdata, state); ++ if (state == ARCHE_PLATFORM_STATE_ACTIVE) ++ wake_up(&arche_pdata->wq); ++ ++ ret = 0; ++exit: ++ spin_unlock_irqrestore(&arche_pdata->wake_lock, flags); ++ mutex_unlock(&arche_pdata->platform_state_mutex); ++ of_node_put(np); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(arche_platform_change_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 (arche_pdata->wake_detect_state == WD_STATE_TIMESYNC) { ++ gb_timesync_irq(arche_pdata->timesync_svc_pdata); ++ goto exit; ++ } ++ ++ 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 begining, 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); ++ } ++ } ++ ++exit: ++ 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 platform_device *pdev = to_platform_device(dev); ++ struct arche_platform_drvdata *arche_pdata = platform_get_drvdata(pdev); ++ int ret = 0; ++ ++retry: ++ mutex_lock(&arche_pdata->platform_state_mutex); ++ if (arche_pdata->state == ARCHE_PLATFORM_STATE_TIME_SYNC) { ++ mutex_unlock(&arche_pdata->platform_state_mutex); ++ ret = wait_event_interruptible( ++ arche_pdata->wq, ++ arche_pdata->state != ARCHE_PLATFORM_STATE_TIME_SYNC); ++ if (ret) ++ return ret; ++ goto retry; ++ } ++ ++ 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"); ++ case ARCHE_PLATFORM_STATE_TIME_SYNC: ++ return sprintf(buf, "time_sync\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 (arche_pdata->svc_reset_gpio < 0) { ++ 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 (arche_pdata->svc_sysboot_gpio < 0) { ++ 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 (arche_pdata->svc_refclk_req < 0) { ++ 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"); ++ ret = arche_pdata->wake_detect_gpio; ++ return ret; ++ } ++ ++ 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); ++ init_waitqueue_head(&arche_pdata->wq); ++ 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; ++ } ++ ++ /* Register callback pointer */ ++ arche_platform_change_state_cb = arche_platform_change_state; ++ ++ /* 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); ++ platform_set_drvdata(pdev, NULL); ++ ++ 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 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 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 struct of_device_id arche_platform_of_match[] = { ++ { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ ++ { }, ++}; ++ ++static struct of_device_id arche_combined_id[] = { ++ { .compatible = "google,arche-platform", }, /* Use PID/VID of SVC device */ ++ { .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"); +--- /dev/null ++++ b/drivers/greybus/arche_platform.h +@@ -0,0 +1,39 @@ ++/* ++ * Arche Platform driver to enable Unipro link. ++ * ++ * Copyright 2015-2016 Google Inc. ++ * Copyright 2015-2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __ARCHE_PLATFORM_H ++#define __ARCHE_PLATFORM_H ++ ++#include "timesync.h" ++ ++enum arche_platform_state { ++ ARCHE_PLATFORM_STATE_OFF, ++ ARCHE_PLATFORM_STATE_ACTIVE, ++ ARCHE_PLATFORM_STATE_STANDBY, ++ ARCHE_PLATFORM_STATE_FW_FLASHING, ++ ARCHE_PLATFORM_STATE_TIME_SYNC, ++}; ++ ++int arche_platform_change_state(enum arche_platform_state state, ++ struct gb_timesync_svc *pdata); ++ ++extern int (*arche_platform_change_state_cb)(enum arche_platform_state state, ++ struct gb_timesync_svc *pdata); ++int __init arche_apb_init(void); ++void __exit arche_apb_exit(void); ++ ++/* Operational states for the APB device */ ++int apb_ctrl_coldboot(struct device *dev); ++int apb_ctrl_fw_flashing(struct device *dev); ++int apb_ctrl_standby_boot(struct device *dev); ++void apb_ctrl_poweroff(struct device *dev); ++void apb_bootret_assert(struct device *dev); ++void apb_bootret_deassert(struct device *dev); ++ ++#endif /* __ARCHE_PLATFORM_H */ diff --git a/greybus_audio.patch b/greybus_audio.patch new file mode 100644 index 00000000000000..0fa4ec2f8b46c8 --- /dev/null +++ b/greybus_audio.patch @@ -0,0 +1,4636 @@ +--- + drivers/greybus/audio_apbridgea.c | 207 ++++ + drivers/greybus/audio_apbridgea.h | 156 +++ + drivers/greybus/audio_codec.c | 1132 +++++++++++++++++++++++++ + drivers/greybus/audio_codec.h | 283 ++++++ + drivers/greybus/audio_gb.c | 228 +++++ + drivers/greybus/audio_manager.c | 184 ++++ + drivers/greybus/audio_manager.h | 83 + + drivers/greybus/audio_manager_module.c | 258 +++++ + drivers/greybus/audio_manager_private.h | 28 + drivers/greybus/audio_manager_sysfs.c | 102 ++ + drivers/greybus/audio_module.c | 482 ++++++++++ + drivers/greybus/audio_topology.c | 1442 ++++++++++++++++++++++++++++++++ + 12 files changed, 4585 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/audio_apbridgea.c +@@ -0,0 +1,207 @@ ++/* ++ * Greybus Audio Device Class Protocol helpers ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++#include "greybus_protocols.h" ++#include "audio_apbridgea.h" ++#include "audio_codec.h" ++ ++int gb_audio_apbridgea_set_config(struct gb_connection *connection, ++ __u16 i2s_port, __u32 format, __u32 rate, ++ __u32 mclk_freq) ++{ ++ struct audio_apbridgea_set_config_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_CONFIG; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.format = cpu_to_le32(format); ++ req.rate = cpu_to_le32(rate); ++ req.mclk_freq = cpu_to_le32(mclk_freq); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_config); ++ ++int gb_audio_apbridgea_register_cport(struct gb_connection *connection, ++ __u16 i2s_port, __u16 cportid, ++ __u8 direction) ++{ ++ struct audio_apbridgea_register_cport_request req; ++ int ret; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.cport = cpu_to_le16(cportid); ++ req.direction = direction; ++ ++ ret = gb_pm_runtime_get_sync(connection->bundle); ++ if (ret) ++ return ret; ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_register_cport); ++ ++int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, ++ __u16 i2s_port, __u16 cportid, ++ __u8 direction) ++{ ++ struct audio_apbridgea_unregister_cport_request req; ++ int ret; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.cport = cpu_to_le16(cportid); ++ req.direction = direction; ++ ++ ret = gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++ ++ gb_pm_runtime_put_autosuspend(connection->bundle); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_unregister_cport); ++ ++int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, ++ __u16 i2s_port, __u16 size) ++{ ++ struct audio_apbridgea_set_tx_data_size_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.size = cpu_to_le16(size); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_tx_data_size); ++ ++int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, ++ __u16 i2s_port) ++{ ++ struct audio_apbridgea_prepare_tx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_TX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_tx); ++ ++int gb_audio_apbridgea_start_tx(struct gb_connection *connection, ++ __u16 i2s_port, __u64 timestamp) ++{ ++ struct audio_apbridgea_start_tx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_TX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.timestamp = cpu_to_le64(timestamp); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_tx); ++ ++int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, __u16 i2s_port) ++{ ++ struct audio_apbridgea_stop_tx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_TX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_tx); ++ ++int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection, ++ __u16 i2s_port) ++{ ++ struct audio_apbridgea_shutdown_tx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_tx); ++ ++int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, ++ __u16 i2s_port, __u16 size) ++{ ++ struct audio_apbridgea_set_rx_data_size_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ req.size = cpu_to_le16(size); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_set_rx_data_size); ++ ++int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, ++ __u16 i2s_port) ++{ ++ struct audio_apbridgea_prepare_rx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_PREPARE_RX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_prepare_rx); ++ ++int gb_audio_apbridgea_start_rx(struct gb_connection *connection, ++ __u16 i2s_port) ++{ ++ struct audio_apbridgea_start_rx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_START_RX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_start_rx); ++ ++int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, __u16 i2s_port) ++{ ++ struct audio_apbridgea_stop_rx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_STOP_RX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_stop_rx); ++ ++int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection, ++ __u16 i2s_port) ++{ ++ struct audio_apbridgea_shutdown_rx_request req; ++ ++ req.hdr.type = AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX; ++ req.hdr.i2s_port = cpu_to_le16(i2s_port); ++ ++ return gb_hd_output(connection->hd, &req, sizeof(req), ++ GB_APB_REQUEST_AUDIO_CONTROL, true); ++} ++EXPORT_SYMBOL_GPL(gb_audio_apbridgea_shutdown_rx); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("greybus:audio-apbridgea"); ++MODULE_DESCRIPTION("Greybus Special APBridgeA Audio Protocol library"); ++MODULE_AUTHOR("Mark Greer <mgreer@animalcreek.com>"); +--- /dev/null ++++ b/drivers/greybus/audio_apbridgea.h +@@ -0,0 +1,156 @@ ++/** ++ * Copyright (c) 2015-2016 Google Inc. ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions are met: ++ * 1. Redistributions of source code must retain the above copyright notice, ++ * this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright notice, ++ * this list of conditions and the following disclaimer in the documentation ++ * and/or other materials provided with the distribution. ++ * 3. Neither the name of the copyright holder nor the names of its ++ * contributors may be used to endorse or promote products derived from this ++ * software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ++ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ++ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ++ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++/* ++ * This is a special protocol for configuring communication over the ++ * I2S bus between the DSP on the MSM8994 and APBridgeA. Therefore, ++ * we can predefine several low-level attributes of the communication ++ * because we know that they are supported. In particular, the following ++ * assumptions are made: ++ * - there are two channels (i.e., stereo) ++ * - the low-level protocol is I2S as defined by Philips/NXP ++ * - the DSP on the MSM8994 is the clock master for MCLK, BCLK, and WCLK ++ * - WCLK changes on the falling edge of BCLK ++ * - WCLK low for left channel; high for right channel ++ * - TX data is sent on the falling edge of BCLK ++ * - RX data is received/latched on the rising edge of BCLK ++ */ ++ ++#ifndef __AUDIO_APBRIDGEA_H ++#define __AUDIO_APBRIDGEA_H ++ ++#define AUDIO_APBRIDGEA_TYPE_SET_CONFIG 0x01 ++#define AUDIO_APBRIDGEA_TYPE_REGISTER_CPORT 0x02 ++#define AUDIO_APBRIDGEA_TYPE_UNREGISTER_CPORT 0x03 ++#define AUDIO_APBRIDGEA_TYPE_SET_TX_DATA_SIZE 0x04 ++ /* 0x05 unused */ ++#define AUDIO_APBRIDGEA_TYPE_PREPARE_TX 0x06 ++#define AUDIO_APBRIDGEA_TYPE_START_TX 0x07 ++#define AUDIO_APBRIDGEA_TYPE_STOP_TX 0x08 ++#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_TX 0x09 ++#define AUDIO_APBRIDGEA_TYPE_SET_RX_DATA_SIZE 0x0a ++ /* 0x0b unused */ ++#define AUDIO_APBRIDGEA_TYPE_PREPARE_RX 0x0c ++#define AUDIO_APBRIDGEA_TYPE_START_RX 0x0d ++#define AUDIO_APBRIDGEA_TYPE_STOP_RX 0x0e ++#define AUDIO_APBRIDGEA_TYPE_SHUTDOWN_RX 0x0f ++ ++#define AUDIO_APBRIDGEA_PCM_FMT_8 BIT(0) ++#define AUDIO_APBRIDGEA_PCM_FMT_16 BIT(1) ++#define AUDIO_APBRIDGEA_PCM_FMT_24 BIT(2) ++#define AUDIO_APBRIDGEA_PCM_FMT_32 BIT(3) ++#define AUDIO_APBRIDGEA_PCM_FMT_64 BIT(4) ++ ++#define AUDIO_APBRIDGEA_PCM_RATE_5512 BIT(0) ++#define AUDIO_APBRIDGEA_PCM_RATE_8000 BIT(1) ++#define AUDIO_APBRIDGEA_PCM_RATE_11025 BIT(2) ++#define AUDIO_APBRIDGEA_PCM_RATE_16000 BIT(3) ++#define AUDIO_APBRIDGEA_PCM_RATE_22050 BIT(4) ++#define AUDIO_APBRIDGEA_PCM_RATE_32000 BIT(5) ++#define AUDIO_APBRIDGEA_PCM_RATE_44100 BIT(6) ++#define AUDIO_APBRIDGEA_PCM_RATE_48000 BIT(7) ++#define AUDIO_APBRIDGEA_PCM_RATE_64000 BIT(8) ++#define AUDIO_APBRIDGEA_PCM_RATE_88200 BIT(9) ++#define AUDIO_APBRIDGEA_PCM_RATE_96000 BIT(10) ++#define AUDIO_APBRIDGEA_PCM_RATE_176400 BIT(11) ++#define AUDIO_APBRIDGEA_PCM_RATE_192000 BIT(12) ++ ++#define AUDIO_APBRIDGEA_DIRECTION_TX BIT(0) ++#define AUDIO_APBRIDGEA_DIRECTION_RX BIT(1) ++ ++/* The I2S port is passed in the 'index' parameter of the USB request */ ++/* The CPort is passed in the 'value' parameter of the USB request */ ++ ++struct audio_apbridgea_hdr { ++ __u8 type; ++ __le16 i2s_port; ++ __u8 data[0]; ++} __packed; ++ ++struct audio_apbridgea_set_config_request { ++ struct audio_apbridgea_hdr hdr; ++ __le32 format; /* AUDIO_APBRIDGEA_PCM_FMT_* */ ++ __le32 rate; /* AUDIO_APBRIDGEA_PCM_RATE_* */ ++ __le32 mclk_freq; /* XXX Remove? */ ++} __packed; ++ ++struct audio_apbridgea_register_cport_request { ++ struct audio_apbridgea_hdr hdr; ++ __le16 cport; ++ __u8 direction; ++} __packed; ++ ++struct audio_apbridgea_unregister_cport_request { ++ struct audio_apbridgea_hdr hdr; ++ __le16 cport; ++ __u8 direction; ++} __packed; ++ ++struct audio_apbridgea_set_tx_data_size_request { ++ struct audio_apbridgea_hdr hdr; ++ __le16 size; ++} __packed; ++ ++struct audio_apbridgea_prepare_tx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_start_tx_request { ++ struct audio_apbridgea_hdr hdr; ++ __le64 timestamp; ++} __packed; ++ ++struct audio_apbridgea_stop_tx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_shutdown_tx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_set_rx_data_size_request { ++ struct audio_apbridgea_hdr hdr; ++ __le16 size; ++} __packed; ++ ++struct audio_apbridgea_prepare_rx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_start_rx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_stop_rx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++struct audio_apbridgea_shutdown_rx_request { ++ struct audio_apbridgea_hdr hdr; ++} __packed; ++ ++#endif /*__AUDIO_APBRIDGEA_H */ +--- /dev/null ++++ b/drivers/greybus/audio_codec.c +@@ -0,0 +1,1132 @@ ++/* ++ * APBridge ALSA SoC dummy codec driver ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++#include <sound/soc.h> ++#include <sound/pcm_params.h> ++#include <uapi/linux/input.h> ++ ++#include "audio_codec.h" ++#include "audio_apbridgea.h" ++#include "audio_manager.h" ++ ++static struct gbaudio_codec_info *gbcodec; ++ ++static struct gbaudio_data_connection * ++find_data(struct gbaudio_module_info *module, int id) ++{ ++ struct gbaudio_data_connection *data; ++ ++ list_for_each_entry(data, &module->data_list, list) { ++ if (id == data->id) ++ return data; ++ } ++ return NULL; ++} ++ ++static struct gbaudio_stream_params * ++find_dai_stream_params(struct gbaudio_codec_info *codec, int id, int stream) ++{ ++ struct gbaudio_codec_dai *dai; ++ ++ list_for_each_entry(dai, &codec->dai_list, list) { ++ if (dai->id == id) ++ return &dai->params[stream]; ++ } ++ return NULL; ++} ++ ++static int gbaudio_module_enable_tx(struct gbaudio_codec_info *codec, ++ struct gbaudio_module_info *module, int id) ++{ ++ int module_state, ret = 0; ++ uint16_t data_cport, i2s_port, cportid; ++ uint8_t sig_bits, channels; ++ uint32_t format, rate; ++ struct gbaudio_data_connection *data; ++ struct gbaudio_stream_params *params; ++ ++ /* find the dai */ ++ data = find_data(module, id); ++ if (!data) { ++ dev_err(module->dev, "%d:DATA connection missing\n", id); ++ return -ENODEV; ++ } ++ module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; ++ ++ params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_PLAYBACK); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ return -EINVAL; ++ } ++ ++ /* register cport */ ++ if (module_state < GBAUDIO_CODEC_STARTUP) { ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_register_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_TX); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "reg_cport failed:%d\n", ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_PLAYBACK] = ++ GBAUDIO_CODEC_STARTUP; ++ dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); ++ } ++ ++ /* hw_params */ ++ if (module_state < GBAUDIO_CODEC_HWPARAMS) { ++ format = params->format; ++ channels = params->channels; ++ rate = params->rate; ++ sig_bits = params->sig_bits; ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, ++ format, rate, channels, sig_bits); ++ if (ret) { ++ dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", ++ ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_PLAYBACK] = ++ GBAUDIO_CODEC_HWPARAMS; ++ dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); ++ } ++ ++ /* prepare */ ++ if (module_state < GBAUDIO_CODEC_PREPARE) { ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_set_tx_data_size(module->mgmt_connection, ++ data_cport, 192); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "set_tx_data_size failed:%d\n", ++ ret); ++ return ret; ++ } ++ ret = gb_audio_gb_activate_tx(module->mgmt_connection, ++ data_cport); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "activate_tx failed:%d\n", ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_PLAYBACK] = ++ GBAUDIO_CODEC_PREPARE; ++ dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); ++ } ++ ++ return 0; ++} ++ ++static int gbaudio_module_disable_tx(struct gbaudio_module_info *module, int id) ++{ ++ int ret; ++ uint16_t data_cport, cportid, i2s_port; ++ int module_state; ++ struct gbaudio_data_connection *data; ++ ++ /* find the dai */ ++ data = find_data(module, id); ++ if (!data) { ++ dev_err(module->dev, "%d:DATA connection missing\n", id); ++ return -ENODEV; ++ } ++ module_state = data->state[SNDRV_PCM_STREAM_PLAYBACK]; ++ ++ if (module_state > GBAUDIO_CODEC_HWPARAMS) { ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_deactivate_tx(module->mgmt_connection, ++ data_cport); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "deactivate_tx failed:%d\n", ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); ++ data->state[SNDRV_PCM_STREAM_PLAYBACK] = ++ GBAUDIO_CODEC_HWPARAMS; ++ } ++ ++ if (module_state > GBAUDIO_CODEC_SHUTDOWN) { ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_unregister_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_TX); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "unregister_cport failed:%d\n", ++ ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); ++ data->state[SNDRV_PCM_STREAM_PLAYBACK] = ++ GBAUDIO_CODEC_SHUTDOWN; ++ } ++ ++ return 0; ++} ++ ++static int gbaudio_module_enable_rx(struct gbaudio_codec_info *codec, ++ struct gbaudio_module_info *module, int id) ++{ ++ int module_state, ret = 0; ++ uint16_t data_cport, i2s_port, cportid; ++ uint8_t sig_bits, channels; ++ uint32_t format, rate; ++ struct gbaudio_data_connection *data; ++ struct gbaudio_stream_params *params; ++ ++ /* find the dai */ ++ data = find_data(module, id); ++ if (!data) { ++ dev_err(module->dev, "%d:DATA connection missing\n", id); ++ return -ENODEV; ++ } ++ module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ params = find_dai_stream_params(codec, id, SNDRV_PCM_STREAM_CAPTURE); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ return -EINVAL; ++ } ++ ++ /* register cport */ ++ if (module_state < GBAUDIO_CODEC_STARTUP) { ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_register_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_RX); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "reg_cport failed:%d\n", ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_CAPTURE] = ++ GBAUDIO_CODEC_STARTUP; ++ dev_dbg(module->dev, "Dynamic Register %d DAI\n", cportid); ++ } ++ ++ /* hw_params */ ++ if (module_state < GBAUDIO_CODEC_HWPARAMS) { ++ format = params->format; ++ channels = params->channels; ++ rate = params->rate; ++ sig_bits = params->sig_bits; ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_set_pcm(module->mgmt_connection, data_cport, ++ format, rate, channels, sig_bits); ++ if (ret) { ++ dev_err_ratelimited(module->dev, "set_pcm failed:%d\n", ++ ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_CAPTURE] = ++ GBAUDIO_CODEC_HWPARAMS; ++ dev_dbg(module->dev, "Dynamic hw_params %d DAI\n", data_cport); ++ } ++ ++ /* prepare */ ++ if (module_state < GBAUDIO_CODEC_PREPARE) { ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_set_rx_data_size(module->mgmt_connection, ++ data_cport, 192); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "set_rx_data_size failed:%d\n", ++ ret); ++ return ret; ++ } ++ ret = gb_audio_gb_activate_rx(module->mgmt_connection, ++ data_cport); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "activate_rx failed:%d\n", ret); ++ return ret; ++ } ++ data->state[SNDRV_PCM_STREAM_CAPTURE] = ++ GBAUDIO_CODEC_PREPARE; ++ dev_dbg(module->dev, "Dynamic prepare %d DAI\n", data_cport); ++ } ++ ++ return 0; ++} ++ ++static int gbaudio_module_disable_rx(struct gbaudio_module_info *module, int id) ++{ ++ int ret; ++ uint16_t data_cport, cportid, i2s_port; ++ int module_state; ++ struct gbaudio_data_connection *data; ++ ++ /* find the dai */ ++ data = find_data(module, id); ++ if (!data) { ++ dev_err(module->dev, "%d:DATA connection missing\n", id); ++ return -ENODEV; ++ } ++ module_state = data->state[SNDRV_PCM_STREAM_CAPTURE]; ++ ++ if (module_state > GBAUDIO_CODEC_HWPARAMS) { ++ data_cport = data->connection->intf_cport_id; ++ ret = gb_audio_gb_deactivate_rx(module->mgmt_connection, ++ data_cport); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "deactivate_rx failed:%d\n", ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Dynamic deactivate %d DAI\n", data_cport); ++ data->state[SNDRV_PCM_STREAM_CAPTURE] = ++ GBAUDIO_CODEC_HWPARAMS; ++ } ++ ++ if (module_state > GBAUDIO_CODEC_SHUTDOWN) { ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_unregister_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_RX); ++ if (ret) { ++ dev_err_ratelimited(module->dev, ++ "unregister_cport failed:%d\n", ++ ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Dynamic Unregister %d DAI\n", cportid); ++ data->state[SNDRV_PCM_STREAM_CAPTURE] = ++ GBAUDIO_CODEC_SHUTDOWN; ++ } ++ ++ return 0; ++} ++ ++int gbaudio_module_update(struct gbaudio_codec_info *codec, ++ struct snd_soc_dapm_widget *w, ++ struct gbaudio_module_info *module, int enable) ++{ ++ int dai_id, ret; ++ char intf_name[NAME_SIZE], dir[NAME_SIZE]; ++ ++ dev_dbg(module->dev, "%s:Module update %s sequence\n", w->name, ++ enable ? "Enable":"Disable"); ++ ++ if ((w->id != snd_soc_dapm_aif_in) && (w->id != snd_soc_dapm_aif_out)){ ++ dev_dbg(codec->dev, "No action required for %s\n", w->name); ++ return 0; ++ } ++ ++ /* parse dai_id from AIF widget's stream_name */ ++ ret = sscanf(w->sname, "%s %d %s", intf_name, &dai_id, dir); ++ if (ret < 3) { ++ dev_err(codec->dev, "Error while parsing dai_id for %s\n", ++ w->name); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&codec->lock); ++ if (w->id == snd_soc_dapm_aif_in) { ++ if (enable) ++ ret = gbaudio_module_enable_tx(codec, module, dai_id); ++ else ++ ret = gbaudio_module_disable_tx(module, dai_id); ++ } else if (w->id == snd_soc_dapm_aif_out) { ++ if (enable) ++ ret = gbaudio_module_enable_rx(codec, module, dai_id); ++ else ++ ret = gbaudio_module_disable_rx(module, dai_id); ++ } ++ ++ mutex_unlock(&codec->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(gbaudio_module_update); ++ ++/* ++ * codec DAI ops ++ */ ++static int gbcodec_startup(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); ++ struct gbaudio_stream_params *params; ++ ++ mutex_lock(&codec->lock); ++ ++ if (list_empty(&codec->module_list)) { ++ dev_err(codec->dev, "No codec module available\n"); ++ mutex_unlock(&codec->lock); ++ return -ENODEV; ++ } ++ ++ params = find_dai_stream_params(codec, dai->id, substream->stream); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ params->state = GBAUDIO_CODEC_STARTUP; ++ mutex_unlock(&codec->lock); ++ /* to prevent suspend in case of active audio */ ++ pm_stay_awake(dai->dev); ++ ++ return 0; ++} ++ ++static void gbcodec_shutdown(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); ++ struct gbaudio_stream_params *params; ++ ++ mutex_lock(&codec->lock); ++ ++ if (list_empty(&codec->module_list)) ++ dev_info(codec->dev, "No codec module available during shutdown\n"); ++ ++ params = find_dai_stream_params(codec, dai->id, substream->stream); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ mutex_unlock(&codec->lock); ++ return; ++ } ++ params->state = GBAUDIO_CODEC_SHUTDOWN; ++ mutex_unlock(&codec->lock); ++ pm_relax(dai->dev); ++ return; ++} ++ ++static int gbcodec_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *hwparams, ++ struct snd_soc_dai *dai) ++{ ++ int ret; ++ uint8_t sig_bits, channels; ++ uint32_t format, rate; ++ struct gbaudio_module_info *module; ++ struct gbaudio_data_connection *data; ++ struct gb_bundle *bundle; ++ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); ++ struct gbaudio_stream_params *params; ++ ++ mutex_lock(&codec->lock); ++ ++ if (list_empty(&codec->module_list)) { ++ dev_err(codec->dev, "No codec module available\n"); ++ mutex_unlock(&codec->lock); ++ return -ENODEV; ++ } ++ ++ /* ++ * assuming, currently only 48000 Hz, 16BIT_LE, stereo ++ * is supported, validate params before configuring codec ++ */ ++ if (params_channels(hwparams) != 2) { ++ dev_err(dai->dev, "Invalid channel count:%d\n", ++ params_channels(hwparams)); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ channels = params_channels(hwparams); ++ ++ if (params_rate(hwparams) != 48000) { ++ dev_err(dai->dev, "Invalid sampling rate:%d\n", ++ params_rate(hwparams)); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ rate = GB_AUDIO_PCM_RATE_48000; ++ ++ if (params_format(hwparams) != SNDRV_PCM_FORMAT_S16_LE) { ++ dev_err(dai->dev, "Invalid format:%d\n", ++ params_format(hwparams)); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ format = GB_AUDIO_PCM_FMT_S16_LE; ++ ++ /* find the data connection */ ++ list_for_each_entry(module, &codec->module_list, list) { ++ data = find_data(module, dai->id); ++ if (data) ++ break; ++ } ++ ++ if (!data) { ++ dev_err(dai->dev, "DATA connection missing\n"); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ ++ params = find_dai_stream_params(codec, dai->id, substream->stream); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ ++ bundle = to_gb_bundle(module->dev); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) { ++ mutex_unlock(&codec->lock); ++ return ret; ++ } ++ ++ ret = gb_audio_apbridgea_set_config(data->connection, 0, ++ AUDIO_APBRIDGEA_PCM_FMT_16, ++ AUDIO_APBRIDGEA_PCM_RATE_48000, ++ 6144000); ++ if (ret) { ++ dev_err_ratelimited(dai->dev, "%d: Error during set_config\n", ++ ret); ++ mutex_unlock(&codec->lock); ++ return ret; ++ } ++ ++ gb_pm_runtime_put_noidle(bundle); ++ ++ params->state = GBAUDIO_CODEC_HWPARAMS; ++ params->format = format; ++ params->rate = rate; ++ params->channels = channels; ++ params->sig_bits = sig_bits; ++ ++ mutex_unlock(&codec->lock); ++ return 0; ++} ++ ++static int gbcodec_prepare(struct snd_pcm_substream *substream, ++ struct snd_soc_dai *dai) ++{ ++ int ret; ++ struct gbaudio_module_info *module; ++ struct gbaudio_data_connection *data; ++ struct gb_bundle *bundle; ++ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); ++ struct gbaudio_stream_params *params; ++ ++ mutex_lock(&codec->lock); ++ ++ if (list_empty(&codec->module_list)) { ++ dev_err(codec->dev, "No codec module available\n"); ++ mutex_unlock(&codec->lock); ++ return -ENODEV; ++ } ++ ++ list_for_each_entry(module, &codec->module_list, list) { ++ /* find the dai */ ++ data = find_data(module, dai->id); ++ if (data) ++ break; ++ } ++ if (!data) { ++ dev_err(dai->dev, "DATA connection missing\n"); ++ mutex_unlock(&codec->lock); ++ return -ENODEV; ++ } ++ ++ params = find_dai_stream_params(codec, dai->id, substream->stream); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ ++ bundle = to_gb_bundle(module->dev); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) { ++ mutex_unlock(&codec->lock); ++ return ret; ++ } ++ ++ switch (substream->stream) { ++ case SNDRV_PCM_STREAM_PLAYBACK: ++ ret = gb_audio_apbridgea_set_tx_data_size(data->connection, 0, ++ 192); ++ break; ++ case SNDRV_PCM_STREAM_CAPTURE: ++ ret = gb_audio_apbridgea_set_rx_data_size(data->connection, 0, ++ 192); ++ break; ++ } ++ if (ret) { ++ mutex_unlock(&codec->lock); ++ dev_err_ratelimited(dai->dev, "set_data_size failed:%d\n", ++ ret); ++ return ret; ++ } ++ ++ gb_pm_runtime_put_noidle(bundle); ++ ++ params->state = GBAUDIO_CODEC_PREPARE; ++ mutex_unlock(&codec->lock); ++ return 0; ++} ++ ++static int gbcodec_mute_stream(struct snd_soc_dai *dai, int mute, int stream) ++{ ++ int ret; ++ struct gbaudio_data_connection *data; ++ struct gbaudio_module_info *module; ++ struct gb_bundle *bundle; ++ struct gbaudio_codec_info *codec = dev_get_drvdata(dai->dev); ++ struct gbaudio_stream_params *params; ++ ++ ++ dev_dbg(dai->dev, "Mute:%d, Direction:%s\n", mute, ++ stream ? "CAPTURE":"PLAYBACK"); ++ ++ mutex_lock(&codec->lock); ++ ++ params = find_dai_stream_params(codec, dai->id, stream); ++ if (!params) { ++ dev_err(codec->dev, "Failed to fetch dai_stream pointer\n"); ++ mutex_unlock(&codec->lock); ++ return -EINVAL; ++ } ++ ++ if (list_empty(&codec->module_list)) { ++ dev_err(codec->dev, "No codec module available\n"); ++ if (mute) { ++ params->state = GBAUDIO_CODEC_STOP; ++ ret = 0; ++ } else { ++ ret = -ENODEV; ++ } ++ mutex_unlock(&codec->lock); ++ return ret; ++ } ++ ++ list_for_each_entry(module, &codec->module_list, list) { ++ /* find the dai */ ++ data = find_data(module, dai->id); ++ if (data) ++ break; ++ } ++ if (!data) { ++ dev_err(dai->dev, "%s:%s DATA connection missing\n", ++ dai->name, module->name); ++ mutex_unlock(&codec->lock); ++ return -ENODEV; ++ } ++ ++ bundle = to_gb_bundle(module->dev); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) { ++ mutex_unlock(&codec->lock); ++ return ret; ++ } ++ ++ if (!mute && !stream) {/* start playback */ ++ ret = gb_audio_apbridgea_prepare_tx(data->connection, ++ 0); ++ if (!ret) ++ ret = gb_audio_apbridgea_start_tx(data->connection, ++ 0, 0); ++ params->state = GBAUDIO_CODEC_START; ++ } else if (!mute && stream) {/* start capture */ ++ ret = gb_audio_apbridgea_prepare_rx(data->connection, ++ 0); ++ if (!ret) ++ ret = gb_audio_apbridgea_start_rx(data->connection, ++ 0); ++ params->state = GBAUDIO_CODEC_START; ++ } else if (mute && !stream) {/* stop playback */ ++ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); ++ if (!ret) ++ ret = gb_audio_apbridgea_shutdown_tx(data->connection, ++ 0); ++ params->state = GBAUDIO_CODEC_STOP; ++ } else if (mute && stream) {/* stop capture */ ++ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); ++ if (!ret) ++ ret = gb_audio_apbridgea_shutdown_rx(data->connection, ++ 0); ++ params->state = GBAUDIO_CODEC_STOP; ++ } else ++ ret = -EINVAL; ++ if (ret) ++ dev_err_ratelimited(dai->dev, ++ "%s:Error during %s %s stream:%d\n", ++ module->name, mute ? "Mute" : "Unmute", ++ stream ? "Capture" : "Playback", ret); ++ ++ gb_pm_runtime_put_noidle(bundle); ++ mutex_unlock(&codec->lock); ++ return ret; ++} ++ ++static struct snd_soc_dai_ops gbcodec_dai_ops = { ++ .startup = gbcodec_startup, ++ .shutdown = gbcodec_shutdown, ++ .hw_params = gbcodec_hw_params, ++ .prepare = gbcodec_prepare, ++ .mute_stream = gbcodec_mute_stream, ++}; ++ ++static struct snd_soc_dai_driver gbaudio_dai[] = { ++ { ++ .name = "apb-i2s0", ++ .id = 0, ++ .playback = { ++ .stream_name = "I2S 0 Playback", ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FORMAT_S16_LE, ++ .rate_max = 48000, ++ .rate_min = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ }, ++ .capture = { ++ .stream_name = "I2S 0 Capture", ++ .rates = SNDRV_PCM_RATE_48000, ++ .formats = SNDRV_PCM_FORMAT_S16_LE, ++ .rate_max = 48000, ++ .rate_min = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ }, ++ .ops = &gbcodec_dai_ops, ++ }, ++}; ++ ++static int gbaudio_init_jack(struct gbaudio_module_info *module, ++ struct snd_soc_codec *codec) ++{ ++ int ret; ++ ++ if (!module->jack_mask) ++ return 0; ++ ++ snprintf(module->jack_name, NAME_SIZE, "GB %d Headset Jack", ++ module->dev_id); ++ ret = snd_soc_jack_new(codec, module->jack_name, module->jack_mask, ++ &module->headset_jack); ++ if (ret) { ++ dev_err(module->dev, "Failed to create new jack\n"); ++ return ret; ++ } ++ ++ if (!module->button_mask) ++ return 0; ++ ++ snprintf(module->button_name, NAME_SIZE, "GB %d Button Jack", ++ module->dev_id); ++ ret = snd_soc_jack_new(codec, module->button_name, module->button_mask, ++ &module->button_jack); ++ if (ret) { ++ dev_err(module->dev, "Failed to create button jack\n"); ++ return ret; ++ } ++ ++ /* ++ * Currently, max 4 buttons are supported with following key mapping ++ * BTN_0 = KEY_MEDIA ++ * BTN_1 = KEY_VOICECOMMAND ++ * BTN_2 = KEY_VOLUMEUP ++ * BTN_3 = KEY_VOLUMEDOWN ++ */ ++ ++ if (module->button_mask & SND_JACK_BTN_0) { ++ ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_0, ++ KEY_MEDIA); ++ if (ret) { ++ dev_err(module->dev, "Failed to set BTN_0\n"); ++ return ret; ++ } ++ } ++ ++ if (module->button_mask & SND_JACK_BTN_1) { ++ ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_1, ++ KEY_VOICECOMMAND); ++ if (ret) { ++ dev_err(module->dev, "Failed to set BTN_1\n"); ++ return ret; ++ } ++ } ++ ++ if (module->button_mask & SND_JACK_BTN_2) { ++ ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_2, ++ KEY_VOLUMEUP); ++ if (ret) { ++ dev_err(module->dev, "Failed to set BTN_2\n"); ++ return ret; ++ } ++ } ++ ++ if (module->button_mask & SND_JACK_BTN_3) { ++ ret = snd_jack_set_key(module->button_jack.jack, SND_JACK_BTN_3, ++ KEY_VOLUMEDOWN); ++ if (ret) { ++ dev_err(module->dev, "Failed to set BTN_0\n"); ++ return ret; ++ } ++ } ++ ++ /* FIXME ++ * verify if this is really required ++ set_bit(INPUT_PROP_NO_DUMMY_RELEASE, ++ module->button_jack.jack->input_dev->propbit); ++ */ ++ ++ return 0; ++} ++ ++int gbaudio_register_module(struct gbaudio_module_info *module) ++{ ++ int ret; ++ struct snd_soc_codec *codec; ++ struct snd_card *card; ++ struct snd_soc_jack *jack = NULL; ++ ++ if (!gbcodec) { ++ dev_err(module->dev, "GB Codec not yet probed\n"); ++ return -EAGAIN; ++ } ++ ++ codec = gbcodec->codec; ++ card = codec->card->snd_card; ++ ++ down_write(&card->controls_rwsem); ++ ++ if (module->num_dais) { ++ dev_err(gbcodec->dev, ++ "%d:DAIs not supported via gbcodec driver\n", ++ module->num_dais); ++ up_write(&card->controls_rwsem); ++ return -EINVAL; ++ } ++ ++ ret = gbaudio_init_jack(module, codec); ++ if (ret) { ++ up_write(&card->controls_rwsem); ++ return ret; ++ } ++ ++ if (module->dapm_widgets) ++ snd_soc_dapm_new_controls(&codec->dapm, module->dapm_widgets, ++ module->num_dapm_widgets); ++ if (module->controls) ++ snd_soc_add_codec_controls(codec, module->controls, ++ module->num_controls); ++ if (module->dapm_routes) ++ snd_soc_dapm_add_routes(&codec->dapm, module->dapm_routes, ++ module->num_dapm_routes); ++ ++ /* card already instantiated, create widgets here only */ ++ if (codec->card->instantiated) { ++ snd_soc_dapm_link_component_dai_widgets(codec->card, ++ &codec->dapm); ++#ifdef CONFIG_SND_JACK ++ /* register jack devices for this module from codec->jack_list */ ++ list_for_each_entry(jack, &codec->jack_list, list) { ++ if ((jack == &module->headset_jack) ++ || (jack == &module->button_jack)) ++ snd_device_register(codec->card->snd_card, ++ jack->jack); ++ } ++#endif ++ } ++ ++ mutex_lock(&gbcodec->lock); ++ list_add(&module->list, &gbcodec->module_list); ++ mutex_unlock(&gbcodec->lock); ++ ++ if (codec->card->instantiated) ++ ret = snd_soc_dapm_new_widgets(&codec->dapm); ++ dev_dbg(codec->dev, "Registered %s module\n", module->name); ++ ++ up_write(&card->controls_rwsem); ++ return ret; ++} ++EXPORT_SYMBOL(gbaudio_register_module); ++ ++static void gbaudio_codec_clean_data_tx(struct gbaudio_data_connection *data) ++{ ++ uint16_t i2s_port, cportid; ++ int ret; ++ ++ if (list_is_singular(&gbcodec->module_list)) { ++ ret = gb_audio_apbridgea_stop_tx(data->connection, 0); ++ if (ret) ++ return; ++ ret = gb_audio_apbridgea_shutdown_tx(data->connection, ++ 0); ++ if (ret) ++ return; ++ } ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_unregister_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_TX); ++ data->state[0] = GBAUDIO_CODEC_SHUTDOWN; ++} ++ ++static void gbaudio_codec_clean_data_rx(struct gbaudio_data_connection *data) ++{ ++ uint16_t i2s_port, cportid; ++ int ret; ++ ++ if (list_is_singular(&gbcodec->module_list)) { ++ ret = gb_audio_apbridgea_stop_rx(data->connection, 0); ++ if (ret) ++ return; ++ ret = gb_audio_apbridgea_shutdown_rx(data->connection, ++ 0); ++ if (ret) ++ return; ++ } ++ i2s_port = 0; /* fixed for now */ ++ cportid = data->connection->hd_cport_id; ++ ret = gb_audio_apbridgea_unregister_cport(data->connection, ++ i2s_port, cportid, ++ AUDIO_APBRIDGEA_DIRECTION_RX); ++ data->state[1] = GBAUDIO_CODEC_SHUTDOWN; ++} ++ ++ ++static void gbaudio_codec_cleanup(struct gbaudio_module_info *module) ++{ ++ struct gbaudio_data_connection *data; ++ int pb_state, cap_state; ++ ++ dev_dbg(gbcodec->dev, "%s: removed, cleanup APBridge\n", module->name); ++ list_for_each_entry(data, &module->data_list, list) { ++ pb_state = data->state[0]; ++ cap_state = data->state[1]; ++ ++ if (pb_state > GBAUDIO_CODEC_SHUTDOWN) ++ gbaudio_codec_clean_data_tx(data); ++ ++ if (cap_state > GBAUDIO_CODEC_SHUTDOWN) ++ gbaudio_codec_clean_data_rx(data); ++ ++ } ++} ++ ++void gbaudio_unregister_module(struct gbaudio_module_info *module) ++{ ++ struct snd_soc_codec *codec = gbcodec->codec; ++ struct snd_card *card = codec->card->snd_card; ++ struct snd_soc_jack *jack, *next_j; ++ int mask; ++ ++ dev_dbg(codec->dev, "Unregister %s module\n", module->name); ++ ++ down_write(&card->controls_rwsem); ++ mutex_lock(&gbcodec->lock); ++ gbaudio_codec_cleanup(module); ++ list_del(&module->list); ++ dev_dbg(codec->dev, "Process Unregister %s module\n", module->name); ++ mutex_unlock(&gbcodec->lock); ++ ++#ifdef CONFIG_SND_JACK ++ /* free jack devices for this module from codec->jack_list */ ++ list_for_each_entry_safe(jack, next_j, &codec->jack_list, list) { ++ if (jack == &module->headset_jack) ++ mask = GBCODEC_JACK_MASK; ++ else if (jack == &module->button_jack) ++ mask = GBCODEC_JACK_BUTTON_MASK; ++ else ++ mask = 0; ++ if (mask) { ++ dev_dbg(module->dev, "Report %s removal\n", ++ jack->jack->id); ++ snd_soc_jack_report(jack, 0, mask); ++ snd_device_free(codec->card->snd_card, jack->jack); ++ list_del(&jack->list); ++ } ++ } ++#endif ++ ++ if (module->dapm_routes) { ++ dev_dbg(codec->dev, "Removing %d routes\n", ++ module->num_dapm_routes); ++ snd_soc_dapm_del_routes(&codec->dapm, module->dapm_routes, ++ module->num_dapm_routes); ++ } ++ if (module->controls) { ++ dev_dbg(codec->dev, "Removing %d controls\n", ++ module->num_controls); ++ snd_soc_remove_codec_controls(codec, module->controls, ++ module->num_controls); ++ } ++ if (module->dapm_widgets) { ++ dev_dbg(codec->dev, "Removing %d widgets\n", ++ module->num_dapm_widgets); ++ snd_soc_dapm_free_controls(&codec->dapm, module->dapm_widgets, ++ module->num_dapm_widgets); ++ } ++ ++ dev_dbg(codec->dev, "Unregistered %s module\n", module->name); ++ ++ up_write(&card->controls_rwsem); ++} ++EXPORT_SYMBOL(gbaudio_unregister_module); ++ ++/* ++ * codec driver ops ++ */ ++static int gbcodec_probe(struct snd_soc_codec *codec) ++{ ++ int i; ++ struct gbaudio_codec_info *info; ++ struct gbaudio_codec_dai *dai; ++ ++ info = devm_kzalloc(codec->dev, sizeof(*info), GFP_KERNEL); ++ if (!info) ++ return -ENOMEM; ++ ++ info->dev = codec->dev; ++ INIT_LIST_HEAD(&info->module_list); ++ mutex_init(&info->lock); ++ INIT_LIST_HEAD(&info->dai_list); ++ ++ /* init dai_list used to maintain runtime stream info */ ++ for (i = 0; i < ARRAY_SIZE(gbaudio_dai); i++) { ++ dai = devm_kzalloc(codec->dev, sizeof(*dai), GFP_KERNEL); ++ if (!dai) ++ return -ENOMEM; ++ dai->id = gbaudio_dai[i].id; ++ list_add(&dai->list, &info->dai_list); ++ } ++ ++ info->codec = codec; ++ snd_soc_codec_set_drvdata(codec, info); ++ gbcodec = info; ++ ++ device_init_wakeup(codec->dev, 1); ++ return 0; ++} ++ ++static int gbcodec_remove(struct snd_soc_codec *codec) ++{ ++ /* Empty function for now */ ++ return 0; ++} ++ ++static u8 gbcodec_reg[GBCODEC_REG_COUNT] = { ++ [GBCODEC_CTL_REG] = GBCODEC_CTL_REG_DEFAULT, ++ [GBCODEC_MUTE_REG] = GBCODEC_MUTE_REG_DEFAULT, ++ [GBCODEC_PB_LVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT, ++ [GBCODEC_PB_RVOL_REG] = GBCODEC_PB_VOL_REG_DEFAULT, ++ [GBCODEC_CAP_LVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT, ++ [GBCODEC_CAP_RVOL_REG] = GBCODEC_CAP_VOL_REG_DEFAULT, ++ [GBCODEC_APB1_MUX_REG] = GBCODEC_APB1_MUX_REG_DEFAULT, ++ [GBCODEC_APB2_MUX_REG] = GBCODEC_APB2_MUX_REG_DEFAULT, ++}; ++ ++static int gbcodec_write(struct snd_soc_codec *codec, unsigned int reg, ++ unsigned int value) ++{ ++ int ret = 0; ++ ++ if (reg == SND_SOC_NOPM) ++ return 0; ++ ++ BUG_ON(reg >= GBCODEC_REG_COUNT); ++ ++ gbcodec_reg[reg] = value; ++ dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, value); ++ ++ return ret; ++} ++ ++static unsigned int gbcodec_read(struct snd_soc_codec *codec, ++ unsigned int reg) ++{ ++ unsigned int val = 0; ++ ++ if (reg == SND_SOC_NOPM) ++ return 0; ++ ++ BUG_ON(reg >= GBCODEC_REG_COUNT); ++ ++ val = gbcodec_reg[reg]; ++ dev_dbg(codec->dev, "reg[%d] = 0x%x\n", reg, val); ++ ++ return val; ++} ++ ++static struct snd_soc_codec_driver soc_codec_dev_gbaudio = { ++ .probe = gbcodec_probe, ++ .remove = gbcodec_remove, ++ ++ .read = gbcodec_read, ++ .write = gbcodec_write, ++ ++ .reg_cache_size = GBCODEC_REG_COUNT, ++ .reg_cache_default = gbcodec_reg_defaults, ++ .reg_word_size = 1, ++ ++ .idle_bias_off = true, ++ .ignore_pmdown_time = 1, ++}; ++ ++#ifdef CONFIG_PM ++static int gbaudio_codec_suspend(struct device *dev) ++{ ++ dev_dbg(dev, "%s: suspend\n", __func__); ++ return 0; ++} ++ ++static int gbaudio_codec_resume(struct device *dev) ++{ ++ dev_dbg(dev, "%s: resume\n", __func__); ++ return 0; ++} ++ ++static const struct dev_pm_ops gbaudio_codec_pm_ops = { ++ .suspend = gbaudio_codec_suspend, ++ .resume = gbaudio_codec_resume, ++}; ++#endif ++ ++static int gbaudio_codec_probe(struct platform_device *pdev) ++{ ++ return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_gbaudio, ++ gbaudio_dai, ARRAY_SIZE(gbaudio_dai)); ++} ++ ++static int gbaudio_codec_remove(struct platform_device *pdev) ++{ ++ snd_soc_unregister_codec(&pdev->dev); ++ return 0; ++} ++ ++static const struct of_device_id greybus_asoc_machine_of_match[] = { ++ { .compatible = "toshiba,apb-dummy-codec", }, ++ {}, ++}; ++ ++static struct platform_driver gbaudio_codec_driver = { ++ .driver = { ++ .name = "apb-dummy-codec", ++ .owner = THIS_MODULE, ++#ifdef CONFIG_PM ++ .pm = &gbaudio_codec_pm_ops, ++#endif ++ .of_match_table = greybus_asoc_machine_of_match, ++ }, ++ .probe = gbaudio_codec_probe, ++ .remove = gbaudio_codec_remove, ++}; ++module_platform_driver(gbaudio_codec_driver); ++ ++MODULE_DESCRIPTION("APBridge ALSA SoC dummy codec driver"); ++MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:apb-dummy-codec"); +--- /dev/null ++++ b/drivers/greybus/audio_codec.h +@@ -0,0 +1,283 @@ ++/* ++ * Greybus audio driver ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __LINUX_GBAUDIO_CODEC_H ++#define __LINUX_GBAUDIO_CODEC_H ++ ++#include <sound/soc.h> ++#include <sound/jack.h> ++ ++#include "greybus.h" ++#include "greybus_protocols.h" ++ ++#define NAME_SIZE 32 ++#define MAX_DAIS 2 /* APB1, APB2 */ ++ ++enum { ++ APB1_PCM = 0, ++ APB2_PCM, ++ NUM_CODEC_DAIS, ++}; ++ ++enum gbcodec_reg_index { ++ GBCODEC_CTL_REG, ++ GBCODEC_MUTE_REG, ++ GBCODEC_PB_LVOL_REG, ++ GBCODEC_PB_RVOL_REG, ++ GBCODEC_CAP_LVOL_REG, ++ GBCODEC_CAP_RVOL_REG, ++ GBCODEC_APB1_MUX_REG, ++ GBCODEC_APB2_MUX_REG, ++ GBCODEC_REG_COUNT ++}; ++ ++/* device_type should be same as defined in audio.h (Android media layer) */ ++enum { ++ GBAUDIO_DEVICE_NONE = 0x0, ++ /* reserved bits */ ++ GBAUDIO_DEVICE_BIT_IN = 0x80000000, ++ GBAUDIO_DEVICE_BIT_DEFAULT = 0x40000000, ++ /* output devices */ ++ GBAUDIO_DEVICE_OUT_SPEAKER = 0x2, ++ GBAUDIO_DEVICE_OUT_WIRED_HEADSET = 0x4, ++ GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE = 0x8, ++ /* input devices */ ++ GBAUDIO_DEVICE_IN_BUILTIN_MIC = GBAUDIO_DEVICE_BIT_IN | 0x4, ++ GBAUDIO_DEVICE_IN_WIRED_HEADSET = GBAUDIO_DEVICE_BIT_IN | 0x10, ++}; ++ ++/* bit 0-SPK, 1-HP, 2-DAC, ++ * 4-MIC, 5-HSMIC, 6-MIC2 ++ */ ++#define GBCODEC_CTL_REG_DEFAULT 0x00 ++ ++/* bit 0,1 - APB1-PB-L/R ++ * bit 2,3 - APB2-PB-L/R ++ * bit 4,5 - APB1-Cap-L/R ++ * bit 6,7 - APB2-Cap-L/R ++ */ ++#define GBCODEC_MUTE_REG_DEFAULT 0x00 ++ ++/* 0-127 steps */ ++#define GBCODEC_PB_VOL_REG_DEFAULT 0x00 ++#define GBCODEC_CAP_VOL_REG_DEFAULT 0x00 ++ ++/* bit 0,1,2 - PB stereo, left, right ++ * bit 8,9,10 - Cap stereo, left, right ++ */ ++#define GBCODEC_APB1_MUX_REG_DEFAULT 0x00 ++#define GBCODEC_APB2_MUX_REG_DEFAULT 0x00 ++ ++#define GBCODEC_JACK_MASK 0x0000FFFF ++#define GBCODEC_JACK_BUTTON_MASK 0xFFFF0000 ++ ++static const u8 gbcodec_reg_defaults[GBCODEC_REG_COUNT] = { ++ GBCODEC_CTL_REG_DEFAULT, ++ GBCODEC_MUTE_REG_DEFAULT, ++ GBCODEC_PB_VOL_REG_DEFAULT, ++ GBCODEC_PB_VOL_REG_DEFAULT, ++ GBCODEC_CAP_VOL_REG_DEFAULT, ++ GBCODEC_CAP_VOL_REG_DEFAULT, ++ GBCODEC_APB1_MUX_REG_DEFAULT, ++ GBCODEC_APB2_MUX_REG_DEFAULT, ++}; ++ ++enum gbaudio_codec_state { ++ GBAUDIO_CODEC_SHUTDOWN = 0, ++ GBAUDIO_CODEC_STARTUP, ++ GBAUDIO_CODEC_HWPARAMS, ++ GBAUDIO_CODEC_PREPARE, ++ GBAUDIO_CODEC_START, ++ GBAUDIO_CODEC_STOP, ++}; ++ ++struct gbaudio_stream_params { ++ int state; ++ uint8_t sig_bits, channels; ++ uint32_t format, rate; ++}; ++ ++struct gbaudio_codec_dai { ++ int id; ++ /* runtime params for playback/capture streams */ ++ struct gbaudio_stream_params params[2]; ++ struct list_head list; ++}; ++ ++struct gbaudio_codec_info { ++ struct device *dev; ++ struct snd_soc_codec *codec; ++ struct list_head module_list; ++ /* to maintain runtime stream params for each DAI */ ++ struct list_head dai_list; ++ struct mutex lock; ++ u8 reg[GBCODEC_REG_COUNT]; ++}; ++ ++struct gbaudio_widget { ++ __u8 id; ++ const char *name; ++ struct list_head list; ++}; ++ ++struct gbaudio_control { ++ __u8 id; ++ char *name; ++ char *wname; ++ const char * const *texts; ++ int items; ++ struct list_head list; ++}; ++ ++struct gbaudio_data_connection { ++ int id; ++ __le16 data_cport; ++ struct gb_connection *connection; ++ struct list_head list; ++ /* maintain runtime state for playback/capture stream */ ++ int state[2]; ++}; ++ ++/* stream direction */ ++#define GB_PLAYBACK BIT(0) ++#define GB_CAPTURE BIT(1) ++ ++enum gbaudio_module_state { ++ GBAUDIO_MODULE_OFF = 0, ++ GBAUDIO_MODULE_ON, ++}; ++ ++struct gbaudio_module_info { ++ /* module info */ ++ struct device *dev; ++ int dev_id; /* check if it should be bundle_id/hd_cport_id */ ++ int vid; ++ int pid; ++ int slot; ++ int type; ++ int set_uevent; ++ char vstr[NAME_SIZE]; ++ char pstr[NAME_SIZE]; ++ struct list_head list; ++ /* need to share this info to above user space */ ++ int manager_id; ++ char name[NAME_SIZE]; ++ unsigned int ip_devices; ++ unsigned int op_devices; ++ ++ /* jack related */ ++ char jack_name[NAME_SIZE]; ++ char button_name[NAME_SIZE]; ++ int jack_type; ++ int jack_mask; ++ int button_mask; ++ int button_status; ++ struct snd_soc_jack headset_jack; ++ struct snd_soc_jack button_jack; ++ ++ /* connection info */ ++ struct gb_connection *mgmt_connection; ++ size_t num_data_connections; ++ struct list_head data_list; ++ ++ /* topology related */ ++ int num_dais; ++ int num_controls; ++ int num_dapm_widgets; ++ int num_dapm_routes; ++ unsigned long dai_offset; ++ unsigned long widget_offset; ++ unsigned long control_offset; ++ unsigned long route_offset; ++ struct snd_kcontrol_new *controls; ++ struct snd_soc_dapm_widget *dapm_widgets; ++ struct snd_soc_dapm_route *dapm_routes; ++ struct snd_soc_dai_driver *dais; ++ ++ struct list_head widget_list; ++ struct list_head ctl_list; ++ struct list_head widget_ctl_list; ++ ++ struct gb_audio_topology *topology; ++}; ++ ++int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, ++ struct gb_audio_topology *tplg_data); ++void gbaudio_tplg_release(struct gbaudio_module_info *module); ++ ++int gbaudio_module_update(struct gbaudio_codec_info *codec, ++ struct snd_soc_dapm_widget *w, ++ struct gbaudio_module_info *module, ++ int enable); ++int gbaudio_register_module(struct gbaudio_module_info *module); ++void gbaudio_unregister_module(struct gbaudio_module_info *module); ++ ++/* protocol related */ ++extern int gb_audio_gb_get_topology(struct gb_connection *connection, ++ struct gb_audio_topology **topology); ++extern int gb_audio_gb_get_control(struct gb_connection *connection, ++ uint8_t control_id, uint8_t index, ++ struct gb_audio_ctl_elem_value *value); ++extern int gb_audio_gb_set_control(struct gb_connection *connection, ++ uint8_t control_id, uint8_t index, ++ struct gb_audio_ctl_elem_value *value); ++extern int gb_audio_gb_enable_widget(struct gb_connection *connection, ++ uint8_t widget_id); ++extern int gb_audio_gb_disable_widget(struct gb_connection *connection, ++ uint8_t widget_id); ++extern int gb_audio_gb_get_pcm(struct gb_connection *connection, ++ uint16_t data_cport, uint32_t *format, ++ uint32_t *rate, uint8_t *channels, ++ uint8_t *sig_bits); ++extern int gb_audio_gb_set_pcm(struct gb_connection *connection, ++ uint16_t data_cport, uint32_t format, ++ uint32_t rate, uint8_t channels, ++ uint8_t sig_bits); ++extern int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, ++ uint16_t data_cport, uint16_t size); ++extern int gb_audio_gb_activate_tx(struct gb_connection *connection, ++ uint16_t data_cport); ++extern int gb_audio_gb_deactivate_tx(struct gb_connection *connection, ++ uint16_t data_cport); ++extern int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, ++ uint16_t data_cport, uint16_t size); ++extern int gb_audio_gb_activate_rx(struct gb_connection *connection, ++ uint16_t data_cport); ++extern int gb_audio_gb_deactivate_rx(struct gb_connection *connection, ++ uint16_t data_cport); ++extern int gb_audio_apbridgea_set_config(struct gb_connection *connection, ++ __u16 i2s_port, __u32 format, ++ __u32 rate, __u32 mclk_freq); ++extern int gb_audio_apbridgea_register_cport(struct gb_connection *connection, ++ __u16 i2s_port, __u16 cportid, ++ __u8 direction); ++extern int gb_audio_apbridgea_unregister_cport(struct gb_connection *connection, ++ __u16 i2s_port, __u16 cportid, ++ __u8 direction); ++extern int gb_audio_apbridgea_set_tx_data_size(struct gb_connection *connection, ++ __u16 i2s_port, __u16 size); ++extern int gb_audio_apbridgea_prepare_tx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_start_tx(struct gb_connection *connection, ++ __u16 i2s_port, __u64 timestamp); ++extern int gb_audio_apbridgea_stop_tx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_shutdown_tx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_set_rx_data_size(struct gb_connection *connection, ++ __u16 i2s_port, __u16 size); ++extern int gb_audio_apbridgea_prepare_rx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_start_rx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_stop_rx(struct gb_connection *connection, ++ __u16 i2s_port); ++extern int gb_audio_apbridgea_shutdown_rx(struct gb_connection *connection, ++ __u16 i2s_port); ++ ++#endif /* __LINUX_GBAUDIO_CODEC_H */ +--- /dev/null ++++ b/drivers/greybus/audio_gb.c +@@ -0,0 +1,228 @@ ++/* ++ * Greybus Audio Device Class Protocol helpers ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++#include "greybus_protocols.h" ++#include "operation.h" ++#include "audio_codec.h" ++ ++/* TODO: Split into separate calls */ ++int gb_audio_gb_get_topology(struct gb_connection *connection, ++ struct gb_audio_topology **topology) ++{ ++ struct gb_audio_get_topology_size_response size_resp; ++ struct gb_audio_topology *topo; ++ uint16_t size; ++ int ret; ++ ++ ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE, ++ NULL, 0, &size_resp, sizeof(size_resp)); ++ if (ret) ++ return ret; ++ ++ size = le16_to_cpu(size_resp.size); ++ if (size < sizeof(*topo)) ++ return -ENODATA; ++ ++ topo = kzalloc(size, GFP_KERNEL); ++ if (!topo) ++ return -ENOMEM; ++ ++ ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_TOPOLOGY, NULL, 0, ++ topo, size); ++ if (ret) { ++ kfree(topo); ++ return ret; ++ } ++ ++ *topology = topo; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_get_topology); ++ ++int gb_audio_gb_get_control(struct gb_connection *connection, ++ uint8_t control_id, uint8_t index, ++ struct gb_audio_ctl_elem_value *value) ++{ ++ struct gb_audio_get_control_request req; ++ struct gb_audio_get_control_response resp; ++ int ret; ++ ++ req.control_id = control_id; ++ req.index = index; ++ ++ ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_CONTROL, ++ &req, sizeof(req), &resp, sizeof(resp)); ++ if (ret) ++ return ret; ++ ++ memcpy(value, &resp.value, sizeof(*value)); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_get_control); ++ ++int gb_audio_gb_set_control(struct gb_connection *connection, ++ uint8_t control_id, uint8_t index, ++ struct gb_audio_ctl_elem_value *value) ++{ ++ struct gb_audio_set_control_request req; ++ ++ req.control_id = control_id; ++ req.index = index; ++ memcpy(&req.value, value, sizeof(req.value)); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_CONTROL, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_set_control); ++ ++int gb_audio_gb_enable_widget(struct gb_connection *connection, ++ uint8_t widget_id) ++{ ++ struct gb_audio_enable_widget_request req; ++ ++ req.widget_id = widget_id; ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_ENABLE_WIDGET, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_enable_widget); ++ ++int gb_audio_gb_disable_widget(struct gb_connection *connection, ++ uint8_t widget_id) ++{ ++ struct gb_audio_disable_widget_request req; ++ ++ req.widget_id = widget_id; ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_DISABLE_WIDGET, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_disable_widget); ++ ++int gb_audio_gb_get_pcm(struct gb_connection *connection, uint16_t data_cport, ++ uint32_t *format, uint32_t *rate, uint8_t *channels, ++ uint8_t *sig_bits) ++{ ++ struct gb_audio_get_pcm_request req; ++ struct gb_audio_get_pcm_response resp; ++ int ret; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ ++ ret = gb_operation_sync(connection, GB_AUDIO_TYPE_GET_PCM, ++ &req, sizeof(req), &resp, sizeof(resp)); ++ if (ret) ++ return ret; ++ ++ *format = le32_to_cpu(resp.format); ++ *rate = le32_to_cpu(resp.rate); ++ *channels = resp.channels; ++ *sig_bits = resp.sig_bits; ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_get_pcm); ++ ++int gb_audio_gb_set_pcm(struct gb_connection *connection, uint16_t data_cport, ++ uint32_t format, uint32_t rate, uint8_t channels, ++ uint8_t sig_bits) ++{ ++ struct gb_audio_set_pcm_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ req.format = cpu_to_le32(format); ++ req.rate = cpu_to_le32(rate); ++ req.channels = channels; ++ req.sig_bits = sig_bits; ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_PCM, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_set_pcm); ++ ++int gb_audio_gb_set_tx_data_size(struct gb_connection *connection, ++ uint16_t data_cport, uint16_t size) ++{ ++ struct gb_audio_set_tx_data_size_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ req.size = cpu_to_le16(size); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_TX_DATA_SIZE, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_set_tx_data_size); ++ ++int gb_audio_gb_activate_tx(struct gb_connection *connection, ++ uint16_t data_cport) ++{ ++ struct gb_audio_activate_tx_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_TX, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_activate_tx); ++ ++int gb_audio_gb_deactivate_tx(struct gb_connection *connection, ++ uint16_t data_cport) ++{ ++ struct gb_audio_deactivate_tx_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_TX, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_tx); ++ ++int gb_audio_gb_set_rx_data_size(struct gb_connection *connection, ++ uint16_t data_cport, uint16_t size) ++{ ++ struct gb_audio_set_rx_data_size_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ req.size = cpu_to_le16(size); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_SET_RX_DATA_SIZE, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_set_rx_data_size); ++ ++int gb_audio_gb_activate_rx(struct gb_connection *connection, ++ uint16_t data_cport) ++{ ++ struct gb_audio_activate_rx_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_ACTIVATE_RX, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_activate_rx); ++ ++int gb_audio_gb_deactivate_rx(struct gb_connection *connection, ++ uint16_t data_cport) ++{ ++ struct gb_audio_deactivate_rx_request req; ++ ++ req.data_cport = cpu_to_le16(data_cport); ++ ++ return gb_operation_sync(connection, GB_AUDIO_TYPE_DEACTIVATE_RX, ++ &req, sizeof(req), NULL, 0); ++} ++EXPORT_SYMBOL_GPL(gb_audio_gb_deactivate_rx); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("greybus:audio-gb"); ++MODULE_DESCRIPTION("Greybus Audio Device Class Protocol library"); ++MODULE_AUTHOR("Mark Greer <mgreer@animalcreek.com>"); +--- /dev/null ++++ b/drivers/greybus/audio_manager.c +@@ -0,0 +1,184 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/string.h> ++#include <linux/sysfs.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/rwlock.h> ++#include <linux/idr.h> ++ ++#include "audio_manager.h" ++#include "audio_manager_private.h" ++ ++static struct kset *manager_kset; ++ ++static LIST_HEAD(modules_list); ++static DECLARE_RWSEM(modules_rwsem); ++static DEFINE_IDA(module_id); ++ ++/* helpers */ ++static struct gb_audio_manager_module *gb_audio_manager_get_locked(int id) ++{ ++ struct gb_audio_manager_module *module; ++ ++ if (id < 0) ++ return NULL; ++ ++ list_for_each_entry(module, &modules_list, list) { ++ if (module->id == id) ++ return module; ++ } ++ ++ return NULL; ++} ++ ++/* public API */ ++int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc) ++{ ++ struct gb_audio_manager_module *module; ++ int id; ++ int err; ++ ++ id = ida_simple_get(&module_id, 0, 0, GFP_KERNEL); ++ err = gb_audio_manager_module_create(&module, manager_kset, ++ id, desc); ++ if (err) { ++ ida_simple_remove(&module_id, id); ++ return err; ++ } ++ ++ /* Add it to the list */ ++ down_write(&modules_rwsem); ++ list_add_tail(&module->list, &modules_list); ++ up_write(&modules_rwsem); ++ ++ return module->id; ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_add); ++ ++int gb_audio_manager_remove(int id) ++{ ++ struct gb_audio_manager_module *module; ++ ++ down_write(&modules_rwsem); ++ ++ module = gb_audio_manager_get_locked(id); ++ if (!module) { ++ up_write(&modules_rwsem); ++ return -EINVAL; ++ } ++ list_del(&module->list); ++ kobject_put(&module->kobj); ++ up_write(&modules_rwsem); ++ ida_simple_remove(&module_id, id); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_remove); ++ ++void gb_audio_manager_remove_all(void) ++{ ++ struct gb_audio_manager_module *module, *next; ++ int is_empty = 1; ++ ++ down_write(&modules_rwsem); ++ ++ list_for_each_entry_safe(module, next, &modules_list, list) { ++ list_del(&module->list); ++ kobject_put(&module->kobj); ++ ida_simple_remove(&module_id, module->id); ++ } ++ ++ is_empty = list_empty(&modules_list); ++ ++ up_write(&modules_rwsem); ++ ++ if (!is_empty) ++ pr_warn("Not all nodes were deleted\n"); ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_remove_all); ++ ++struct gb_audio_manager_module *gb_audio_manager_get_module(int id) ++{ ++ struct gb_audio_manager_module *module; ++ ++ down_read(&modules_rwsem); ++ module = gb_audio_manager_get_locked(id); ++ kobject_get(&module->kobj); ++ up_read(&modules_rwsem); ++ return module; ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_get_module); ++ ++void gb_audio_manager_put_module(struct gb_audio_manager_module *module) ++{ ++ kobject_put(&module->kobj); ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_put_module); ++ ++int gb_audio_manager_dump_module(int id) ++{ ++ struct gb_audio_manager_module *module; ++ ++ down_read(&modules_rwsem); ++ module = gb_audio_manager_get_locked(id); ++ up_read(&modules_rwsem); ++ ++ if (!module) ++ return -EINVAL; ++ ++ gb_audio_manager_module_dump(module); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_dump_module); ++ ++void gb_audio_manager_dump_all(void) ++{ ++ struct gb_audio_manager_module *module; ++ int count = 0; ++ ++ down_read(&modules_rwsem); ++ list_for_each_entry(module, &modules_list, list) { ++ gb_audio_manager_module_dump(module); ++ count++; ++ } ++ up_read(&modules_rwsem); ++ ++ pr_info("Number of connected modules: %d\n", count); ++} ++EXPORT_SYMBOL_GPL(gb_audio_manager_dump_all); ++ ++/* ++ * module init/deinit ++ */ ++static int __init manager_init(void) ++{ ++ manager_kset = kset_create_and_add(GB_AUDIO_MANAGER_NAME, NULL, ++ kernel_kobj); ++ if (!manager_kset) ++ return -ENOMEM; ++ ++#ifdef GB_AUDIO_MANAGER_SYSFS ++ gb_audio_manager_sysfs_init(&manager_kset->kobj); ++#endif ++ ++ return 0; ++} ++ ++static void __exit manager_exit(void) ++{ ++ gb_audio_manager_remove_all(); ++ kset_unregister(manager_kset); ++ ida_destroy(&module_id); ++} ++ ++module_init(manager_init); ++module_exit(manager_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Svetlin Ankov <ankov_svetlin@projectara.com>"); +--- /dev/null ++++ b/drivers/greybus/audio_manager.h +@@ -0,0 +1,83 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef _GB_AUDIO_MANAGER_H_ ++#define _GB_AUDIO_MANAGER_H_ ++ ++#include <linux/kobject.h> ++#include <linux/list.h> ++ ++#define GB_AUDIO_MANAGER_NAME "gb_audio_manager" ++#define GB_AUDIO_MANAGER_MODULE_NAME_LEN 64 ++#define GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "63" ++ ++struct gb_audio_manager_module_descriptor { ++ char name[GB_AUDIO_MANAGER_MODULE_NAME_LEN]; ++ int slot; ++ int vid; ++ int pid; ++ int cport; ++ unsigned int ip_devices; ++ unsigned int op_devices; ++}; ++ ++struct gb_audio_manager_module { ++ struct kobject kobj; ++ struct list_head list; ++ int id; ++ struct gb_audio_manager_module_descriptor desc; ++}; ++ ++/* ++ * Creates a new gb_audio_manager_module_descriptor, using the specified ++ * descriptor. ++ * ++ * Returns a negative result on error, or the id of the newly created module. ++ * ++ */ ++int gb_audio_manager_add(struct gb_audio_manager_module_descriptor *desc); ++ ++/* ++ * Removes a connected gb_audio_manager_module_descriptor for the specified ID. ++ * ++ * Returns zero on success, or a negative value on error. ++ */ ++int gb_audio_manager_remove(int id); ++ ++/* ++ * Removes all connected gb_audio_modules ++ * ++ * Returns zero on success, or a negative value on error. ++ */ ++void gb_audio_manager_remove_all(void); ++ ++/* ++ * Retrieves a gb_audio_manager_module_descriptor for the specified id. ++ * Returns the gb_audio_manager_module_descriptor structure, ++ * or NULL if there is no module with the specified ID. ++ */ ++struct gb_audio_manager_module *gb_audio_manager_get_module(int id); ++ ++/* ++ * Decreases the refcount of the module, obtained by the get function. ++ * Modules are removed via gb_audio_manager_remove ++ */ ++void gb_audio_manager_put_module(struct gb_audio_manager_module *module); ++ ++/* ++ * Dumps the module for the specified id ++ * Return 0 on success ++ */ ++int gb_audio_manager_dump_module(int id); ++ ++/* ++ * Dumps all connected modules ++ */ ++void gb_audio_manager_dump_all(void); ++ ++#endif /* _GB_AUDIO_MANAGER_H_ */ +--- /dev/null ++++ b/drivers/greybus/audio_manager_module.c +@@ -0,0 +1,258 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/slab.h> ++ ++#include "audio_manager.h" ++#include "audio_manager_private.h" ++ ++#define to_gb_audio_module_attr(x) \ ++ container_of(x, struct gb_audio_manager_module_attribute, attr) ++#define to_gb_audio_module(x) \ ++ container_of(x, struct gb_audio_manager_module, kobj) ++ ++struct gb_audio_manager_module_attribute { ++ struct attribute attr; ++ ssize_t (*show)(struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, ++ char *buf); ++ ssize_t (*store)(struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, ++ const char *buf, size_t count); ++}; ++ ++static ssize_t gb_audio_module_attr_show( ++ struct kobject *kobj, struct attribute *attr, char *buf) ++{ ++ struct gb_audio_manager_module_attribute *attribute; ++ struct gb_audio_manager_module *module; ++ ++ attribute = to_gb_audio_module_attr(attr); ++ module = to_gb_audio_module(kobj); ++ ++ if (!attribute->show) ++ return -EIO; ++ ++ return attribute->show(module, attribute, buf); ++} ++ ++static ssize_t gb_audio_module_attr_store(struct kobject *kobj, ++ struct attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct gb_audio_manager_module_attribute *attribute; ++ struct gb_audio_manager_module *module; ++ ++ attribute = to_gb_audio_module_attr(attr); ++ module = to_gb_audio_module(kobj); ++ ++ if (!attribute->store) ++ return -EIO; ++ ++ return attribute->store(module, attribute, buf, len); ++} ++ ++static const struct sysfs_ops gb_audio_module_sysfs_ops = { ++ .show = gb_audio_module_attr_show, ++ .store = gb_audio_module_attr_store, ++}; ++ ++static void gb_audio_module_release(struct kobject *kobj) ++{ ++ struct gb_audio_manager_module *module = to_gb_audio_module(kobj); ++ ++ pr_info("Destroying audio module #%d\n", module->id); ++ /* TODO -> delete from list */ ++ kfree(module); ++} ++ ++static ssize_t gb_audio_module_name_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%s", module->desc.name); ++} ++ ++static struct gb_audio_manager_module_attribute gb_audio_module_name_attribute = ++ __ATTR(name, 0664, gb_audio_module_name_show, NULL); ++ ++static ssize_t gb_audio_module_slot_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d", module->desc.slot); ++} ++ ++static struct gb_audio_manager_module_attribute gb_audio_module_slot_attribute = ++ __ATTR(slot, 0664, gb_audio_module_slot_show, NULL); ++ ++static ssize_t gb_audio_module_vid_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d", module->desc.vid); ++} ++ ++static struct gb_audio_manager_module_attribute gb_audio_module_vid_attribute = ++ __ATTR(vid, 0664, gb_audio_module_vid_show, NULL); ++ ++static ssize_t gb_audio_module_pid_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d", module->desc.pid); ++} ++ ++static struct gb_audio_manager_module_attribute gb_audio_module_pid_attribute = ++ __ATTR(pid, 0664, gb_audio_module_pid_show, NULL); ++ ++static ssize_t gb_audio_module_cport_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "%d", module->desc.cport); ++} ++ ++static struct gb_audio_manager_module_attribute ++ gb_audio_module_cport_attribute = ++ __ATTR(cport, 0664, gb_audio_module_cport_show, NULL); ++ ++static ssize_t gb_audio_module_ip_devices_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "0x%X", module->desc.ip_devices); ++} ++ ++static struct gb_audio_manager_module_attribute ++ gb_audio_module_ip_devices_attribute = ++ __ATTR(ip_devices, 0664, gb_audio_module_ip_devices_show, NULL); ++ ++static ssize_t gb_audio_module_op_devices_show( ++ struct gb_audio_manager_module *module, ++ struct gb_audio_manager_module_attribute *attr, char *buf) ++{ ++ return sprintf(buf, "0x%X", module->desc.op_devices); ++} ++ ++static struct gb_audio_manager_module_attribute ++ gb_audio_module_op_devices_attribute = ++ __ATTR(op_devices, 0664, gb_audio_module_op_devices_show, NULL); ++ ++static struct attribute *gb_audio_module_default_attrs[] = { ++ &gb_audio_module_name_attribute.attr, ++ &gb_audio_module_slot_attribute.attr, ++ &gb_audio_module_vid_attribute.attr, ++ &gb_audio_module_pid_attribute.attr, ++ &gb_audio_module_cport_attribute.attr, ++ &gb_audio_module_ip_devices_attribute.attr, ++ &gb_audio_module_op_devices_attribute.attr, ++ NULL, /* need to NULL terminate the list of attributes */ ++}; ++ ++static struct kobj_type gb_audio_module_type = { ++ .sysfs_ops = &gb_audio_module_sysfs_ops, ++ .release = gb_audio_module_release, ++ .default_attrs = gb_audio_module_default_attrs, ++}; ++ ++static void send_add_uevent(struct gb_audio_manager_module *module) ++{ ++ char name_string[128]; ++ char slot_string[64]; ++ char vid_string[64]; ++ char pid_string[64]; ++ char cport_string[64]; ++ char ip_devices_string[64]; ++ char op_devices_string[64]; ++ ++ char *envp[] = { ++ name_string, ++ slot_string, ++ vid_string, ++ pid_string, ++ cport_string, ++ ip_devices_string, ++ op_devices_string, ++ NULL ++ }; ++ ++ snprintf(name_string, 128, "NAME=%s", module->desc.name); ++ snprintf(slot_string, 64, "SLOT=%d", module->desc.slot); ++ snprintf(vid_string, 64, "VID=%d", module->desc.vid); ++ snprintf(pid_string, 64, "PID=%d", module->desc.pid); ++ snprintf(cport_string, 64, "CPORT=%d", module->desc.cport); ++ snprintf(ip_devices_string, 64, "I/P DEVICES=0x%X", ++ module->desc.ip_devices); ++ snprintf(op_devices_string, 64, "O/P DEVICES=0x%X", ++ module->desc.op_devices); ++ ++ kobject_uevent_env(&module->kobj, KOBJ_ADD, envp); ++} ++ ++int gb_audio_manager_module_create( ++ struct gb_audio_manager_module **module, ++ struct kset *manager_kset, ++ int id, struct gb_audio_manager_module_descriptor *desc) ++{ ++ int err; ++ struct gb_audio_manager_module *m; ++ ++ m = kzalloc(sizeof(*m), GFP_ATOMIC); ++ if (!m) ++ return -ENOMEM; ++ ++ /* Initialize the node */ ++ INIT_LIST_HEAD(&m->list); ++ ++ /* Set the module id */ ++ m->id = id; ++ ++ /* Copy the provided descriptor */ ++ memcpy(&m->desc, desc, sizeof(*desc)); ++ ++ /* set the kset */ ++ m->kobj.kset = manager_kset; ++ ++ /* ++ * Initialize and add the kobject to the kernel. All the default files ++ * will be created here. As we have already specified a kset for this ++ * kobject, we don't have to set a parent for the kobject, the kobject ++ * will be placed beneath that kset automatically. ++ */ ++ err = kobject_init_and_add(&m->kobj, &gb_audio_module_type, NULL, "%d", ++ id); ++ if (err) { ++ pr_err("failed initializing kobject for audio module #%d\n", ++ id); ++ kobject_put(&m->kobj); ++ return err; ++ } ++ ++ /* ++ * Notify the object was created ++ */ ++ send_add_uevent(m); ++ ++ *module = m; ++ pr_info("Created audio module #%d\n", id); ++ return 0; ++} ++ ++void gb_audio_manager_module_dump(struct gb_audio_manager_module *module) ++{ ++ pr_info("audio module #%d name=%s slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X o/p devices=0x%X\n", ++ module->id, ++ module->desc.name, ++ module->desc.slot, ++ module->desc.vid, ++ module->desc.pid, ++ module->desc.cport, ++ module->desc.ip_devices, ++ module->desc.op_devices); ++} +--- /dev/null ++++ b/drivers/greybus/audio_manager_private.h +@@ -0,0 +1,28 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef _GB_AUDIO_MANAGER_PRIVATE_H_ ++#define _GB_AUDIO_MANAGER_PRIVATE_H_ ++ ++#include <linux/kobject.h> ++ ++#include "audio_manager.h" ++ ++int gb_audio_manager_module_create( ++ struct gb_audio_manager_module **module, ++ struct kset *manager_kset, ++ int id, struct gb_audio_manager_module_descriptor *desc); ++ ++/* module destroyed via kobject_put */ ++ ++void gb_audio_manager_module_dump(struct gb_audio_manager_module *module); ++ ++/* sysfs control */ ++void gb_audio_manager_sysfs_init(struct kobject *kobj); ++ ++#endif /* _GB_AUDIO_MANAGER_PRIVATE_H_ */ +--- /dev/null ++++ b/drivers/greybus/audio_manager_sysfs.c +@@ -0,0 +1,102 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2015-2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/string.h> ++#include <linux/sysfs.h> ++ ++#include "audio_manager.h" ++#include "audio_manager_private.h" ++ ++static ssize_t manager_sysfs_add_store( ++ struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct gb_audio_manager_module_descriptor desc = { {0} }; ++ ++ int num = sscanf(buf, ++ "name=%" GB_AUDIO_MANAGER_MODULE_NAME_LEN_SSCANF "s " ++ "slot=%d vid=%d pid=%d cport=%d i/p devices=0x%X" ++ "o/p devices=0x%X", ++ desc.name, &desc.slot, &desc.vid, &desc.pid, ++ &desc.cport, &desc.ip_devices, &desc.op_devices); ++ ++ if (num != 7) ++ return -EINVAL; ++ ++ num = gb_audio_manager_add(&desc); ++ if (num < 0) ++ return -EINVAL; ++ ++ return count; ++} ++ ++static struct kobj_attribute manager_add_attribute = ++ __ATTR(add, 0664, NULL, manager_sysfs_add_store); ++ ++static ssize_t manager_sysfs_remove_store( ++ struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int id; ++ ++ int num = sscanf(buf, "%d", &id); ++ ++ if (num != 1) ++ return -EINVAL; ++ ++ num = gb_audio_manager_remove(id); ++ if (num) ++ return num; ++ ++ return count; ++} ++ ++static struct kobj_attribute manager_remove_attribute = ++ __ATTR(remove, 0664, NULL, manager_sysfs_remove_store); ++ ++static ssize_t manager_sysfs_dump_store( ++ struct kobject *kobj, struct kobj_attribute *attr, ++ const char *buf, size_t count) ++{ ++ int id; ++ ++ int num = sscanf(buf, "%d", &id); ++ ++ if (num == 1) { ++ num = gb_audio_manager_dump_module(id); ++ if (num) ++ return num; ++ } else if (!strncmp("all", buf, 3)) ++ gb_audio_manager_dump_all(); ++ else ++ return -EINVAL; ++ ++ return count; ++} ++ ++static struct kobj_attribute manager_dump_attribute = ++ __ATTR(dump, 0664, NULL, manager_sysfs_dump_store); ++ ++static void manager_sysfs_init_attribute( ++ struct kobject *kobj, struct kobj_attribute *kattr) ++{ ++ int err; ++ ++ err = sysfs_create_file(kobj, &kattr->attr); ++ if (err) { ++ pr_warn("creating the sysfs entry for %s failed: %d\n", ++ kattr->attr.name, err); ++ } ++} ++ ++void gb_audio_manager_sysfs_init(struct kobject *kobj) ++{ ++ manager_sysfs_init_attribute(kobj, &manager_add_attribute); ++ manager_sysfs_init_attribute(kobj, &manager_remove_attribute); ++ manager_sysfs_init_attribute(kobj, &manager_dump_attribute); ++} +--- /dev/null ++++ b/drivers/greybus/audio_module.c +@@ -0,0 +1,482 @@ ++/* ++ * Greybus audio driver ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <sound/soc.h> ++#include <sound/pcm_params.h> ++ ++#include "audio_codec.h" ++#include "audio_apbridgea.h" ++#include "audio_manager.h" ++ ++/* ++ * gb_snd management functions ++ */ ++ ++static int gbaudio_request_jack(struct gbaudio_module_info *module, ++ struct gb_audio_jack_event_request *req) ++{ ++ int report; ++ struct snd_jack *jack = module->headset_jack.jack; ++ struct snd_jack *btn_jack = module->button_jack.jack; ++ ++ if (!jack) { ++ dev_err_ratelimited(module->dev, ++ "Invalid jack event received:type: %u, event: %u\n", ++ req->jack_attribute, req->event); ++ return -EINVAL; ++ } ++ ++ dev_warn_ratelimited(module->dev, ++ "Jack Event received: type: %u, event: %u\n", ++ req->jack_attribute, req->event); ++ ++ if (req->event == GB_AUDIO_JACK_EVENT_REMOVAL) { ++ module->jack_type = 0; ++ if (btn_jack && module->button_status) { ++ snd_soc_jack_report(&module->button_jack, 0, ++ module->button_mask); ++ module->button_status = 0; ++ } ++ snd_soc_jack_report(&module->headset_jack, 0, ++ module->jack_mask); ++ return 0; ++ } ++ ++ report = req->jack_attribute & module->jack_mask; ++ if (!report) { ++ dev_err_ratelimited(module->dev, ++ "Invalid jack event received:type: %u, event: %u\n", ++ req->jack_attribute, req->event); ++ return -EINVAL; ++ } ++ ++ if (module->jack_type) ++ dev_warn_ratelimited(module->dev, ++ "Modifying jack from %d to %d\n", ++ module->jack_type, report); ++ ++ module->jack_type = report; ++ snd_soc_jack_report(&module->headset_jack, report, module->jack_mask); ++ ++ return 0; ++} ++ ++static int gbaudio_request_button(struct gbaudio_module_info *module, ++ struct gb_audio_button_event_request *req) ++{ ++ int soc_button_id, report; ++ struct snd_jack *btn_jack = module->button_jack.jack; ++ ++ if (!btn_jack) { ++ dev_err_ratelimited(module->dev, ++ "Invalid button event received:type: %u, event: %u\n", ++ req->button_id, req->event); ++ return -EINVAL; ++ } ++ ++ dev_warn_ratelimited(module->dev, ++ "Button Event received: id: %u, event: %u\n", ++ req->button_id, req->event); ++ ++ /* currently supports 4 buttons only */ ++ if (!module->jack_type) { ++ dev_err_ratelimited(module->dev, ++ "Jack not present. Bogus event!!\n"); ++ return -EINVAL; ++ } ++ ++ report = module->button_status & module->button_mask; ++ soc_button_id = 0; ++ ++ switch (req->button_id) { ++ case 1: ++ soc_button_id = SND_JACK_BTN_0 & module->button_mask; ++ break; ++ ++ case 2: ++ soc_button_id = SND_JACK_BTN_1 & module->button_mask; ++ break; ++ ++ case 3: ++ soc_button_id = SND_JACK_BTN_2 & module->button_mask; ++ break; ++ ++ case 4: ++ soc_button_id = SND_JACK_BTN_3 & module->button_mask; ++ break; ++ } ++ ++ if (!soc_button_id) { ++ dev_err_ratelimited(module->dev, ++ "Invalid button request received\n"); ++ return -EINVAL; ++ } ++ ++ if (req->event == GB_AUDIO_BUTTON_EVENT_PRESS) ++ report = report | soc_button_id; ++ else ++ report = report & ~soc_button_id; ++ ++ module->button_status = report; ++ ++ snd_soc_jack_report(&module->button_jack, report, module->button_mask); ++ ++ return 0; ++} ++ ++static int gbaudio_request_stream(struct gbaudio_module_info *module, ++ struct gb_audio_streaming_event_request *req) ++{ ++ dev_warn(module->dev, "Audio Event received: cport: %u, event: %u\n", ++ req->data_cport, req->event); ++ ++ return 0; ++} ++ ++static int gbaudio_codec_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gbaudio_module_info *module = ++ greybus_get_drvdata(connection->bundle); ++ struct gb_operation_msg_hdr *header = op->request->header; ++ struct gb_audio_streaming_event_request *stream_req; ++ struct gb_audio_jack_event_request *jack_req; ++ struct gb_audio_button_event_request *button_req; ++ int ret; ++ ++ switch (header->type) { ++ case GB_AUDIO_TYPE_STREAMING_EVENT: ++ stream_req = op->request->payload; ++ ret = gbaudio_request_stream(module, stream_req); ++ break; ++ ++ case GB_AUDIO_TYPE_JACK_EVENT: ++ jack_req = op->request->payload; ++ ret = gbaudio_request_jack(module, jack_req); ++ break; ++ ++ case GB_AUDIO_TYPE_BUTTON_EVENT: ++ button_req = op->request->payload; ++ ret = gbaudio_request_button(module, button_req); ++ break; ++ ++ default: ++ dev_err_ratelimited(&connection->bundle->dev, ++ "Invalid Audio Event received\n"); ++ return -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static int gb_audio_add_mgmt_connection(struct gbaudio_module_info *gbmodule, ++ struct greybus_descriptor_cport *cport_desc, ++ struct gb_bundle *bundle) ++{ ++ struct gb_connection *connection; ++ ++ /* Management Cport */ ++ if (gbmodule->mgmt_connection) { ++ dev_err(&bundle->dev, ++ "Can't have multiple Management connections\n"); ++ return -ENODEV; ++ } ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gbaudio_codec_request_handler); ++ if (IS_ERR(connection)) ++ return PTR_ERR(connection); ++ ++ greybus_set_drvdata(bundle, gbmodule); ++ gbmodule->mgmt_connection = connection; ++ ++ return 0; ++} ++ ++static int gb_audio_add_data_connection(struct gbaudio_module_info *gbmodule, ++ struct greybus_descriptor_cport *cport_desc, ++ struct gb_bundle *bundle) ++{ ++ struct gb_connection *connection; ++ struct gbaudio_data_connection *dai; ++ ++ dai = devm_kzalloc(gbmodule->dev, sizeof(*dai), GFP_KERNEL); ++ if (!dai) { ++ dev_err(gbmodule->dev, "DAI Malloc failure\n"); ++ return -ENOMEM; ++ } ++ ++ connection = gb_connection_create_offloaded(bundle, ++ le16_to_cpu(cport_desc->id), ++ GB_CONNECTION_FLAG_CSD); ++ if (IS_ERR(connection)) { ++ devm_kfree(gbmodule->dev, dai); ++ return PTR_ERR(connection); ++ } ++ ++ greybus_set_drvdata(bundle, gbmodule); ++ dai->id = 0; ++ dai->data_cport = connection->intf_cport_id; ++ dai->connection = connection; ++ list_add(&dai->list, &gbmodule->data_list); ++ ++ return 0; ++} ++ ++/* ++ * This is the basic hook get things initialized and registered w/ gb ++ */ ++ ++static int gb_audio_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct device *dev = &bundle->dev; ++ struct gbaudio_module_info *gbmodule; ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_audio_manager_module_descriptor desc; ++ struct gbaudio_data_connection *dai, *_dai; ++ int ret, i; ++ struct gb_audio_topology *topology; ++ ++ /* There should be at least one Management and one Data cport */ ++ if (bundle->num_cports < 2) ++ return -ENODEV; ++ ++ /* ++ * There can be only one Management connection and any number of data ++ * connections. ++ */ ++ gbmodule = devm_kzalloc(dev, sizeof(*gbmodule), GFP_KERNEL); ++ if (!gbmodule) ++ return -ENOMEM; ++ ++ gbmodule->num_data_connections = bundle->num_cports - 1; ++ INIT_LIST_HEAD(&gbmodule->data_list); ++ INIT_LIST_HEAD(&gbmodule->widget_list); ++ INIT_LIST_HEAD(&gbmodule->ctl_list); ++ INIT_LIST_HEAD(&gbmodule->widget_ctl_list); ++ gbmodule->dev = dev; ++ snprintf(gbmodule->name, NAME_SIZE, "%s.%s", dev->driver->name, ++ dev_name(dev)); ++ greybus_set_drvdata(bundle, gbmodule); ++ ++ /* Create all connections */ ++ for (i = 0; i < bundle->num_cports; i++) { ++ cport_desc = &bundle->cport_desc[i]; ++ ++ switch (cport_desc->protocol_id) { ++ case GREYBUS_PROTOCOL_AUDIO_MGMT: ++ ret = gb_audio_add_mgmt_connection(gbmodule, cport_desc, ++ bundle); ++ if (ret) ++ goto destroy_connections; ++ break; ++ case GREYBUS_PROTOCOL_AUDIO_DATA: ++ ret = gb_audio_add_data_connection(gbmodule, cport_desc, ++ bundle); ++ if (ret) ++ goto destroy_connections; ++ break; ++ default: ++ dev_err(dev, "Unsupported protocol: 0x%02x\n", ++ cport_desc->protocol_id); ++ ret = -ENODEV; ++ goto destroy_connections; ++ } ++ } ++ ++ /* There must be a management cport */ ++ if (!gbmodule->mgmt_connection) { ++ ret = -EINVAL; ++ dev_err(dev, "Missing management connection\n"); ++ goto destroy_connections; ++ } ++ ++ /* Initialize management connection */ ++ ret = gb_connection_enable(gbmodule->mgmt_connection); ++ if (ret) { ++ dev_err(dev, "%d: Error while enabling mgmt connection\n", ret); ++ goto destroy_connections; ++ } ++ gbmodule->dev_id = gbmodule->mgmt_connection->intf->interface_id; ++ ++ /* ++ * FIXME: malloc for topology happens via audio_gb driver ++ * should be done within codec driver itself ++ */ ++ ret = gb_audio_gb_get_topology(gbmodule->mgmt_connection, &topology); ++ if (ret) { ++ dev_err(dev, "%d:Error while fetching topology\n", ret); ++ goto disable_connection; ++ } ++ ++ /* process topology data */ ++ ret = gbaudio_tplg_parse_data(gbmodule, topology); ++ if (ret) { ++ dev_err(dev, "%d:Error while parsing topology data\n", ++ ret); ++ goto free_topology; ++ } ++ gbmodule->topology = topology; ++ ++ /* Initialize data connections */ ++ list_for_each_entry(dai, &gbmodule->data_list, list) { ++ ret = gb_connection_enable(dai->connection); ++ if (ret) { ++ dev_err(dev, ++ "%d:Error while enabling %d:data connection\n", ++ ret, dai->data_cport); ++ goto disable_data_connection; ++ } ++ } ++ ++ /* register module with gbcodec */ ++ ret = gbaudio_register_module(gbmodule); ++ if (ret) ++ goto disable_data_connection; ++ ++ /* inform above layer for uevent */ ++ dev_dbg(dev, "Inform set_event:%d to above layer\n", 1); ++ /* prepare for the audio manager */ ++ strlcpy(desc.name, gbmodule->name, GB_AUDIO_MANAGER_MODULE_NAME_LEN); ++ desc.slot = 1; /* todo */ ++ desc.vid = 2; /* todo */ ++ desc.pid = 3; /* todo */ ++ desc.cport = gbmodule->dev_id; ++ desc.op_devices = gbmodule->op_devices; ++ desc.ip_devices = gbmodule->ip_devices; ++ gbmodule->manager_id = gb_audio_manager_add(&desc); ++ ++ dev_dbg(dev, "Add GB Audio device:%s\n", gbmodule->name); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++disable_data_connection: ++ list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) ++ gb_connection_disable(dai->connection); ++ gbaudio_tplg_release(gbmodule); ++ gbmodule->topology = NULL; ++ ++free_topology: ++ kfree(topology); ++ ++disable_connection: ++ gb_connection_disable(gbmodule->mgmt_connection); ++ ++destroy_connections: ++ list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) { ++ gb_connection_destroy(dai->connection); ++ list_del(&dai->list); ++ devm_kfree(dev, dai); ++ } ++ ++ if (gbmodule->mgmt_connection) ++ gb_connection_destroy(gbmodule->mgmt_connection); ++ ++ devm_kfree(dev, gbmodule); ++ ++ return ret; ++} ++ ++static void gb_audio_disconnect(struct gb_bundle *bundle) ++{ ++ struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); ++ struct gbaudio_data_connection *dai, *_dai; ++ ++ gb_pm_runtime_get_sync(bundle); ++ ++ /* cleanup module related resources first */ ++ gbaudio_unregister_module(gbmodule); ++ ++ /* inform uevent to above layers */ ++ gb_audio_manager_remove(gbmodule->manager_id); ++ ++ gbaudio_tplg_release(gbmodule); ++ kfree(gbmodule->topology); ++ gbmodule->topology = NULL; ++ gb_connection_disable(gbmodule->mgmt_connection); ++ list_for_each_entry_safe(dai, _dai, &gbmodule->data_list, list) { ++ gb_connection_disable(dai->connection); ++ gb_connection_destroy(dai->connection); ++ list_del(&dai->list); ++ devm_kfree(gbmodule->dev, dai); ++ } ++ gb_connection_destroy(gbmodule->mgmt_connection); ++ gbmodule->mgmt_connection = NULL; ++ ++ devm_kfree(&bundle->dev, gbmodule); ++} ++ ++static const struct greybus_bundle_id gb_audio_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_audio_id_table); ++ ++#ifdef CONFIG_PM ++static int gb_audio_suspend(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); ++ struct gbaudio_data_connection *dai; ++ ++ list_for_each_entry(dai, &gbmodule->data_list, list) ++ gb_connection_disable(dai->connection); ++ ++ gb_connection_disable(gbmodule->mgmt_connection); ++ ++ return 0; ++} ++ ++static int gb_audio_resume(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gbaudio_module_info *gbmodule = greybus_get_drvdata(bundle); ++ struct gbaudio_data_connection *dai; ++ int ret; ++ ++ ret = gb_connection_enable(gbmodule->mgmt_connection); ++ if (ret) { ++ dev_err(dev, "%d:Error while enabling mgmt connection\n", ret); ++ return ret; ++ } ++ ++ list_for_each_entry(dai, &gbmodule->data_list, list) { ++ ret = gb_connection_enable(dai->connection); ++ if (ret) { ++ dev_err(dev, ++ "%d:Error while enabling %d:data connection\n", ++ ret, dai->data_cport); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_audio_pm_ops = { ++ SET_RUNTIME_PM_OPS(gb_audio_suspend, gb_audio_resume, NULL) ++}; ++ ++static struct greybus_driver gb_audio_driver = { ++ .name = "gb-audio", ++ .probe = gb_audio_probe, ++ .disconnect = gb_audio_disconnect, ++ .id_table = gb_audio_id_table, ++ .driver.pm = &gb_audio_pm_ops, ++}; ++module_greybus_driver(gb_audio_driver); ++ ++MODULE_DESCRIPTION("Greybus Audio module driver"); ++MODULE_AUTHOR("Vaibhav Agarwal <vaibhav.agarwal@linaro.org>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:gbaudio-module"); +--- /dev/null ++++ b/drivers/greybus/audio_topology.c +@@ -0,0 +1,1442 @@ ++/* ++ * Greybus audio driver ++ * Copyright 2015-2016 Google Inc. ++ * Copyright 2015-2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "audio_codec.h" ++#include "greybus_protocols.h" ++ ++#define GBAUDIO_INVALID_ID 0xFF ++ ++/* mixer control */ ++struct gb_mixer_control { ++ int min, max; ++ unsigned int reg, rreg, shift, rshift, invert; ++}; ++ ++struct gbaudio_ctl_pvt { ++ unsigned int ctl_id; ++ unsigned int data_cport; ++ unsigned int access; ++ unsigned int vcount; ++ struct gb_audio_ctl_elem_info *info; ++}; ++ ++static struct gbaudio_module_info *find_gb_module( ++ struct gbaudio_codec_info *codec, ++ char const *name) ++{ ++ int dev_id, ret; ++ char begin[NAME_SIZE]; ++ struct gbaudio_module_info *module; ++ ++ if (!name) ++ return NULL; ++ ++ ret = sscanf(name, "%s %d", begin, &dev_id); ++ dev_dbg(codec->dev, "%s:Find module#%d\n", __func__, dev_id); ++ ++ mutex_lock(&codec->lock); ++ list_for_each_entry(module, &codec->module_list, list) { ++ if (module->dev_id == dev_id) { ++ mutex_unlock(&codec->lock); ++ return module; ++ } ++ } ++ mutex_unlock(&codec->lock); ++ dev_warn(codec->dev, "%s: module#%d missing in codec list\n", name, ++ dev_id); ++ return NULL; ++} ++ ++static const char *gbaudio_map_controlid(struct gbaudio_module_info *module, ++ __u8 control_id, __u8 index) ++{ ++ struct gbaudio_control *control; ++ ++ if (control_id == GBAUDIO_INVALID_ID) ++ return NULL; ++ ++ list_for_each_entry(control, &module->ctl_list, list) { ++ if (control->id == control_id) { ++ if (index == GBAUDIO_INVALID_ID) ++ return control->name; ++ if (index >= control->items) ++ return NULL; ++ return control->texts[index]; ++ } ++ } ++ list_for_each_entry(control, &module->widget_ctl_list, list) { ++ if (control->id == control_id) { ++ if (index == GBAUDIO_INVALID_ID) ++ return control->name; ++ if (index >= control->items) ++ return NULL; ++ return control->texts[index]; ++ } ++ } ++ return NULL; ++} ++ ++static int gbaudio_map_controlname(struct gbaudio_module_info *module, ++ const char *name) ++{ ++ struct gbaudio_control *control; ++ ++ list_for_each_entry(control, &module->ctl_list, list) { ++ if (!strncmp(control->name, name, NAME_SIZE)) ++ return control->id; ++ } ++ ++ dev_warn(module->dev, "%s: missing in modules controls list\n", name); ++ ++ return -EINVAL; ++} ++ ++static int gbaudio_map_wcontrolname(struct gbaudio_module_info *module, ++ const char *name) ++{ ++ struct gbaudio_control *control; ++ ++ list_for_each_entry(control, &module->widget_ctl_list, list) { ++ if (!strncmp(control->wname, name, NAME_SIZE)) ++ return control->id; ++ } ++ dev_warn(module->dev, "%s: missing in modules controls list\n", name); ++ ++ return -EINVAL; ++} ++ ++static int gbaudio_map_widgetname(struct gbaudio_module_info *module, ++ const char *name) ++{ ++ struct gbaudio_widget *widget; ++ list_for_each_entry(widget, &module->widget_list, list) { ++ if (!strncmp(widget->name, name, NAME_SIZE)) ++ return widget->id; ++ } ++ dev_warn(module->dev, "%s: missing in modules widgets list\n", name); ++ ++ return -EINVAL; ++} ++ ++static const char *gbaudio_map_widgetid(struct gbaudio_module_info *module, ++ __u8 widget_id) ++{ ++ struct gbaudio_widget *widget; ++ ++ list_for_each_entry(widget, &module->widget_list, list) { ++ if (widget->id == widget_id) ++ return widget->name; ++ } ++ return NULL; ++} ++ ++static const char **gb_generate_enum_strings(struct gbaudio_module_info *gb, ++ struct gb_audio_enumerated *gbenum) ++{ ++ const char **strings; ++ int i; ++ __u8 *data; ++ ++ strings = devm_kzalloc(gb->dev, sizeof(char *) * gbenum->items, ++ GFP_KERNEL); ++ data = gbenum->names; ++ ++ for (i = 0; i < gbenum->items; i++) { ++ strings[i] = (const char *)data; ++ while (*data != '\0') ++ data++; ++ data++; ++ } ++ ++ return strings; ++} ++ ++static int gbcodec_mixer_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ unsigned int max; ++ const char *name; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_info *info; ++ struct gbaudio_module_info *module; ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ ++ if (!info) { ++ dev_err(module->dev, "NULL info for %s\n", uinfo->id.name); ++ return -EINVAL; ++ } ++ ++ /* update uinfo */ ++ uinfo->access = data->access; ++ uinfo->count = data->vcount; ++ uinfo->type = (snd_ctl_elem_type_t)info->type; ++ ++ switch (info->type) { ++ case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: ++ case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: ++ uinfo->value.integer.min = info->value.integer.min; ++ uinfo->value.integer.max = info->value.integer.max; ++ break; ++ case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: ++ max = info->value.enumerated.items; ++ uinfo->value.enumerated.items = max; ++ if (uinfo->value.enumerated.item > max - 1) ++ uinfo->value.enumerated.item = max - 1; ++ module = find_gb_module(gbcodec, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ name = gbaudio_map_controlid(module, data->ctl_id, ++ uinfo->value.enumerated.item); ++ strlcpy(uinfo->value.enumerated.name, name, NAME_SIZE); ++ break; ++ default: ++ dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", ++ info->type, kcontrol->id.name); ++ break; ++ } ++ return 0; ++} ++ ++static int gbcodec_mixer_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret; ++ struct gb_audio_ctl_elem_info *info; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ return ret; ++ } ++ ++ /* update ucontrol */ ++ switch (info->type) { ++ case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: ++ case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: ++ ucontrol->value.integer.value[0] = ++ gbvalue.value.integer_value[0]; ++ if (data->vcount == 2) ++ ucontrol->value.integer.value[1] = ++ gbvalue.value.integer_value[1]; ++ break; ++ case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: ++ ucontrol->value.enumerated.item[0] = ++ gbvalue.value.enumerated_item[0]; ++ if (data->vcount == 2) ++ ucontrol->value.enumerated.item[1] = ++ gbvalue.value.enumerated_item[1]; ++ break; ++ default: ++ dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", ++ info->type, kcontrol->id.name); ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int gbcodec_mixer_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret = 0; ++ struct gb_audio_ctl_elem_info *info; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ bundle = to_gb_bundle(module->dev); ++ ++ /* update ucontrol */ ++ switch (info->type) { ++ case GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN: ++ case GB_AUDIO_CTL_ELEM_TYPE_INTEGER: ++ gbvalue.value.integer_value[0] = ++ ucontrol->value.integer.value[0]; ++ if (data->vcount == 2) ++ gbvalue.value.integer_value[1] = ++ ucontrol->value.integer.value[1]; ++ break; ++ case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: ++ gbvalue.value.enumerated_item[0] = ++ ucontrol->value.enumerated.item[0]; ++ if (data->vcount == 2) ++ gbvalue.value.enumerated_item[1] = ++ ucontrol->value.enumerated.item[1]; ++ break; ++ default: ++ dev_err(codec->dev, "Invalid type: %d for %s:kcontrol\n", ++ info->type, kcontrol->id.name); ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret) ++ return ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_set_control(module->mgmt_connection, data->ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ } ++ ++ return ret; ++} ++ ++#define SOC_MIXER_GB(xname, kcount, data) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .count = kcount, .info = gbcodec_mixer_ctl_info, \ ++ .get = gbcodec_mixer_ctl_get, .put = gbcodec_mixer_ctl_put, \ ++ .private_value = (unsigned long)data } ++ ++/* ++ * although below callback functions seems redundant to above functions. ++ * same are kept to allow provision for different handling in case ++ * of DAPM related sequencing, etc. ++ */ ++static int gbcodec_mixer_dapm_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ int platform_max, platform_min; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_info *info; ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct snd_soc_codec *codec = widget->codec; ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ ++ /* update uinfo */ ++ platform_max = info->value.integer.max; ++ platform_min = info->value.integer.min; ++ ++ if (platform_max == 1 && ++ !strnstr(kcontrol->id.name, " Volume", NAME_SIZE)) ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ else ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ ++ uinfo->count = data->vcount; ++ uinfo->value.integer.min = 0; ++ if (info->value.integer.min < 0 && ++ (uinfo->type == SNDRV_CTL_ELEM_TYPE_INTEGER)) ++ uinfo->value.integer.max = platform_max - platform_min; ++ else ++ uinfo->value.integer.max = platform_max; ++ ++ return 0; ++} ++ ++static int gbcodec_mixer_dapm_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret; ++ struct gb_audio_ctl_elem_info *info; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct snd_soc_codec *codec = widget->codec; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ bundle = to_gb_bundle(module->dev); ++ ++ if (data->vcount == 2) ++ dev_warn(widget->dapm->dev, ++ "GB: Control '%s' is stereo, which is not supported\n", ++ kcontrol->id.name); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_get_control(module->mgmt_connection, data->ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ return ret; ++ } ++ /* update ucontrol */ ++ ucontrol->value.integer.value[0] = gbvalue.value.integer_value[0]; ++ ++ return ret; ++} ++ ++static int gbcodec_mixer_dapm_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret, wi, max, connect; ++ unsigned int mask, val; ++ struct gb_audio_ctl_elem_info *info; ++ struct gbaudio_ctl_pvt *data; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct snd_soc_codec *codec = widget->codec; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ dev_dbg(codec->dev, "Entered %s:%s\n", __func__, kcontrol->id.name); ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ data = (struct gbaudio_ctl_pvt *)kcontrol->private_value; ++ info = (struct gb_audio_ctl_elem_info *)data->info; ++ bundle = to_gb_bundle(module->dev); ++ ++ if (data->vcount == 2) ++ dev_warn(widget->dapm->dev, ++ "GB: Control '%s' is stereo, which is not supported\n", ++ kcontrol->id.name); ++ ++ max = info->value.integer.max; ++ mask = (1 << fls(max)) - 1; ++ val = (ucontrol->value.integer.value[0] & mask); ++ connect = !!val; ++ ++ /* update ucontrol */ ++ if (gbvalue.value.integer_value[0] != val) { ++ for (wi = 0; wi < wlist->num_widgets; wi++) { ++ widget = wlist->widgets[wi]; ++ ++ widget->value = val; ++ widget->dapm->update = NULL; ++ snd_soc_dapm_mixer_update_power(widget, kcontrol, ++ connect); ++ } ++ gbvalue.value.integer_value[0] = ++ ucontrol->value.integer.value[0]; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_set_control(module->mgmt_connection, ++ data->ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, ++ "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ } ++ } ++ ++ return ret; ++} ++ ++#define SOC_DAPM_MIXER_GB(xname, kcount, data) \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \ ++ .count = kcount, .info = gbcodec_mixer_dapm_ctl_info, \ ++ .get = gbcodec_mixer_dapm_ctl_get, .put = gbcodec_mixer_dapm_ctl_put, \ ++ .private_value = (unsigned long)data} ++ ++static int gbcodec_event_spk(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ /* Ensure GB speaker is connected */ ++ ++ return 0; ++} ++ ++static int gbcodec_event_hp(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ /* Ensure GB module supports jack slot */ ++ ++ return 0; ++} ++ ++static int gbcodec_event_int_mic(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *k, int event) ++{ ++ /* Ensure GB module supports jack slot */ ++ ++ return 0; ++} ++ ++static int gbaudio_validate_kcontrol_count(struct gb_audio_widget *w) ++{ ++ int ret = 0; ++ ++ switch (w->type) { ++ case snd_soc_dapm_spk: ++ case snd_soc_dapm_hp: ++ case snd_soc_dapm_mic: ++ case snd_soc_dapm_output: ++ case snd_soc_dapm_input: ++ if (w->ncontrols) ++ ret = -EINVAL; ++ break; ++ case snd_soc_dapm_switch: ++ case snd_soc_dapm_mux: ++ if (w->ncontrols != 1) ++ ret = -EINVAL; ++ break; ++ default: ++ break; ++ } ++ ++ return ret; ++} ++ ++static int gbcodec_enum_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret, ctl_id; ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); ++ if (ctl_id < 0) ++ return -EINVAL; ++ ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ return ret; ++ } ++ ++ ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; ++ if (e->shift_l != e->shift_r) ++ ucontrol->value.enumerated.item[1] = ++ gbvalue.value.enumerated_item[1]; ++ ++ return 0; ++} ++ ++static int gbcodec_enum_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret, ctl_id; ++ struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct gb_bundle *bundle; ++ ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ ctl_id = gbaudio_map_controlname(module, kcontrol->id.name); ++ if (ctl_id < 0) ++ return -EINVAL; ++ ++ if (ucontrol->value.enumerated.item[0] > e->max - 1) ++ return -EINVAL; ++ gbvalue.value.enumerated_item[0] = ucontrol->value.enumerated.item[0]; ++ ++ if (e->shift_l != e->shift_r) { ++ if (ucontrol->value.enumerated.item[1] > e->max - 1) ++ return -EINVAL; ++ gbvalue.value.enumerated_item[1] = ++ ucontrol->value.enumerated.item[1]; ++ } ++ ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ } ++ ++ return ret; ++} ++ ++static int gbaudio_tplg_create_enum_kctl(struct gbaudio_module_info *gb, ++ struct snd_kcontrol_new *kctl, ++ struct gb_audio_control *ctl) ++{ ++ struct soc_enum *gbe; ++ struct gb_audio_enumerated *gb_enum; ++ int i; ++ ++ gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); ++ if (!gbe) ++ return -ENOMEM; ++ ++ gb_enum = &ctl->info.value.enumerated; ++ ++ /* since count=1, and reg is dummy */ ++ gbe->max = gb_enum->items; ++ gbe->texts = gb_generate_enum_strings(gb, gb_enum); ++ ++ /* debug enum info */ ++ dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, ++ gb_enum->names_length); ++ for (i = 0; i < gb_enum->items; i++) ++ dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); ++ ++ *kctl = (struct snd_kcontrol_new) ++ SOC_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_ctl_get, ++ gbcodec_enum_ctl_put); ++ return 0; ++} ++ ++static int gbaudio_tplg_create_kcontrol(struct gbaudio_module_info *gb, ++ struct snd_kcontrol_new *kctl, ++ struct gb_audio_control *ctl) ++{ ++ int ret = 0; ++ struct gbaudio_ctl_pvt *ctldata; ++ ++ switch (ctl->iface) { ++ case SNDRV_CTL_ELEM_IFACE_MIXER: ++ switch (ctl->info.type) { ++ case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: ++ ret = gbaudio_tplg_create_enum_kctl(gb, kctl, ctl); ++ break; ++ default: ++ ctldata = devm_kzalloc(gb->dev, ++ sizeof(struct gbaudio_ctl_pvt), ++ GFP_KERNEL); ++ if (!ctldata) ++ return -ENOMEM; ++ ctldata->ctl_id = ctl->id; ++ ctldata->data_cport = ctl->data_cport; ++ ctldata->access = ctl->access; ++ ctldata->vcount = ctl->count_values; ++ ctldata->info = &ctl->info; ++ *kctl = (struct snd_kcontrol_new) ++ SOC_MIXER_GB(ctl->name, ctl->count, ctldata); ++ ctldata = NULL; ++ break; ++ } ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ dev_dbg(gb->dev, "%s:%d control created\n", ctl->name, ctl->id); ++ return ret; ++} ++ ++static int gbcodec_enum_dapm_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret, ctl_id; ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct gbaudio_module_info *module; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct snd_soc_codec *codec = widget->codec; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ struct gb_bundle *bundle; ++ ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); ++ if (ctl_id < 0) ++ return -EINVAL; ++ ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ return ret; ++ } ++ ++ ucontrol->value.enumerated.item[0] = gbvalue.value.enumerated_item[0]; ++ if (e->shift_l != e->shift_r) ++ ucontrol->value.enumerated.item[1] = ++ gbvalue.value.enumerated_item[1]; ++ ++ return 0; ++} ++ ++static int gbcodec_enum_dapm_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ int ret, wi, ctl_id; ++ unsigned int val, mux, change; ++ unsigned int mask; ++ struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol); ++ struct snd_soc_dapm_widget *widget = wlist->widgets[0]; ++ struct gb_audio_ctl_elem_value gbvalue; ++ struct gbaudio_module_info *module; ++ struct snd_soc_codec *codec = widget->codec; ++ struct gbaudio_codec_info *gb = snd_soc_codec_get_drvdata(codec); ++ struct soc_enum *e = (struct soc_enum *)kcontrol->private_value; ++ struct gb_bundle *bundle; ++ ++ if (ucontrol->value.enumerated.item[0] > e->max - 1) ++ return -EINVAL; ++ ++ module = find_gb_module(gb, kcontrol->id.name); ++ if (!module) ++ return -EINVAL; ++ ++ ctl_id = gbaudio_map_wcontrolname(module, kcontrol->id.name); ++ if (ctl_id < 0) ++ return -EINVAL; ++ ++ change = 0; ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_get_control(module->mgmt_connection, ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ return ret; ++ } ++ ++ mux = ucontrol->value.enumerated.item[0]; ++ val = mux << e->shift_l; ++ mask = e->mask << e->shift_l; ++ ++ if (gbvalue.value.enumerated_item[0] != ++ ucontrol->value.enumerated.item[0]) { ++ change = 1; ++ gbvalue.value.enumerated_item[0] = ++ ucontrol->value.enumerated.item[0]; ++ } ++ ++ if (e->shift_l != e->shift_r) { ++ if (ucontrol->value.enumerated.item[1] > e->max - 1) ++ return -EINVAL; ++ val |= ucontrol->value.enumerated.item[1] << e->shift_r; ++ mask |= e->mask << e->shift_r; ++ if (gbvalue.value.enumerated_item[1] != ++ ucontrol->value.enumerated.item[1]) { ++ change = 1; ++ gbvalue.value.enumerated_item[1] = ++ ucontrol->value.enumerated.item[1]; ++ } ++ } ++ ++ if (change) { ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_audio_gb_set_control(module->mgmt_connection, ctl_id, ++ GB_AUDIO_INVALID_INDEX, &gbvalue); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ if (ret) { ++ dev_err_ratelimited(codec->dev, ++ "%d:Error in %s for %s\n", ret, ++ __func__, kcontrol->id.name); ++ } ++ for (wi = 0; wi < wlist->num_widgets; wi++) { ++ widget = wlist->widgets[wi]; ++ ++ widget->value = val; ++ widget->dapm->update = NULL; ++ snd_soc_dapm_mux_update_power(widget, kcontrol, mux, e); ++ } ++ } ++ ++ return change; ++} ++ ++static int gbaudio_tplg_create_enum_ctl(struct gbaudio_module_info *gb, ++ struct snd_kcontrol_new *kctl, ++ struct gb_audio_control *ctl) ++{ ++ struct soc_enum *gbe; ++ struct gb_audio_enumerated *gb_enum; ++ int i; ++ ++ gbe = devm_kzalloc(gb->dev, sizeof(*gbe), GFP_KERNEL); ++ if (!gbe) ++ return -ENOMEM; ++ ++ gb_enum = &ctl->info.value.enumerated; ++ ++ /* since count=1, and reg is dummy */ ++ gbe->max = gb_enum->items; ++ gbe->texts = gb_generate_enum_strings(gb, gb_enum); ++ ++ /* debug enum info */ ++ dev_dbg(gb->dev, "Max:%d, name_length:%d\n", gb_enum->items, ++ gb_enum->names_length); ++ for (i = 0; i < gb_enum->items; i++) ++ dev_dbg(gb->dev, "src[%d]: %s\n", i, gbe->texts[i]); ++ ++ *kctl = (struct snd_kcontrol_new) ++ SOC_DAPM_ENUM_EXT(ctl->name, *gbe, gbcodec_enum_dapm_ctl_get, ++ gbcodec_enum_dapm_ctl_put); ++ return 0; ++} ++ ++static int gbaudio_tplg_create_mixer_ctl(struct gbaudio_module_info *gb, ++ struct snd_kcontrol_new *kctl, ++ struct gb_audio_control *ctl) ++{ ++ struct gbaudio_ctl_pvt *ctldata; ++ ++ ctldata = devm_kzalloc(gb->dev, sizeof(struct gbaudio_ctl_pvt), ++ GFP_KERNEL); ++ if (!ctldata) ++ return -ENOMEM; ++ ctldata->ctl_id = ctl->id; ++ ctldata->data_cport = ctl->data_cport; ++ ctldata->access = ctl->access; ++ ctldata->vcount = ctl->count_values; ++ ctldata->info = &ctl->info; ++ *kctl = (struct snd_kcontrol_new) ++ SOC_DAPM_MIXER_GB(ctl->name, ctl->count, ctldata); ++ ++ return 0; ++} ++ ++static int gbaudio_tplg_create_wcontrol(struct gbaudio_module_info *gb, ++ struct snd_kcontrol_new *kctl, ++ struct gb_audio_control *ctl) ++{ ++ int ret; ++ ++ switch (ctl->iface) { ++ case SNDRV_CTL_ELEM_IFACE_MIXER: ++ switch (ctl->info.type) { ++ case GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED: ++ ret = gbaudio_tplg_create_enum_ctl(gb, kctl, ctl); ++ break; ++ default: ++ ret = gbaudio_tplg_create_mixer_ctl(gb, kctl, ctl); ++ break; ++ } ++ break; ++ default: ++ return -EINVAL; ++ ++ } ++ ++ dev_dbg(gb->dev, "%s:%d DAPM control created, ret:%d\n", ctl->name, ++ ctl->id, ret); ++ return ret; ++} ++ ++static int gbaudio_widget_event(struct snd_soc_dapm_widget *w, ++ struct snd_kcontrol *kcontrol, int event) ++{ ++ int wid; ++ int ret; ++ struct snd_soc_codec *codec = w->codec; ++ struct gbaudio_codec_info *gbcodec = snd_soc_codec_get_drvdata(codec); ++ struct gbaudio_module_info *module; ++ struct gb_bundle *bundle; ++ ++ dev_dbg(codec->dev, "%s %s %d\n", __func__, w->name, event); ++ ++ /* Find relevant module */ ++ module = find_gb_module(gbcodec, w->name); ++ if (!module) ++ return -EINVAL; ++ ++ /* map name to widget id */ ++ wid = gbaudio_map_widgetname(module, w->name); ++ if (wid < 0) { ++ dev_err(codec->dev, "Invalid widget name:%s\n", w->name); ++ return -EINVAL; ++ } ++ ++ bundle = to_gb_bundle(module->dev); ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ switch (event) { ++ case SND_SOC_DAPM_PRE_PMU: ++ ret = gb_audio_gb_enable_widget(module->mgmt_connection, wid); ++ if (!ret) ++ ret = gbaudio_module_update(gbcodec, w, module, 1); ++ break; ++ case SND_SOC_DAPM_POST_PMD: ++ ret = gb_audio_gb_disable_widget(module->mgmt_connection, wid); ++ if (!ret) ++ ret = gbaudio_module_update(gbcodec, w, module, 0); ++ break; ++ } ++ if (ret) ++ dev_err_ratelimited(codec->dev, ++ "%d: widget, event:%d failed:%d\n", wid, ++ event, ret); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int gbaudio_tplg_create_widget(struct gbaudio_module_info *module, ++ struct snd_soc_dapm_widget *dw, ++ struct gb_audio_widget *w, int *w_size) ++{ ++ int i, ret, csize; ++ struct snd_kcontrol_new *widget_kctls; ++ struct gb_audio_control *curr; ++ struct gbaudio_control *control, *_control; ++ size_t size; ++ char temp_name[NAME_SIZE]; ++ ++ ret = gbaudio_validate_kcontrol_count(w); ++ if (ret) { ++ dev_err(module->dev, "Inavlid kcontrol count=%d for %s\n", ++ w->ncontrols, w->name); ++ return ret; ++ } ++ ++ /* allocate memory for kcontrol */ ++ if (w->ncontrols) { ++ size = sizeof(struct snd_kcontrol_new) * w->ncontrols; ++ widget_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); ++ if (!widget_kctls) ++ return -ENOMEM; ++ } ++ ++ *w_size = sizeof(struct gb_audio_widget); ++ ++ /* create relevant kcontrols */ ++ curr = w->ctl; ++ for (i = 0; i < w->ncontrols; i++) { ++ ret = gbaudio_tplg_create_wcontrol(module, &widget_kctls[i], ++ curr); ++ if (ret) { ++ dev_err(module->dev, ++ "%s:%d type widget_ctl not supported\n", ++ curr->name, curr->iface); ++ goto error; ++ } ++ control = devm_kzalloc(module->dev, ++ sizeof(struct gbaudio_control), ++ GFP_KERNEL); ++ if (!control) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ control->id = curr->id; ++ control->name = curr->name; ++ control->wname = w->name; ++ ++ if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { ++ struct gb_audio_enumerated *gbenum = ++ &curr->info.value.enumerated; ++ ++ csize = offsetof(struct gb_audio_control, info); ++ csize += offsetof(struct gb_audio_ctl_elem_info, value); ++ csize += offsetof(struct gb_audio_enumerated, names); ++ csize += gbenum->names_length; ++ control->texts = (const char * const *) ++ gb_generate_enum_strings(module, gbenum); ++ control->items = gbenum->items; ++ } else ++ csize = sizeof(struct gb_audio_control); ++ *w_size += csize; ++ curr = (void *)curr + csize; ++ list_add(&control->list, &module->widget_ctl_list); ++ dev_dbg(module->dev, "%s: control of type %d created\n", ++ widget_kctls[i].name, widget_kctls[i].iface); ++ } ++ ++ /* Prefix dev_id to widget control_name */ ++ strlcpy(temp_name, w->name, NAME_SIZE); ++ snprintf(w->name, NAME_SIZE, "GB %d %s", module->dev_id, temp_name); ++ ++ switch (w->type) { ++ case snd_soc_dapm_spk: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_SPK(w->name, gbcodec_event_spk); ++ module->op_devices |= GBAUDIO_DEVICE_OUT_SPEAKER; ++ break; ++ case snd_soc_dapm_hp: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_HP(w->name, gbcodec_event_hp); ++ module->op_devices |= (GBAUDIO_DEVICE_OUT_WIRED_HEADSET ++ | GBAUDIO_DEVICE_OUT_WIRED_HEADPHONE); ++ module->ip_devices |= GBAUDIO_DEVICE_IN_WIRED_HEADSET; ++ break; ++ case snd_soc_dapm_mic: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_MIC(w->name, gbcodec_event_int_mic); ++ module->ip_devices |= GBAUDIO_DEVICE_IN_BUILTIN_MIC; ++ break; ++ case snd_soc_dapm_output: ++ *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_OUTPUT(w->name); ++ break; ++ case snd_soc_dapm_input: ++ *dw = (struct snd_soc_dapm_widget)SND_SOC_DAPM_INPUT(w->name); ++ break; ++ case snd_soc_dapm_switch: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_SWITCH_E(w->name, SND_SOC_NOPM, 0, 0, ++ widget_kctls, gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ case snd_soc_dapm_pga: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_PGA_E(w->name, SND_SOC_NOPM, 0, 0, NULL, 0, ++ gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ case snd_soc_dapm_mixer: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_MIXER_E(w->name, SND_SOC_NOPM, 0, 0, NULL, ++ 0, gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ case snd_soc_dapm_mux: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_MUX_E(w->name, SND_SOC_NOPM, 0, 0, ++ widget_kctls, gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ case snd_soc_dapm_aif_in: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_AIF_IN_E(w->name, w->sname, 0, ++ SND_SOC_NOPM, ++ 0, 0, gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ case snd_soc_dapm_aif_out: ++ *dw = (struct snd_soc_dapm_widget) ++ SND_SOC_DAPM_AIF_OUT_E(w->name, w->sname, 0, ++ SND_SOC_NOPM, ++ 0, 0, gbaudio_widget_event, ++ SND_SOC_DAPM_PRE_PMU | ++ SND_SOC_DAPM_POST_PMD); ++ break; ++ default: ++ ret = -EINVAL; ++ goto error; ++ } ++ ++ dev_dbg(module->dev, "%s: widget of type %d created\n", dw->name, ++ dw->id); ++ return 0; ++error: ++ list_for_each_entry_safe(control, _control, &module->widget_ctl_list, ++ list) { ++ list_del(&control->list); ++ devm_kfree(module->dev, control); ++ } ++ return ret; ++} ++ ++static int gbaudio_tplg_process_kcontrols(struct gbaudio_module_info *module, ++ struct gb_audio_control *controls) ++{ ++ int i, csize, ret; ++ struct snd_kcontrol_new *dapm_kctls; ++ struct gb_audio_control *curr; ++ struct gbaudio_control *control, *_control; ++ size_t size; ++ char temp_name[NAME_SIZE]; ++ ++ size = sizeof(struct snd_kcontrol_new) * module->num_controls; ++ dapm_kctls = devm_kzalloc(module->dev, size, GFP_KERNEL); ++ if (!dapm_kctls) ++ return -ENOMEM; ++ ++ curr = controls; ++ for (i = 0; i < module->num_controls; i++) { ++ ret = gbaudio_tplg_create_kcontrol(module, &dapm_kctls[i], ++ curr); ++ if (ret) { ++ dev_err(module->dev, "%s:%d type not supported\n", ++ curr->name, curr->iface); ++ goto error; ++ } ++ control = devm_kzalloc(module->dev, sizeof(struct ++ gbaudio_control), ++ GFP_KERNEL); ++ if (!control) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ control->id = curr->id; ++ /* Prefix dev_id to widget_name */ ++ strlcpy(temp_name, curr->name, NAME_SIZE); ++ snprintf(curr->name, NAME_SIZE, "GB %d %s", module->dev_id, ++ temp_name); ++ control->name = curr->name; ++ if (curr->info.type == GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED) { ++ struct gb_audio_enumerated *gbenum = ++ &curr->info.value.enumerated; ++ ++ csize = offsetof(struct gb_audio_control, info); ++ csize += offsetof(struct gb_audio_ctl_elem_info, value); ++ csize += offsetof(struct gb_audio_enumerated, names); ++ csize += gbenum->names_length; ++ control->texts = (const char * const *) ++ gb_generate_enum_strings(module, gbenum); ++ control->items = gbenum->items; ++ } else ++ csize = sizeof(struct gb_audio_control); ++ ++ list_add(&control->list, &module->ctl_list); ++ dev_dbg(module->dev, "%d:%s created of type %d\n", curr->id, ++ curr->name, curr->info.type); ++ curr = (void *)curr + csize; ++ } ++ module->controls = dapm_kctls; ++ ++ return 0; ++error: ++ list_for_each_entry_safe(control, _control, &module->ctl_list, ++ list) { ++ list_del(&control->list); ++ devm_kfree(module->dev, control); ++ } ++ devm_kfree(module->dev, dapm_kctls); ++ return ret; ++} ++ ++static int gbaudio_tplg_process_widgets(struct gbaudio_module_info *module, ++ struct gb_audio_widget *widgets) ++{ ++ int i, ret, w_size; ++ struct snd_soc_dapm_widget *dapm_widgets; ++ struct gb_audio_widget *curr; ++ struct gbaudio_widget *widget, *_widget; ++ size_t size; ++ ++ size = sizeof(struct snd_soc_dapm_widget) * module->num_dapm_widgets; ++ dapm_widgets = devm_kzalloc(module->dev, size, GFP_KERNEL); ++ if (!dapm_widgets) ++ return -ENOMEM; ++ ++ curr = widgets; ++ for (i = 0; i < module->num_dapm_widgets; i++) { ++ ret = gbaudio_tplg_create_widget(module, &dapm_widgets[i], ++ curr, &w_size); ++ if (ret) { ++ dev_err(module->dev, "%s:%d type not supported\n", ++ curr->name, curr->type); ++ goto error; ++ } ++ widget = devm_kzalloc(module->dev, sizeof(struct ++ gbaudio_widget), ++ GFP_KERNEL); ++ if (!widget) { ++ ret = -ENOMEM; ++ goto error; ++ } ++ widget->id = curr->id; ++ widget->name = curr->name; ++ list_add(&widget->list, &module->widget_list); ++ curr = (void *)curr + w_size; ++ } ++ module->dapm_widgets = dapm_widgets; ++ ++ return 0; ++ ++error: ++ list_for_each_entry_safe(widget, _widget, &module->widget_list, ++ list) { ++ list_del(&widget->list); ++ devm_kfree(module->dev, widget); ++ } ++ devm_kfree(module->dev, dapm_widgets); ++ return ret; ++} ++ ++static int gbaudio_tplg_process_routes(struct gbaudio_module_info *module, ++ struct gb_audio_route *routes) ++{ ++ int i, ret; ++ struct snd_soc_dapm_route *dapm_routes; ++ struct gb_audio_route *curr; ++ size_t size; ++ ++ size = sizeof(struct snd_soc_dapm_route) * module->num_dapm_routes; ++ dapm_routes = devm_kzalloc(module->dev, size, GFP_KERNEL); ++ if (!dapm_routes) ++ return -ENOMEM; ++ ++ module->dapm_routes = dapm_routes; ++ curr = routes; ++ ++ for (i = 0; i < module->num_dapm_routes; i++) { ++ dapm_routes->sink = ++ gbaudio_map_widgetid(module, curr->destination_id); ++ if (!dapm_routes->sink) { ++ dev_err(module->dev, "%d:%d:%d:%d - Invalid sink\n", ++ curr->source_id, curr->destination_id, ++ curr->control_id, curr->index); ++ ret = -EINVAL; ++ goto error; ++ } ++ dapm_routes->source = ++ gbaudio_map_widgetid(module, curr->source_id); ++ if (!dapm_routes->source) { ++ dev_err(module->dev, "%d:%d:%d:%d - Invalid source\n", ++ curr->source_id, curr->destination_id, ++ curr->control_id, curr->index); ++ ret = -EINVAL; ++ goto error; ++ } ++ dapm_routes->control = ++ gbaudio_map_controlid(module, ++ curr->control_id, ++ curr->index); ++ if ((curr->control_id != GBAUDIO_INVALID_ID) && ++ !dapm_routes->control) { ++ dev_err(module->dev, "%d:%d:%d:%d - Invalid control\n", ++ curr->source_id, curr->destination_id, ++ curr->control_id, curr->index); ++ ret = -EINVAL; ++ goto error; ++ } ++ dev_dbg(module->dev, "Route {%s, %s, %s}\n", dapm_routes->sink, ++ (dapm_routes->control) ? dapm_routes->control:"NULL", ++ dapm_routes->source); ++ dapm_routes++; ++ curr++; ++ } ++ ++ return 0; ++ ++error: ++ devm_kfree(module->dev, module->dapm_routes); ++ return ret; ++} ++ ++static int gbaudio_tplg_process_header(struct gbaudio_module_info *module, ++ struct gb_audio_topology *tplg_data) ++{ ++ /* fetch no. of kcontrols, widgets & routes */ ++ module->num_controls = tplg_data->num_controls; ++ module->num_dapm_widgets = tplg_data->num_widgets; ++ module->num_dapm_routes = tplg_data->num_routes; ++ ++ /* update block offset */ ++ module->dai_offset = (unsigned long)&tplg_data->data; ++ module->control_offset = module->dai_offset + tplg_data->size_dais; ++ module->widget_offset = module->control_offset + ++ tplg_data->size_controls; ++ module->route_offset = module->widget_offset + ++ tplg_data->size_widgets; ++ ++ dev_dbg(module->dev, "DAI offset is 0x%lx\n", module->dai_offset); ++ dev_dbg(module->dev, "control offset is %lx\n", ++ module->control_offset); ++ dev_dbg(module->dev, "widget offset is %lx\n", module->widget_offset); ++ dev_dbg(module->dev, "route offset is %lx\n", module->route_offset); ++ ++ return 0; ++} ++ ++int gbaudio_tplg_parse_data(struct gbaudio_module_info *module, ++ struct gb_audio_topology *tplg_data) ++{ ++ int ret; ++ struct gb_audio_control *controls; ++ struct gb_audio_widget *widgets; ++ struct gb_audio_route *routes; ++ ++ if (!tplg_data) ++ return -EINVAL; ++ ++ ret = gbaudio_tplg_process_header(module, tplg_data); ++ if (ret) { ++ dev_err(module->dev, "%d: Error in parsing topology header\n", ++ ret); ++ return ret; ++ } ++ ++ /* process control */ ++ controls = (struct gb_audio_control *)module->control_offset; ++ ret = gbaudio_tplg_process_kcontrols(module, controls); ++ if (ret) { ++ dev_err(module->dev, ++ "%d: Error in parsing controls data\n", ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Control parsing finished\n"); ++ ++ /* process widgets */ ++ widgets = (struct gb_audio_widget *)module->widget_offset; ++ ret = gbaudio_tplg_process_widgets(module, widgets); ++ if (ret) { ++ dev_err(module->dev, ++ "%d: Error in parsing widgets data\n", ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Widget parsing finished\n"); ++ ++ /* process route */ ++ routes = (struct gb_audio_route *)module->route_offset; ++ ret = gbaudio_tplg_process_routes(module, routes); ++ if (ret) { ++ dev_err(module->dev, ++ "%d: Error in parsing routes data\n", ret); ++ return ret; ++ } ++ dev_dbg(module->dev, "Route parsing finished\n"); ++ ++ /* parse jack capabilities */ ++ if (tplg_data->jack_type) { ++ module->jack_mask = tplg_data->jack_type & GBCODEC_JACK_MASK; ++ module->button_mask = tplg_data->jack_type & ++ GBCODEC_JACK_BUTTON_MASK; ++ } ++ ++ return ret; ++} ++ ++void gbaudio_tplg_release(struct gbaudio_module_info *module) ++{ ++ struct gbaudio_control *control, *_control; ++ struct gbaudio_widget *widget, *_widget; ++ ++ if (!module->topology) ++ return; ++ ++ /* release kcontrols */ ++ list_for_each_entry_safe(control, _control, &module->ctl_list, ++ list) { ++ list_del(&control->list); ++ devm_kfree(module->dev, control); ++ } ++ if (module->controls) ++ devm_kfree(module->dev, module->controls); ++ ++ /* release widget controls */ ++ list_for_each_entry_safe(control, _control, &module->widget_ctl_list, ++ list) { ++ list_del(&control->list); ++ devm_kfree(module->dev, control); ++ } ++ ++ /* release widgets */ ++ list_for_each_entry_safe(widget, _widget, &module->widget_list, ++ list) { ++ list_del(&widget->list); ++ devm_kfree(module->dev, widget); ++ } ++ if (module->dapm_widgets) ++ devm_kfree(module->dev, module->dapm_widgets); ++ ++ /* release routes */ ++ if (module->dapm_routes) ++ devm_kfree(module->dev, module->dapm_routes); ++} diff --git a/greybus_bootrom.patch b/greybus_bootrom.patch new file mode 100644 index 00000000000000..b93f2267e3bada --- /dev/null +++ b/greybus_bootrom.patch @@ -0,0 +1,531 @@ +--- + drivers/greybus/bootrom.c | 524 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 524 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/bootrom.c +@@ -0,0 +1,524 @@ ++/* ++ * BOOTROM Greybus driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/firmware.h> ++#include <linux/jiffies.h> ++#include <linux/mutex.h> ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++#include "firmware.h" ++ ++/* Timeout, in jiffies, within which the next request must be received */ ++#define NEXT_REQ_TIMEOUT_MS 1000 ++ ++/* ++ * FIXME: Reduce this timeout once svc core handles parallel processing of ++ * events from the SVC, which are handled sequentially today. ++ */ ++#define MODE_SWITCH_TIMEOUT_MS 10000 ++ ++enum next_request_type { ++ NEXT_REQ_FIRMWARE_SIZE, ++ NEXT_REQ_GET_FIRMWARE, ++ NEXT_REQ_READY_TO_BOOT, ++ NEXT_REQ_MODE_SWITCH, ++}; ++ ++struct gb_bootrom { ++ struct gb_connection *connection; ++ const struct firmware *fw; ++ u8 protocol_major; ++ u8 protocol_minor; ++ enum next_request_type next_request; ++ struct delayed_work dwork; ++ struct mutex mutex; /* Protects bootrom->fw */ ++}; ++ ++static void free_firmware(struct gb_bootrom *bootrom) ++{ ++ if (!bootrom->fw) ++ return; ++ ++ release_firmware(bootrom->fw); ++ bootrom->fw = NULL; ++} ++ ++static void gb_bootrom_timedout(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct gb_bootrom *bootrom = container_of(dwork, struct gb_bootrom, dwork); ++ struct device *dev = &bootrom->connection->bundle->dev; ++ const char *reason; ++ ++ switch (bootrom->next_request) { ++ case NEXT_REQ_FIRMWARE_SIZE: ++ reason = "Firmware Size Request"; ++ break; ++ case NEXT_REQ_GET_FIRMWARE: ++ reason = "Get Firmware Request"; ++ break; ++ case NEXT_REQ_READY_TO_BOOT: ++ reason = "Ready to Boot Request"; ++ break; ++ case NEXT_REQ_MODE_SWITCH: ++ reason = "Interface Mode Switch"; ++ break; ++ default: ++ reason = NULL; ++ dev_err(dev, "Invalid next-request: %u", bootrom->next_request); ++ break; ++ } ++ ++ dev_err(dev, "Timed out waiting for %s from the Module\n", reason); ++ ++ mutex_lock(&bootrom->mutex); ++ free_firmware(bootrom); ++ mutex_unlock(&bootrom->mutex); ++ ++ /* TODO: Power-off Module ? */ ++} ++ ++static void gb_bootrom_set_timeout(struct gb_bootrom *bootrom, ++ enum next_request_type next, unsigned long timeout) ++{ ++ bootrom->next_request = next; ++ schedule_delayed_work(&bootrom->dwork, msecs_to_jiffies(timeout)); ++} ++ ++static void gb_bootrom_cancel_timeout(struct gb_bootrom *bootrom) ++{ ++ cancel_delayed_work_sync(&bootrom->dwork); ++} ++ ++/* ++ * The es2 chip doesn't have VID/PID programmed into the hardware and we need to ++ * hack that up to distinguish different modules and their firmware blobs. ++ * ++ * This fetches VID/PID (over bootrom protocol) for es2 chip only, when VID/PID ++ * already sent during hotplug are 0. ++ * ++ * Otherwise, we keep intf->vendor_id/product_id same as what's passed ++ * during hotplug. ++ */ ++static void bootrom_es2_fixup_vid_pid(struct gb_bootrom *bootrom) ++{ ++ struct gb_bootrom_get_vid_pid_response response; ++ struct gb_connection *connection = bootrom->connection; ++ struct gb_interface *intf = connection->bundle->intf; ++ int ret; ++ ++ if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_GMP_IDS)) ++ return; ++ ++ ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_GET_VID_PID, ++ NULL, 0, &response, sizeof(response)); ++ if (ret) { ++ dev_err(&connection->bundle->dev, ++ "Bootrom get vid/pid operation failed (%d)\n", ret); ++ return; ++ } ++ ++ /* ++ * NOTE: This is hacked, so that the same values of VID/PID can be used ++ * by next firmware level as well. The uevent for bootrom will still ++ * have VID/PID as 0, though after this point the sysfs files will start ++ * showing the updated values. But yeah, that's a bit racy as the same ++ * sysfs files would be showing 0 before this point. ++ */ ++ intf->vendor_id = le32_to_cpu(response.vendor_id); ++ intf->product_id = le32_to_cpu(response.product_id); ++ ++ dev_dbg(&connection->bundle->dev, "Bootrom got vid (0x%x)/pid (0x%x)\n", ++ intf->vendor_id, intf->product_id); ++} ++ ++/* This returns path of the firmware blob on the disk */ ++static int find_firmware(struct gb_bootrom *bootrom, u8 stage) ++{ ++ struct gb_connection *connection = bootrom->connection; ++ struct gb_interface *intf = connection->bundle->intf; ++ char firmware_name[49]; ++ int rc; ++ ++ /* Already have a firmware, free it */ ++ free_firmware(bootrom); ++ ++ /* Bootrom protocol is only supported for loading Stage 2 firmware */ ++ if (stage != 2) { ++ dev_err(&connection->bundle->dev, "Invalid boot stage: %u\n", ++ stage); ++ return -EINVAL; ++ } ++ ++ /* ++ * Create firmware name ++ * ++ * XXX Name it properly.. ++ */ ++ snprintf(firmware_name, sizeof(firmware_name), ++ FW_NAME_PREFIX "%08x_%08x_%08x_%08x_s2l.tftf", ++ intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, ++ intf->vendor_id, intf->product_id); ++ ++ // FIXME: ++ // Turn to dev_dbg later after everyone has valid bootloaders with good ++ // ids, but leave this as dev_info for now to make it easier to track ++ // down "empty" vid/pid modules. ++ dev_info(&connection->bundle->dev, "Firmware file '%s' requested\n", ++ firmware_name); ++ ++ rc = request_firmware(&bootrom->fw, firmware_name, ++ &connection->bundle->dev); ++ if (rc) { ++ dev_err(&connection->bundle->dev, ++ "failed to find %s firmware (%d)\n", firmware_name, rc); ++ } ++ ++ return rc; ++} ++ ++static int gb_bootrom_firmware_size_request(struct gb_operation *op) ++{ ++ struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); ++ struct gb_bootrom_firmware_size_request *size_request = op->request->payload; ++ struct gb_bootrom_firmware_size_response *size_response; ++ struct device *dev = &op->connection->bundle->dev; ++ int ret; ++ ++ /* Disable timeouts */ ++ gb_bootrom_cancel_timeout(bootrom); ++ ++ if (op->request->payload_size != sizeof(*size_request)) { ++ dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", ++ __func__, op->request->payload_size, ++ sizeof(*size_request)); ++ ret = -EINVAL; ++ goto queue_work; ++ } ++ ++ mutex_lock(&bootrom->mutex); ++ ++ ret = find_firmware(bootrom, size_request->stage); ++ if (ret) ++ goto unlock; ++ ++ if (!gb_operation_response_alloc(op, sizeof(*size_response), ++ GFP_KERNEL)) { ++ dev_err(dev, "%s: error allocating response\n", __func__); ++ free_firmware(bootrom); ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ size_response = op->response->payload; ++ size_response->size = cpu_to_le32(bootrom->fw->size); ++ ++ dev_dbg(dev, "%s: firmware size %d bytes\n", __func__, size_response->size); ++ ++unlock: ++ mutex_unlock(&bootrom->mutex); ++ ++queue_work: ++ if (!ret) { ++ /* Refresh timeout */ ++ gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, ++ NEXT_REQ_TIMEOUT_MS); ++ } ++ ++ return ret; ++} ++ ++static int gb_bootrom_get_firmware(struct gb_operation *op) ++{ ++ struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); ++ const struct firmware *fw; ++ struct gb_bootrom_get_firmware_request *firmware_request; ++ struct gb_bootrom_get_firmware_response *firmware_response; ++ struct device *dev = &op->connection->bundle->dev; ++ unsigned int offset, size; ++ enum next_request_type next_request; ++ int ret = 0; ++ ++ /* Disable timeouts */ ++ gb_bootrom_cancel_timeout(bootrom); ++ ++ if (op->request->payload_size != sizeof(*firmware_request)) { ++ dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", ++ __func__, op->request->payload_size, ++ sizeof(*firmware_request)); ++ ret = -EINVAL; ++ goto queue_work; ++ } ++ ++ mutex_lock(&bootrom->mutex); ++ ++ fw = bootrom->fw; ++ if (!fw) { ++ dev_err(dev, "%s: firmware not available\n", __func__); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ firmware_request = op->request->payload; ++ offset = le32_to_cpu(firmware_request->offset); ++ size = le32_to_cpu(firmware_request->size); ++ ++ if (offset >= fw->size || size > fw->size - offset) { ++ dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", ++ offset, size); ++ ret = -EINVAL; ++ goto unlock; ++ } ++ ++ if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, ++ GFP_KERNEL)) { ++ dev_err(dev, "%s: error allocating response\n", __func__); ++ ret = -ENOMEM; ++ goto unlock; ++ } ++ ++ firmware_response = op->response->payload; ++ memcpy(firmware_response->data, fw->data + offset, size); ++ ++ dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", offset, ++ size); ++ ++unlock: ++ mutex_unlock(&bootrom->mutex); ++ ++queue_work: ++ /* Refresh timeout */ ++ if (!ret && (offset + size == fw->size)) ++ next_request = NEXT_REQ_READY_TO_BOOT; ++ else ++ next_request = NEXT_REQ_GET_FIRMWARE; ++ ++ gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_MS); ++ ++ return ret; ++} ++ ++static int gb_bootrom_ready_to_boot(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_bootrom *bootrom = gb_connection_get_data(connection); ++ struct gb_bootrom_ready_to_boot_request *rtb_request; ++ struct device *dev = &connection->bundle->dev; ++ u8 status; ++ int ret = 0; ++ ++ /* Disable timeouts */ ++ gb_bootrom_cancel_timeout(bootrom); ++ ++ if (op->request->payload_size != sizeof(*rtb_request)) { ++ dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", ++ __func__, op->request->payload_size, ++ sizeof(*rtb_request)); ++ ret = -EINVAL; ++ goto queue_work; ++ } ++ ++ rtb_request = op->request->payload; ++ status = rtb_request->status; ++ ++ /* Return error if the blob was invalid */ ++ if (status == GB_BOOTROM_BOOT_STATUS_INVALID) { ++ ret = -EINVAL; ++ goto queue_work; ++ } ++ ++ /* ++ * XXX Should we return error for insecure firmware? ++ */ ++ dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); ++ ++queue_work: ++ /* ++ * Refresh timeout, the Interface shall load the new personality and ++ * send a new hotplug request, which shall get rid of the bootrom ++ * connection. As that can take some time, increase the timeout a bit. ++ */ ++ gb_bootrom_set_timeout(bootrom, NEXT_REQ_MODE_SWITCH, ++ MODE_SWITCH_TIMEOUT_MS); ++ ++ return ret; ++} ++ ++static int gb_bootrom_request_handler(struct gb_operation *op) ++{ ++ u8 type = op->type; ++ ++ switch (type) { ++ case GB_BOOTROM_TYPE_FIRMWARE_SIZE: ++ return gb_bootrom_firmware_size_request(op); ++ case GB_BOOTROM_TYPE_GET_FIRMWARE: ++ return gb_bootrom_get_firmware(op); ++ case GB_BOOTROM_TYPE_READY_TO_BOOT: ++ return gb_bootrom_ready_to_boot(op); ++ default: ++ dev_err(&op->connection->bundle->dev, ++ "unsupported request: %u\n", type); ++ return -EINVAL; ++ } ++} ++ ++static int gb_bootrom_get_version(struct gb_bootrom *bootrom) ++{ ++ struct gb_bundle *bundle = bootrom->connection->bundle; ++ struct gb_bootrom_version_request request; ++ struct gb_bootrom_version_response response; ++ int ret; ++ ++ request.major = GB_BOOTROM_VERSION_MAJOR; ++ request.minor = GB_BOOTROM_VERSION_MINOR; ++ ++ ret = gb_operation_sync(bootrom->connection, ++ GB_BOOTROM_TYPE_VERSION, ++ &request, sizeof(request), &response, ++ sizeof(response)); ++ if (ret) { ++ dev_err(&bundle->dev, ++ "failed to get protocol version: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (response.major > request.major) { ++ dev_err(&bundle->dev, ++ "unsupported major protocol version (%u > %u)\n", ++ response.major, request.major); ++ return -ENOTSUPP; ++ } ++ ++ bootrom->protocol_major = response.major; ++ bootrom->protocol_minor = response.minor; ++ ++ dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major, ++ response.minor); ++ ++ return 0; ++} ++ ++static int gb_bootrom_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_bootrom *bootrom; ++ int ret; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_BOOTROM) ++ return -ENODEV; ++ ++ bootrom = kzalloc(sizeof(*bootrom), GFP_KERNEL); ++ if (!bootrom) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, ++ le16_to_cpu(cport_desc->id), ++ gb_bootrom_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto err_free_bootrom; ++ } ++ ++ gb_connection_set_data(connection, bootrom); ++ ++ bootrom->connection = connection; ++ ++ mutex_init(&bootrom->mutex); ++ INIT_DELAYED_WORK(&bootrom->dwork, gb_bootrom_timedout); ++ greybus_set_drvdata(bundle, bootrom); ++ ++ ret = gb_connection_enable_tx(connection); ++ if (ret) ++ goto err_connection_destroy; ++ ++ ret = gb_bootrom_get_version(bootrom); ++ if (ret) ++ goto err_connection_disable; ++ ++ bootrom_es2_fixup_vid_pid(bootrom); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto err_connection_disable; ++ ++ /* Refresh timeout */ ++ gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, ++ NEXT_REQ_TIMEOUT_MS); ++ ++ /* Tell bootrom we're ready. */ ++ ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_AP_READY, NULL, 0, ++ NULL, 0); ++ if (ret) { ++ dev_err(&connection->bundle->dev, ++ "failed to send AP READY: %d\n", ret); ++ goto err_cancel_timeout; ++ } ++ ++ dev_dbg(&bundle->dev, "AP_READY sent\n"); ++ ++ return 0; ++ ++err_cancel_timeout: ++ gb_bootrom_cancel_timeout(bootrom); ++err_connection_disable: ++ gb_connection_disable(connection); ++err_connection_destroy: ++ gb_connection_destroy(connection); ++err_free_bootrom: ++ kfree(bootrom); ++ ++ return ret; ++} ++ ++static void gb_bootrom_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_bootrom *bootrom = greybus_get_drvdata(bundle); ++ ++ dev_dbg(&bundle->dev, "%s\n", __func__); ++ ++ gb_connection_disable(bootrom->connection); ++ ++ /* Disable timeouts */ ++ gb_bootrom_cancel_timeout(bootrom); ++ ++ /* ++ * Release firmware: ++ * ++ * As the connection and the delayed work are already disabled, we don't ++ * need to lock access to bootrom->fw here. ++ */ ++ free_firmware(bootrom); ++ ++ gb_connection_destroy(bootrom->connection); ++ kfree(bootrom); ++} ++ ++static const struct greybus_bundle_id gb_bootrom_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BOOTROM) }, ++ { } ++}; ++ ++static struct greybus_driver gb_bootrom_driver = { ++ .name = "bootrom", ++ .probe = gb_bootrom_probe, ++ .disconnect = gb_bootrom_disconnect, ++ .id_table = gb_bootrom_id_table, ++}; ++ ++module_greybus_driver(gb_bootrom_driver); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_build.patch b/greybus_build.patch new file mode 100644 index 00000000000000..a06d8f7f199f0e --- /dev/null +++ b/greybus_build.patch @@ -0,0 +1,344 @@ +--- + drivers/Kconfig | 2 + drivers/Makefile | 1 + drivers/greybus/Kconfig | 219 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/Makefile | 96 ++++++++++++++++++++ + 4 files changed, 318 insertions(+) + +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconf + + source "drivers/fpga/Kconfig" + ++source "drivers/greybus/Kconfig" ++ + endmenu +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/ + obj-$(CONFIG_ANDROID) += android/ + obj-$(CONFIG_NVMEM) += nvmem/ + obj-$(CONFIG_FPGA) += fpga/ ++obj-$(CONFIG_GREYBUS) += greybus/ +--- /dev/null ++++ b/drivers/greybus/Kconfig +@@ -0,0 +1,219 @@ ++menuconfig GREYBUS ++ tristate "Greybus support" ++ depends on SYSFS ++ ---help--- ++ This option enables the Greybus driver core. Greybus is an ++ hardware protocol that was designed to provide Unipro with a ++ sane application layer. It was originally designed for the ++ ARA project, a module phone system, but has shown up in other ++ phones, and can be tunneled over other busses in order to ++ control hardware devices. ++ ++ Say Y here to enable support for these types of drivers. ++ ++ To compile this code as a module, chose M here: the module ++ will be called greybus.ko ++ ++if GREYBUS ++ ++config GREYBUS_ES2 ++ tristate "Greybus ES3 USB host controller" ++ depends on USB ++ ---help--- ++ Select this option if you have a Toshiba ES3 USB device that ++ acts as a Greybus "host controller". This device is a bridge ++ from a USB device to a Unipro network. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-es2.ko ++ ++config GREYBUS_AUDIO ++ tristate "Greybus Audio Class driver" ++ depends on SOUND ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Audio Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-audio.ko ++ ++config GREYBUS_BOOTROM ++ tristate "Greybus Bootrom Class driver" ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Bootrom Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-bootrom.ko ++ ++config GREYBUS_CAMERA ++ tristate "Greybus Camera Class driver" ++ depends on MEDIA && LEDS_CLASS_FLASH && BROKEN ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Camera Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-camera.ko ++ ++config GREYBUS_FIRMWARE ++ tristate "Greybus Firmware Download Class driver" ++ depends on SPI ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Firmware Download Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-firmware.ko ++ ++config GREYBUS_HID ++ tristate "Greybus HID Class driver" ++ depends on HID && INPUT ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus HID Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-hid.ko ++ ++config GREYBUS_LIGHT ++ tristate "Greybus LED Class driver" ++ depends on LEDS_CLASS ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus LED Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-light.ko ++ ++config GREYBUS_LOG ++ tristate "Greybus Debug Log Class driver" ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Debug Log Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-log.ko ++ ++config GREYBUS_LOOPBACK ++ tristate "Greybus Loopback Class driver" ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Debug Log Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-log.ko ++ ++config GREYBUS_POWER ++ tristate "Greybus Powersupply Class driver" ++ depends on POWER_SUPPLY ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Powersupply Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-power-supply.ko ++ ++config GREYBUS_RAW ++ tristate "Greybus Raw Class driver" ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Raw Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-raw.ko ++ ++config GREYBUS_VIBRATOR ++ tristate "Greybus Vibrator Motor Class driver" ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus Vibrator Motor Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-vibrator.ko ++ ++menuconfig GREYBUS_BRIDGED_PHY ++ tristate "Greybus Bridged PHY Class drivers" ++ ---help--- ++ Select this option to pick from a variety of Greybus Bridged ++ PHY class drivers. These drivers emulate a number of ++ different "traditional" busses by tunneling them over Greybus. ++ Examples of this include serial, SPI, USB, and others. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-phy.ko ++ ++if GREYBUS_BRIDGED_PHY ++ ++config GREYBUS_GPIO ++ tristate "Greybus GPIO Bridged PHY driver" ++ depends on GPIO ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus GPIO Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-gpio.ko ++ ++config GREYBUS_I2C ++ tristate "Greybus I2C Bridged PHY driver" ++ depends on I2C ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus I2C Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-i2c.ko ++ ++config GREYBUS_PWM ++ tristate "Greybus PWM Bridged PHY driver" ++ depends on PWM ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus PWM Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-pwm.ko ++ ++config GREYBUS_SDIO ++ tristate "Greybus SDIO Bridged PHY driver" ++ depends on MMC ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus SDIO Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-sdio.ko ++ ++config GREYBUS_SPI ++ tristate "Greybus SPI Bridged PHY driver" ++ depends on SPI ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus SPI Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-spi.ko ++ ++config GREYBUS_UART ++ tristate "Greybus UART Bridged PHY driver" ++ depends on TTY ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus UART Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-uart.ko ++ ++config GREYBUS_USB ++ tristate "Greybus USB Host Bridged PHY driver" ++ depends on USB ++ ---help--- ++ Select this option if you have a device that follows the ++ Greybus USB Host Bridged PHY Class specification. ++ ++ To compile this code as a module, chose M here: the module ++ will be called gb-usb.ko ++ ++endif # GREYBUS_BRIDGED_PHY ++endif # GREYBUS +--- /dev/null ++++ b/drivers/greybus/Makefile +@@ -0,0 +1,96 @@ ++# Greybus core ++greybus-y := core.o \ ++ debugfs.o \ ++ hd.o \ ++ manifest.o \ ++ module.o \ ++ interface.o \ ++ bundle.o \ ++ connection.o \ ++ control.o \ ++ svc.o \ ++ svc_watchdog.o \ ++ operation.o \ ++ timesync.o \ ++ timesync_platform.o ++ ++obj-$(CONFIG_GREYBUS) += greybus.o ++ ++# needed for trace events ++ccflags-y += -I$(src) ++ ++ ++# Greybus Host controller drivers ++gb-es2-y := es2.o ++ ++obj-$(CONFIG_GREYBUS_ES2) += gb-es2.o ++ ++# Greybus class drivers ++gb-bootrom-y := bootrom.o ++gb-camera-y := camera.o ++gb-firmware-y := fw-core.o fw-download.o fw-management.o authentication.o ++gb-spilib-y := spilib.o ++gb-hid-y := hid.o ++gb-light-y := light.o ++gb-log-y := log.o ++gb-loopback-y := loopback.o ++gb-power-supply-y := power_supply.o ++gb-raw-y := raw.o ++gb-vibrator-y := vibrator.o ++ ++obj-$(CONFIG_GREYBUS_BOOTROM) += gb-bootrom.o ++obj-$(CONFIG_GREYBUS_CAMERA) += gb-camera.o ++obj-$(CONFIG_GREYBUS_FIRMWARE) += gb-firmware.o gb-spilib.o ++obj-$(CONFIG_GREYBUS_HID) += gb-hid.o ++obj-$(CONFIG_GREYBUS_LIGHT) += gb-light.o ++obj-$(CONFIG_GREYBUS_LOG) += gb-log.o ++obj-$(CONFIG_GREYBUS_LOOPBACK) += gb-loopback.o ++obj-$(CONFIG_GREYBUS_POWER) += gb-power-supply.o ++obj-$(CONFIG_GREYBUS_RAW) += gb-raw.o ++obj-$(CONFIG_GREYBUS_VIBRATOR) += gb-vibrator.o ++ ++# Greybus Audio is a bunch of modules ++gb-audio-module-y := audio_module.o audio_topology.o ++gb-audio-codec-y := audio_codec.o ++gb-audio-gb-y := audio_gb.o ++gb-audio-apbridgea-y := audio_apbridgea.o ++gb-audio-manager-y := audio_manager.o audio_manager_module.o ++ ++# Greybus Audio sysfs helpers can be useful when debugging ++#GB_AUDIO_MANAGER_SYSFS ?= true ++#ifeq ($(GB_AUDIO_MANAGER_SYSFS),true) ++#gb-audio-manager-y += audio_manager_sysfs.o ++#ccflags-y += -DGB_AUDIO_MANAGER_SYSFS ++#endif ++ ++obj-$(CONFIG_GREYBUS_AUDIO_MSM8994) += gb-audio-codec.o ++obj-$(CONFIG_GREYBUS_AUDIO_MSM8994) += gb-audio-module.o ++obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-gb.o ++obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-apbridgea.o ++obj-$(CONFIG_GREYBUS_AUDIO) += gb-audio-manager.o ++ ++ ++# Greybus Bridged PHY drivers ++gb-gbphy-y := gbphy.o ++gb-gpio-y := gpio.o ++gb-i2c-y := i2c.o ++gb-pwm-y := pwm.o ++gb-sdio-y := sdio.o ++gb-spi-y := spi.o ++gb-uart-y := uart.o ++gb-usb-y := usb.o ++ ++obj-$(CONFIG_GREYBUS_BRIDGED_PHY) += gb-gbphy.o ++obj-$(CONFIG_GREYBUS_GPIO) += gb-gpio.o ++obj-$(CONFIG_GREYBUS_I2C) += gb-i2c.o ++obj-$(CONFIG_GREYBUS_PWM) += gb-pwm.o ++obj-$(CONFIG_GREYBUS_SDIO) += gb-sdio.o ++obj-$(CONFIG_GREYBUS_SPI) += gb-spi.o gb-spilib.o ++obj-$(CONFIG_GREYBUS_UART) += gb-uart.o ++obj-$(CONFIG_GREYBUS_USB) += gb-usb.o ++ ++ ++# Greybus Platform driver ++gb-arche-y := arche-platform.o arche-apb-ctrl.o ++ ++obj-$(CONFIG_USB_HSIC_USB3613) += gb-arche.o diff --git a/greybus_camera.patch b/greybus_camera.patch new file mode 100644 index 00000000000000..0900dd1bf85672 --- /dev/null +++ b/greybus_camera.patch @@ -0,0 +1,1538 @@ +--- + drivers/greybus/camera.c | 1400 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/gb-camera.h | 127 +++ + 2 files changed, 1527 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/camera.c +@@ -0,0 +1,1400 @@ ++/* ++ * Greybus Camera protocol driver. ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/debugfs.h> ++#include <linux/fs.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/string.h> ++#include <linux/uaccess.h> ++#include <linux/vmalloc.h> ++ ++#include "gb-camera.h" ++#include "greybus.h" ++#include "greybus_protocols.h" ++ ++enum gb_camera_debugs_buffer_id { ++ GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, ++ GB_CAMERA_DEBUGFS_BUFFER_STREAMS, ++ GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, ++ GB_CAMERA_DEBUGFS_BUFFER_FLUSH, ++ GB_CAMERA_DEBUGFS_BUFFER_MAX, ++}; ++ ++struct gb_camera_debugfs_buffer { ++ char data[PAGE_SIZE]; ++ size_t length; ++}; ++ ++enum gb_camera_state { ++ GB_CAMERA_STATE_UNCONFIGURED, ++ GB_CAMERA_STATE_CONFIGURED, ++}; ++ ++/** ++ * struct gb_camera - A Greybus Camera Device ++ * @connection: the greybus connection for camera management ++ * @data_connection: the greybus connection for camera data ++ * @data_cport_id: the data CPort ID on the module side ++ * @mutex: protects the connection and state fields ++ * @state: the current module state ++ * @debugfs: debugfs entries for camera protocol operations testing ++ * @module: Greybus camera module registered to HOST processor. ++ */ ++struct gb_camera { ++ struct gb_bundle *bundle; ++ struct gb_connection *connection; ++ struct gb_connection *data_connection; ++ u16 data_cport_id; ++ ++ struct mutex mutex; ++ enum gb_camera_state state; ++ ++ struct { ++ struct dentry *root; ++ struct gb_camera_debugfs_buffer *buffers; ++ } debugfs; ++ ++ struct gb_camera_module module; ++}; ++ ++struct gb_camera_stream_config { ++ unsigned int width; ++ unsigned int height; ++ unsigned int format; ++ unsigned int vc; ++ unsigned int dt[2]; ++ unsigned int max_size; ++}; ++ ++struct gb_camera_fmt_info { ++ enum v4l2_mbus_pixelcode mbus_code; ++ unsigned int gb_format; ++ unsigned int bpp; ++}; ++ ++/* GB format to media code map */ ++static const struct gb_camera_fmt_info gb_fmt_info[] = { ++ { ++ .mbus_code = V4L2_MBUS_FMT_UYVY8_1X16, ++ .gb_format = 0x01, ++ .bpp = 16, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_NV12_1x8, ++ .gb_format = 0x12, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_NV21_1x8, ++ .gb_format = 0x13, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_YU12_1x8, ++ .gb_format = 0x16, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_YV12_1x8, ++ .gb_format = 0x17, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_JPEG_1X8, ++ .gb_format = 0x40, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_GB_CAM_METADATA_1X8, ++ .gb_format = 0x41, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_GB_CAM_DEBUG_DATA_1X8, ++ .gb_format = 0x42, ++ .bpp = 0, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SBGGR10_1X10, ++ .gb_format = 0x80, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGBRG10_1X10, ++ .gb_format = 0x81, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGRBG10_1X10, ++ .gb_format = 0x82, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SRGGB10_1X10, ++ .gb_format = 0x83, ++ .bpp = 10, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SBGGR12_1X12, ++ .gb_format = 0x84, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGBRG12_1X12, ++ .gb_format = 0x85, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SGRBG12_1X12, ++ .gb_format = 0x86, ++ .bpp = 12, ++ }, ++ { ++ .mbus_code = V4L2_MBUS_FMT_SRGGB12_1X12, ++ .gb_format = 0x87, ++ .bpp = 12, ++ }, ++}; ++ ++static const struct gb_camera_fmt_info *gb_camera_get_format_info(u16 gb_fmt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { ++ if (gb_fmt_info[i].gb_format == gb_fmt) ++ return &gb_fmt_info[i]; ++ } ++ ++ return NULL; ++} ++ ++#define ES2_APB_CDSI0_CPORT 16 ++#define ES2_APB_CDSI1_CPORT 17 ++ ++#define GB_CAMERA_MAX_SETTINGS_SIZE 8192 ++ ++#define gcam_dbg(gcam, format...) dev_dbg(&gcam->bundle->dev, format) ++#define gcam_info(gcam, format...) dev_info(&gcam->bundle->dev, format) ++#define gcam_err(gcam, format...) dev_err(&gcam->bundle->dev, format) ++ ++static int gb_camera_operation_sync_flags(struct gb_connection *connection, ++ int type, unsigned int flags, ++ void *request, size_t request_size, ++ void *response, size_t *response_size) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_operation_create_flags(connection, type, request_size, ++ *response_size, flags, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: synchronous operation of type 0x%02x failed: %d\n", ++ connection->name, type, ret); ++ } else { ++ *response_size = operation->response->payload_size; ++ ++ if (operation->response->payload_size) ++ memcpy(response, operation->response->payload, ++ operation->response->payload_size); ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static int gb_camera_get_max_pkt_size(struct gb_camera *gcam, ++ struct gb_camera_configure_streams_response *resp) ++{ ++ unsigned int max_pkt_size = 0; ++ unsigned int i; ++ ++ for (i = 0; i < resp->num_streams; i++) { ++ struct gb_camera_stream_config_response *cfg = &resp->config[i]; ++ const struct gb_camera_fmt_info *fmt_info; ++ unsigned int pkt_size; ++ ++ fmt_info = gb_camera_get_format_info(cfg->format); ++ if (!fmt_info) { ++ gcam_err(gcam, "unsupported greybus image format: %d\n", ++ cfg->format); ++ return -EIO; ++ } ++ ++ if (fmt_info->bpp == 0) { ++ pkt_size = le32_to_cpu(cfg->max_pkt_size); ++ ++ if (pkt_size == 0) { ++ gcam_err(gcam, ++ "Stream %u: invalid zero maximum packet size\n", ++ i); ++ return -EIO; ++ } ++ } else { ++ pkt_size = le16_to_cpu(cfg->width) * fmt_info->bpp / 8; ++ ++ if (pkt_size != le32_to_cpu(cfg->max_pkt_size)) { ++ gcam_err(gcam, ++ "Stream %u: maximum packet size mismatch (%u/%u)\n", ++ i, pkt_size, cfg->max_pkt_size); ++ return -EIO; ++ } ++ } ++ ++ max_pkt_size = max(pkt_size, max_pkt_size); ++ } ++ ++ return max_pkt_size; ++} ++ ++/* ++ * Validate the stream configuration response verifying padding is correctly ++ * set and the returned number of streams is supported ++ */ ++static const int gb_camera_configure_streams_validate_response( ++ struct gb_camera *gcam, ++ struct gb_camera_configure_streams_response *resp, ++ unsigned int nstreams) ++{ ++ unsigned int i; ++ ++ /* Validate the returned response structure */ ++ if (resp->padding[0] || resp->padding[1]) { ++ gcam_err(gcam, "response padding != 0\n"); ++ return -EIO; ++ } ++ ++ if (resp->num_streams > nstreams) { ++ gcam_err(gcam, "got #streams %u > request %u\n", ++ resp->num_streams, nstreams); ++ return -EIO; ++ } ++ ++ for (i = 0; i < resp->num_streams; i++) { ++ struct gb_camera_stream_config_response *cfg = &resp->config[i]; ++ if (cfg->padding) { ++ gcam_err(gcam, "stream #%u padding != 0\n", i); ++ return -EIO; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Hardware Configuration ++ */ ++ ++static int gb_camera_set_intf_power_mode(struct gb_camera *gcam, u8 intf_id, ++ bool hs) ++{ ++ struct gb_svc *svc = gcam->connection->hd->svc; ++ int ret; ++ ++ if (hs) ++ ret = gb_svc_intf_set_power_mode(svc, intf_id, ++ GB_SVC_UNIPRO_HS_SERIES_A, ++ GB_SVC_UNIPRO_FAST_MODE, 2, 2, ++ GB_SVC_SMALL_AMPLITUDE, ++ GB_SVC_NO_DE_EMPHASIS, ++ GB_SVC_UNIPRO_FAST_MODE, 2, 2, ++ GB_SVC_PWRM_RXTERMINATION | ++ GB_SVC_PWRM_TXTERMINATION, 0, ++ NULL, NULL); ++ else ++ ret = gb_svc_intf_set_power_mode(svc, intf_id, ++ GB_SVC_UNIPRO_HS_SERIES_A, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ GB_SVC_SMALL_AMPLITUDE, ++ GB_SVC_NO_DE_EMPHASIS, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ 0, 0, ++ NULL, NULL); ++ ++ return ret; ++} ++ ++static int gb_camera_set_power_mode(struct gb_camera *gcam, bool hs) ++{ ++ struct gb_interface *intf = gcam->connection->intf; ++ struct gb_svc *svc = gcam->connection->hd->svc; ++ int ret; ++ ++ ret = gb_camera_set_intf_power_mode(gcam, intf->interface_id, hs); ++ if (ret < 0) { ++ gcam_err(gcam, "failed to set module interface to %s (%d)\n", ++ hs ? "HS" : "PWM", ret); ++ return ret; ++ } ++ ++ ret = gb_camera_set_intf_power_mode(gcam, svc->ap_intf_id, hs); ++ if (ret < 0) { ++ gb_camera_set_intf_power_mode(gcam, intf->interface_id, !hs); ++ gcam_err(gcam, "failed to set AP interface to %s (%d)\n", ++ hs ? "HS" : "PWM", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++struct ap_csi_config_request { ++ __u8 csi_id; ++ __u8 flags; ++#define GB_CAMERA_CSI_FLAG_CLOCK_CONTINUOUS 0x01 ++ __u8 num_lanes; ++ __u8 padding; ++ __le32 csi_clk_freq; ++ __le32 max_pkt_size; ++} __packed; ++ ++/* ++ * TODO: Compute the number of lanes dynamically based on bandwidth ++ * requirements. ++ */ ++#define GB_CAMERA_CSI_NUM_DATA_LANES 4 ++ ++#define GB_CAMERA_CSI_CLK_FREQ_MAX 999000000U ++#define GB_CAMERA_CSI_CLK_FREQ_MIN 100000000U ++#define GB_CAMERA_CSI_CLK_FREQ_MARGIN 150000000U ++ ++static int gb_camera_setup_data_connection(struct gb_camera *gcam, ++ struct gb_camera_configure_streams_response *resp, ++ struct gb_camera_csi_params *csi_params) ++{ ++ struct ap_csi_config_request csi_cfg; ++ struct gb_connection *conn; ++ unsigned int clk_freq; ++ int ret; ++ ++ /* ++ * Create the data connection between the camera module data CPort and ++ * APB CDSI1. The CDSI1 CPort ID is hardcoded by the ES2 bridge. ++ */ ++ conn = gb_connection_create_offloaded(gcam->bundle, gcam->data_cport_id, ++ GB_CONNECTION_FLAG_NO_FLOWCTRL | ++ GB_CONNECTION_FLAG_CDSI1); ++ if (IS_ERR(conn)) ++ return PTR_ERR(conn); ++ ++ gcam->data_connection = conn; ++ gb_connection_set_data(conn, gcam); ++ ++ ret = gb_connection_enable(conn); ++ if (ret) ++ goto error_conn_destroy; ++ ++ /* Set the UniPro link to high speed mode. */ ++ ret = gb_camera_set_power_mode(gcam, true); ++ if (ret < 0) ++ goto error_conn_disable; ++ ++ /* ++ * Configure the APB-A CSI-2 transmitter. ++ * ++ * Hardcode the number of lanes to 4 and compute the bus clock frequency ++ * based on the module bandwidth requirements with a safety margin. ++ */ ++ memset(&csi_cfg, 0, sizeof(csi_cfg)); ++ csi_cfg.csi_id = 1; ++ csi_cfg.flags = 0; ++ csi_cfg.num_lanes = GB_CAMERA_CSI_NUM_DATA_LANES; ++ ++ clk_freq = resp->data_rate / 2 / GB_CAMERA_CSI_NUM_DATA_LANES; ++ clk_freq = clamp(clk_freq + GB_CAMERA_CSI_CLK_FREQ_MARGIN, ++ GB_CAMERA_CSI_CLK_FREQ_MIN, ++ GB_CAMERA_CSI_CLK_FREQ_MAX); ++ csi_cfg.csi_clk_freq = clk_freq; ++ ++ ret = gb_camera_get_max_pkt_size(gcam, resp); ++ if (ret < 0) { ++ ret = -EIO; ++ goto error_power; ++ } ++ csi_cfg.max_pkt_size = ret; ++ ++ ret = gb_hd_output(gcam->connection->hd, &csi_cfg, ++ sizeof(csi_cfg), ++ GB_APB_REQUEST_CSI_TX_CONTROL, false); ++ if (ret < 0) { ++ gcam_err(gcam, "failed to start the CSI transmitter\n"); ++ goto error_power; ++ } ++ ++ if (csi_params) { ++ csi_params->clk_freq = csi_cfg.csi_clk_freq; ++ csi_params->num_lanes = csi_cfg.num_lanes; ++ } ++ ++ return 0; ++ ++error_power: ++ gb_camera_set_power_mode(gcam, false); ++error_conn_disable: ++ gb_connection_disable(gcam->data_connection); ++error_conn_destroy: ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++ return ret; ++} ++ ++static void gb_camera_teardown_data_connection(struct gb_camera *gcam) ++{ ++ struct ap_csi_config_request csi_cfg; ++ int ret; ++ ++ /* Stop the APB1 CSI transmitter. */ ++ memset(&csi_cfg, 0, sizeof(csi_cfg)); ++ csi_cfg.csi_id = 1; ++ ++ ret = gb_hd_output(gcam->connection->hd, &csi_cfg, ++ sizeof(csi_cfg), ++ GB_APB_REQUEST_CSI_TX_CONTROL, false); ++ ++ if (ret < 0) ++ gcam_err(gcam, "failed to stop the CSI transmitter\n"); ++ ++ /* Set the UniPro link to low speed mode. */ ++ gb_camera_set_power_mode(gcam, false); ++ ++ /* Destroy the data connection. */ ++ gb_connection_disable(gcam->data_connection); ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Camera Protocol Operations ++ */ ++ ++static int gb_camera_capabilities(struct gb_camera *gcam, ++ u8 *capabilities, size_t *size) ++{ ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(gcam->bundle); ++ if (ret) ++ return ret; ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_camera_operation_sync_flags(gcam->connection, ++ GB_CAMERA_TYPE_CAPABILITIES, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ NULL, 0, ++ (void *)capabilities, size); ++ if (ret) ++ gcam_err(gcam, "failed to retrieve capabilities: %d\n", ret); ++ ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ gb_pm_runtime_put_autosuspend(gcam->bundle); ++ ++ return ret; ++} ++ ++static int gb_camera_configure_streams(struct gb_camera *gcam, ++ unsigned int *num_streams, ++ unsigned int *flags, ++ struct gb_camera_stream_config *streams, ++ struct gb_camera_csi_params *csi_params) ++{ ++ struct gb_camera_configure_streams_request *req; ++ struct gb_camera_configure_streams_response *resp; ++ unsigned int nstreams = *num_streams; ++ unsigned int i; ++ size_t req_size; ++ size_t resp_size; ++ int ret; ++ ++ if (nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ req_size = sizeof(*req) + nstreams * sizeof(req->config[0]); ++ resp_size = sizeof(*resp) + nstreams * sizeof(resp->config[0]); ++ ++ req = kmalloc(req_size, GFP_KERNEL); ++ resp = kmalloc(resp_size, GFP_KERNEL); ++ if (!req || !resp) { ++ kfree(req); ++ kfree(resp); ++ return -ENOMEM; ++ } ++ ++ req->num_streams = nstreams; ++ req->flags = *flags; ++ req->padding = 0; ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config_request *cfg = &req->config[i]; ++ ++ cfg->width = cpu_to_le16(streams[i].width); ++ cfg->height = cpu_to_le16(streams[i].height); ++ cfg->format = cpu_to_le16(streams[i].format); ++ cfg->padding = 0; ++ } ++ ++ mutex_lock(&gcam->mutex); ++ ++ ret = gb_pm_runtime_get_sync(gcam->bundle); ++ if (ret) ++ goto done_skip_pm_put; ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_camera_operation_sync_flags(gcam->connection, ++ GB_CAMERA_TYPE_CONFIGURE_STREAMS, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ req, req_size, ++ resp, &resp_size); ++ if (ret < 0) ++ goto done; ++ ++ ret = gb_camera_configure_streams_validate_response(gcam, resp, ++ nstreams); ++ if (ret < 0) ++ goto done; ++ ++ *flags = resp->flags; ++ *num_streams = resp->num_streams; ++ ++ for (i = 0; i < resp->num_streams; ++i) { ++ struct gb_camera_stream_config_response *cfg = &resp->config[i]; ++ ++ streams[i].width = le16_to_cpu(cfg->width); ++ streams[i].height = le16_to_cpu(cfg->height); ++ streams[i].format = le16_to_cpu(cfg->format); ++ streams[i].vc = cfg->virtual_channel; ++ streams[i].dt[0] = cfg->data_type[0]; ++ streams[i].dt[1] = cfg->data_type[1]; ++ streams[i].max_size = le32_to_cpu(cfg->max_size); ++ } ++ ++ if ((resp->flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) || ++ (req->flags & GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY)) ++ goto done; ++ ++ if (gcam->state == GB_CAMERA_STATE_CONFIGURED) { ++ gb_camera_teardown_data_connection(gcam); ++ gcam->state = GB_CAMERA_STATE_UNCONFIGURED; ++ ++ /* ++ * When unconfiguring streams release the PM runtime reference ++ * that was acquired when streams were configured. The bundle ++ * won't be suspended until the PM runtime reference acquired at ++ * the beginning of this function gets released right before ++ * returning. ++ */ ++ gb_pm_runtime_put_noidle(gcam->bundle); ++ } ++ ++ if (resp->num_streams == 0) ++ goto done; ++ ++ /* ++ * Make sure the bundle won't be suspended until streams get ++ * unconfigured after the stream is configured successfully ++ */ ++ gb_pm_runtime_get_noresume(gcam->bundle); ++ ++ /* Setup CSI-2 connection from APB-A to AP */ ++ ret = gb_camera_setup_data_connection(gcam, resp, csi_params); ++ if (ret < 0) { ++ memset(req, 0, sizeof(*req)); ++ gb_operation_sync(gcam->connection, ++ GB_CAMERA_TYPE_CONFIGURE_STREAMS, ++ req, sizeof(*req), ++ resp, sizeof(*resp)); ++ *flags = 0; ++ *num_streams = 0; ++ gb_pm_runtime_put_noidle(gcam->bundle); ++ goto done; ++ } ++ ++ gcam->state = GB_CAMERA_STATE_CONFIGURED; ++ ++done: ++ gb_pm_runtime_put_autosuspend(gcam->bundle); ++ ++done_skip_pm_put: ++ mutex_unlock(&gcam->mutex); ++ kfree(req); ++ kfree(resp); ++ return ret; ++} ++ ++static int gb_camera_capture(struct gb_camera *gcam, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings) ++{ ++ struct gb_camera_capture_request *req; ++ size_t req_size; ++ int ret; ++ ++ if (settings_size > GB_CAMERA_MAX_SETTINGS_SIZE) ++ return -EINVAL; ++ ++ req_size = sizeof(*req) + settings_size; ++ req = kmalloc(req_size, GFP_KERNEL); ++ if (!req) ++ return -ENOMEM; ++ ++ req->request_id = cpu_to_le32(request_id); ++ req->streams = streams; ++ req->padding = 0; ++ req->num_frames = cpu_to_le16(num_frames); ++ memcpy(req->settings, settings, settings_size); ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_CAPTURE, ++ req, req_size, NULL, 0); ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ kfree(req); ++ ++ return ret; ++} ++ ++static int gb_camera_flush(struct gb_camera *gcam, u32 *request_id) ++{ ++ struct gb_camera_flush_response resp; ++ int ret; ++ ++ mutex_lock(&gcam->mutex); ++ ++ if (!gcam->connection) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ ret = gb_operation_sync(gcam->connection, GB_CAMERA_TYPE_FLUSH, NULL, 0, ++ &resp, sizeof(resp)); ++ ++ if (ret < 0) ++ goto done; ++ ++ if (request_id) ++ *request_id = le32_to_cpu(resp.request_id); ++ ++done: ++ mutex_unlock(&gcam->mutex); ++ ++ return ret; ++} ++ ++static int gb_camera_request_handler(struct gb_operation *op) ++{ ++ struct gb_camera *gcam = gb_connection_get_data(op->connection); ++ struct gb_camera_metadata_request *payload; ++ struct gb_message *request; ++ ++ if (op->type != GB_CAMERA_TYPE_METADATA) { ++ gcam_err(gcam, "Unsupported unsolicited event: %u\n", op->type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*payload)) { ++ gcam_err(gcam, "Wrong event size received (%zu < %zu)\n", ++ request->payload_size, sizeof(*payload)); ++ return -EINVAL; ++ } ++ ++ payload = request->payload; ++ ++ gcam_dbg(gcam, "received metadata for request %u, frame %u, stream %u\n", ++ payload->request_id, payload->frame_number, payload->stream); ++ ++ return 0; ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Interface with HOST gmp camera. ++ */ ++static unsigned int gb_camera_mbus_to_gb(enum v4l2_mbus_pixelcode mbus_code) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { ++ if (gb_fmt_info[i].mbus_code == mbus_code) ++ return gb_fmt_info[i].gb_format; ++ } ++ return gb_fmt_info[0].gb_format; ++} ++ ++static enum v4l2_mbus_pixelcode gb_camera_gb_to_mbus(u16 gb_fmt) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_fmt_info); i++) { ++ if (gb_fmt_info[i].gb_format == gb_fmt) ++ return gb_fmt_info[i].mbus_code; ++ } ++ return gb_fmt_info[0].mbus_code; ++} ++ ++static ssize_t gb_camera_op_capabilities(void *priv, char *data, size_t len) ++{ ++ struct gb_camera *gcam = priv; ++ size_t capabilities_len = len; ++ int ret; ++ ++ ret = gb_camera_capabilities(gcam, data, &capabilities_len); ++ if (ret) ++ return ret; ++ ++ return capabilities_len; ++} ++ ++static int gb_camera_op_configure_streams(void *priv, unsigned int *nstreams, ++ unsigned int *flags, struct gb_camera_stream *streams, ++ struct gb_camera_csi_params *csi_params) ++{ ++ struct gb_camera *gcam = priv; ++ struct gb_camera_stream_config *gb_streams; ++ unsigned int gb_flags = 0; ++ unsigned int gb_nstreams = *nstreams; ++ unsigned int i; ++ int ret; ++ ++ if (gb_nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ gb_streams = kzalloc(gb_nstreams * sizeof(*gb_streams), GFP_KERNEL); ++ if (!gb_streams) ++ return -ENOMEM; ++ ++ for (i = 0; i < gb_nstreams; i++) { ++ gb_streams[i].width = streams[i].width; ++ gb_streams[i].height = streams[i].height; ++ gb_streams[i].format = ++ gb_camera_mbus_to_gb(streams[i].pixel_code); ++ } ++ ++ if (*flags & GB_CAMERA_IN_FLAG_TEST) ++ gb_flags |= GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY; ++ ++ ret = gb_camera_configure_streams(gcam, &gb_nstreams, ++ &gb_flags, gb_streams, csi_params); ++ if (ret < 0) ++ goto done; ++ if (gb_nstreams > *nstreams) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ *flags = 0; ++ if (gb_flags & GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED) ++ *flags |= GB_CAMERA_OUT_FLAG_ADJUSTED; ++ ++ for (i = 0; i < gb_nstreams; i++) { ++ streams[i].width = gb_streams[i].width; ++ streams[i].height = gb_streams[i].height; ++ streams[i].vc = gb_streams[i].vc; ++ streams[i].dt[0] = gb_streams[i].dt[0]; ++ streams[i].dt[1] = gb_streams[i].dt[1]; ++ streams[i].max_size = gb_streams[i].max_size; ++ streams[i].pixel_code = ++ gb_camera_gb_to_mbus(gb_streams[i].format); ++ } ++ *nstreams = gb_nstreams; ++ ++done: ++ kfree(gb_streams); ++ return ret; ++} ++ ++static int gb_camera_op_capture(void *priv, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings) ++{ ++ struct gb_camera *gcam = priv; ++ ++ return gb_camera_capture(gcam, request_id, streams, num_frames, ++ settings_size, settings); ++} ++ ++static int gb_camera_op_flush(void *priv, u32 *request_id) ++{ ++ struct gb_camera *gcam = priv; ++ ++ return gb_camera_flush(gcam, request_id); ++} ++ ++static const struct gb_camera_ops gb_cam_ops = { ++ .capabilities = gb_camera_op_capabilities, ++ .configure_streams = gb_camera_op_configure_streams, ++ .capture = gb_camera_op_capture, ++ .flush = gb_camera_op_flush, ++}; ++ ++/* ----------------------------------------------------------------------------- ++ * DebugFS ++ */ ++ ++static ssize_t gb_camera_debugfs_capabilities(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES]; ++ size_t size = 1024; ++ unsigned int i; ++ u8 *caps; ++ int ret; ++ ++ caps = kmalloc(size, GFP_KERNEL); ++ if (!caps) ++ return -ENOMEM; ++ ++ ret = gb_camera_capabilities(gcam, caps, &size); ++ if (ret < 0) ++ goto done; ++ ++ /* ++ * hex_dump_to_buffer() doesn't return the number of bytes dumped prior ++ * to v4.0, we need our own implementation :-( ++ */ ++ buffer->length = 0; ++ ++ for (i = 0; i < size; i += 16) { ++ unsigned int nbytes = min_t(unsigned int, size - i, 16); ++ ++ buffer->length += sprintf(buffer->data + buffer->length, ++ "%*ph\n", nbytes, caps + i); ++ } ++ ++done: ++ kfree(caps); ++ return ret; ++} ++ ++static ssize_t gb_camera_debugfs_configure_streams(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_STREAMS]; ++ struct gb_camera_stream_config *streams; ++ unsigned int nstreams; ++ unsigned int flags; ++ unsigned int i; ++ char *token; ++ int ret; ++ ++ /* Retrieve number of streams to configure */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ++ ret = kstrtouint(token, 10, &nstreams); ++ if (ret < 0) ++ return ret; ++ ++ if (nstreams > GB_CAMERA_MAX_STREAMS) ++ return -EINVAL; ++ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ++ ret = kstrtouint(token, 10, &flags); ++ if (ret < 0) ++ return ret; ++ ++ /* For each stream to configure parse width, height and format */ ++ streams = kzalloc(nstreams * sizeof(*streams), GFP_KERNEL); ++ if (!streams) ++ return -ENOMEM; ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config *stream = &streams[i]; ++ ++ /* width */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ret = kstrtouint(token, 10, &stream->width); ++ if (ret < 0) ++ goto done; ++ ++ /* height */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ goto done; ++ ++ ret = kstrtouint(token, 10, &stream->height); ++ if (ret < 0) ++ goto done; ++ ++ /* Image format code */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ goto done; ++ ++ ret = kstrtouint(token, 16, &stream->format); ++ if (ret < 0) ++ goto done; ++ } ++ ++ ret = gb_camera_configure_streams(gcam, &nstreams, &flags, streams, ++ NULL); ++ if (ret < 0) ++ goto done; ++ ++ buffer->length = sprintf(buffer->data, "%u;%u;", nstreams, flags); ++ ++ for (i = 0; i < nstreams; ++i) { ++ struct gb_camera_stream_config *stream = &streams[i]; ++ ++ buffer->length += sprintf(buffer->data + buffer->length, ++ "%u;%u;%u;%u;%u;%u;%u;", ++ stream->width, stream->height, ++ stream->format, stream->vc, ++ stream->dt[0], stream->dt[1], ++ stream->max_size); ++ } ++ ++ ret = len; ++ ++done: ++ kfree(streams); ++ return ret; ++}; ++ ++static ssize_t gb_camera_debugfs_capture(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ unsigned int request_id; ++ unsigned int streams_mask; ++ unsigned int num_frames; ++ char *token; ++ int ret; ++ ++ /* Request id */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 10, &request_id); ++ if (ret < 0) ++ return ret; ++ ++ /* Stream mask */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 16, &streams_mask); ++ if (ret < 0) ++ return ret; ++ ++ /* number of frames */ ++ token = strsep(&buf, ";"); ++ if (token == NULL) ++ return -EINVAL; ++ ret = kstrtouint(token, 10, &num_frames); ++ if (ret < 0) ++ return ret; ++ ++ ret = gb_camera_capture(gcam, request_id, streams_mask, num_frames, 0, ++ NULL); ++ if (ret < 0) ++ return ret; ++ ++ return len; ++} ++ ++static ssize_t gb_camera_debugfs_flush(struct gb_camera *gcam, ++ char *buf, size_t len) ++{ ++ struct gb_camera_debugfs_buffer *buffer = ++ &gcam->debugfs.buffers[GB_CAMERA_DEBUGFS_BUFFER_FLUSH]; ++ unsigned int req_id; ++ int ret; ++ ++ ret = gb_camera_flush(gcam, &req_id); ++ if (ret < 0) ++ return ret; ++ ++ buffer->length = sprintf(buffer->data, "%u", req_id); ++ ++ return len; ++} ++ ++struct gb_camera_debugfs_entry { ++ const char *name; ++ unsigned int mask; ++ unsigned int buffer; ++ ssize_t (*execute)(struct gb_camera *gcam, char *buf, size_t len); ++}; ++ ++static const struct gb_camera_debugfs_entry gb_camera_debugfs_entries[] = { ++ { ++ .name = "capabilities", ++ .mask = S_IFREG | S_IRUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPABILITIES, ++ .execute = gb_camera_debugfs_capabilities, ++ }, { ++ .name = "configure_streams", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_STREAMS, ++ .execute = gb_camera_debugfs_configure_streams, ++ }, { ++ .name = "capture", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_CAPTURE, ++ .execute = gb_camera_debugfs_capture, ++ }, { ++ .name = "flush", ++ .mask = S_IFREG | S_IRUGO | S_IWUGO, ++ .buffer = GB_CAMERA_DEBUGFS_BUFFER_FLUSH, ++ .execute = gb_camera_debugfs_flush, ++ }, ++}; ++ ++static ssize_t gb_camera_debugfs_read(struct file *file, char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ const struct gb_camera_debugfs_entry *op = file->private_data; ++ struct gb_camera *gcam = file->f_inode->i_private; ++ struct gb_camera_debugfs_buffer *buffer; ++ ssize_t ret; ++ ++ /* For read-only entries the operation is triggered by a read. */ ++ if (!(op->mask & S_IWUGO)) { ++ ret = op->execute(gcam, NULL, 0); ++ if (ret < 0) ++ return ret; ++ } ++ ++ buffer = &gcam->debugfs.buffers[op->buffer]; ++ ++ return simple_read_from_buffer(buf, len, offset, buffer->data, ++ buffer->length); ++} ++ ++static ssize_t gb_camera_debugfs_write(struct file *file, ++ const char __user *buf, size_t len, ++ loff_t *offset) ++{ ++ const struct gb_camera_debugfs_entry *op = file->private_data; ++ struct gb_camera *gcam = file->f_inode->i_private; ++ ssize_t ret; ++ char *kbuf; ++ ++ if (len > 1024) ++ return -EINVAL; ++ ++ kbuf = kmalloc(len + 1, GFP_KERNEL); ++ if (kbuf == NULL) ++ return -ENOMEM; ++ ++ if (copy_from_user(kbuf, buf, len)) { ++ ret = -EFAULT; ++ goto done; ++ } ++ ++ kbuf[len] = '\0'; ++ ++ ret = op->execute(gcam, kbuf, len); ++ ++done: ++ kfree(kbuf); ++ return ret; ++} ++ ++static int gb_camera_debugfs_open(struct inode *inode, struct file *file) ++{ ++ unsigned int i; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { ++ const struct gb_camera_debugfs_entry *entry = ++ &gb_camera_debugfs_entries[i]; ++ ++ if (!strcmp(file->f_path.dentry->d_iname, entry->name)) { ++ file->private_data = (void *)entry; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static const struct file_operations gb_camera_debugfs_ops = { ++ .open = gb_camera_debugfs_open, ++ .read = gb_camera_debugfs_read, ++ .write = gb_camera_debugfs_write, ++}; ++ ++static int gb_camera_debugfs_init(struct gb_camera *gcam) ++{ ++ struct gb_connection *connection = gcam->connection; ++ char dirname[27]; ++ unsigned int i; ++ ++ /* ++ * Create root debugfs entry and a file entry for each camera operation. ++ */ ++ snprintf(dirname, 27, "camera-%u.%u", connection->intf->interface_id, ++ gcam->bundle->id); ++ ++ gcam->debugfs.root = debugfs_create_dir(dirname, gb_debugfs_get()); ++ if (IS_ERR(gcam->debugfs.root)) { ++ gcam_err(gcam, "debugfs root create failed (%ld)\n", ++ PTR_ERR(gcam->debugfs.root)); ++ return PTR_ERR(gcam->debugfs.root); ++ } ++ ++ gcam->debugfs.buffers = vmalloc(sizeof(*gcam->debugfs.buffers) * ++ GB_CAMERA_DEBUGFS_BUFFER_MAX); ++ if (!gcam->debugfs.buffers) ++ return -ENOMEM; ++ ++ for (i = 0; i < ARRAY_SIZE(gb_camera_debugfs_entries); ++i) { ++ const struct gb_camera_debugfs_entry *entry = ++ &gb_camera_debugfs_entries[i]; ++ struct dentry *dentry; ++ ++ gcam->debugfs.buffers[i].length = 0; ++ ++ dentry = debugfs_create_file(entry->name, entry->mask, ++ gcam->debugfs.root, gcam, ++ &gb_camera_debugfs_ops); ++ if (IS_ERR(dentry)) { ++ gcam_err(gcam, ++ "debugfs operation %s create failed (%ld)\n", ++ entry->name, PTR_ERR(dentry)); ++ return PTR_ERR(dentry); ++ } ++ } ++ ++ return 0; ++} ++ ++static void gb_camera_debugfs_cleanup(struct gb_camera *gcam) ++{ ++ debugfs_remove_recursive(gcam->debugfs.root); ++ ++ vfree(gcam->debugfs.buffers); ++} ++ ++/* ----------------------------------------------------------------------------- ++ * Init & Cleanup ++ */ ++ ++static void gb_camera_cleanup(struct gb_camera *gcam) ++{ ++ gb_camera_debugfs_cleanup(gcam); ++ ++ mutex_lock(&gcam->mutex); ++ if (gcam->data_connection) { ++ gb_connection_disable(gcam->data_connection); ++ gb_connection_destroy(gcam->data_connection); ++ gcam->data_connection = NULL; ++ } ++ ++ if (gcam->connection) { ++ gb_connection_disable(gcam->connection); ++ gb_connection_destroy(gcam->connection); ++ gcam->connection = NULL; ++ } ++ mutex_unlock(&gcam->mutex); ++} ++ ++static void gb_camera_release_module(struct kref *ref) ++{ ++ struct gb_camera_module *cam_mod = ++ container_of(ref, struct gb_camera_module, refcount); ++ kfree(cam_mod->priv); ++} ++ ++static int gb_camera_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct gb_connection *conn; ++ struct gb_camera *gcam; ++ u16 mgmt_cport_id = 0; ++ u16 data_cport_id = 0; ++ unsigned int i; ++ int ret; ++ ++ /* ++ * The camera bundle must contain exactly two CPorts, one for the ++ * camera management protocol and one for the camera data protocol. ++ */ ++ if (bundle->num_cports != 2) ++ return -ENODEV; ++ ++ for (i = 0; i < bundle->num_cports; ++i) { ++ struct greybus_descriptor_cport *desc = &bundle->cport_desc[i]; ++ ++ switch (desc->protocol_id) { ++ case GREYBUS_PROTOCOL_CAMERA_MGMT: ++ mgmt_cport_id = le16_to_cpu(desc->id); ++ break; ++ case GREYBUS_PROTOCOL_CAMERA_DATA: ++ data_cport_id = le16_to_cpu(desc->id); ++ break; ++ default: ++ return -ENODEV; ++ } ++ } ++ ++ if (!mgmt_cport_id || !data_cport_id) ++ return -ENODEV; ++ ++ gcam = kzalloc(sizeof(*gcam), GFP_KERNEL); ++ if (!gcam) ++ return -ENOMEM; ++ ++ mutex_init(&gcam->mutex); ++ ++ gcam->bundle = bundle; ++ gcam->state = GB_CAMERA_STATE_UNCONFIGURED; ++ gcam->data_cport_id = data_cport_id; ++ ++ conn = gb_connection_create(bundle, mgmt_cport_id, ++ gb_camera_request_handler); ++ if (IS_ERR(conn)) { ++ ret = PTR_ERR(conn); ++ goto error; ++ } ++ ++ gcam->connection = conn; ++ gb_connection_set_data(conn, gcam); ++ ++ ret = gb_connection_enable(conn); ++ if (ret) ++ goto error; ++ ++ ret = gb_camera_debugfs_init(gcam); ++ if (ret < 0) ++ goto error; ++ ++ gcam->module.priv = gcam; ++ gcam->module.ops = &gb_cam_ops; ++ gcam->module.interface_id = gcam->connection->intf->interface_id; ++ gcam->module.release = gb_camera_release_module; ++ ret = gb_camera_register(&gcam->module); ++ if (ret < 0) ++ goto error; ++ ++ greybus_set_drvdata(bundle, gcam); ++ ++ gb_pm_runtime_put_autosuspend(gcam->bundle); ++ ++ return 0; ++ ++error: ++ gb_camera_cleanup(gcam); ++ kfree(gcam); ++ return ret; ++} ++ ++static void gb_camera_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ gb_camera_cleanup(gcam); ++ gb_camera_unregister(&gcam->module); ++} ++ ++static const struct greybus_bundle_id gb_camera_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, ++ { }, ++}; ++ ++#ifdef CONFIG_PM ++static int gb_camera_suspend(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ ++ if (gcam->data_connection) ++ gb_connection_disable(gcam->data_connection); ++ ++ gb_connection_disable(gcam->connection); ++ ++ return 0; ++} ++ ++static int gb_camera_resume(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gb_camera *gcam = greybus_get_drvdata(bundle); ++ int ret; ++ ++ ret = gb_connection_enable(gcam->connection); ++ if (ret) { ++ gcam_err(gcam, "failed to enable connection: %d\n", ret); ++ return ret; ++ } ++ ++ if (gcam->data_connection) { ++ ret = gb_connection_enable(gcam->data_connection); ++ if (ret) { ++ gcam_err(gcam, ++ "failed to enable data connection: %d\n", ret); ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_camera_pm_ops = { ++ SET_RUNTIME_PM_OPS(gb_camera_suspend, gb_camera_resume, NULL) ++}; ++ ++static struct greybus_driver gb_camera_driver = { ++ .name = "camera", ++ .probe = gb_camera_probe, ++ .disconnect = gb_camera_disconnect, ++ .id_table = gb_camera_id_table, ++ .driver.pm = &gb_camera_pm_ops, ++}; ++ ++module_greybus_driver(gb_camera_driver); ++ ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/gb-camera.h +@@ -0,0 +1,127 @@ ++/* ++ * Greybus Camera protocol driver. ++ * ++ * Copyright 2015 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++#ifndef __GB_CAMERA_H ++#define __GB_CAMERA_H ++ ++#include <linux/v4l2-mediabus.h> ++ ++/* Input flags need to be set from the caller */ ++#define GB_CAMERA_IN_FLAG_TEST (1 << 0) ++/* Output flags returned */ ++#define GB_CAMERA_OUT_FLAG_ADJUSTED (1 << 0) ++ ++/** ++ * struct gb_camera_stream - Represents greybus camera stream. ++ * @width: Stream width in pixels. ++ * @height: Stream height in pixels. ++ * @pixel_code: Media bus pixel code. ++ * @vc: MIPI CSI virtual channel. ++ * @dt: MIPI CSI data types. Most formats use a single data type, in which case ++ * the second element will be ignored. ++ * @max_size: Maximum size of a frame in bytes. The camera module guarantees ++ * that all data between the Frame Start and Frame End packet for ++ * the associated virtual channel and data type(s) will not exceed ++ * this size. ++ */ ++struct gb_camera_stream { ++ unsigned int width; ++ unsigned int height; ++ enum v4l2_mbus_pixelcode pixel_code; ++ unsigned int vc; ++ unsigned int dt[2]; ++ unsigned int max_size; ++}; ++ ++/** ++ * struct gb_camera_csi_params - CSI configuration parameters ++ * @num_lanes: number of CSI data lanes ++ * @clk_freq: CSI clock frequency in Hz ++ */ ++struct gb_camera_csi_params { ++ unsigned int num_lanes; ++ unsigned int clk_freq; ++}; ++ ++/** ++ * struct gb_camera_ops - Greybus camera operations, used by the Greybus camera ++ * driver to expose operations to the host camera driver. ++ * @capabilities: Retrieve camera capabilities and store them in the buffer ++ * 'buf' capabilities. The buffer maximum size is specified by ++ * the caller in the 'size' parameter, and the effective ++ * capabilities size is returned from the function. If the buffer ++ * size is too small to hold the capabilities an error is ++ * returned and the buffer is left untouched. ++ * ++ * @configure_streams: Negotiate configuration and prepare the module for video ++ * capture. The caller specifies the number of streams it ++ * requests in the 'nstreams' argument and the associated ++ * streams configurations in the 'streams' argument. The ++ * GB_CAMERA_IN_FLAG_TEST 'flag' can be set to test a ++ * configuration without applying it, otherwise the ++ * configuration is applied by the module. The module can ++ * decide to modify the requested configuration, including ++ * using a different number of streams. In that case the ++ * modified configuration won't be applied, the ++ * GB_CAMERA_OUT_FLAG_ADJUSTED 'flag' will be set upon ++ * return, and the modified configuration and number of ++ * streams stored in 'streams' and 'array'. The module ++ * returns its CSI-2 bus parameters in the 'csi_params' ++ * structure in all cases. ++ * ++ * @capture: Submit a capture request. The supplied 'request_id' must be unique ++ * and higher than the IDs of all the previously submitted requests. ++ * The 'streams' argument specifies which streams are affected by the ++ * request in the form of a bitmask, with bits corresponding to the ++ * configured streams indexes. If the request contains settings, the ++ * 'settings' argument points to the settings buffer and its size is ++ * specified by the 'settings_size' argument. Otherwise the 'settings' ++ * argument should be set to NULL and 'settings_size' to 0. ++ * ++ * @flush: Flush the capture requests queue. Return the ID of the last request ++ * that will processed by the device before it stops transmitting video ++ * frames. All queued capture requests with IDs higher than the returned ++ * ID will be dropped without being processed. ++ */ ++struct gb_camera_ops { ++ ssize_t (*capabilities)(void *priv, char *buf, size_t len); ++ int (*configure_streams)(void *priv, unsigned int *nstreams, ++ unsigned int *flags, struct gb_camera_stream *streams, ++ struct gb_camera_csi_params *csi_params); ++ int (*capture)(void *priv, u32 request_id, ++ unsigned int streams, unsigned int num_frames, ++ size_t settings_size, const void *settings); ++ int (*flush)(void *priv, u32 *request_id); ++}; ++ ++/** ++ * struct gb_camera_module - Represents greybus camera module. ++ * @priv: Module private data, passed to all camera operations. ++ * @ops: Greybus camera operation callbacks. ++ * @interface_id: Interface id of the module. ++ * @refcount: Reference counting object. ++ * @release: Module release function. ++ * @list: List entry in the camera modules list. ++ */ ++struct gb_camera_module { ++ void *priv; ++ const struct gb_camera_ops *ops; ++ ++ unsigned int interface_id; ++ struct kref refcount; ++ void (*release)(struct kref *kref); ++ struct list_head list; /* Global list */ ++}; ++ ++#define gb_camera_call(f, op, args...) \ ++ (!(f) ? -ENODEV : (((f)->ops->op) ? \ ++ (f)->ops->op((f)->priv, ##args) : -ENOIOCTLCMD)) ++ ++int gb_camera_register(struct gb_camera_module *module); ++int gb_camera_unregister(struct gb_camera_module *module); ++ ++#endif /* __GB_CAMERA_H */ diff --git a/greybus_core.patch b/greybus_core.patch new file mode 100644 index 00000000000000..84ecc0b4c25779 --- /dev/null +++ b/greybus_core.patch @@ -0,0 +1,4431 @@ + +--- + drivers/greybus/arpc.h | 109 +++ + drivers/greybus/authentication.c | 429 ++++++++++++++ + drivers/greybus/bundle.c | 253 ++++++++ + drivers/greybus/bundle.h | 90 ++ + drivers/greybus/connection.c | 938 +++++++++++++++++++++++++++++++ + drivers/greybus/connection.h | 129 ++++ + drivers/greybus/control.c | 635 ++++++++++++++++++++ + drivers/greybus/control.h | 65 ++ + drivers/greybus/core.c | 361 +++++++++++ + drivers/greybus/debugfs.c | 31 + + drivers/greybus/devices | 11 + drivers/greybus/greybus.h | 154 +++++ + drivers/greybus/greybus_authentication.h | 120 +++ + drivers/greybus/greybus_id.h | 26 + drivers/greybus/greybus_manifest.h | 177 +++++ + drivers/greybus/manifest.c | 535 +++++++++++++++++ + drivers/greybus/manifest.h | 16 + drivers/greybus/module.c | 238 +++++++ + drivers/greybus/module.h | 34 + + 19 files changed, 4351 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/arpc.h +@@ -0,0 +1,109 @@ ++/* ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef __ARPC_H ++#define __ARPC_H ++ ++/* APBridgeA RPC (ARPC) */ ++ ++enum arpc_result { ++ ARPC_SUCCESS = 0x00, ++ ARPC_NO_MEMORY = 0x01, ++ ARPC_INVALID = 0x02, ++ ARPC_TIMEOUT = 0x03, ++ ARPC_UNKNOWN_ERROR = 0xff, ++}; ++ ++struct arpc_request_message { ++ __le16 id; /* RPC unique id */ ++ __le16 size; /* Size in bytes of header + payload */ ++ __u8 type; /* RPC type */ ++ __u8 data[0]; /* ARPC data */ ++} __packed; ++ ++struct arpc_response_message { ++ __le16 id; /* RPC unique id */ ++ __u8 result; /* Result of RPC */ ++} __packed; ++ ++ ++/* ARPC requests */ ++#define ARPC_TYPE_CPORT_CONNECTED 0x01 ++#define ARPC_TYPE_CPORT_QUIESCE 0x02 ++#define ARPC_TYPE_CPORT_CLEAR 0x03 ++#define ARPC_TYPE_CPORT_FLUSH 0x04 ++#define ARPC_TYPE_CPORT_SHUTDOWN 0x05 ++ ++struct arpc_cport_connected_req { ++ __le16 cport_id; ++} __packed; ++ ++struct arpc_cport_quiesce_req { ++ __le16 cport_id; ++ __le16 peer_space; ++ __le16 timeout; ++} __packed; ++ ++struct arpc_cport_clear_req { ++ __le16 cport_id; ++} __packed; ++ ++struct arpc_cport_flush_req { ++ __le16 cport_id; ++} __packed; ++ ++struct arpc_cport_shutdown_req { ++ __le16 cport_id; ++ __le16 timeout; ++ __u8 phase; ++} __packed; ++ ++#endif /* __ARPC_H */ +--- /dev/null ++++ b/drivers/greybus/authentication.c +@@ -0,0 +1,429 @@ ++/* ++ * Greybus Component Authentication Protocol (CAP) Driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++ ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/ioctl.h> ++#include <linux/uaccess.h> ++ ++#include "greybus_authentication.h" ++#include "firmware.h" ++#include "greybus.h" ++ ++#define CAP_TIMEOUT_MS 1000 ++ ++/* ++ * Number of minor devices this driver supports. ++ * There will be exactly one required per Interface. ++ */ ++#define NUM_MINORS U8_MAX ++ ++struct gb_cap { ++ struct device *parent; ++ struct gb_connection *connection; ++ struct kref kref; ++ struct list_head node; ++ bool disabled; /* connection getting disabled */ ++ ++ struct mutex mutex; ++ struct cdev cdev; ++ struct device *class_device; ++ dev_t dev_num; ++}; ++ ++static struct class *cap_class; ++static dev_t cap_dev_num; ++static DEFINE_IDA(cap_minors_map); ++static LIST_HEAD(cap_list); ++static DEFINE_MUTEX(list_mutex); ++ ++static void cap_kref_release(struct kref *kref) ++{ ++ struct gb_cap *cap = container_of(kref, struct gb_cap, kref); ++ ++ kfree(cap); ++} ++ ++/* ++ * All users of cap take a reference (from within list_mutex lock), before ++ * they get a pointer to play with. And the structure will be freed only after ++ * the last user has put the reference to it. ++ */ ++static void put_cap(struct gb_cap *cap) ++{ ++ kref_put(&cap->kref, cap_kref_release); ++} ++ ++/* Caller must call put_cap() after using struct gb_cap */ ++static struct gb_cap *get_cap(struct cdev *cdev) ++{ ++ struct gb_cap *cap; ++ ++ mutex_lock(&list_mutex); ++ ++ list_for_each_entry(cap, &cap_list, node) { ++ if (&cap->cdev == cdev) { ++ kref_get(&cap->kref); ++ goto unlock; ++ } ++ } ++ ++ cap = NULL; ++ ++unlock: ++ mutex_unlock(&list_mutex); ++ ++ return cap; ++} ++ ++static int cap_get_endpoint_uid(struct gb_cap *cap, u8 *euid) ++{ ++ struct gb_connection *connection = cap->connection; ++ struct gb_cap_get_endpoint_uid_response response; ++ int ret; ++ ++ ret = gb_operation_sync(connection, GB_CAP_TYPE_GET_ENDPOINT_UID, NULL, ++ 0, &response, sizeof(response)); ++ if (ret) { ++ dev_err(cap->parent, "failed to get endpoint uid (%d)\n", ret); ++ return ret; ++ } ++ ++ memcpy(euid, response.uid, sizeof(response.uid)); ++ ++ return 0; ++} ++ ++static int cap_get_ims_certificate(struct gb_cap *cap, u32 class, u32 id, ++ u8 *certificate, u32 *size, u8 *result) ++{ ++ struct gb_connection *connection = cap->connection; ++ struct gb_cap_get_ims_certificate_request *request; ++ struct gb_cap_get_ims_certificate_response *response; ++ size_t max_size = gb_operation_get_payload_size_max(connection); ++ struct gb_operation *op; ++ int ret; ++ ++ op = gb_operation_create_flags(connection, ++ GB_CAP_TYPE_GET_IMS_CERTIFICATE, ++ sizeof(*request), max_size, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ GFP_KERNEL); ++ if (!op) ++ return -ENOMEM; ++ ++ request = op->request->payload; ++ request->certificate_class = cpu_to_le32(class); ++ request->certificate_id = cpu_to_le32(id); ++ ++ ret = gb_operation_request_send_sync(op); ++ if (ret) { ++ dev_err(cap->parent, "failed to get certificate (%d)\n", ret); ++ goto done; ++ } ++ ++ response = op->response->payload; ++ *result = response->result_code; ++ *size = op->response->payload_size - sizeof(*response); ++ memcpy(certificate, response->certificate, *size); ++ ++done: ++ gb_operation_put(op); ++ return ret; ++} ++ ++static int cap_authenticate(struct gb_cap *cap, u32 auth_type, u8 *uid, ++ u8 *challenge, u8 *result, u8 *auth_response, ++ u32 *signature_size, u8 *signature) ++{ ++ struct gb_connection *connection = cap->connection; ++ struct gb_cap_authenticate_request *request; ++ struct gb_cap_authenticate_response *response; ++ size_t max_size = gb_operation_get_payload_size_max(connection); ++ struct gb_operation *op; ++ int ret; ++ ++ op = gb_operation_create_flags(connection, GB_CAP_TYPE_AUTHENTICATE, ++ sizeof(*request), max_size, ++ GB_OPERATION_FLAG_SHORT_RESPONSE, ++ GFP_KERNEL); ++ if (!op) ++ return -ENOMEM; ++ ++ request = op->request->payload; ++ request->auth_type = cpu_to_le32(auth_type); ++ memcpy(request->uid, uid, sizeof(request->uid)); ++ memcpy(request->challenge, challenge, sizeof(request->challenge)); ++ ++ ret = gb_operation_request_send_sync(op); ++ if (ret) { ++ dev_err(cap->parent, "failed to authenticate (%d)\n", ret); ++ goto done; ++ } ++ ++ response = op->response->payload; ++ *result = response->result_code; ++ *signature_size = op->response->payload_size - sizeof(*response); ++ memcpy(auth_response, response->response, sizeof(response->response)); ++ memcpy(signature, response->signature, *signature_size); ++ ++done: ++ gb_operation_put(op); ++ return ret; ++} ++ ++/* Char device fops */ ++ ++static int cap_open(struct inode *inode, struct file *file) ++{ ++ struct gb_cap *cap = get_cap(inode->i_cdev); ++ ++ /* cap structure can't get freed until file descriptor is closed */ ++ if (cap) { ++ file->private_data = cap; ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++static int cap_release(struct inode *inode, struct file *file) ++{ ++ struct gb_cap *cap = file->private_data; ++ ++ put_cap(cap); ++ return 0; ++} ++ ++static int cap_ioctl(struct gb_cap *cap, unsigned int cmd, ++ void __user *buf) ++{ ++ struct cap_ioc_get_endpoint_uid endpoint_uid; ++ struct cap_ioc_get_ims_certificate *ims_cert; ++ struct cap_ioc_authenticate *authenticate; ++ size_t size; ++ int ret; ++ ++ switch (cmd) { ++ case CAP_IOC_GET_ENDPOINT_UID: ++ ret = cap_get_endpoint_uid(cap, endpoint_uid.uid); ++ if (ret) ++ return ret; ++ ++ if (copy_to_user(buf, &endpoint_uid, sizeof(endpoint_uid))) ++ return -EFAULT; ++ ++ return 0; ++ case CAP_IOC_GET_IMS_CERTIFICATE: ++ size = sizeof(*ims_cert); ++ ims_cert = memdup_user(buf, size); ++ if (IS_ERR(ims_cert)) ++ return PTR_ERR(ims_cert); ++ ++ ret = cap_get_ims_certificate(cap, ims_cert->certificate_class, ++ ims_cert->certificate_id, ++ ims_cert->certificate, ++ &ims_cert->cert_size, ++ &ims_cert->result_code); ++ if (!ret && copy_to_user(buf, ims_cert, size)) ++ ret = -EFAULT; ++ kfree(ims_cert); ++ ++ return ret; ++ case CAP_IOC_AUTHENTICATE: ++ size = sizeof(*authenticate); ++ authenticate = memdup_user(buf, size); ++ if (IS_ERR(authenticate)) ++ return PTR_ERR(authenticate); ++ ++ ret = cap_authenticate(cap, authenticate->auth_type, ++ authenticate->uid, ++ authenticate->challenge, ++ &authenticate->result_code, ++ authenticate->response, ++ &authenticate->signature_size, ++ authenticate->signature); ++ if (!ret && copy_to_user(buf, authenticate, size)) ++ ret = -EFAULT; ++ kfree(authenticate); ++ ++ return ret; ++ default: ++ return -ENOTTY; ++ } ++} ++ ++static long cap_ioctl_unlocked(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct gb_cap *cap = file->private_data; ++ struct gb_bundle *bundle = cap->connection->bundle; ++ int ret = -ENODEV; ++ ++ /* ++ * Serialize ioctls. ++ * ++ * We don't want the user to do multiple authentication operations in ++ * parallel. ++ * ++ * This is also used to protect ->disabled, which is used to check if ++ * the connection is getting disconnected, so that we don't start any ++ * new operations. ++ */ ++ mutex_lock(&cap->mutex); ++ if (!cap->disabled) { ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (!ret) { ++ ret = cap_ioctl(cap, cmd, (void __user *)arg); ++ gb_pm_runtime_put_autosuspend(bundle); ++ } ++ } ++ mutex_unlock(&cap->mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations cap_fops = { ++ .owner = THIS_MODULE, ++ .open = cap_open, ++ .release = cap_release, ++ .unlocked_ioctl = cap_ioctl_unlocked, ++}; ++ ++int gb_cap_connection_init(struct gb_connection *connection) ++{ ++ struct gb_cap *cap; ++ int ret, minor; ++ ++ if (!connection) ++ return 0; ++ ++ cap = kzalloc(sizeof(*cap), GFP_KERNEL); ++ if (!cap) ++ return -ENOMEM; ++ ++ cap->parent = &connection->bundle->dev; ++ cap->connection = connection; ++ mutex_init(&cap->mutex); ++ gb_connection_set_data(connection, cap); ++ kref_init(&cap->kref); ++ ++ mutex_lock(&list_mutex); ++ list_add(&cap->node, &cap_list); ++ mutex_unlock(&list_mutex); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto err_list_del; ++ ++ minor = ida_simple_get(&cap_minors_map, 0, NUM_MINORS, GFP_KERNEL); ++ if (minor < 0) { ++ ret = minor; ++ goto err_connection_disable; ++ } ++ ++ /* Add a char device to allow userspace to interact with cap */ ++ cap->dev_num = MKDEV(MAJOR(cap_dev_num), minor); ++ cdev_init(&cap->cdev, &cap_fops); ++ ++ ret = cdev_add(&cap->cdev, cap->dev_num, 1); ++ if (ret) ++ goto err_remove_ida; ++ ++ /* Add a soft link to the previously added char-dev within the bundle */ ++ cap->class_device = device_create(cap_class, cap->parent, cap->dev_num, ++ NULL, "gb-authenticate-%d", minor); ++ if (IS_ERR(cap->class_device)) { ++ ret = PTR_ERR(cap->class_device); ++ goto err_del_cdev; ++ } ++ ++ return 0; ++ ++err_del_cdev: ++ cdev_del(&cap->cdev); ++err_remove_ida: ++ ida_simple_remove(&cap_minors_map, minor); ++err_connection_disable: ++ gb_connection_disable(connection); ++err_list_del: ++ mutex_lock(&list_mutex); ++ list_del(&cap->node); ++ mutex_unlock(&list_mutex); ++ ++ put_cap(cap); ++ ++ return ret; ++} ++ ++void gb_cap_connection_exit(struct gb_connection *connection) ++{ ++ struct gb_cap *cap; ++ ++ if (!connection) ++ return; ++ ++ cap = gb_connection_get_data(connection); ++ ++ device_destroy(cap_class, cap->dev_num); ++ cdev_del(&cap->cdev); ++ ida_simple_remove(&cap_minors_map, MINOR(cap->dev_num)); ++ ++ /* ++ * Disallow any new ioctl operations on the char device and wait for ++ * existing ones to finish. ++ */ ++ mutex_lock(&cap->mutex); ++ cap->disabled = true; ++ mutex_unlock(&cap->mutex); ++ ++ /* All pending greybus operations should have finished by now */ ++ gb_connection_disable(cap->connection); ++ ++ /* Disallow new users to get access to the cap structure */ ++ mutex_lock(&list_mutex); ++ list_del(&cap->node); ++ mutex_unlock(&list_mutex); ++ ++ /* ++ * All current users of cap would have taken a reference to it by ++ * now, we can drop our reference and wait the last user will get ++ * cap freed. ++ */ ++ put_cap(cap); ++} ++ ++int cap_init(void) ++{ ++ int ret; ++ ++ cap_class = class_create(THIS_MODULE, "gb_authenticate"); ++ if (IS_ERR(cap_class)) ++ return PTR_ERR(cap_class); ++ ++ ret = alloc_chrdev_region(&cap_dev_num, 0, NUM_MINORS, ++ "gb_authenticate"); ++ if (ret) ++ goto err_remove_class; ++ ++ return 0; ++ ++err_remove_class: ++ class_destroy(cap_class); ++ return ret; ++} ++ ++void cap_exit(void) ++{ ++ unregister_chrdev_region(cap_dev_num, NUM_MINORS); ++ class_destroy(cap_class); ++ ida_destroy(&cap_minors_map); ++} +--- /dev/null ++++ b/drivers/greybus/bundle.c +@@ -0,0 +1,253 @@ ++/* ++ * Greybus bundles ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++static ssize_t bundle_class_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ ++ return sprintf(buf, "0x%02x\n", bundle->class); ++} ++static DEVICE_ATTR_RO(bundle_class); ++ ++static ssize_t bundle_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ ++ return sprintf(buf, "%u\n", bundle->id); ++} ++static DEVICE_ATTR_RO(bundle_id); ++ ++static ssize_t state_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ ++ if (bundle->state == NULL) ++ return sprintf(buf, "\n"); ++ ++ return sprintf(buf, "%s\n", bundle->state); ++} ++ ++static ssize_t state_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ ++ kfree(bundle->state); ++ bundle->state = kstrdup(buf, GFP_KERNEL); ++ if (!bundle->state) ++ return -ENOMEM; ++ ++ /* Tell userspace that the file contents changed */ ++ sysfs_notify(&bundle->dev.kobj, NULL, "state"); ++ ++ return size; ++} ++static DEVICE_ATTR_RW(state); ++ ++static struct attribute *bundle_attrs[] = { ++ &dev_attr_bundle_class.attr, ++ &dev_attr_bundle_id.attr, ++ &dev_attr_state.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(bundle); ++ ++static struct gb_bundle *gb_bundle_find(struct gb_interface *intf, ++ u8 bundle_id) ++{ ++ struct gb_bundle *bundle; ++ ++ list_for_each_entry(bundle, &intf->bundles, links) { ++ if (bundle->id == bundle_id) ++ return bundle; ++ } ++ ++ return NULL; ++} ++ ++static void gb_bundle_release(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ ++ trace_gb_bundle_release(bundle); ++ ++ kfree(bundle->state); ++ kfree(bundle->cport_desc); ++ kfree(bundle); ++} ++ ++#ifdef CONFIG_PM ++static void gb_bundle_disable_all_connections(struct gb_bundle *bundle) ++{ ++ struct gb_connection *connection; ++ ++ list_for_each_entry(connection, &bundle->connections, bundle_links) ++ gb_connection_disable(connection); ++} ++ ++static void gb_bundle_enable_all_connections(struct gb_bundle *bundle) ++{ ++ struct gb_connection *connection; ++ ++ list_for_each_entry(connection, &bundle->connections, bundle_links) ++ gb_connection_enable(connection); ++} ++ ++static int gb_bundle_suspend(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ const struct dev_pm_ops *pm = dev->driver->pm; ++ int ret; ++ ++ if (pm && pm->runtime_suspend) { ++ ret = pm->runtime_suspend(&bundle->dev); ++ if (ret) ++ return ret; ++ } else { ++ gb_bundle_disable_all_connections(bundle); ++ } ++ ++ ret = gb_control_bundle_suspend(bundle->intf->control, bundle->id); ++ if (ret) { ++ if (pm && pm->runtime_resume) ++ ret = pm->runtime_resume(dev); ++ else ++ gb_bundle_enable_all_connections(bundle); ++ ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_bundle_resume(struct device *dev) ++{ ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ const struct dev_pm_ops *pm = dev->driver->pm; ++ int ret; ++ ++ ret = gb_control_bundle_resume(bundle->intf->control, bundle->id); ++ if (ret) ++ return ret; ++ ++ if (pm && pm->runtime_resume) { ++ ret = pm->runtime_resume(dev); ++ if (ret) ++ return ret; ++ } else { ++ gb_bundle_enable_all_connections(bundle); ++ } ++ ++ return 0; ++} ++ ++static int gb_bundle_idle(struct device *dev) ++{ ++ pm_runtime_mark_last_busy(dev); ++ pm_request_autosuspend(dev); ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_bundle_pm_ops = { ++ SET_RUNTIME_PM_OPS(gb_bundle_suspend, gb_bundle_resume, gb_bundle_idle) ++}; ++ ++struct device_type greybus_bundle_type = { ++ .name = "greybus_bundle", ++ .release = gb_bundle_release, ++ .pm = &gb_bundle_pm_ops, ++}; ++ ++/* ++ * Create a gb_bundle structure to represent a discovered ++ * bundle. Returns a pointer to the new bundle or a null ++ * pointer if a failure occurs due to memory exhaustion. ++ */ ++struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, ++ u8 class) ++{ ++ struct gb_bundle *bundle; ++ ++ if (bundle_id == BUNDLE_ID_NONE) { ++ dev_err(&intf->dev, "can't use bundle id %u\n", bundle_id); ++ return NULL; ++ } ++ ++ /* ++ * Reject any attempt to reuse a bundle id. We initialize ++ * these serially, so there's no need to worry about keeping ++ * the interface bundle list locked here. ++ */ ++ if (gb_bundle_find(intf, bundle_id)) { ++ dev_err(&intf->dev, "duplicate bundle id %u\n", bundle_id); ++ return NULL; ++ } ++ ++ bundle = kzalloc(sizeof(*bundle), GFP_KERNEL); ++ if (!bundle) ++ return NULL; ++ ++ bundle->intf = intf; ++ bundle->id = bundle_id; ++ bundle->class = class; ++ INIT_LIST_HEAD(&bundle->connections); ++ ++ bundle->dev.parent = &intf->dev; ++ bundle->dev.bus = &greybus_bus_type; ++ bundle->dev.type = &greybus_bundle_type; ++ bundle->dev.groups = bundle_groups; ++ bundle->dev.dma_mask = intf->dev.dma_mask; ++ device_initialize(&bundle->dev); ++ dev_set_name(&bundle->dev, "%s.%d", dev_name(&intf->dev), bundle_id); ++ ++ list_add(&bundle->links, &intf->bundles); ++ ++ trace_gb_bundle_create(bundle); ++ ++ return bundle; ++} ++ ++int gb_bundle_add(struct gb_bundle *bundle) ++{ ++ int ret; ++ ++ ret = device_add(&bundle->dev); ++ if (ret) { ++ dev_err(&bundle->dev, "failed to register bundle: %d\n", ret); ++ return ret; ++ } ++ ++ trace_gb_bundle_add(bundle); ++ ++ return 0; ++} ++ ++/* ++ * Tear down a previously set up bundle. ++ */ ++void gb_bundle_destroy(struct gb_bundle *bundle) ++{ ++ trace_gb_bundle_destroy(bundle); ++ ++ if (device_is_registered(&bundle->dev)) ++ device_del(&bundle->dev); ++ ++ list_del(&bundle->links); ++ ++ put_device(&bundle->dev); ++} +--- /dev/null ++++ b/drivers/greybus/bundle.h +@@ -0,0 +1,90 @@ ++/* ++ * Greybus bundles ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __BUNDLE_H ++#define __BUNDLE_H ++ ++#include <linux/list.h> ++ ++#define BUNDLE_ID_NONE U8_MAX ++ ++/* Greybus "public" definitions" */ ++struct gb_bundle { ++ struct device dev; ++ struct gb_interface *intf; ++ ++ u8 id; ++ u8 class; ++ u8 class_major; ++ u8 class_minor; ++ ++ size_t num_cports; ++ struct greybus_descriptor_cport *cport_desc; ++ ++ struct list_head connections; ++ u8 *state; ++ ++ struct list_head links; /* interface->bundles */ ++}; ++#define to_gb_bundle(d) container_of(d, struct gb_bundle, dev) ++ ++/* Greybus "private" definitions" */ ++struct gb_bundle *gb_bundle_create(struct gb_interface *intf, u8 bundle_id, ++ u8 class); ++int gb_bundle_add(struct gb_bundle *bundle); ++void gb_bundle_destroy(struct gb_bundle *bundle); ++ ++/* Bundle Runtime PM wrappers */ ++#ifdef CONFIG_PM ++static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle) ++{ ++ int retval; ++ ++ retval = pm_runtime_get_sync(&bundle->dev); ++ if (retval < 0) { ++ dev_err(&bundle->dev, ++ "pm_runtime_get_sync failed: %d\n", retval); ++ pm_runtime_put_noidle(&bundle->dev); ++ return retval; ++ } ++ ++ return 0; ++} ++ ++static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle) ++{ ++ int retval; ++ ++ pm_runtime_mark_last_busy(&bundle->dev); ++ retval = pm_runtime_put_autosuspend(&bundle->dev); ++ ++ return retval; ++} ++ ++static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle) ++{ ++ pm_runtime_get_noresume(&bundle->dev); ++} ++ ++static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle) ++{ ++ pm_runtime_put_noidle(&bundle->dev); ++} ++ ++#else ++static inline int gb_pm_runtime_get_sync(struct gb_bundle *bundle) ++{ return 0; } ++static inline int gb_pm_runtime_put_autosuspend(struct gb_bundle *bundle) ++{ return 0; } ++ ++static inline void gb_pm_runtime_get_noresume(struct gb_bundle *bundle) {} ++static inline void gb_pm_runtime_put_noidle(struct gb_bundle *bundle) {} ++#endif ++ ++#endif /* __BUNDLE_H */ +--- /dev/null ++++ b/drivers/greybus/connection.c +@@ -0,0 +1,938 @@ ++/* ++ * Greybus connections ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++ ++#define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000 ++ ++ ++static void gb_connection_kref_release(struct kref *kref); ++ ++ ++static DEFINE_SPINLOCK(gb_connections_lock); ++static DEFINE_MUTEX(gb_connection_mutex); ++ ++ ++/* Caller holds gb_connection_mutex. */ ++static bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id) ++{ ++ struct gb_host_device *hd = intf->hd; ++ struct gb_connection *connection; ++ ++ list_for_each_entry(connection, &hd->connections, hd_links) { ++ if (connection->intf == intf && ++ connection->intf_cport_id == cport_id) ++ return true; ++ } ++ ++ return false; ++} ++ ++static void gb_connection_get(struct gb_connection *connection) ++{ ++ kref_get(&connection->kref); ++ ++ trace_gb_connection_get(connection); ++} ++ ++static void gb_connection_put(struct gb_connection *connection) ++{ ++ trace_gb_connection_put(connection); ++ ++ kref_put(&connection->kref, gb_connection_kref_release); ++} ++ ++/* ++ * Returns a reference-counted pointer to the connection if found. ++ */ ++static struct gb_connection * ++gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct gb_connection *connection; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gb_connections_lock, flags); ++ list_for_each_entry(connection, &hd->connections, hd_links) ++ if (connection->hd_cport_id == cport_id) { ++ gb_connection_get(connection); ++ goto found; ++ } ++ connection = NULL; ++found: ++ spin_unlock_irqrestore(&gb_connections_lock, flags); ++ ++ return connection; ++} ++ ++/* ++ * Callback from the host driver to let us know that data has been ++ * received on the bundle. ++ */ ++void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, ++ u8 *data, size_t length) ++{ ++ struct gb_connection *connection; ++ ++ trace_gb_hd_in(hd); ++ ++ connection = gb_connection_hd_find(hd, cport_id); ++ if (!connection) { ++ dev_err(&hd->dev, ++ "nonexistent connection (%zu bytes dropped)\n", length); ++ return; ++ } ++ gb_connection_recv(connection, data, length); ++ gb_connection_put(connection); ++} ++EXPORT_SYMBOL_GPL(greybus_data_rcvd); ++ ++static void gb_connection_kref_release(struct kref *kref) ++{ ++ struct gb_connection *connection; ++ ++ connection = container_of(kref, struct gb_connection, kref); ++ ++ trace_gb_connection_release(connection); ++ ++ kfree(connection); ++} ++ ++static void gb_connection_init_name(struct gb_connection *connection) ++{ ++ u16 hd_cport_id = connection->hd_cport_id; ++ u16 cport_id = 0; ++ u8 intf_id = 0; ++ ++ if (connection->intf) { ++ intf_id = connection->intf->interface_id; ++ cport_id = connection->intf_cport_id; ++ } ++ ++ snprintf(connection->name, sizeof(connection->name), ++ "%u/%u:%u", hd_cport_id, intf_id, cport_id); ++} ++ ++/* ++ * _gb_connection_create() - create a Greybus connection ++ * @hd: host device of the connection ++ * @hd_cport_id: host-device cport id, or -1 for dynamic allocation ++ * @intf: remote interface, or NULL for static connections ++ * @bundle: remote-interface bundle (may be NULL) ++ * @cport_id: remote-interface cport id, or 0 for static connections ++ * @handler: request handler (may be NULL) ++ * @flags: connection flags ++ * ++ * Create a Greybus connection, representing the bidirectional link ++ * between a CPort on a (local) Greybus host device and a CPort on ++ * another Greybus interface. ++ * ++ * A connection also maintains the state of operations sent over the ++ * connection. ++ * ++ * Serialised against concurrent create and destroy using the ++ * gb_connection_mutex. ++ * ++ * Return: A pointer to the new connection if successful, or an ERR_PTR ++ * otherwise. ++ */ ++static struct gb_connection * ++_gb_connection_create(struct gb_host_device *hd, int hd_cport_id, ++ struct gb_interface *intf, ++ struct gb_bundle *bundle, int cport_id, ++ gb_request_handler_t handler, ++ unsigned long flags) ++{ ++ struct gb_connection *connection; ++ int ret; ++ ++ mutex_lock(&gb_connection_mutex); ++ ++ if (intf && gb_connection_cport_in_use(intf, cport_id)) { ++ dev_err(&intf->dev, "cport %u already in use\n", cport_id); ++ ret = -EBUSY; ++ goto err_unlock; ++ } ++ ++ ret = gb_hd_cport_allocate(hd, hd_cport_id, flags); ++ if (ret < 0) { ++ dev_err(&hd->dev, "failed to allocate cport: %d\n", ret); ++ goto err_unlock; ++ } ++ hd_cport_id = ret; ++ ++ connection = kzalloc(sizeof(*connection), GFP_KERNEL); ++ if (!connection) { ++ ret = -ENOMEM; ++ goto err_hd_cport_release; ++ } ++ ++ connection->hd_cport_id = hd_cport_id; ++ connection->intf_cport_id = cport_id; ++ connection->hd = hd; ++ connection->intf = intf; ++ connection->bundle = bundle; ++ connection->handler = handler; ++ connection->flags = flags; ++ if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES)) ++ connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL; ++ connection->state = GB_CONNECTION_STATE_DISABLED; ++ ++ atomic_set(&connection->op_cycle, 0); ++ mutex_init(&connection->mutex); ++ spin_lock_init(&connection->lock); ++ INIT_LIST_HEAD(&connection->operations); ++ ++ connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1, ++ dev_name(&hd->dev), hd_cport_id); ++ if (!connection->wq) { ++ ret = -ENOMEM; ++ goto err_free_connection; ++ } ++ ++ kref_init(&connection->kref); ++ ++ gb_connection_init_name(connection); ++ ++ spin_lock_irq(&gb_connections_lock); ++ list_add(&connection->hd_links, &hd->connections); ++ ++ if (bundle) ++ list_add(&connection->bundle_links, &bundle->connections); ++ else ++ INIT_LIST_HEAD(&connection->bundle_links); ++ ++ spin_unlock_irq(&gb_connections_lock); ++ ++ mutex_unlock(&gb_connection_mutex); ++ ++ trace_gb_connection_create(connection); ++ ++ return connection; ++ ++err_free_connection: ++ kfree(connection); ++err_hd_cport_release: ++ gb_hd_cport_release(hd, hd_cport_id); ++err_unlock: ++ mutex_unlock(&gb_connection_mutex); ++ ++ return ERR_PTR(ret); ++} ++ ++struct gb_connection * ++gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id, ++ gb_request_handler_t handler) ++{ ++ return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler, ++ GB_CONNECTION_FLAG_HIGH_PRIO); ++} ++ ++struct gb_connection * ++gb_connection_create_control(struct gb_interface *intf) ++{ ++ return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL, ++ GB_CONNECTION_FLAG_CONTROL | ++ GB_CONNECTION_FLAG_HIGH_PRIO); ++} ++ ++struct gb_connection * ++gb_connection_create(struct gb_bundle *bundle, u16 cport_id, ++ gb_request_handler_t handler) ++{ ++ struct gb_interface *intf = bundle->intf; ++ ++ return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, ++ handler, 0); ++} ++EXPORT_SYMBOL_GPL(gb_connection_create); ++ ++struct gb_connection * ++gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id, ++ gb_request_handler_t handler, ++ unsigned long flags) ++{ ++ struct gb_interface *intf = bundle->intf; ++ ++ if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK)) ++ flags &= ~GB_CONNECTION_FLAG_CORE_MASK; ++ ++ return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id, ++ handler, flags); ++} ++EXPORT_SYMBOL_GPL(gb_connection_create_flags); ++ ++struct gb_connection * ++gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id, ++ unsigned long flags) ++{ ++ flags |= GB_CONNECTION_FLAG_OFFLOADED; ++ ++ return gb_connection_create_flags(bundle, cport_id, NULL, flags); ++} ++EXPORT_SYMBOL_GPL(gb_connection_create_offloaded); ++ ++static int gb_connection_hd_cport_enable(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->cport_enable) ++ return 0; ++ ++ ret = hd->driver->cport_enable(hd, connection->hd_cport_id, ++ connection->flags); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to enable host cport: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void gb_connection_hd_cport_disable(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->cport_disable) ++ return; ++ ++ ret = hd->driver->cport_disable(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to disable host cport: %d\n", ++ connection->name, ret); ++ } ++} ++ ++static int gb_connection_hd_cport_connected(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->cport_connected) ++ return 0; ++ ++ ret = hd->driver->cport_connected(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to set connected state: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_connection_hd_cport_flush(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->cport_flush) ++ return 0; ++ ++ ret = hd->driver->cport_flush(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to flush host cport: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_connection_hd_cport_quiesce(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ size_t peer_space; ++ int ret; ++ ++ peer_space = sizeof(struct gb_operation_msg_hdr) + ++ sizeof(struct gb_cport_shutdown_request); ++ ++ if (connection->mode_switch) ++ peer_space += sizeof(struct gb_operation_msg_hdr); ++ ++ ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id, ++ peer_space, ++ GB_CONNECTION_CPORT_QUIESCE_TIMEOUT); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to quiesce host cport: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_connection_hd_cport_clear(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ ret = hd->driver->cport_clear(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to clear host cport: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Request the SVC to create a connection from AP's cport to interface's ++ * cport. ++ */ ++static int ++gb_connection_svc_connection_create(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ struct gb_interface *intf; ++ u8 cport_flags; ++ int ret; ++ ++ if (gb_connection_is_static(connection)) ++ return 0; ++ ++ intf = connection->intf; ++ ++ /* ++ * Enable either E2EFC or CSD, unless no flow control is requested. ++ */ ++ cport_flags = GB_SVC_CPORT_FLAG_CSV_N; ++ if (gb_connection_flow_control_disabled(connection)) { ++ cport_flags |= GB_SVC_CPORT_FLAG_CSD_N; ++ } else if (gb_connection_e2efc_enabled(connection)) { ++ cport_flags |= GB_SVC_CPORT_FLAG_CSD_N | ++ GB_SVC_CPORT_FLAG_E2EFC; ++ } ++ ++ ret = gb_svc_connection_create(hd->svc, ++ hd->svc->ap_intf_id, ++ connection->hd_cport_id, ++ intf->interface_id, ++ connection->intf_cport_id, ++ cport_flags); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to create svc connection: %d\n", ++ connection->name, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++gb_connection_svc_connection_destroy(struct gb_connection *connection) ++{ ++ if (gb_connection_is_static(connection)) ++ return; ++ ++ gb_svc_connection_destroy(connection->hd->svc, ++ connection->hd->svc->ap_intf_id, ++ connection->hd_cport_id, ++ connection->intf->interface_id, ++ connection->intf_cport_id); ++} ++ ++/* Inform Interface about active CPorts */ ++static int gb_connection_control_connected(struct gb_connection *connection) ++{ ++ struct gb_control *control; ++ u16 cport_id = connection->intf_cport_id; ++ int ret; ++ ++ if (gb_connection_is_static(connection)) ++ return 0; ++ ++ if (gb_connection_is_control(connection)) ++ return 0; ++ ++ control = connection->intf->control; ++ ++ ret = gb_control_connected_operation(control, cport_id); ++ if (ret) { ++ dev_err(&connection->bundle->dev, ++ "failed to connect cport: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void ++gb_connection_control_disconnecting(struct gb_connection *connection) ++{ ++ struct gb_control *control; ++ u16 cport_id = connection->intf_cport_id; ++ int ret; ++ ++ if (gb_connection_is_static(connection)) ++ return; ++ ++ control = connection->intf->control; ++ ++ ret = gb_control_disconnecting_operation(control, cport_id); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to send disconnecting: %d\n", ++ connection->name, ret); ++ } ++} ++ ++static void ++gb_connection_control_disconnected(struct gb_connection *connection) ++{ ++ struct gb_control *control; ++ u16 cport_id = connection->intf_cport_id; ++ int ret; ++ ++ if (gb_connection_is_static(connection)) ++ return; ++ ++ control = connection->intf->control; ++ ++ if (gb_connection_is_control(connection)) { ++ if (connection->mode_switch) { ++ ret = gb_control_mode_switch_operation(control); ++ if (ret) { ++ /* ++ * Allow mode switch to time out waiting for ++ * mailbox event. ++ */ ++ return; ++ } ++ } ++ ++ return; ++ } ++ ++ ret = gb_control_disconnected_operation(control, cport_id); ++ if (ret) { ++ dev_warn(&connection->bundle->dev, ++ "failed to disconnect cport: %d\n", ret); ++ } ++} ++ ++static int gb_connection_shutdown_operation(struct gb_connection *connection, ++ u8 phase) ++{ ++ struct gb_cport_shutdown_request *req; ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_operation_create_core(connection, ++ GB_REQUEST_TYPE_CPORT_SHUTDOWN, ++ sizeof(*req), 0, 0, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ req = operation->request->payload; ++ req->phase = phase; ++ ++ ret = gb_operation_request_send_sync(operation); ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static int gb_connection_cport_shutdown(struct gb_connection *connection, ++ u8 phase) ++{ ++ struct gb_host_device *hd = connection->hd; ++ const struct gb_hd_driver *drv = hd->driver; ++ int ret; ++ ++ if (gb_connection_is_static(connection)) ++ return 0; ++ ++ if (gb_connection_is_offloaded(connection)) { ++ if (!drv->cport_shutdown) ++ return 0; ++ ++ ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase, ++ GB_OPERATION_TIMEOUT_DEFAULT); ++ } else { ++ ret = gb_connection_shutdown_operation(connection, phase); ++ } ++ ++ if (ret) { ++ dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d\n", ++ connection->name, phase, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int ++gb_connection_cport_shutdown_phase_1(struct gb_connection *connection) ++{ ++ return gb_connection_cport_shutdown(connection, 1); ++} ++ ++static int ++gb_connection_cport_shutdown_phase_2(struct gb_connection *connection) ++{ ++ return gb_connection_cport_shutdown(connection, 2); ++} ++ ++/* ++ * Cancel all active operations on a connection. ++ * ++ * Locking: Called with connection lock held and state set to DISABLED or ++ * DISCONNECTING. ++ */ ++static void gb_connection_cancel_operations(struct gb_connection *connection, ++ int errno) ++ __must_hold(&connection->lock) ++{ ++ struct gb_operation *operation; ++ ++ while (!list_empty(&connection->operations)) { ++ operation = list_last_entry(&connection->operations, ++ struct gb_operation, links); ++ gb_operation_get(operation); ++ spin_unlock_irq(&connection->lock); ++ ++ if (gb_operation_is_incoming(operation)) ++ gb_operation_cancel_incoming(operation, errno); ++ else ++ gb_operation_cancel(operation, errno); ++ ++ gb_operation_put(operation); ++ ++ spin_lock_irq(&connection->lock); ++ } ++} ++ ++/* ++ * Cancel all active incoming operations on a connection. ++ * ++ * Locking: Called with connection lock held and state set to ENABLED_TX. ++ */ ++static void ++gb_connection_flush_incoming_operations(struct gb_connection *connection, ++ int errno) ++ __must_hold(&connection->lock) ++{ ++ struct gb_operation *operation; ++ bool incoming; ++ ++ while (!list_empty(&connection->operations)) { ++ incoming = false; ++ list_for_each_entry(operation, &connection->operations, ++ links) { ++ if (gb_operation_is_incoming(operation)) { ++ gb_operation_get(operation); ++ incoming = true; ++ break; ++ } ++ } ++ ++ if (!incoming) ++ break; ++ ++ spin_unlock_irq(&connection->lock); ++ ++ /* FIXME: flush, not cancel? */ ++ gb_operation_cancel_incoming(operation, errno); ++ gb_operation_put(operation); ++ ++ spin_lock_irq(&connection->lock); ++ } ++} ++ ++/* ++ * _gb_connection_enable() - enable a connection ++ * @connection: connection to enable ++ * @rx: whether to enable incoming requests ++ * ++ * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and ++ * ENABLED_TX->ENABLED state transitions. ++ * ++ * Locking: Caller holds connection->mutex. ++ */ ++static int _gb_connection_enable(struct gb_connection *connection, bool rx) ++{ ++ int ret; ++ ++ /* Handle ENABLED_TX -> ENABLED transitions. */ ++ if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) { ++ if (!(connection->handler && rx)) ++ return 0; ++ ++ spin_lock_irq(&connection->lock); ++ connection->state = GB_CONNECTION_STATE_ENABLED; ++ spin_unlock_irq(&connection->lock); ++ ++ return 0; ++ } ++ ++ ret = gb_connection_hd_cport_enable(connection); ++ if (ret) ++ return ret; ++ ++ ret = gb_connection_svc_connection_create(connection); ++ if (ret) ++ goto err_hd_cport_clear; ++ ++ ret = gb_connection_hd_cport_connected(connection); ++ if (ret) ++ goto err_svc_connection_destroy; ++ ++ spin_lock_irq(&connection->lock); ++ if (connection->handler && rx) ++ connection->state = GB_CONNECTION_STATE_ENABLED; ++ else ++ connection->state = GB_CONNECTION_STATE_ENABLED_TX; ++ spin_unlock_irq(&connection->lock); ++ ++ ret = gb_connection_control_connected(connection); ++ if (ret) ++ goto err_control_disconnecting; ++ ++ return 0; ++ ++err_control_disconnecting: ++ spin_lock_irq(&connection->lock); ++ connection->state = GB_CONNECTION_STATE_DISCONNECTING; ++ gb_connection_cancel_operations(connection, -ESHUTDOWN); ++ spin_unlock_irq(&connection->lock); ++ ++ /* Transmit queue should already be empty. */ ++ gb_connection_hd_cport_flush(connection); ++ ++ gb_connection_control_disconnecting(connection); ++ gb_connection_cport_shutdown_phase_1(connection); ++ gb_connection_hd_cport_quiesce(connection); ++ gb_connection_cport_shutdown_phase_2(connection); ++ gb_connection_control_disconnected(connection); ++ connection->state = GB_CONNECTION_STATE_DISABLED; ++err_svc_connection_destroy: ++ gb_connection_svc_connection_destroy(connection); ++err_hd_cport_clear: ++ gb_connection_hd_cport_clear(connection); ++ ++ gb_connection_hd_cport_disable(connection); ++ ++ return ret; ++} ++ ++int gb_connection_enable(struct gb_connection *connection) ++{ ++ int ret = 0; ++ ++ mutex_lock(&connection->mutex); ++ ++ if (connection->state == GB_CONNECTION_STATE_ENABLED) ++ goto out_unlock; ++ ++ ret = _gb_connection_enable(connection, true); ++ if (!ret) ++ trace_gb_connection_enable(connection); ++ ++out_unlock: ++ mutex_unlock(&connection->mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_connection_enable); ++ ++int gb_connection_enable_tx(struct gb_connection *connection) ++{ ++ int ret = 0; ++ ++ mutex_lock(&connection->mutex); ++ ++ if (connection->state == GB_CONNECTION_STATE_ENABLED) { ++ ret = -EINVAL; ++ goto out_unlock; ++ } ++ ++ if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) ++ goto out_unlock; ++ ++ ret = _gb_connection_enable(connection, false); ++ if (!ret) ++ trace_gb_connection_enable(connection); ++ ++out_unlock: ++ mutex_unlock(&connection->mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_connection_enable_tx); ++ ++void gb_connection_disable_rx(struct gb_connection *connection) ++{ ++ mutex_lock(&connection->mutex); ++ ++ spin_lock_irq(&connection->lock); ++ if (connection->state != GB_CONNECTION_STATE_ENABLED) { ++ spin_unlock_irq(&connection->lock); ++ goto out_unlock; ++ } ++ connection->state = GB_CONNECTION_STATE_ENABLED_TX; ++ gb_connection_flush_incoming_operations(connection, -ESHUTDOWN); ++ spin_unlock_irq(&connection->lock); ++ ++ trace_gb_connection_disable(connection); ++ ++out_unlock: ++ mutex_unlock(&connection->mutex); ++} ++EXPORT_SYMBOL_GPL(gb_connection_disable_rx); ++ ++void gb_connection_mode_switch_prepare(struct gb_connection *connection) ++{ ++ connection->mode_switch = true; ++} ++ ++void gb_connection_mode_switch_complete(struct gb_connection *connection) ++{ ++ gb_connection_svc_connection_destroy(connection); ++ gb_connection_hd_cport_clear(connection); ++ ++ gb_connection_hd_cport_disable(connection); ++ ++ connection->mode_switch = false; ++} ++ ++void gb_connection_disable(struct gb_connection *connection) ++{ ++ mutex_lock(&connection->mutex); ++ ++ if (connection->state == GB_CONNECTION_STATE_DISABLED) ++ goto out_unlock; ++ ++ trace_gb_connection_disable(connection); ++ ++ spin_lock_irq(&connection->lock); ++ connection->state = GB_CONNECTION_STATE_DISCONNECTING; ++ gb_connection_cancel_operations(connection, -ESHUTDOWN); ++ spin_unlock_irq(&connection->lock); ++ ++ gb_connection_hd_cport_flush(connection); ++ ++ gb_connection_control_disconnecting(connection); ++ gb_connection_cport_shutdown_phase_1(connection); ++ gb_connection_hd_cport_quiesce(connection); ++ gb_connection_cport_shutdown_phase_2(connection); ++ gb_connection_control_disconnected(connection); ++ ++ connection->state = GB_CONNECTION_STATE_DISABLED; ++ ++ /* control-connection tear down is deferred when mode switching */ ++ if (!connection->mode_switch) { ++ gb_connection_svc_connection_destroy(connection); ++ gb_connection_hd_cport_clear(connection); ++ ++ gb_connection_hd_cport_disable(connection); ++ } ++ ++out_unlock: ++ mutex_unlock(&connection->mutex); ++} ++EXPORT_SYMBOL_GPL(gb_connection_disable); ++ ++/* Disable a connection without communicating with the remote end. */ ++void gb_connection_disable_forced(struct gb_connection *connection) ++{ ++ mutex_lock(&connection->mutex); ++ ++ if (connection->state == GB_CONNECTION_STATE_DISABLED) ++ goto out_unlock; ++ ++ trace_gb_connection_disable(connection); ++ ++ spin_lock_irq(&connection->lock); ++ connection->state = GB_CONNECTION_STATE_DISABLED; ++ gb_connection_cancel_operations(connection, -ESHUTDOWN); ++ spin_unlock_irq(&connection->lock); ++ ++ gb_connection_hd_cport_flush(connection); ++ ++ gb_connection_svc_connection_destroy(connection); ++ gb_connection_hd_cport_clear(connection); ++ ++ gb_connection_hd_cport_disable(connection); ++out_unlock: ++ mutex_unlock(&connection->mutex); ++} ++EXPORT_SYMBOL_GPL(gb_connection_disable_forced); ++ ++/* Caller must have disabled the connection before destroying it. */ ++void gb_connection_destroy(struct gb_connection *connection) ++{ ++ if (!connection) ++ return; ++ ++ if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED)) ++ gb_connection_disable(connection); ++ ++ mutex_lock(&gb_connection_mutex); ++ ++ spin_lock_irq(&gb_connections_lock); ++ list_del(&connection->bundle_links); ++ list_del(&connection->hd_links); ++ spin_unlock_irq(&gb_connections_lock); ++ ++ destroy_workqueue(connection->wq); ++ ++ gb_hd_cport_release(connection->hd, connection->hd_cport_id); ++ connection->hd_cport_id = CPORT_ID_BAD; ++ ++ mutex_unlock(&gb_connection_mutex); ++ ++ gb_connection_put(connection); ++} ++EXPORT_SYMBOL_GPL(gb_connection_destroy); ++ ++void gb_connection_latency_tag_enable(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->latency_tag_enable) ++ return; ++ ++ ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to enable latency tag: %d\n", ++ connection->name, ret); ++ } ++} ++EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable); ++ ++void gb_connection_latency_tag_disable(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ int ret; ++ ++ if (!hd->driver->latency_tag_disable) ++ return; ++ ++ ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to disable latency tag: %d\n", ++ connection->name, ret); ++ } ++} ++EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable); +--- /dev/null ++++ b/drivers/greybus/connection.h +@@ -0,0 +1,129 @@ ++/* ++ * Greybus connections ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __CONNECTION_H ++#define __CONNECTION_H ++ ++#include <linux/list.h> ++#include <linux/kfifo.h> ++ ++#define GB_CONNECTION_FLAG_CSD BIT(0) ++#define GB_CONNECTION_FLAG_NO_FLOWCTRL BIT(1) ++#define GB_CONNECTION_FLAG_OFFLOADED BIT(2) ++#define GB_CONNECTION_FLAG_CDSI1 BIT(3) ++#define GB_CONNECTION_FLAG_CONTROL BIT(4) ++#define GB_CONNECTION_FLAG_HIGH_PRIO BIT(5) ++ ++#define GB_CONNECTION_FLAG_CORE_MASK GB_CONNECTION_FLAG_CONTROL ++ ++enum gb_connection_state { ++ GB_CONNECTION_STATE_DISABLED = 0, ++ GB_CONNECTION_STATE_ENABLED_TX = 1, ++ GB_CONNECTION_STATE_ENABLED = 2, ++ GB_CONNECTION_STATE_DISCONNECTING = 3, ++}; ++ ++struct gb_operation; ++ ++typedef int (*gb_request_handler_t)(struct gb_operation *); ++ ++struct gb_connection { ++ struct gb_host_device *hd; ++ struct gb_interface *intf; ++ struct gb_bundle *bundle; ++ struct kref kref; ++ u16 hd_cport_id; ++ u16 intf_cport_id; ++ ++ struct list_head hd_links; ++ struct list_head bundle_links; ++ ++ gb_request_handler_t handler; ++ unsigned long flags; ++ ++ struct mutex mutex; ++ spinlock_t lock; ++ enum gb_connection_state state; ++ struct list_head operations; ++ ++ char name[16]; ++ struct workqueue_struct *wq; ++ ++ atomic_t op_cycle; ++ ++ void *private; ++ ++ bool mode_switch; ++}; ++ ++struct gb_connection *gb_connection_create_static(struct gb_host_device *hd, ++ u16 hd_cport_id, gb_request_handler_t handler); ++struct gb_connection *gb_connection_create_control(struct gb_interface *intf); ++struct gb_connection *gb_connection_create(struct gb_bundle *bundle, ++ u16 cport_id, gb_request_handler_t handler); ++struct gb_connection *gb_connection_create_flags(struct gb_bundle *bundle, ++ u16 cport_id, gb_request_handler_t handler, ++ unsigned long flags); ++struct gb_connection *gb_connection_create_offloaded(struct gb_bundle *bundle, ++ u16 cport_id, unsigned long flags); ++void gb_connection_destroy(struct gb_connection *connection); ++ ++static inline bool gb_connection_is_static(struct gb_connection *connection) ++{ ++ return !connection->intf; ++} ++ ++int gb_connection_enable(struct gb_connection *connection); ++int gb_connection_enable_tx(struct gb_connection *connection); ++void gb_connection_disable_rx(struct gb_connection *connection); ++void gb_connection_disable(struct gb_connection *connection); ++void gb_connection_disable_forced(struct gb_connection *connection); ++ ++void gb_connection_mode_switch_prepare(struct gb_connection *connection); ++void gb_connection_mode_switch_complete(struct gb_connection *connection); ++ ++void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id, ++ u8 *data, size_t length); ++ ++void gb_connection_latency_tag_enable(struct gb_connection *connection); ++void gb_connection_latency_tag_disable(struct gb_connection *connection); ++ ++static inline bool gb_connection_e2efc_enabled(struct gb_connection *connection) ++{ ++ return !(connection->flags & GB_CONNECTION_FLAG_CSD); ++} ++ ++static inline bool ++gb_connection_flow_control_disabled(struct gb_connection *connection) ++{ ++ return connection->flags & GB_CONNECTION_FLAG_NO_FLOWCTRL; ++} ++ ++static inline bool gb_connection_is_offloaded(struct gb_connection *connection) ++{ ++ return connection->flags & GB_CONNECTION_FLAG_OFFLOADED; ++} ++ ++static inline bool gb_connection_is_control(struct gb_connection *connection) ++{ ++ return connection->flags & GB_CONNECTION_FLAG_CONTROL; ++} ++ ++static inline void *gb_connection_get_data(struct gb_connection *connection) ++{ ++ return connection->private; ++} ++ ++static inline void gb_connection_set_data(struct gb_connection *connection, ++ void *data) ++{ ++ connection->private = data; ++} ++ ++#endif /* __CONNECTION_H */ +--- /dev/null ++++ b/drivers/greybus/control.c +@@ -0,0 +1,635 @@ ++/* ++ * Greybus CPort control protocol. ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include "greybus.h" ++ ++/* Highest control-protocol version supported */ ++#define GB_CONTROL_VERSION_MAJOR 0 ++#define GB_CONTROL_VERSION_MINOR 1 ++ ++ ++static int gb_control_get_version(struct gb_control *control) ++{ ++ struct gb_interface *intf = control->connection->intf; ++ struct gb_control_version_request request; ++ struct gb_control_version_response response; ++ int ret; ++ ++ request.major = GB_CONTROL_VERSION_MAJOR; ++ request.minor = GB_CONTROL_VERSION_MINOR; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_VERSION, ++ &request, sizeof(request), &response, ++ sizeof(response)); ++ if (ret) { ++ dev_err(&intf->dev, ++ "failed to get control-protocol version: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (response.major > request.major) { ++ dev_err(&intf->dev, ++ "unsupported major control-protocol version (%u > %u)\n", ++ response.major, request.major); ++ return -ENOTSUPP; ++ } ++ ++ control->protocol_major = response.major; ++ control->protocol_minor = response.minor; ++ ++ dev_dbg(&intf->dev, "%s - %u.%u\n", __func__, response.major, ++ response.minor); ++ ++ return 0; ++} ++ ++static int gb_control_get_bundle_version(struct gb_control *control, ++ struct gb_bundle *bundle) ++{ ++ struct gb_interface *intf = control->connection->intf; ++ struct gb_control_bundle_version_request request; ++ struct gb_control_bundle_version_response response; ++ int ret; ++ ++ request.bundle_id = bundle->id; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_BUNDLE_VERSION, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&intf->dev, ++ "failed to get bundle %u class version: %d\n", ++ bundle->id, ret); ++ return ret; ++ } ++ ++ bundle->class_major = response.major; ++ bundle->class_minor = response.minor; ++ ++ dev_dbg(&intf->dev, "%s - %u: %u.%u\n", __func__, bundle->id, ++ response.major, response.minor); ++ ++ return 0; ++} ++ ++int gb_control_get_bundle_versions(struct gb_control *control) ++{ ++ struct gb_interface *intf = control->connection->intf; ++ struct gb_bundle *bundle; ++ int ret; ++ ++ if (!control->has_bundle_version) ++ return 0; ++ ++ list_for_each_entry(bundle, &intf->bundles, links) { ++ ret = gb_control_get_bundle_version(control, bundle); ++ if (ret) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++/* Get Manifest's size from the interface */ ++int gb_control_get_manifest_size_operation(struct gb_interface *intf) ++{ ++ struct gb_control_get_manifest_size_response response; ++ struct gb_connection *connection = intf->control->connection; ++ int ret; ++ ++ ret = gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST_SIZE, ++ NULL, 0, &response, sizeof(response)); ++ if (ret) { ++ dev_err(&connection->intf->dev, ++ "failed to get manifest size: %d\n", ret); ++ return ret; ++ } ++ ++ return le16_to_cpu(response.size); ++} ++ ++/* Reads Manifest from the interface */ ++int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, ++ size_t size) ++{ ++ struct gb_connection *connection = intf->control->connection; ++ ++ return gb_operation_sync(connection, GB_CONTROL_TYPE_GET_MANIFEST, ++ NULL, 0, manifest, size); ++} ++ ++int gb_control_connected_operation(struct gb_control *control, u16 cport_id) ++{ ++ struct gb_control_connected_request request; ++ ++ request.cport_id = cpu_to_le16(cport_id); ++ return gb_operation_sync(control->connection, GB_CONTROL_TYPE_CONNECTED, ++ &request, sizeof(request), NULL, 0); ++} ++ ++int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id) ++{ ++ struct gb_control_disconnected_request request; ++ ++ request.cport_id = cpu_to_le16(cport_id); ++ return gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_DISCONNECTED, &request, ++ sizeof(request), NULL, 0); ++} ++ ++int gb_control_disconnecting_operation(struct gb_control *control, ++ u16 cport_id) ++{ ++ struct gb_control_disconnecting_request *request; ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_operation_create_core(control->connection, ++ GB_CONTROL_TYPE_DISCONNECTING, ++ sizeof(*request), 0, 0, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ request = operation->request->payload; ++ request->cport_id = cpu_to_le16(cport_id); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) { ++ dev_err(&control->dev, "failed to send disconnecting: %d\n", ++ ret); ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++int gb_control_mode_switch_operation(struct gb_control *control) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_operation_create_core(control->connection, ++ GB_CONTROL_TYPE_MODE_SWITCH, ++ 0, 0, GB_OPERATION_FLAG_UNIDIRECTIONAL, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) ++ dev_err(&control->dev, "failed to send mode switch: %d\n", ret); ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++int gb_control_timesync_enable(struct gb_control *control, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk) ++{ ++ struct gb_control_timesync_enable_request request; ++ ++ request.count = count; ++ request.frame_time = cpu_to_le64(frame_time); ++ request.strobe_delay = cpu_to_le32(strobe_delay); ++ request.refclk = cpu_to_le32(refclk); ++ return gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_TIMESYNC_ENABLE, &request, ++ sizeof(request), NULL, 0); ++} ++ ++int gb_control_timesync_disable(struct gb_control *control) ++{ ++ return gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_TIMESYNC_DISABLE, NULL, 0, ++ NULL, 0); ++} ++ ++int gb_control_timesync_get_last_event(struct gb_control *control, ++ u64 *frame_time) ++{ ++ struct gb_control_timesync_get_last_event_response response; ++ int ret; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT, ++ NULL, 0, &response, sizeof(response)); ++ if (!ret) ++ *frame_time = le64_to_cpu(response.frame_time); ++ return ret; ++} ++ ++int gb_control_timesync_authoritative(struct gb_control *control, ++ u64 *frame_time) ++{ ++ struct gb_control_timesync_authoritative_request request; ++ int i; ++ ++ for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) ++ request.frame_time[i] = cpu_to_le64(frame_time[i]); ++ ++ return gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE, ++ &request, sizeof(request), ++ NULL, 0); ++} ++ ++static int gb_control_bundle_pm_status_map(u8 status) ++{ ++ switch (status) { ++ case GB_CONTROL_BUNDLE_PM_INVAL: ++ return -EINVAL; ++ case GB_CONTROL_BUNDLE_PM_BUSY: ++ return -EBUSY; ++ case GB_CONTROL_BUNDLE_PM_NA: ++ return -ENOMSG; ++ case GB_CONTROL_BUNDLE_PM_FAIL: ++ default: ++ return -EREMOTEIO; ++ } ++} ++ ++int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id) ++{ ++ struct gb_control_bundle_pm_request request; ++ struct gb_control_bundle_pm_response response; ++ int ret; ++ ++ request.bundle_id = bundle_id; ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_BUNDLE_SUSPEND, &request, ++ sizeof(request), &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, "failed to send bundle %u suspend: %d\n", ++ bundle_id, ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_BUNDLE_PM_OK) { ++ dev_err(&control->dev, "failed to suspend bundle %u: %d\n", ++ bundle_id, response.status); ++ return gb_control_bundle_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id) ++{ ++ struct gb_control_bundle_pm_request request; ++ struct gb_control_bundle_pm_response response; ++ int ret; ++ ++ request.bundle_id = bundle_id; ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_BUNDLE_RESUME, &request, ++ sizeof(request), &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, "failed to send bundle %u resume: %d\n", ++ bundle_id, ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_BUNDLE_PM_OK) { ++ dev_err(&control->dev, "failed to resume bundle %u: %d\n", ++ bundle_id, response.status); ++ return gb_control_bundle_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id) ++{ ++ struct gb_control_bundle_pm_request request; ++ struct gb_control_bundle_pm_response response; ++ int ret; ++ ++ request.bundle_id = bundle_id; ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_BUNDLE_DEACTIVATE, &request, ++ sizeof(request), &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, ++ "failed to send bundle %u deactivate: %d\n", bundle_id, ++ ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_BUNDLE_PM_OK) { ++ dev_err(&control->dev, "failed to deactivate bundle %u: %d\n", ++ bundle_id, response.status); ++ return gb_control_bundle_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id) ++{ ++ struct gb_control_bundle_pm_request request; ++ struct gb_control_bundle_pm_response response; ++ int ret; ++ ++ if (!control->has_bundle_activate) ++ return 0; ++ ++ request.bundle_id = bundle_id; ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_BUNDLE_ACTIVATE, &request, ++ sizeof(request), &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, ++ "failed to send bundle %u activate: %d\n", bundle_id, ++ ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_BUNDLE_PM_OK) { ++ dev_err(&control->dev, "failed to activate bundle %u: %d\n", ++ bundle_id, response.status); ++ return gb_control_bundle_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++static int gb_control_interface_pm_status_map(u8 status) ++{ ++ switch (status) { ++ case GB_CONTROL_INTF_PM_BUSY: ++ return -EBUSY; ++ case GB_CONTROL_INTF_PM_NA: ++ return -ENOMSG; ++ default: ++ return -EREMOTEIO; ++ } ++} ++ ++int gb_control_interface_suspend_prepare(struct gb_control *control) ++{ ++ struct gb_control_intf_pm_response response; ++ int ret; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE, NULL, 0, ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, ++ "failed to send interface suspend prepare: %d\n", ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_INTF_PM_OK) { ++ dev_err(&control->dev, "interface error while preparing suspend: %d\n", ++ response.status); ++ return gb_control_interface_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++int gb_control_interface_deactivate_prepare(struct gb_control *control) ++{ ++ struct gb_control_intf_pm_response response; ++ int ret; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE, NULL, ++ 0, &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, "failed to send interface deactivate prepare: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_INTF_PM_OK) { ++ dev_err(&control->dev, "interface error while preparing deactivate: %d\n", ++ response.status); ++ return gb_control_interface_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++int gb_control_interface_hibernate_abort(struct gb_control *control) ++{ ++ struct gb_control_intf_pm_response response; ++ int ret; ++ ++ ret = gb_operation_sync(control->connection, ++ GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT, NULL, 0, ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&control->dev, ++ "failed to send interface aborting hibernate: %d\n", ++ ret); ++ return ret; ++ } ++ ++ if (response.status != GB_CONTROL_INTF_PM_OK) { ++ dev_err(&control->dev, "interface error while aborting hibernate: %d\n", ++ response.status); ++ return gb_control_interface_pm_status_map(response.status); ++ } ++ ++ return 0; ++} ++ ++static ssize_t vendor_string_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_control *control = to_gb_control(dev); ++ ++ return scnprintf(buf, PAGE_SIZE, "%s\n", control->vendor_string); ++} ++static DEVICE_ATTR_RO(vendor_string); ++ ++static ssize_t product_string_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_control *control = to_gb_control(dev); ++ ++ return scnprintf(buf, PAGE_SIZE, "%s\n", control->product_string); ++} ++static DEVICE_ATTR_RO(product_string); ++ ++static struct attribute *control_attrs[] = { ++ &dev_attr_vendor_string.attr, ++ &dev_attr_product_string.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(control); ++ ++static void gb_control_release(struct device *dev) ++{ ++ struct gb_control *control = to_gb_control(dev); ++ ++ gb_connection_destroy(control->connection); ++ ++ kfree(control->vendor_string); ++ kfree(control->product_string); ++ ++ kfree(control); ++} ++ ++struct device_type greybus_control_type = { ++ .name = "greybus_control", ++ .release = gb_control_release, ++}; ++ ++struct gb_control *gb_control_create(struct gb_interface *intf) ++{ ++ struct gb_connection *connection; ++ struct gb_control *control; ++ ++ control = kzalloc(sizeof(*control), GFP_KERNEL); ++ if (!control) ++ return ERR_PTR(-ENOMEM); ++ ++ control->intf = intf; ++ ++ connection = gb_connection_create_control(intf); ++ if (IS_ERR(connection)) { ++ dev_err(&intf->dev, ++ "failed to create control connection: %ld\n", ++ PTR_ERR(connection)); ++ kfree(control); ++ return ERR_CAST(connection); ++ } ++ ++ control->connection = connection; ++ ++ control->dev.parent = &intf->dev; ++ control->dev.bus = &greybus_bus_type; ++ control->dev.type = &greybus_control_type; ++ control->dev.groups = control_groups; ++ control->dev.dma_mask = intf->dev.dma_mask; ++ device_initialize(&control->dev); ++ dev_set_name(&control->dev, "%s.ctrl", dev_name(&intf->dev)); ++ ++ gb_connection_set_data(control->connection, control); ++ ++ return control; ++} ++ ++int gb_control_enable(struct gb_control *control) ++{ ++ int ret; ++ ++ dev_dbg(&control->connection->intf->dev, "%s\n", __func__); ++ ++ ret = gb_connection_enable_tx(control->connection); ++ if (ret) { ++ dev_err(&control->connection->intf->dev, ++ "failed to enable control connection: %d\n", ++ ret); ++ return ret; ++ } ++ ++ ret = gb_control_get_version(control); ++ if (ret) ++ goto err_disable_connection; ++ ++ if (control->protocol_major > 0 || control->protocol_minor > 1) ++ control->has_bundle_version = true; ++ ++ /* FIXME: use protocol version instead */ ++ if (!(control->intf->quirks & GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE)) ++ control->has_bundle_activate = true; ++ ++ return 0; ++ ++err_disable_connection: ++ gb_connection_disable(control->connection); ++ ++ return ret; ++} ++ ++void gb_control_disable(struct gb_control *control) ++{ ++ dev_dbg(&control->connection->intf->dev, "%s\n", __func__); ++ ++ if (control->intf->disconnected) ++ gb_connection_disable_forced(control->connection); ++ else ++ gb_connection_disable(control->connection); ++} ++ ++int gb_control_suspend(struct gb_control *control) ++{ ++ gb_connection_disable(control->connection); ++ ++ return 0; ++} ++ ++int gb_control_resume(struct gb_control *control) ++{ ++ int ret; ++ ++ ret = gb_connection_enable_tx(control->connection); ++ if (ret) { ++ dev_err(&control->connection->intf->dev, ++ "failed to enable control connection: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int gb_control_add(struct gb_control *control) ++{ ++ int ret; ++ ++ ret = device_add(&control->dev); ++ if (ret) { ++ dev_err(&control->dev, ++ "failed to register control device: %d\n", ++ ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++void gb_control_del(struct gb_control *control) ++{ ++ if (device_is_registered(&control->dev)) ++ device_del(&control->dev); ++} ++ ++struct gb_control *gb_control_get(struct gb_control *control) ++{ ++ get_device(&control->dev); ++ ++ return control; ++} ++ ++void gb_control_put(struct gb_control *control) ++{ ++ put_device(&control->dev); ++} ++ ++void gb_control_mode_switch_prepare(struct gb_control *control) ++{ ++ gb_connection_mode_switch_prepare(control->connection); ++} ++ ++void gb_control_mode_switch_complete(struct gb_control *control) ++{ ++ gb_connection_mode_switch_complete(control->connection); ++} +--- /dev/null ++++ b/drivers/greybus/control.h +@@ -0,0 +1,65 @@ ++/* ++ * Greybus CPort control protocol ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __CONTROL_H ++#define __CONTROL_H ++ ++struct gb_control { ++ struct device dev; ++ struct gb_interface *intf; ++ ++ struct gb_connection *connection; ++ ++ u8 protocol_major; ++ u8 protocol_minor; ++ ++ bool has_bundle_activate; ++ bool has_bundle_version; ++ ++ char *vendor_string; ++ char *product_string; ++}; ++#define to_gb_control(d) container_of(d, struct gb_control, dev) ++ ++struct gb_control *gb_control_create(struct gb_interface *intf); ++int gb_control_enable(struct gb_control *control); ++void gb_control_disable(struct gb_control *control); ++int gb_control_suspend(struct gb_control *control); ++int gb_control_resume(struct gb_control *control); ++int gb_control_add(struct gb_control *control); ++void gb_control_del(struct gb_control *control); ++struct gb_control *gb_control_get(struct gb_control *control); ++void gb_control_put(struct gb_control *control); ++ ++int gb_control_get_bundle_versions(struct gb_control *control); ++int gb_control_connected_operation(struct gb_control *control, u16 cport_id); ++int gb_control_disconnected_operation(struct gb_control *control, u16 cport_id); ++int gb_control_disconnecting_operation(struct gb_control *control, ++ u16 cport_id); ++int gb_control_mode_switch_operation(struct gb_control *control); ++void gb_control_mode_switch_prepare(struct gb_control *control); ++void gb_control_mode_switch_complete(struct gb_control *control); ++int gb_control_get_manifest_size_operation(struct gb_interface *intf); ++int gb_control_get_manifest_operation(struct gb_interface *intf, void *manifest, ++ size_t size); ++int gb_control_timesync_enable(struct gb_control *control, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk); ++int gb_control_timesync_disable(struct gb_control *control); ++int gb_control_timesync_get_last_event(struct gb_control *control, ++ u64 *frame_time); ++int gb_control_timesync_authoritative(struct gb_control *control, ++ u64 *frame_time); ++int gb_control_bundle_suspend(struct gb_control *control, u8 bundle_id); ++int gb_control_bundle_resume(struct gb_control *control, u8 bundle_id); ++int gb_control_bundle_deactivate(struct gb_control *control, u8 bundle_id); ++int gb_control_bundle_activate(struct gb_control *control, u8 bundle_id); ++int gb_control_interface_suspend_prepare(struct gb_control *control); ++int gb_control_interface_deactivate_prepare(struct gb_control *control); ++int gb_control_interface_hibernate_abort(struct gb_control *control); ++#endif /* __CONTROL_H */ +--- /dev/null ++++ b/drivers/greybus/core.c +@@ -0,0 +1,361 @@ ++/* ++ * Greybus "Core" ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#define CREATE_TRACE_POINTS ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++#define GB_BUNDLE_AUTOSUSPEND_MS 3000 ++ ++/* Allow greybus to be disabled at boot if needed */ ++static bool nogreybus; ++#ifdef MODULE ++module_param(nogreybus, bool, 0444); ++#else ++core_param(nogreybus, nogreybus, bool, 0444); ++#endif ++int greybus_disabled(void) ++{ ++ return nogreybus; ++} ++EXPORT_SYMBOL_GPL(greybus_disabled); ++ ++static bool greybus_match_one_id(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) && ++ (id->vendor != bundle->intf->vendor_id)) ++ return false; ++ ++ if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) && ++ (id->product != bundle->intf->product_id)) ++ return false; ++ ++ if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) && ++ (id->class != bundle->class)) ++ return false; ++ ++ return true; ++} ++ ++static const struct greybus_bundle_id * ++greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id) ++{ ++ if (id == NULL) ++ return NULL; ++ ++ for (; id->vendor || id->product || id->class || id->driver_info; ++ id++) { ++ if (greybus_match_one_id(bundle, id)) ++ return id; ++ } ++ ++ return NULL; ++} ++ ++static int greybus_match_device(struct device *dev, struct device_driver *drv) ++{ ++ struct greybus_driver *driver = to_greybus_driver(drv); ++ struct gb_bundle *bundle; ++ const struct greybus_bundle_id *id; ++ ++ if (!is_gb_bundle(dev)) ++ return 0; ++ ++ bundle = to_gb_bundle(dev); ++ ++ id = greybus_match_id(bundle, driver->id_table); ++ if (id) ++ return 1; ++ /* FIXME - Dynamic ids? */ ++ return 0; ++} ++ ++static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct gb_host_device *hd; ++ struct gb_module *module = NULL; ++ struct gb_interface *intf = NULL; ++ struct gb_control *control = NULL; ++ struct gb_bundle *bundle = NULL; ++ struct gb_svc *svc = NULL; ++ ++ if (is_gb_host_device(dev)) { ++ hd = to_gb_host_device(dev); ++ } else if (is_gb_module(dev)) { ++ module = to_gb_module(dev); ++ hd = module->hd; ++ } else if (is_gb_interface(dev)) { ++ intf = to_gb_interface(dev); ++ module = intf->module; ++ hd = intf->hd; ++ } else if (is_gb_control(dev)) { ++ control = to_gb_control(dev); ++ intf = control->intf; ++ module = intf->module; ++ hd = intf->hd; ++ } else if (is_gb_bundle(dev)) { ++ bundle = to_gb_bundle(dev); ++ intf = bundle->intf; ++ module = intf->module; ++ hd = intf->hd; ++ } else if (is_gb_svc(dev)) { ++ svc = to_gb_svc(dev); ++ hd = svc->hd; ++ } else { ++ dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n"); ++ return -EINVAL; ++ } ++ ++ if (add_uevent_var(env, "BUS=%u", hd->bus_id)) ++ return -ENOMEM; ++ ++ if (module) { ++ if (add_uevent_var(env, "MODULE=%u", module->module_id)) ++ return -ENOMEM; ++ } ++ ++ if (intf) { ++ if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", ++ intf->vendor_id, intf->product_id)) ++ return -ENOMEM; ++ } ++ ++ if (bundle) { ++ // FIXME ++ // add a uevent that can "load" a bundle type ++ // This is what we need to bind a driver to so use the info ++ // in gmod here as well ++ ++ if (add_uevent_var(env, "BUNDLE=%u", bundle->id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) ++ return -ENOMEM; ++ } ++ ++ return 0; ++} ++ ++static void greybus_shutdown(struct device *dev) ++{ ++ if (is_gb_host_device(dev)) { ++ struct gb_host_device *hd; ++ ++ hd = to_gb_host_device(dev); ++ gb_hd_shutdown(hd); ++ } ++} ++ ++struct bus_type greybus_bus_type = { ++ .name = "greybus", ++ .match = greybus_match_device, ++ .uevent = greybus_uevent, ++ .shutdown = greybus_shutdown, ++}; ++ ++static int greybus_probe(struct device *dev) ++{ ++ struct greybus_driver *driver = to_greybus_driver(dev->driver); ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ const struct greybus_bundle_id *id; ++ int retval; ++ ++ /* match id */ ++ id = greybus_match_id(bundle, driver->id_table); ++ if (!id) ++ return -ENODEV; ++ ++ retval = pm_runtime_get_sync(&bundle->intf->dev); ++ if (retval < 0) { ++ pm_runtime_put_noidle(&bundle->intf->dev); ++ return retval; ++ } ++ ++ retval = gb_control_bundle_activate(bundle->intf->control, bundle->id); ++ if (retval) { ++ pm_runtime_put(&bundle->intf->dev); ++ return retval; ++ } ++ ++ /* ++ * Unbound bundle devices are always deactivated. During probe, the ++ * Runtime PM is set to enabled and active and the usage count is ++ * incremented. If the driver supports runtime PM, it should call ++ * pm_runtime_put() in its probe routine and pm_runtime_get_sync() ++ * in remove routine. ++ */ ++ pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS); ++ pm_runtime_use_autosuspend(dev); ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ retval = driver->probe(bundle, id); ++ if (retval) { ++ /* ++ * Catch buggy drivers that fail to destroy their connections. ++ */ ++ WARN_ON(!list_empty(&bundle->connections)); ++ ++ gb_control_bundle_deactivate(bundle->intf->control, bundle->id); ++ ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ pm_runtime_dont_use_autosuspend(dev); ++ pm_runtime_put(&bundle->intf->dev); ++ ++ return retval; ++ } ++ ++ gb_timesync_schedule_synchronous(bundle->intf); ++ ++ pm_runtime_put(&bundle->intf->dev); ++ ++ return 0; ++} ++ ++static int greybus_remove(struct device *dev) ++{ ++ struct greybus_driver *driver = to_greybus_driver(dev->driver); ++ struct gb_bundle *bundle = to_gb_bundle(dev); ++ struct gb_connection *connection; ++ int retval; ++ ++ retval = pm_runtime_get_sync(dev); ++ if (retval < 0) ++ dev_err(dev, "failed to resume bundle: %d\n", retval); ++ ++ /* ++ * Disable (non-offloaded) connections early in case the interface is ++ * already gone to avoid unceccessary operation timeouts during ++ * driver disconnect. Otherwise, only disable incoming requests. ++ */ ++ list_for_each_entry(connection, &bundle->connections, bundle_links) { ++ if (gb_connection_is_offloaded(connection)) ++ continue; ++ ++ if (bundle->intf->disconnected) ++ gb_connection_disable_forced(connection); ++ else ++ gb_connection_disable_rx(connection); ++ } ++ ++ driver->disconnect(bundle); ++ ++ /* Catch buggy drivers that fail to destroy their connections. */ ++ WARN_ON(!list_empty(&bundle->connections)); ++ ++ if (!bundle->intf->disconnected) ++ gb_control_bundle_deactivate(bundle->intf->control, bundle->id); ++ ++ pm_runtime_put_noidle(dev); ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_dont_use_autosuspend(dev); ++ pm_runtime_put_noidle(dev); ++ ++ return 0; ++} ++ ++int greybus_register_driver(struct greybus_driver *driver, struct module *owner, ++ const char *mod_name) ++{ ++ int retval; ++ ++ if (greybus_disabled()) ++ return -ENODEV; ++ ++ driver->driver.bus = &greybus_bus_type; ++ driver->driver.name = driver->name; ++ driver->driver.probe = greybus_probe; ++ driver->driver.remove = greybus_remove; ++ driver->driver.owner = owner; ++ driver->driver.mod_name = mod_name; ++ ++ retval = driver_register(&driver->driver); ++ if (retval) ++ return retval; ++ ++ pr_info("registered new driver %s\n", driver->name); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(greybus_register_driver); ++ ++void greybus_deregister_driver(struct greybus_driver *driver) ++{ ++ driver_unregister(&driver->driver); ++} ++EXPORT_SYMBOL_GPL(greybus_deregister_driver); ++ ++static int __init gb_init(void) ++{ ++ int retval; ++ ++ if (greybus_disabled()) ++ return -ENODEV; ++ ++ BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD); ++ ++ gb_debugfs_init(); ++ ++ retval = bus_register(&greybus_bus_type); ++ if (retval) { ++ pr_err("bus_register failed (%d)\n", retval); ++ goto error_bus; ++ } ++ ++ retval = gb_hd_init(); ++ if (retval) { ++ pr_err("gb_hd_init failed (%d)\n", retval); ++ goto error_hd; ++ } ++ ++ retval = gb_operation_init(); ++ if (retval) { ++ pr_err("gb_operation_init failed (%d)\n", retval); ++ goto error_operation; ++ } ++ ++ retval = gb_timesync_init(); ++ if (retval) { ++ pr_err("gb_timesync_init failed\n"); ++ goto error_timesync; ++ } ++ return 0; /* Success */ ++ ++error_timesync: ++ gb_operation_exit(); ++error_operation: ++ gb_hd_exit(); ++error_hd: ++ bus_unregister(&greybus_bus_type); ++error_bus: ++ gb_debugfs_cleanup(); ++ ++ return retval; ++} ++module_init(gb_init); ++ ++static void __exit gb_exit(void) ++{ ++ gb_timesync_exit(); ++ gb_operation_exit(); ++ gb_hd_exit(); ++ bus_unregister(&greybus_bus_type); ++ gb_debugfs_cleanup(); ++ tracepoint_synchronize_unregister(); ++} ++module_exit(gb_exit); ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); +--- /dev/null ++++ b/drivers/greybus/debugfs.c +@@ -0,0 +1,31 @@ ++/* ++ * Greybus debugfs code ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/debugfs.h> ++ ++#include "greybus.h" ++ ++static struct dentry *gb_debug_root; ++ ++void __init gb_debugfs_init(void) ++{ ++ gb_debug_root = debugfs_create_dir("greybus", NULL); ++} ++ ++void gb_debugfs_cleanup(void) ++{ ++ debugfs_remove_recursive(gb_debug_root); ++ gb_debug_root = NULL; ++} ++ ++struct dentry *gb_debugfs_get(void) ++{ ++ return gb_debug_root; ++} ++EXPORT_SYMBOL_GPL(gb_debugfs_get); +--- /dev/null ++++ b/drivers/greybus/devices +@@ -0,0 +1,11 @@ ++T: Bus=01 Lev=03 Prnt=07 Port=02 Cnt=03 Dev#= 12 Spd=12 MxCh= 0 ++D: Ver= 2.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs= 1 ++P: Vendor=ffff ProdID=0001 Rev= 1.00 ++S: Manufacturer=Greybus ++S: Product=SVC Bridge ++S: SerialNumber=12239 ++C:* #Ifs= 1 Cfg#= 1 Atr=c0 MxPwr=100mA ++I:* If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=es1_ap_driver ++E: Ad=81(I) Atr=03(Int.) MxPS= 64 Ivl=64ms ++E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl=0ms ++E: Ad=02(O) Atr=02(Bulk) MxPS= 64 Ivl=0ms +--- /dev/null ++++ b/drivers/greybus/greybus.h +@@ -0,0 +1,154 @@ ++/* ++ * Greybus driver and device API ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __LINUX_GREYBUS_H ++#define __LINUX_GREYBUS_H ++ ++#ifdef __KERNEL__ ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++#include <linux/module.h> ++#include <linux/pm_runtime.h> ++#include <linux/idr.h> ++ ++#include "greybus_id.h" ++#include "greybus_manifest.h" ++#include "greybus_protocols.h" ++#include "manifest.h" ++#include "hd.h" ++#include "svc.h" ++#include "control.h" ++#include "module.h" ++#include "interface.h" ++#include "bundle.h" ++#include "connection.h" ++#include "operation.h" ++#include "timesync.h" ++ ++/* Matches up with the Greybus Protocol specification document */ ++#define GREYBUS_VERSION_MAJOR 0x00 ++#define GREYBUS_VERSION_MINOR 0x01 ++ ++#define GREYBUS_ID_MATCH_DEVICE \ ++ (GREYBUS_ID_MATCH_VENDOR | GREYBUS_ID_MATCH_PRODUCT) ++ ++#define GREYBUS_DEVICE(v, p) \ ++ .match_flags = GREYBUS_ID_MATCH_DEVICE, \ ++ .vendor = (v), \ ++ .product = (p), ++ ++#define GREYBUS_DEVICE_CLASS(c) \ ++ .match_flags = GREYBUS_ID_MATCH_CLASS, \ ++ .class = (c), ++ ++/* Maximum number of CPorts */ ++#define CPORT_ID_MAX 4095 /* UniPro max id is 4095 */ ++#define CPORT_ID_BAD U16_MAX ++ ++struct greybus_driver { ++ const char *name; ++ ++ int (*probe)(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id); ++ void (*disconnect)(struct gb_bundle *bundle); ++ ++ const struct greybus_bundle_id *id_table; ++ ++ struct device_driver driver; ++}; ++#define to_greybus_driver(d) container_of(d, struct greybus_driver, driver) ++ ++static inline void greybus_set_drvdata(struct gb_bundle *bundle, void *data) ++{ ++ dev_set_drvdata(&bundle->dev, data); ++} ++ ++static inline void *greybus_get_drvdata(struct gb_bundle *bundle) ++{ ++ return dev_get_drvdata(&bundle->dev); ++} ++ ++/* Don't call these directly, use the module_greybus_driver() macro instead */ ++int greybus_register_driver(struct greybus_driver *driver, ++ struct module *module, const char *mod_name); ++void greybus_deregister_driver(struct greybus_driver *driver); ++ ++/* define to get proper THIS_MODULE and KBUILD_MODNAME values */ ++#define greybus_register(driver) \ ++ greybus_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) ++#define greybus_deregister(driver) \ ++ greybus_deregister_driver(driver) ++ ++/** ++ * module_greybus_driver() - Helper macro for registering a Greybus driver ++ * @__greybus_driver: greybus_driver structure ++ * ++ * Helper macro for Greybus drivers to set up proper module init / exit ++ * functions. Replaces module_init() and module_exit() and keeps people from ++ * printing pointless things to the kernel log when their driver is loaded. ++ */ ++#define module_greybus_driver(__greybus_driver) \ ++ module_driver(__greybus_driver, greybus_register, greybus_deregister) ++ ++int greybus_disabled(void); ++ ++void gb_debugfs_init(void); ++void gb_debugfs_cleanup(void); ++struct dentry *gb_debugfs_get(void); ++ ++extern struct bus_type greybus_bus_type; ++ ++extern struct device_type greybus_hd_type; ++extern struct device_type greybus_module_type; ++extern struct device_type greybus_interface_type; ++extern struct device_type greybus_control_type; ++extern struct device_type greybus_bundle_type; ++extern struct device_type greybus_svc_type; ++ ++static inline int is_gb_host_device(const struct device *dev) ++{ ++ return dev->type == &greybus_hd_type; ++} ++ ++static inline int is_gb_module(const struct device *dev) ++{ ++ return dev->type == &greybus_module_type; ++} ++ ++static inline int is_gb_interface(const struct device *dev) ++{ ++ return dev->type == &greybus_interface_type; ++} ++ ++static inline int is_gb_control(const struct device *dev) ++{ ++ return dev->type == &greybus_control_type; ++} ++ ++static inline int is_gb_bundle(const struct device *dev) ++{ ++ return dev->type == &greybus_bundle_type; ++} ++ ++static inline int is_gb_svc(const struct device *dev) ++{ ++ return dev->type == &greybus_svc_type; ++} ++ ++static inline bool cport_id_valid(struct gb_host_device *hd, u16 cport_id) ++{ ++ return cport_id != CPORT_ID_BAD && cport_id < hd->num_cports; ++} ++ ++#endif /* __KERNEL__ */ ++#endif /* __LINUX_GREYBUS_H */ +--- /dev/null ++++ b/drivers/greybus/greybus_authentication.h +@@ -0,0 +1,120 @@ ++/* ++ * Greybus Component Authentication User Header ++ * ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef __GREYBUS_AUTHENTICATION_USER_H ++#define __GREYBUS_AUTHENTICATION_USER_H ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++#define CAP_CERTIFICATE_MAX_SIZE 1600 ++#define CAP_SIGNATURE_MAX_SIZE 320 ++ ++/* Certificate class types */ ++#define CAP_CERT_IMS_EAPC 0x00000001 ++#define CAP_CERT_IMS_EASC 0x00000002 ++#define CAP_CERT_IMS_EARC 0x00000003 ++#define CAP_CERT_IMS_IAPC 0x00000004 ++#define CAP_CERT_IMS_IASC 0x00000005 ++#define CAP_CERT_IMS_IARC 0x00000006 ++ ++/* IMS Certificate response result codes */ ++#define CAP_IMS_RESULT_CERT_FOUND 0x00 ++#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01 ++#define CAP_IMS_RESULT_CERT_CORRUPT 0x02 ++#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03 ++ ++/* Authentication types */ ++#define CAP_AUTH_IMS_PRI 0x00000001 ++#define CAP_AUTH_IMS_SEC 0x00000002 ++#define CAP_AUTH_IMS_RSA 0x00000003 ++ ++/* Authenticate response result codes */ ++#define CAP_AUTH_RESULT_CR_SUCCESS 0x00 ++#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01 ++#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02 ++#define CAP_AUTH_RESULT_CR_NO_KEY 0x03 ++#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04 ++ ++ ++/* IOCTL support */ ++struct cap_ioc_get_endpoint_uid { ++ __u8 uid[8]; ++} __attribute__ ((__packed__)); ++ ++struct cap_ioc_get_ims_certificate { ++ __u32 certificate_class; ++ __u32 certificate_id; ++ ++ __u8 result_code; ++ __u32 cert_size; ++ __u8 certificate[CAP_CERTIFICATE_MAX_SIZE]; ++} __attribute__ ((__packed__)); ++ ++struct cap_ioc_authenticate { ++ __u32 auth_type; ++ __u8 uid[8]; ++ __u8 challenge[32]; ++ ++ __u8 result_code; ++ __u8 response[64]; ++ __u32 signature_size; ++ __u8 signature[CAP_SIGNATURE_MAX_SIZE]; ++} __attribute__ ((__packed__)); ++ ++#define CAP_IOCTL_BASE 'C' ++#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid) ++#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate) ++#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate) ++ ++#endif /* __GREYBUS_AUTHENTICATION_USER_H */ +--- /dev/null ++++ b/drivers/greybus/greybus_id.h +@@ -0,0 +1,26 @@ ++/* FIXME ++ * move this to include/linux/mod_devicetable.h when merging ++ */ ++ ++#ifndef __LINUX_GREYBUS_ID_H ++#define __LINUX_GREYBUS_ID_H ++ ++#include <linux/types.h> ++#include <linux/mod_devicetable.h> ++ ++ ++struct greybus_bundle_id { ++ __u16 match_flags; ++ __u32 vendor; ++ __u32 product; ++ __u8 class; ++ ++ kernel_ulong_t driver_info __aligned(sizeof(kernel_ulong_t)); ++}; ++ ++/* Used to match the greybus_bundle_id */ ++#define GREYBUS_ID_MATCH_VENDOR BIT(0) ++#define GREYBUS_ID_MATCH_PRODUCT BIT(1) ++#define GREYBUS_ID_MATCH_CLASS BIT(2) ++ ++#endif /* __LINUX_GREYBUS_ID_H */ +--- /dev/null ++++ b/drivers/greybus/greybus_manifest.h +@@ -0,0 +1,177 @@ ++/* ++ * Greybus manifest definition ++ * ++ * See "Greybus Application Protocol" document (version 0.1) for ++ * details on these values and structures. ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 and BSD licenses. ++ */ ++ ++#ifndef __GREYBUS_MANIFEST_H ++#define __GREYBUS_MANIFEST_H ++ ++enum greybus_descriptor_type { ++ GREYBUS_TYPE_INVALID = 0x00, ++ GREYBUS_TYPE_INTERFACE = 0x01, ++ GREYBUS_TYPE_STRING = 0x02, ++ GREYBUS_TYPE_BUNDLE = 0x03, ++ GREYBUS_TYPE_CPORT = 0x04, ++}; ++ ++enum greybus_protocol { ++ GREYBUS_PROTOCOL_CONTROL = 0x00, ++ /* 0x01 is unused */ ++ GREYBUS_PROTOCOL_GPIO = 0x02, ++ GREYBUS_PROTOCOL_I2C = 0x03, ++ GREYBUS_PROTOCOL_UART = 0x04, ++ GREYBUS_PROTOCOL_HID = 0x05, ++ GREYBUS_PROTOCOL_USB = 0x06, ++ GREYBUS_PROTOCOL_SDIO = 0x07, ++ GREYBUS_PROTOCOL_POWER_SUPPLY = 0x08, ++ GREYBUS_PROTOCOL_PWM = 0x09, ++ /* 0x0a is unused */ ++ GREYBUS_PROTOCOL_SPI = 0x0b, ++ GREYBUS_PROTOCOL_DISPLAY = 0x0c, ++ GREYBUS_PROTOCOL_CAMERA_MGMT = 0x0d, ++ GREYBUS_PROTOCOL_SENSOR = 0x0e, ++ GREYBUS_PROTOCOL_LIGHTS = 0x0f, ++ GREYBUS_PROTOCOL_VIBRATOR = 0x10, ++ GREYBUS_PROTOCOL_LOOPBACK = 0x11, ++ GREYBUS_PROTOCOL_AUDIO_MGMT = 0x12, ++ GREYBUS_PROTOCOL_AUDIO_DATA = 0x13, ++ GREYBUS_PROTOCOL_SVC = 0x14, ++ GREYBUS_PROTOCOL_BOOTROM = 0x15, ++ GREYBUS_PROTOCOL_CAMERA_DATA = 0x16, ++ GREYBUS_PROTOCOL_FW_DOWNLOAD = 0x17, ++ GREYBUS_PROTOCOL_FW_MANAGEMENT = 0x18, ++ GREYBUS_PROTOCOL_AUTHENTICATION = 0x19, ++ GREYBUS_PROTOCOL_LOG = 0x1a, ++ /* ... */ ++ GREYBUS_PROTOCOL_RAW = 0xfe, ++ GREYBUS_PROTOCOL_VENDOR = 0xff, ++}; ++ ++enum greybus_class_type { ++ GREYBUS_CLASS_CONTROL = 0x00, ++ /* 0x01 is unused */ ++ /* 0x02 is unused */ ++ /* 0x03 is unused */ ++ /* 0x04 is unused */ ++ GREYBUS_CLASS_HID = 0x05, ++ /* 0x06 is unused */ ++ /* 0x07 is unused */ ++ GREYBUS_CLASS_POWER_SUPPLY = 0x08, ++ /* 0x09 is unused */ ++ GREYBUS_CLASS_BRIDGED_PHY = 0x0a, ++ /* 0x0b is unused */ ++ GREYBUS_CLASS_DISPLAY = 0x0c, ++ GREYBUS_CLASS_CAMERA = 0x0d, ++ GREYBUS_CLASS_SENSOR = 0x0e, ++ GREYBUS_CLASS_LIGHTS = 0x0f, ++ GREYBUS_CLASS_VIBRATOR = 0x10, ++ GREYBUS_CLASS_LOOPBACK = 0x11, ++ GREYBUS_CLASS_AUDIO = 0x12, ++ /* 0x13 is unused */ ++ /* 0x14 is unused */ ++ GREYBUS_CLASS_BOOTROM = 0x15, ++ GREYBUS_CLASS_FW_MANAGEMENT = 0x16, ++ GREYBUS_CLASS_LOG = 0x17, ++ /* ... */ ++ GREYBUS_CLASS_RAW = 0xfe, ++ GREYBUS_CLASS_VENDOR = 0xff, ++}; ++ ++enum { ++ GREYBUS_INTERFACE_FEATURE_TIMESYNC = BIT(0), ++}; ++ ++/* ++ * The string in a string descriptor is not NUL-terminated. The ++ * size of the descriptor will be rounded up to a multiple of 4 ++ * bytes, by padding the string with 0x00 bytes if necessary. ++ */ ++struct greybus_descriptor_string { ++ __u8 length; ++ __u8 id; ++ __u8 string[0]; ++} __packed; ++ ++/* ++ * An interface descriptor describes information about an interface as a whole, ++ * *not* the functions within it. ++ */ ++struct greybus_descriptor_interface { ++ __u8 vendor_stringid; ++ __u8 product_stringid; ++ __u8 features; ++ __u8 pad; ++} __packed; ++ ++/* ++ * An bundle descriptor defines an identification number and a class for ++ * each bundle. ++ * ++ * @id: Uniquely identifies a bundle within a interface, its sole purpose is to ++ * allow CPort descriptors to specify which bundle they are associated with. ++ * The first bundle will have id 0, second will have 1 and so on. ++ * ++ * The largest CPort id associated with an bundle (defined by a ++ * CPort descriptor in the manifest) is used to determine how to ++ * encode the device id and module number in UniPro packets ++ * that use the bundle. ++ * ++ * @class: It is used by kernel to know the functionality provided by the ++ * bundle and will be matched against drivers functinality while probing greybus ++ * driver. It should contain one of the values defined in ++ * 'enum greybus_class_type'. ++ * ++ */ ++struct greybus_descriptor_bundle { ++ __u8 id; /* interface-relative id (0..) */ ++ __u8 class; ++ __u8 pad[2]; ++} __packed; ++ ++/* ++ * A CPort descriptor indicates the id of the bundle within the ++ * module it's associated with, along with the CPort id used to ++ * address the CPort. The protocol id defines the format of messages ++ * exchanged using the CPort. ++ */ ++struct greybus_descriptor_cport { ++ __le16 id; ++ __u8 bundle; ++ __u8 protocol_id; /* enum greybus_protocol */ ++} __packed; ++ ++struct greybus_descriptor_header { ++ __le16 size; ++ __u8 type; /* enum greybus_descriptor_type */ ++ __u8 pad; ++} __packed; ++ ++struct greybus_descriptor { ++ struct greybus_descriptor_header header; ++ union { ++ struct greybus_descriptor_string string; ++ struct greybus_descriptor_interface interface; ++ struct greybus_descriptor_bundle bundle; ++ struct greybus_descriptor_cport cport; ++ }; ++} __packed; ++ ++struct greybus_manifest_header { ++ __le16 size; ++ __u8 version_major; ++ __u8 version_minor; ++} __packed; ++ ++struct greybus_manifest { ++ struct greybus_manifest_header header; ++ struct greybus_descriptor descriptors[0]; ++} __packed; ++ ++#endif /* __GREYBUS_MANIFEST_H */ +--- /dev/null ++++ b/drivers/greybus/manifest.c +@@ -0,0 +1,535 @@ ++/* ++ * Greybus manifest parsing ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++ ++static const char *get_descriptor_type_string(u8 type) ++{ ++ switch(type) { ++ case GREYBUS_TYPE_INVALID: ++ return "invalid"; ++ case GREYBUS_TYPE_STRING: ++ return "string"; ++ case GREYBUS_TYPE_INTERFACE: ++ return "interface"; ++ case GREYBUS_TYPE_CPORT: ++ return "cport"; ++ case GREYBUS_TYPE_BUNDLE: ++ return "bundle"; ++ default: ++ WARN_ON(1); ++ return "unknown"; ++ } ++} ++ ++/* ++ * We scan the manifest once to identify where all the descriptors ++ * are. The result is a list of these manifest_desc structures. We ++ * then pick through them for what we're looking for (starting with ++ * the interface descriptor). As each is processed we remove it from ++ * the list. When we're done the list should (probably) be empty. ++ */ ++struct manifest_desc { ++ struct list_head links; ++ ++ size_t size; ++ void *data; ++ enum greybus_descriptor_type type; ++}; ++ ++static void release_manifest_descriptor(struct manifest_desc *descriptor) ++{ ++ list_del(&descriptor->links); ++ kfree(descriptor); ++} ++ ++static void release_manifest_descriptors(struct gb_interface *intf) ++{ ++ struct manifest_desc *descriptor; ++ struct manifest_desc *next; ++ ++ list_for_each_entry_safe(descriptor, next, &intf->manifest_descs, links) ++ release_manifest_descriptor(descriptor); ++} ++ ++static void release_cport_descriptors(struct list_head *head, u8 bundle_id) ++{ ++ struct manifest_desc *desc, *tmp; ++ struct greybus_descriptor_cport *desc_cport; ++ ++ list_for_each_entry_safe(desc, tmp, head, links) { ++ desc_cport = desc->data; ++ ++ if (desc->type != GREYBUS_TYPE_CPORT) ++ continue; ++ ++ if (desc_cport->bundle == bundle_id) ++ release_manifest_descriptor(desc); ++ } ++} ++ ++static struct manifest_desc *get_next_bundle_desc(struct gb_interface *intf) ++{ ++ struct manifest_desc *descriptor; ++ struct manifest_desc *next; ++ ++ list_for_each_entry_safe(descriptor, next, &intf->manifest_descs, links) ++ if (descriptor->type == GREYBUS_TYPE_BUNDLE) ++ return descriptor; ++ ++ return NULL; ++} ++ ++/* ++ * Validate the given descriptor. Its reported size must fit within ++ * the number of bytes remaining, and it must have a recognized ++ * type. Check that the reported size is at least as big as what ++ * we expect to see. (It could be bigger, perhaps for a new version ++ * of the format.) ++ * ++ * Returns the (non-zero) number of bytes consumed by the descriptor, ++ * or a negative errno. ++ */ ++static int identify_descriptor(struct gb_interface *intf, ++ struct greybus_descriptor *desc, size_t size) ++{ ++ struct greybus_descriptor_header *desc_header = &desc->header; ++ struct manifest_desc *descriptor; ++ size_t desc_size; ++ size_t expected_size; ++ ++ if (size < sizeof(*desc_header)) { ++ dev_err(&intf->dev, "manifest too small (%zu < %zu)\n", ++ size, sizeof(*desc_header)); ++ return -EINVAL; /* Must at least have header */ ++ } ++ ++ desc_size = le16_to_cpu(desc_header->size); ++ if (desc_size > size) { ++ dev_err(&intf->dev, "descriptor too big (%zu > %zu)\n", ++ desc_size, size); ++ return -EINVAL; ++ } ++ ++ /* Descriptor needs to at least have a header */ ++ expected_size = sizeof(*desc_header); ++ ++ switch (desc_header->type) { ++ case GREYBUS_TYPE_STRING: ++ expected_size += sizeof(struct greybus_descriptor_string); ++ expected_size += desc->string.length; ++ ++ /* String descriptors are padded to 4 byte boundaries */ ++ expected_size = ALIGN(expected_size, 4); ++ break; ++ case GREYBUS_TYPE_INTERFACE: ++ expected_size += sizeof(struct greybus_descriptor_interface); ++ break; ++ case GREYBUS_TYPE_BUNDLE: ++ expected_size += sizeof(struct greybus_descriptor_bundle); ++ break; ++ case GREYBUS_TYPE_CPORT: ++ expected_size += sizeof(struct greybus_descriptor_cport); ++ break; ++ case GREYBUS_TYPE_INVALID: ++ default: ++ dev_err(&intf->dev, "invalid descriptor type (%u)\n", ++ desc_header->type); ++ return -EINVAL; ++ } ++ ++ if (desc_size < expected_size) { ++ dev_err(&intf->dev, "%s descriptor too small (%zu < %zu)\n", ++ get_descriptor_type_string(desc_header->type), ++ desc_size, expected_size); ++ return -EINVAL; ++ } ++ ++ /* Descriptor bigger than what we expect */ ++ if (desc_size > expected_size) { ++ dev_warn(&intf->dev, "%s descriptor size mismatch (want %zu got %zu)\n", ++ get_descriptor_type_string(desc_header->type), ++ expected_size, desc_size); ++ } ++ ++ descriptor = kzalloc(sizeof(*descriptor), GFP_KERNEL); ++ if (!descriptor) ++ return -ENOMEM; ++ ++ descriptor->size = desc_size; ++ descriptor->data = (char *)desc + sizeof(*desc_header); ++ descriptor->type = desc_header->type; ++ list_add_tail(&descriptor->links, &intf->manifest_descs); ++ ++ /* desc_size is positive and is known to fit in a signed int */ ++ ++ return desc_size; ++} ++ ++/* ++ * Find the string descriptor having the given id, validate it, and ++ * allocate a duplicate copy of it. The duplicate has an extra byte ++ * which guarantees the returned string is NUL-terminated. ++ * ++ * String index 0 is valid (it represents "no string"), and for ++ * that a null pointer is returned. ++ * ++ * Otherwise returns a pointer to a newly-allocated copy of the ++ * descriptor string, or an error-coded pointer on failure. ++ */ ++static char *gb_string_get(struct gb_interface *intf, u8 string_id) ++{ ++ struct greybus_descriptor_string *desc_string; ++ struct manifest_desc *descriptor; ++ bool found = false; ++ char *string; ++ ++ /* A zero string id means no string (but no error) */ ++ if (!string_id) ++ return NULL; ++ ++ list_for_each_entry(descriptor, &intf->manifest_descs, links) { ++ if (descriptor->type != GREYBUS_TYPE_STRING) ++ continue; ++ ++ desc_string = descriptor->data; ++ if (desc_string->id == string_id) { ++ found = true; ++ break; ++ } ++ } ++ if (!found) ++ return ERR_PTR(-ENOENT); ++ ++ /* Allocate an extra byte so we can guarantee it's NUL-terminated */ ++ string = kmemdup(&desc_string->string, desc_string->length + 1, ++ GFP_KERNEL); ++ if (!string) ++ return ERR_PTR(-ENOMEM); ++ string[desc_string->length] = '\0'; ++ ++ /* Ok we've used this string, so we're done with it */ ++ release_manifest_descriptor(descriptor); ++ ++ return string; ++} ++ ++/* ++ * Find cport descriptors in the manifest associated with the given ++ * bundle, and set up data structures for the functions that use ++ * them. Returns the number of cports set up for the bundle, or 0 ++ * if there is an error. ++ */ ++static u32 gb_manifest_parse_cports(struct gb_bundle *bundle) ++{ ++ struct gb_interface *intf = bundle->intf; ++ struct greybus_descriptor_cport *desc_cport; ++ struct manifest_desc *desc, *next, *tmp; ++ LIST_HEAD(list); ++ u8 bundle_id = bundle->id; ++ u16 cport_id; ++ u32 count = 0; ++ int i; ++ ++ /* Set up all cport descriptors associated with this bundle */ ++ list_for_each_entry_safe(desc, next, &intf->manifest_descs, links) { ++ if (desc->type != GREYBUS_TYPE_CPORT) ++ continue; ++ ++ desc_cport = desc->data; ++ if (desc_cport->bundle != bundle_id) ++ continue; ++ ++ cport_id = le16_to_cpu(desc_cport->id); ++ if (cport_id > CPORT_ID_MAX) ++ goto exit; ++ ++ /* Nothing else should have its cport_id as control cport id */ ++ if (cport_id == GB_CONTROL_CPORT_ID) { ++ dev_err(&bundle->dev, "invalid cport id found (%02u)\n", ++ cport_id); ++ goto exit; ++ } ++ ++ /* ++ * Found one, move it to our temporary list after checking for ++ * duplicates. ++ */ ++ list_for_each_entry(tmp, &list, links) { ++ desc_cport = tmp->data; ++ if (cport_id == le16_to_cpu(desc_cport->id)) { ++ dev_err(&bundle->dev, ++ "duplicate CPort %u found\n", ++ cport_id); ++ goto exit; ++ } ++ } ++ list_move_tail(&desc->links, &list); ++ count++; ++ } ++ ++ if (!count) ++ return 0; ++ ++ bundle->cport_desc = kcalloc(count, sizeof(*bundle->cport_desc), ++ GFP_KERNEL); ++ if (!bundle->cport_desc) ++ goto exit; ++ ++ bundle->num_cports = count; ++ ++ i = 0; ++ list_for_each_entry_safe(desc, next, &list, links) { ++ desc_cport = desc->data; ++ memcpy(&bundle->cport_desc[i++], desc_cport, ++ sizeof(*desc_cport)); ++ ++ /* Release the cport descriptor */ ++ release_manifest_descriptor(desc); ++ } ++ ++ return count; ++exit: ++ release_cport_descriptors(&list, bundle_id); ++ /* ++ * Free all cports for this bundle to avoid 'excess descriptors' ++ * warnings. ++ */ ++ release_cport_descriptors(&intf->manifest_descs, bundle_id); ++ ++ return 0; /* Error; count should also be 0 */ ++} ++ ++/* ++ * Find bundle descriptors in the manifest and set up their data ++ * structures. Returns the number of bundles set up for the ++ * given interface. ++ */ ++static u32 gb_manifest_parse_bundles(struct gb_interface *intf) ++{ ++ struct manifest_desc *desc; ++ struct gb_bundle *bundle; ++ struct gb_bundle *bundle_next; ++ u32 count = 0; ++ u8 bundle_id; ++ u8 class; ++ ++ while ((desc = get_next_bundle_desc(intf))) { ++ struct greybus_descriptor_bundle *desc_bundle; ++ ++ /* Found one. Set up its bundle structure*/ ++ desc_bundle = desc->data; ++ bundle_id = desc_bundle->id; ++ class = desc_bundle->class; ++ ++ /* Done with this bundle descriptor */ ++ release_manifest_descriptor(desc); ++ ++ /* Ignore any legacy control bundles */ ++ if (bundle_id == GB_CONTROL_BUNDLE_ID) { ++ dev_dbg(&intf->dev, "%s - ignoring control bundle\n", ++ __func__); ++ release_cport_descriptors(&intf->manifest_descs, ++ bundle_id); ++ continue; ++ } ++ ++ /* Nothing else should have its class set to control class */ ++ if (class == GREYBUS_CLASS_CONTROL) { ++ dev_err(&intf->dev, ++ "bundle %u cannot use control class\n", ++ bundle_id); ++ goto cleanup; ++ } ++ ++ bundle = gb_bundle_create(intf, bundle_id, class); ++ if (!bundle) ++ goto cleanup; ++ ++ /* ++ * Now go set up this bundle's functions and cports. ++ * ++ * A 'bundle' represents a device in greybus. It may require ++ * multiple cports for its functioning. If we fail to setup any ++ * cport of a bundle, we better reject the complete bundle as ++ * the device may not be able to function properly then. ++ * ++ * But, failing to setup a cport of bundle X doesn't mean that ++ * the device corresponding to bundle Y will not work properly. ++ * Bundles should be treated as separate independent devices. ++ * ++ * While parsing manifest for an interface, treat bundles as ++ * separate entities and don't reject entire interface and its ++ * bundles on failing to initialize a cport. But make sure the ++ * bundle which needs the cport, gets destroyed properly. ++ */ ++ if (!gb_manifest_parse_cports(bundle)) { ++ gb_bundle_destroy(bundle); ++ continue; ++ } ++ ++ count++; ++ } ++ ++ return count; ++cleanup: ++ /* An error occurred; undo any changes we've made */ ++ list_for_each_entry_safe(bundle, bundle_next, &intf->bundles, links) { ++ gb_bundle_destroy(bundle); ++ count--; ++ } ++ return 0; /* Error; count should also be 0 */ ++} ++ ++static bool gb_manifest_parse_interface(struct gb_interface *intf, ++ struct manifest_desc *interface_desc) ++{ ++ struct greybus_descriptor_interface *desc_intf = interface_desc->data; ++ struct gb_control *control = intf->control; ++ char *str; ++ ++ /* Handle the strings first--they can fail */ ++ str = gb_string_get(intf, desc_intf->vendor_stringid); ++ if (IS_ERR(str)) ++ return false; ++ control->vendor_string = str; ++ ++ str = gb_string_get(intf, desc_intf->product_stringid); ++ if (IS_ERR(str)) ++ goto out_free_vendor_string; ++ control->product_string = str; ++ ++ /* Assign feature flags communicated via manifest */ ++ intf->features = desc_intf->features; ++ ++ /* Release the interface descriptor, now that we're done with it */ ++ release_manifest_descriptor(interface_desc); ++ ++ /* An interface must have at least one bundle descriptor */ ++ if (!gb_manifest_parse_bundles(intf)) { ++ dev_err(&intf->dev, "manifest bundle descriptors not valid\n"); ++ goto out_err; ++ } ++ ++ return true; ++out_err: ++ kfree(control->product_string); ++ control->product_string = NULL; ++out_free_vendor_string: ++ kfree(control->vendor_string); ++ control->vendor_string = NULL; ++ ++ return false; ++} ++ ++/* ++ * Parse a buffer containing an interface manifest. ++ * ++ * If we find anything wrong with the content/format of the buffer ++ * we reject it. ++ * ++ * The first requirement is that the manifest's version is ++ * one we can parse. ++ * ++ * We make an initial pass through the buffer and identify all of ++ * the descriptors it contains, keeping track for each its type ++ * and the location size of its data in the buffer. ++ * ++ * Next we scan the descriptors, looking for an interface descriptor; ++ * there must be exactly one of those. When found, we record the ++ * information it contains, and then remove that descriptor (and any ++ * string descriptors it refers to) from further consideration. ++ * ++ * After that we look for the interface's bundles--there must be at ++ * least one of those. ++ * ++ * Returns true if parsing was successful, false otherwise. ++ */ ++bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size) ++{ ++ struct greybus_manifest *manifest; ++ struct greybus_manifest_header *header; ++ struct greybus_descriptor *desc; ++ struct manifest_desc *descriptor; ++ struct manifest_desc *interface_desc = NULL; ++ u16 manifest_size; ++ u32 found = 0; ++ bool result; ++ ++ /* Manifest descriptor list should be empty here */ ++ if (WARN_ON(!list_empty(&intf->manifest_descs))) ++ return false; ++ ++ /* we have to have at _least_ the manifest header */ ++ if (size < sizeof(*header)) { ++ dev_err(&intf->dev, "short manifest (%zu < %zu)\n", ++ size, sizeof(*header)); ++ return false; ++ } ++ ++ /* Make sure the size is right */ ++ manifest = data; ++ header = &manifest->header; ++ manifest_size = le16_to_cpu(header->size); ++ if (manifest_size != size) { ++ dev_err(&intf->dev, "manifest size mismatch (%zu != %u)\n", ++ size, manifest_size); ++ return false; ++ } ++ ++ /* Validate major/minor number */ ++ if (header->version_major > GREYBUS_VERSION_MAJOR) { ++ dev_err(&intf->dev, "manifest version too new (%u.%u > %u.%u)\n", ++ header->version_major, header->version_minor, ++ GREYBUS_VERSION_MAJOR, GREYBUS_VERSION_MINOR); ++ return false; ++ } ++ ++ /* OK, find all the descriptors */ ++ desc = manifest->descriptors; ++ size -= sizeof(*header); ++ while (size) { ++ int desc_size; ++ ++ desc_size = identify_descriptor(intf, desc, size); ++ if (desc_size < 0) { ++ result = false; ++ goto out; ++ } ++ desc = (struct greybus_descriptor *)((char *)desc + desc_size); ++ size -= desc_size; ++ } ++ ++ /* There must be a single interface descriptor */ ++ list_for_each_entry(descriptor, &intf->manifest_descs, links) { ++ if (descriptor->type == GREYBUS_TYPE_INTERFACE) ++ if (!found++) ++ interface_desc = descriptor; ++ } ++ if (found != 1) { ++ dev_err(&intf->dev, "manifest must have 1 interface descriptor (%u found)\n", ++ found); ++ result = false; ++ goto out; ++ } ++ ++ /* Parse the manifest, starting with the interface descriptor */ ++ result = gb_manifest_parse_interface(intf, interface_desc); ++ ++ /* ++ * We really should have no remaining descriptors, but we ++ * don't know what newer format manifests might leave. ++ */ ++ if (result && !list_empty(&intf->manifest_descs)) ++ dev_info(&intf->dev, "excess descriptors in interface manifest\n"); ++out: ++ release_manifest_descriptors(intf); ++ ++ return result; ++} +--- /dev/null ++++ b/drivers/greybus/manifest.h +@@ -0,0 +1,16 @@ ++/* ++ * Greybus manifest parsing ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __MANIFEST_H ++#define __MANIFEST_H ++ ++struct gb_interface; ++bool gb_manifest_parse(struct gb_interface *intf, void *data, size_t size); ++ ++#endif /* __MANIFEST_H */ +--- /dev/null ++++ b/drivers/greybus/module.c +@@ -0,0 +1,238 @@ ++/* ++ * Greybus Module code ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++ ++static ssize_t eject_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct gb_module *module = to_gb_module(dev); ++ struct gb_interface *intf; ++ size_t i; ++ long val; ++ int ret; ++ ++ ret = kstrtol(buf, 0, &val); ++ if (ret) ++ return ret; ++ ++ if (!val) ++ return len; ++ ++ for (i = 0; i < module->num_interfaces; ++i) { ++ intf = module->interfaces[i]; ++ ++ mutex_lock(&intf->mutex); ++ /* Set flag to prevent concurrent activation. */ ++ intf->ejected = true; ++ gb_interface_disable(intf); ++ gb_interface_deactivate(intf); ++ mutex_unlock(&intf->mutex); ++ } ++ ++ /* Tell the SVC to eject the primary interface. */ ++ ret = gb_svc_intf_eject(module->hd->svc, module->module_id); ++ if (ret) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR_WO(eject); ++ ++static ssize_t module_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_module *module = to_gb_module(dev); ++ ++ return sprintf(buf, "%u\n", module->module_id); ++} ++static DEVICE_ATTR_RO(module_id); ++ ++static ssize_t num_interfaces_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_module *module = to_gb_module(dev); ++ ++ return sprintf(buf, "%zu\n", module->num_interfaces); ++} ++static DEVICE_ATTR_RO(num_interfaces); ++ ++static struct attribute *module_attrs[] = { ++ &dev_attr_eject.attr, ++ &dev_attr_module_id.attr, ++ &dev_attr_num_interfaces.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(module); ++ ++static void gb_module_release(struct device *dev) ++{ ++ struct gb_module *module = to_gb_module(dev); ++ ++ trace_gb_module_release(module); ++ ++ kfree(module); ++} ++ ++struct device_type greybus_module_type = { ++ .name = "greybus_module", ++ .release = gb_module_release, ++}; ++ ++struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, ++ size_t num_interfaces) ++{ ++ struct gb_interface *intf; ++ struct gb_module *module; ++ int i; ++ ++ module = kzalloc(sizeof(*module) + num_interfaces * sizeof(intf), ++ GFP_KERNEL); ++ if (!module) ++ return NULL; ++ ++ module->hd = hd; ++ module->module_id = module_id; ++ module->num_interfaces = num_interfaces; ++ ++ module->dev.parent = &hd->dev; ++ module->dev.bus = &greybus_bus_type; ++ module->dev.type = &greybus_module_type; ++ module->dev.groups = module_groups; ++ module->dev.dma_mask = hd->dev.dma_mask; ++ device_initialize(&module->dev); ++ dev_set_name(&module->dev, "%d-%u", hd->bus_id, module_id); ++ ++ trace_gb_module_create(module); ++ ++ for (i = 0; i < num_interfaces; ++i) { ++ intf = gb_interface_create(module, module_id + i); ++ if (!intf) { ++ dev_err(&module->dev, "failed to create interface %u\n", ++ module_id + i); ++ goto err_put_interfaces; ++ } ++ module->interfaces[i] = intf; ++ } ++ ++ return module; ++ ++err_put_interfaces: ++ for (--i; i > 0; --i) ++ gb_interface_put(module->interfaces[i]); ++ ++ put_device(&module->dev); ++ ++ return NULL; ++} ++ ++/* ++ * Register and enable an interface after first attempting to activate it. ++ */ ++static void gb_module_register_interface(struct gb_interface *intf) ++{ ++ struct gb_module *module = intf->module; ++ u8 intf_id = intf->interface_id; ++ int ret; ++ ++ mutex_lock(&intf->mutex); ++ ++ ret = gb_interface_activate(intf); ++ if (ret) { ++ if (intf->type != GB_INTERFACE_TYPE_DUMMY) { ++ dev_err(&module->dev, ++ "failed to activate interface %u: %d\n", ++ intf_id, ret); ++ } ++ ++ gb_interface_add(intf); ++ goto err_unlock; ++ } ++ ++ ret = gb_interface_add(intf); ++ if (ret) ++ goto err_interface_deactivate; ++ ++ ret = gb_interface_enable(intf); ++ if (ret) { ++ dev_err(&module->dev, "failed to enable interface %u: %d\n", ++ intf_id, ret); ++ goto err_interface_deactivate; ++ } ++ ++ mutex_unlock(&intf->mutex); ++ ++ return; ++ ++err_interface_deactivate: ++ gb_interface_deactivate(intf); ++err_unlock: ++ mutex_unlock(&intf->mutex); ++} ++ ++static void gb_module_deregister_interface(struct gb_interface *intf) ++{ ++ /* Mark as disconnected to prevent I/O during disable. */ ++ if (intf->module->disconnected) ++ intf->disconnected = true; ++ ++ mutex_lock(&intf->mutex); ++ intf->removed = true; ++ gb_interface_disable(intf); ++ gb_interface_deactivate(intf); ++ mutex_unlock(&intf->mutex); ++ ++ gb_interface_del(intf); ++} ++ ++/* Register a module and its interfaces. */ ++int gb_module_add(struct gb_module *module) ++{ ++ size_t i; ++ int ret; ++ ++ ret = device_add(&module->dev); ++ if (ret) { ++ dev_err(&module->dev, "failed to register module: %d\n", ret); ++ return ret; ++ } ++ ++ trace_gb_module_add(module); ++ ++ for (i = 0; i < module->num_interfaces; ++i) ++ gb_module_register_interface(module->interfaces[i]); ++ ++ return 0; ++} ++ ++/* Deregister a module and its interfaces. */ ++void gb_module_del(struct gb_module *module) ++{ ++ size_t i; ++ ++ for (i = 0; i < module->num_interfaces; ++i) ++ gb_module_deregister_interface(module->interfaces[i]); ++ ++ trace_gb_module_del(module); ++ ++ device_del(&module->dev); ++} ++ ++void gb_module_put(struct gb_module *module) ++{ ++ size_t i; ++ ++ for (i = 0; i < module->num_interfaces; ++i) ++ gb_interface_put(module->interfaces[i]); ++ ++ put_device(&module->dev); ++} +--- /dev/null ++++ b/drivers/greybus/module.h +@@ -0,0 +1,34 @@ ++/* ++ * Greybus Module code ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __MODULE_H ++#define __MODULE_H ++ ++struct gb_module { ++ struct device dev; ++ struct gb_host_device *hd; ++ ++ struct list_head hd_node; ++ ++ u8 module_id; ++ size_t num_interfaces; ++ ++ bool disconnected; ++ ++ struct gb_interface *interfaces[0]; ++}; ++#define to_gb_module(d) container_of(d, struct gb_module, dev) ++ ++struct gb_module *gb_module_create(struct gb_host_device *hd, u8 module_id, ++ size_t num_interfaces); ++int gb_module_add(struct gb_module *module); ++void gb_module_del(struct gb_module *module); ++void gb_module_put(struct gb_module *module); ++ ++#endif /* __MODULE_H */ diff --git a/greybus_docs.patch b/greybus_docs.patch new file mode 100644 index 00000000000000..63ea77e7f598f1 --- /dev/null +++ b/greybus_docs.patch @@ -0,0 +1,282 @@ +--- + Documentation/ABI/testing/sysfs-bus-greybus | 275 ++++++++++++++++++++++++++++ + 1 file changed, 275 insertions(+) + +--- /dev/null ++++ b/Documentation/ABI/testing/sysfs-bus-greybus +@@ -0,0 +1,275 @@ ++What: /sys/bus/greybus/devices/greybusN ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The "root" greybus device for the Greybus device tree, or bus, ++ where N is a dynamically assigned 1-based id. ++ ++What: /sys/bus/greybus/devices/greybusN/bus_id ++Date: April 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The ID of the "root" greybus device, or bus. ++ ++What: /sys/bus/greybus/devices/N-M ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ A Module M on the bus N, where M is the 1-byte interface ++ ID of the module's primary interface. ++ ++What: /sys/bus/greybus/devices/N-M/eject ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Writing a non-zero argument to this attibute disables the ++ module's interfaces before physically ejecting it. ++ ++What: /sys/bus/greybus/devices/N-M/module_id ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The ID of a Greybus module, corresponding to the ID of its ++ primary interface. ++ ++What: /sys/bus/greybus/devices/N-M/num_interfaces ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The number of interfaces of a module. ++ ++What: /sys/bus/greybus/devices/N-M.I ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ An Interface I on the bus N and module N-M, where I is the ++ 1-byte interface ID. ++ ++What: /sys/bus/greybus/devices/N-M.I/current_now ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Current measurement of the interface in microamps (uA) ++ ++What: /sys/bus/greybus/devices/N-M.I/ddbl1_manufacturer_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Unipro Device Descriptor Block Level 1 manufacturer ID for the ++ greybus Interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/ddbl1_product_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Unipro Device Descriptor Block Level 1 product ID for the ++ greybus Interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/interface_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The ID of a Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/interface_type ++Date: June 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The type of a Greybus interface; "dummy", "unipro", "greybus", ++ or "unknown". ++ ++What: /sys/bus/greybus/devices/N-M.I/power_now ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Power measurement of the interface in microwatts (uW) ++ ++What: /sys/bus/greybus/devices/N-M.I/power_state ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ This file reflects the power state of a Greybus interface. If ++ the value read from it is "on", then power is currently ++ supplied to the interface. Otherwise it will read "off" and ++ power is currently not supplied to the interface. ++ ++ If the value read is "off", then writing "on" (or '1', 'y', ++ 'Y') to this file will enable power to the interface and an ++ attempt to boot and possibly enumerate it will be made. Note ++ that on errors, the interface will again be powered down. ++ ++ If the value read is "on", then writing "off" (or '0', 'n', ++ 'N') to this file will power down the interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/product_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Product ID of a Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/serial_number ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Serial Number of the Greybus interface, represented by a 64 bit ++ hexadecimal number. ++ ++What: /sys/bus/greybus/devices/N-M.I/vendor_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Vendor ID of a Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I/voltage_now ++Date: March 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Voltage measurement of the interface in microvolts (uV) ++ ++What: /sys/bus/greybus/devices/N-M.I.ctrl ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Abstract control device for interface I that represents the ++ current mode of an enumerated Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I.ctrl/product_string ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Product ID string of a Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I.ctrl/vendor_string ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Vendor ID string of a Greybus interface. ++ ++What: /sys/bus/greybus/devices/N-M.I.B ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ A bundle B on the Interface I, B is replaced by a 1-byte ++ number representing the bundle. ++ ++What: /sys/bus/greybus/devices/N-M.I.B/bundle_class ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The greybus class of the bundle B. ++ ++What: /sys/bus/greybus/devices/N-M.I.B/bundle_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The interface-unique id of the bundle B. ++ ++What: /sys/bus/greybus/devices/N-M.I.B/gpbX ++Date: April 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The General Purpose Bridged PHY device of the bundle B, ++ where X is a dynamically assigned 0-based id. ++ ++What: /sys/bus/greybus/devices/N-M.I.B/state ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ A bundle has a state that is managed by the userspace ++ Endo process. This file allows that Endo to signal ++ other Android HALs that the state of the bundle has ++ changed to a specific value. When written to, any ++ process watching the file will be woken up, and the new ++ value can be read. It's a "poor-man's IPC", yes, but ++ simplifies the Android userspace code immensely. ++ ++What: /sys/bus/greybus/devices/N-svc ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The singleton SVC device of bus N. ++ ++What: /sys/bus/greybus/devices/N-svc/ap_intf_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The AP interface ID, a 1-byte non-zero integer which ++ defines the position of the AP module on the frame. ++ The interface positions are defined in the GMP ++ Module Developer Kit. ++ ++What: /sys/bus/greybus/devices/N-svc/endo_id ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The Endo ID, which is a 2-byte hexadecimal value ++ defined by the Endo layout scheme, documented in ++ the GMP Module Developer Kit. ++ ++What: /sys/bus/greybus/devices/N-svc/intf_eject ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ Write the number of the interface that you wish to ++ forcibly eject from the system. ++ ++What: /sys/bus/greybus/devices/N-svc/version ++Date: October 2015 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ The version number of the firmware in the SVC device. ++ ++What: /sys/bus/greybus/devices/N-svc/watchdog ++Date: October 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ If the SVC watchdog is enabled or not. Writing 0 to this ++ file will disable the watchdog, writing 1 will enable it. ++ ++What: /sys/bus/greybus/devices/N-svc/watchdog_action ++Date: July 2016 ++KernelVersion: 4.9 ++Contact: Greg Kroah-Hartman <greg@kroah.com> ++Description: ++ This attribute indicates the action to be performed upon SVC ++ watchdog bite. ++ ++ The action can be one of the "reset" or "panic". Writing either ++ one of the "reset" or "panic" will change the behavior of SVC ++ watchdog bite. Default value is "reset". ++ ++ "reset" means the UniPro subsystem is to be reset. ++ ++ "panic" means SVC watchdog bite will cause kernel to panic. diff --git a/greybus_es2.patch b/greybus_es2.patch new file mode 100644 index 00000000000000..348f530ba46d6b --- /dev/null +++ b/greybus_es2.patch @@ -0,0 +1,1604 @@ +--- + drivers/greybus/es2.c | 1597 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1597 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/es2.c +@@ -0,0 +1,1597 @@ ++/* ++ * Greybus "AP" USB driver for "ES2" controller chips ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/kthread.h> ++#include <linux/sizes.h> ++#include <linux/usb.h> ++#include <linux/kfifo.h> ++#include <linux/debugfs.h> ++#include <linux/list.h> ++#include <asm/unaligned.h> ++ ++#include "arpc.h" ++#include "greybus.h" ++#include "greybus_trace.h" ++#include "connection.h" ++ ++ ++/* Default timeout for USB vendor requests. */ ++#define ES2_USB_CTRL_TIMEOUT 500 ++ ++/* Default timeout for ARPC CPort requests */ ++#define ES2_ARPC_CPORT_TIMEOUT 500 ++ ++/* Fixed CPort numbers */ ++#define ES2_CPORT_CDSI0 16 ++#define ES2_CPORT_CDSI1 17 ++ ++/* Memory sizes for the buffers sent to/from the ES2 controller */ ++#define ES2_GBUF_MSG_SIZE_MAX 2048 ++ ++/* Memory sizes for the ARPC buffers */ ++#define ARPC_OUT_SIZE_MAX U16_MAX ++#define ARPC_IN_SIZE_MAX 128 ++ ++static const struct usb_device_id id_table[] = { ++ { USB_DEVICE(0x18d1, 0x1eaf) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(usb, id_table); ++ ++#define APB1_LOG_SIZE SZ_16K ++ ++/* ++ * Number of CPort IN urbs in flight at any point in time. ++ * Adjust if we are having stalls in the USB buffer due to not enough urbs in ++ * flight. ++ */ ++#define NUM_CPORT_IN_URB 4 ++ ++/* Number of CPort OUT urbs in flight at any point in time. ++ * Adjust if we get messages saying we are out of urbs in the system log. ++ */ ++#define NUM_CPORT_OUT_URB 8 ++ ++/* ++ * Number of ARPC in urbs in flight at any point in time. ++ */ ++#define NUM_ARPC_IN_URB 2 ++ ++/* ++ * @endpoint: bulk in endpoint for CPort data ++ * @urb: array of urbs for the CPort in messages ++ * @buffer: array of buffers for the @cport_in_urb urbs ++ */ ++struct es2_cport_in { ++ __u8 endpoint; ++ struct urb *urb[NUM_CPORT_IN_URB]; ++ u8 *buffer[NUM_CPORT_IN_URB]; ++}; ++ ++/** ++ * es2_ap_dev - ES2 USB Bridge to AP structure ++ * @usb_dev: pointer to the USB device we are. ++ * @usb_intf: pointer to the USB interface we are bound to. ++ * @hd: pointer to our gb_host_device structure ++ ++ * @cport_in: endpoint, urbs and buffer for cport in messages ++ * @cport_out_endpoint: endpoint for for cport out messages ++ * @cport_out_urb: array of urbs for the CPort out messages ++ * @cport_out_urb_busy: array of flags to see if the @cport_out_urb is busy or ++ * not. ++ * @cport_out_urb_cancelled: array of flags indicating whether the ++ * corresponding @cport_out_urb is being cancelled ++ * @cport_out_urb_lock: locks the @cport_out_urb_busy "list" ++ * ++ * @apb_log_task: task pointer for logging thread ++ * @apb_log_dentry: file system entry for the log file interface ++ * @apb_log_enable_dentry: file system entry for enabling logging ++ * @apb_log_fifo: kernel FIFO to carry logged data ++ * @arpc_urb: array of urbs for the ARPC in messages ++ * @arpc_buffer: array of buffers for the @arpc_urb urbs ++ * @arpc_endpoint_in: bulk in endpoint for APBridgeA RPC ++ * @arpc_id_cycle: gives an unique id to ARPC ++ * @arpc_lock: locks ARPC list ++ * @arpcs: list of in progress ARPCs ++ */ ++struct es2_ap_dev { ++ struct usb_device *usb_dev; ++ struct usb_interface *usb_intf; ++ struct gb_host_device *hd; ++ ++ struct es2_cport_in cport_in; ++ __u8 cport_out_endpoint; ++ struct urb *cport_out_urb[NUM_CPORT_OUT_URB]; ++ bool cport_out_urb_busy[NUM_CPORT_OUT_URB]; ++ bool cport_out_urb_cancelled[NUM_CPORT_OUT_URB]; ++ spinlock_t cport_out_urb_lock; ++ ++ bool cdsi1_in_use; ++ ++ struct task_struct *apb_log_task; ++ struct dentry *apb_log_dentry; ++ struct dentry *apb_log_enable_dentry; ++ DECLARE_KFIFO(apb_log_fifo, char, APB1_LOG_SIZE); ++ ++ __u8 arpc_endpoint_in; ++ struct urb *arpc_urb[NUM_ARPC_IN_URB]; ++ u8 *arpc_buffer[NUM_ARPC_IN_URB]; ++ ++ int arpc_id_cycle; ++ spinlock_t arpc_lock; ++ struct list_head arpcs; ++}; ++ ++/** ++ * timesync_enable_request - Enable timesync in an APBridge ++ * @count: number of TimeSync Pulses to expect ++ * @frame_time: the initial FrameTime at the first TimeSync Pulse ++ * @strobe_delay: the expected delay in microseconds between each TimeSync Pulse ++ * @refclk: The AP mandated reference clock to run FrameTime at ++ */ ++struct timesync_enable_request { ++ __u8 count; ++ __le64 frame_time; ++ __le32 strobe_delay; ++ __le32 refclk; ++} __packed; ++ ++/** ++ * timesync_authoritative_request - Transmit authoritative FrameTime to APBridge ++ * @frame_time: An array of authoritative FrameTimes provided by the SVC ++ * and relayed to the APBridge by the AP ++ */ ++struct timesync_authoritative_request { ++ __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; ++} __packed; ++ ++struct arpc { ++ struct list_head list; ++ struct arpc_request_message *req; ++ struct arpc_response_message *resp; ++ struct completion response_received; ++ bool active; ++}; ++ ++static inline struct es2_ap_dev *hd_to_es2(struct gb_host_device *hd) ++{ ++ return (struct es2_ap_dev *)&hd->hd_priv; ++} ++ ++static void cport_out_callback(struct urb *urb); ++static void usb_log_enable(struct es2_ap_dev *es2); ++static void usb_log_disable(struct es2_ap_dev *es2); ++static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, ++ size_t size, int *result, unsigned int timeout); ++ ++static int output_sync(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) ++{ ++ struct usb_device *udev = es2->usb_dev; ++ u8 *data; ++ int retval; ++ ++ data = kmalloc(size, GFP_KERNEL); ++ if (!data) ++ return -ENOMEM; ++ memcpy(data, req, size); ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ cmd, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, ++ 0, 0, data, size, ES2_USB_CTRL_TIMEOUT); ++ if (retval < 0) ++ dev_err(&udev->dev, "%s: return error %d\n", __func__, retval); ++ else ++ retval = 0; ++ ++ kfree(data); ++ return retval; ++} ++ ++static void ap_urb_complete(struct urb *urb) ++{ ++ struct usb_ctrlrequest *dr = urb->context; ++ ++ kfree(dr); ++ usb_free_urb(urb); ++} ++ ++static int output_async(struct es2_ap_dev *es2, void *req, u16 size, u8 cmd) ++{ ++ struct usb_device *udev = es2->usb_dev; ++ struct urb *urb; ++ struct usb_ctrlrequest *dr; ++ u8 *buf; ++ int retval; ++ ++ urb = usb_alloc_urb(0, GFP_ATOMIC); ++ if (!urb) ++ return -ENOMEM; ++ ++ dr = kmalloc(sizeof(*dr) + size, GFP_ATOMIC); ++ if (!dr) { ++ usb_free_urb(urb); ++ return -ENOMEM; ++ } ++ ++ buf = (u8 *)dr + sizeof(*dr); ++ memcpy(buf, req, size); ++ ++ dr->bRequest = cmd; ++ dr->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE; ++ dr->wValue = 0; ++ dr->wIndex = 0; ++ dr->wLength = cpu_to_le16(size); ++ ++ usb_fill_control_urb(urb, udev, usb_sndctrlpipe(udev, 0), ++ (unsigned char *)dr, buf, size, ++ ap_urb_complete, dr); ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) { ++ usb_free_urb(urb); ++ kfree(dr); ++ } ++ return retval; ++} ++ ++static int output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, ++ bool async) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ ++ if (async) ++ return output_async(es2, req, size, cmd); ++ ++ return output_sync(es2, req, size, cmd); ++} ++ ++static int es2_cport_in_enable(struct es2_ap_dev *es2, ++ struct es2_cport_in *cport_in) ++{ ++ struct urb *urb; ++ int ret; ++ int i; ++ ++ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { ++ urb = cport_in->urb[i]; ++ ++ ret = usb_submit_urb(urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&es2->usb_dev->dev, ++ "failed to submit in-urb: %d\n", ret); ++ goto err_kill_urbs; ++ } ++ } ++ ++ return 0; ++ ++err_kill_urbs: ++ for (--i; i >= 0; --i) { ++ urb = cport_in->urb[i]; ++ usb_kill_urb(urb); ++ } ++ ++ return ret; ++} ++ ++static void es2_cport_in_disable(struct es2_ap_dev *es2, ++ struct es2_cport_in *cport_in) ++{ ++ struct urb *urb; ++ int i; ++ ++ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { ++ urb = cport_in->urb[i]; ++ usb_kill_urb(urb); ++ } ++} ++ ++static int es2_arpc_in_enable(struct es2_ap_dev *es2) ++{ ++ struct urb *urb; ++ int ret; ++ int i; ++ ++ for (i = 0; i < NUM_ARPC_IN_URB; ++i) { ++ urb = es2->arpc_urb[i]; ++ ++ ret = usb_submit_urb(urb, GFP_KERNEL); ++ if (ret) { ++ dev_err(&es2->usb_dev->dev, ++ "failed to submit arpc in-urb: %d\n", ret); ++ goto err_kill_urbs; ++ } ++ } ++ ++ return 0; ++ ++err_kill_urbs: ++ for (--i; i >= 0; --i) { ++ urb = es2->arpc_urb[i]; ++ usb_kill_urb(urb); ++ } ++ ++ return ret; ++} ++ ++static void es2_arpc_in_disable(struct es2_ap_dev *es2) ++{ ++ struct urb *urb; ++ int i; ++ ++ for (i = 0; i < NUM_ARPC_IN_URB; ++i) { ++ urb = es2->arpc_urb[i]; ++ usb_kill_urb(urb); ++ } ++} ++ ++static struct urb *next_free_urb(struct es2_ap_dev *es2, gfp_t gfp_mask) ++{ ++ struct urb *urb = NULL; ++ unsigned long flags; ++ int i; ++ ++ spin_lock_irqsave(&es2->cport_out_urb_lock, flags); ++ ++ /* Look in our pool of allocated urbs first, as that's the "fastest" */ ++ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { ++ if (es2->cport_out_urb_busy[i] == false && ++ es2->cport_out_urb_cancelled[i] == false) { ++ es2->cport_out_urb_busy[i] = true; ++ urb = es2->cport_out_urb[i]; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); ++ if (urb) ++ return urb; ++ ++ /* ++ * Crap, pool is empty, complain to the syslog and go allocate one ++ * dynamically as we have to succeed. ++ */ ++ dev_dbg(&es2->usb_dev->dev, ++ "No free CPort OUT urbs, having to dynamically allocate one!\n"); ++ return usb_alloc_urb(0, gfp_mask); ++} ++ ++static void free_urb(struct es2_ap_dev *es2, struct urb *urb) ++{ ++ unsigned long flags; ++ int i; ++ /* ++ * See if this was an urb in our pool, if so mark it "free", otherwise ++ * we need to free it ourselves. ++ */ ++ spin_lock_irqsave(&es2->cport_out_urb_lock, flags); ++ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { ++ if (urb == es2->cport_out_urb[i]) { ++ es2->cport_out_urb_busy[i] = false; ++ urb = NULL; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); ++ ++ /* If urb is not NULL, then we need to free this urb */ ++ usb_free_urb(urb); ++} ++ ++/* ++ * We (ab)use the operation-message header pad bytes to transfer the ++ * cport id in order to minimise overhead. ++ */ ++static void ++gb_message_cport_pack(struct gb_operation_msg_hdr *header, u16 cport_id) ++{ ++ header->pad[0] = cport_id; ++} ++ ++/* Clear the pad bytes used for the CPort id */ ++static void gb_message_cport_clear(struct gb_operation_msg_hdr *header) ++{ ++ header->pad[0] = 0; ++} ++ ++/* Extract the CPort id packed into the header, and clear it */ ++static u16 gb_message_cport_unpack(struct gb_operation_msg_hdr *header) ++{ ++ u16 cport_id = header->pad[0]; ++ ++ gb_message_cport_clear(header); ++ ++ return cport_id; ++} ++ ++/* ++ * Returns zero if the message was successfully queued, or a negative errno ++ * otherwise. ++ */ ++static int message_send(struct gb_host_device *hd, u16 cport_id, ++ struct gb_message *message, gfp_t gfp_mask) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ size_t buffer_size; ++ int retval; ++ struct urb *urb; ++ unsigned long flags; ++ ++ /* ++ * The data actually transferred will include an indication ++ * of where the data should be sent. Do one last check of ++ * the target CPort id before filling it in. ++ */ ++ if (!cport_id_valid(hd, cport_id)) { ++ dev_err(&udev->dev, "invalid cport %u\n", cport_id); ++ return -EINVAL; ++ } ++ ++ /* Find a free urb */ ++ urb = next_free_urb(es2, gfp_mask); ++ if (!urb) ++ return -ENOMEM; ++ ++ spin_lock_irqsave(&es2->cport_out_urb_lock, flags); ++ message->hcpriv = urb; ++ spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); ++ ++ /* Pack the cport id into the message header */ ++ gb_message_cport_pack(message->header, cport_id); ++ ++ buffer_size = sizeof(*message->header) + message->payload_size; ++ ++ usb_fill_bulk_urb(urb, udev, ++ usb_sndbulkpipe(udev, ++ es2->cport_out_endpoint), ++ message->buffer, buffer_size, ++ cport_out_callback, message); ++ urb->transfer_flags |= URB_ZERO_PACKET; ++ ++ trace_gb_message_submit(message); ++ ++ retval = usb_submit_urb(urb, gfp_mask); ++ if (retval) { ++ dev_err(&udev->dev, "failed to submit out-urb: %d\n", retval); ++ ++ spin_lock_irqsave(&es2->cport_out_urb_lock, flags); ++ message->hcpriv = NULL; ++ spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); ++ ++ free_urb(es2, urb); ++ gb_message_cport_clear(message->header); ++ ++ return retval; ++ } ++ ++ return 0; ++} ++ ++/* ++ * Can not be called in atomic context. ++ */ ++static void message_cancel(struct gb_message *message) ++{ ++ struct gb_host_device *hd = message->operation->connection->hd; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct urb *urb; ++ int i; ++ ++ might_sleep(); ++ ++ spin_lock_irq(&es2->cport_out_urb_lock); ++ urb = message->hcpriv; ++ ++ /* Prevent dynamically allocated urb from being deallocated. */ ++ usb_get_urb(urb); ++ ++ /* Prevent pre-allocated urb from being reused. */ ++ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { ++ if (urb == es2->cport_out_urb[i]) { ++ es2->cport_out_urb_cancelled[i] = true; ++ break; ++ } ++ } ++ spin_unlock_irq(&es2->cport_out_urb_lock); ++ ++ usb_kill_urb(urb); ++ ++ if (i < NUM_CPORT_OUT_URB) { ++ spin_lock_irq(&es2->cport_out_urb_lock); ++ es2->cport_out_urb_cancelled[i] = false; ++ spin_unlock_irq(&es2->cport_out_urb_lock); ++ } ++ ++ usb_free_urb(urb); ++} ++ ++static int es2_cport_allocate(struct gb_host_device *hd, int cport_id, ++ unsigned long flags) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct ida *id_map = &hd->cport_id_map; ++ int ida_start, ida_end; ++ ++ switch (cport_id) { ++ case ES2_CPORT_CDSI0: ++ case ES2_CPORT_CDSI1: ++ dev_err(&hd->dev, "cport %d not available\n", cport_id); ++ return -EBUSY; ++ } ++ ++ if (flags & GB_CONNECTION_FLAG_OFFLOADED && ++ flags & GB_CONNECTION_FLAG_CDSI1) { ++ if (es2->cdsi1_in_use) { ++ dev_err(&hd->dev, "CDSI1 already in use\n"); ++ return -EBUSY; ++ } ++ ++ es2->cdsi1_in_use = true; ++ ++ return ES2_CPORT_CDSI1; ++ } ++ ++ if (cport_id < 0) { ++ ida_start = 0; ++ ida_end = hd->num_cports; ++ } else if (cport_id < hd->num_cports) { ++ ida_start = cport_id; ++ ida_end = cport_id + 1; ++ } else { ++ dev_err(&hd->dev, "cport %d not available\n", cport_id); ++ return -EINVAL; ++ } ++ ++ return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); ++} ++ ++static void es2_cport_release(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ ++ switch (cport_id) { ++ case ES2_CPORT_CDSI1: ++ es2->cdsi1_in_use = false; ++ return; ++ } ++ ++ ida_simple_remove(&hd->cport_id_map, cport_id); ++} ++ ++static int cport_enable(struct gb_host_device *hd, u16 cport_id, ++ unsigned long flags) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ struct gb_apb_request_cport_flags *req; ++ u32 connection_flags; ++ int ret; ++ ++ req = kzalloc(sizeof(*req), GFP_KERNEL); ++ if (!req) ++ return -ENOMEM; ++ ++ connection_flags = 0; ++ if (flags & GB_CONNECTION_FLAG_CONTROL) ++ connection_flags |= GB_APB_CPORT_FLAG_CONTROL; ++ if (flags & GB_CONNECTION_FLAG_HIGH_PRIO) ++ connection_flags |= GB_APB_CPORT_FLAG_HIGH_PRIO; ++ ++ req->flags = cpu_to_le32(connection_flags); ++ ++ dev_dbg(&hd->dev, "%s - cport = %u, flags = %02x\n", __func__, ++ cport_id, connection_flags); ++ ++ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_CPORT_FLAGS, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, cport_id, 0, ++ req, sizeof(*req), ES2_USB_CTRL_TIMEOUT); ++ if (ret != sizeof(*req)) { ++ dev_err(&udev->dev, "failed to set cport flags for port %d\n", ++ cport_id); ++ if (ret >= 0) ++ ret = -EIO; ++ ++ goto out; ++ } ++ ++ ret = 0; ++out: ++ kfree(req); ++ ++ return ret; ++} ++ ++static int es2_cport_connected(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct device *dev = &es2->usb_dev->dev; ++ struct arpc_cport_connected_req req; ++ int ret; ++ ++ req.cport_id = cpu_to_le16(cport_id); ++ ret = arpc_sync(es2, ARPC_TYPE_CPORT_CONNECTED, &req, sizeof(req), ++ NULL, ES2_ARPC_CPORT_TIMEOUT); ++ if (ret) { ++ dev_err(dev, "failed to set connected state for cport %u: %d\n", ++ cport_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int es2_cport_flush(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct device *dev = &es2->usb_dev->dev; ++ struct arpc_cport_flush_req req; ++ int ret; ++ ++ req.cport_id = cpu_to_le16(cport_id); ++ ret = arpc_sync(es2, ARPC_TYPE_CPORT_FLUSH, &req, sizeof(req), ++ NULL, ES2_ARPC_CPORT_TIMEOUT); ++ if (ret) { ++ dev_err(dev, "failed to flush cport %u: %d\n", cport_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int es2_cport_shutdown(struct gb_host_device *hd, u16 cport_id, ++ u8 phase, unsigned int timeout) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct device *dev = &es2->usb_dev->dev; ++ struct arpc_cport_shutdown_req req; ++ int result; ++ int ret; ++ ++ if (timeout > U16_MAX) ++ return -EINVAL; ++ ++ req.cport_id = cpu_to_le16(cport_id); ++ req.timeout = cpu_to_le16(timeout); ++ req.phase = phase; ++ ret = arpc_sync(es2, ARPC_TYPE_CPORT_SHUTDOWN, &req, sizeof(req), ++ &result, ES2_ARPC_CPORT_TIMEOUT + timeout); ++ if (ret) { ++ dev_err(dev, "failed to send shutdown over cport %u: %d (%d)\n", ++ cport_id, ret, result); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int es2_cport_quiesce(struct gb_host_device *hd, u16 cport_id, ++ size_t peer_space, unsigned int timeout) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct device *dev = &es2->usb_dev->dev; ++ struct arpc_cport_quiesce_req req; ++ int result; ++ int ret; ++ ++ if (peer_space > U16_MAX) ++ return -EINVAL; ++ ++ if (timeout > U16_MAX) ++ return -EINVAL; ++ ++ req.cport_id = cpu_to_le16(cport_id); ++ req.peer_space = cpu_to_le16(peer_space); ++ req.timeout = cpu_to_le16(timeout); ++ ret = arpc_sync(es2, ARPC_TYPE_CPORT_QUIESCE, &req, sizeof(req), ++ &result, ES2_ARPC_CPORT_TIMEOUT + timeout); ++ if (ret) { ++ dev_err(dev, "failed to quiesce cport %u: %d (%d)\n", ++ cport_id, ret, result); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int es2_cport_clear(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct device *dev = &es2->usb_dev->dev; ++ struct arpc_cport_clear_req req; ++ int ret; ++ ++ req.cport_id = cpu_to_le16(cport_id); ++ ret = arpc_sync(es2, ARPC_TYPE_CPORT_CLEAR, &req, sizeof(req), ++ NULL, ES2_ARPC_CPORT_TIMEOUT); ++ if (ret) { ++ dev_err(dev, "failed to clear cport %u: %d\n", cport_id, ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int latency_tag_enable(struct gb_host_device *hd, u16 cport_id) ++{ ++ int retval; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_LATENCY_TAG_EN, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, cport_id, 0, NULL, ++ 0, ES2_USB_CTRL_TIMEOUT); ++ ++ if (retval < 0) ++ dev_err(&udev->dev, "Cannot enable latency tag for cport %d\n", ++ cport_id); ++ return retval; ++} ++ ++static int latency_tag_disable(struct gb_host_device *hd, u16 cport_id) ++{ ++ int retval; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_LATENCY_TAG_DIS, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, cport_id, 0, NULL, ++ 0, ES2_USB_CTRL_TIMEOUT); ++ ++ if (retval < 0) ++ dev_err(&udev->dev, "Cannot disable latency tag for cport %d\n", ++ cport_id); ++ return retval; ++} ++ ++static int timesync_enable(struct gb_host_device *hd, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk) ++{ ++ int retval; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ struct gb_control_timesync_enable_request *request; ++ ++ request = kzalloc(sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ request->count = count; ++ request->frame_time = cpu_to_le64(frame_time); ++ request->strobe_delay = cpu_to_le32(strobe_delay); ++ request->refclk = cpu_to_le32(refclk); ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_TIMESYNC_ENABLE, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, 0, 0, request, ++ sizeof(*request), ES2_USB_CTRL_TIMEOUT); ++ if (retval < 0) ++ dev_err(&udev->dev, "Cannot enable timesync %d\n", retval); ++ ++ kfree(request); ++ return retval; ++} ++ ++static int timesync_disable(struct gb_host_device *hd) ++{ ++ int retval; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_TIMESYNC_DISABLE, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, 0, 0, NULL, ++ 0, ES2_USB_CTRL_TIMEOUT); ++ if (retval < 0) ++ dev_err(&udev->dev, "Cannot disable timesync %d\n", retval); ++ ++ return retval; ++} ++ ++static int timesync_authoritative(struct gb_host_device *hd, u64 *frame_time) ++{ ++ int retval, i; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ struct timesync_authoritative_request *request; ++ ++ request = kzalloc(sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) ++ request->frame_time[i] = cpu_to_le64(frame_time[i]); ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_TIMESYNC_AUTHORITATIVE, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, 0, 0, request, ++ sizeof(*request), ES2_USB_CTRL_TIMEOUT); ++ if (retval < 0) ++ dev_err(&udev->dev, "Cannot timesync authoritative out %d\n", retval); ++ ++ kfree(request); ++ return retval; ++} ++ ++static int timesync_get_last_event(struct gb_host_device *hd, u64 *frame_time) ++{ ++ int retval; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ struct usb_device *udev = es2->usb_dev; ++ __le64 *response_frame_time; ++ ++ response_frame_time = kzalloc(sizeof(*response_frame_time), GFP_KERNEL); ++ if (!response_frame_time) ++ return -ENOMEM; ++ ++ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), ++ GB_APB_REQUEST_TIMESYNC_GET_LAST_EVENT, ++ USB_DIR_IN | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, 0, 0, response_frame_time, ++ sizeof(*response_frame_time), ++ ES2_USB_CTRL_TIMEOUT); ++ ++ if (retval != sizeof(*response_frame_time)) { ++ dev_err(&udev->dev, "Cannot get last TimeSync event: %d\n", ++ retval); ++ ++ if (retval >= 0) ++ retval = -EIO; ++ ++ goto out; ++ } ++ *frame_time = le64_to_cpu(*response_frame_time); ++ retval = 0; ++out: ++ kfree(response_frame_time); ++ return retval; ++} ++ ++static struct gb_hd_driver es2_driver = { ++ .hd_priv_size = sizeof(struct es2_ap_dev), ++ .message_send = message_send, ++ .message_cancel = message_cancel, ++ .cport_allocate = es2_cport_allocate, ++ .cport_release = es2_cport_release, ++ .cport_enable = cport_enable, ++ .cport_connected = es2_cport_connected, ++ .cport_flush = es2_cport_flush, ++ .cport_shutdown = es2_cport_shutdown, ++ .cport_quiesce = es2_cport_quiesce, ++ .cport_clear = es2_cport_clear, ++ .latency_tag_enable = latency_tag_enable, ++ .latency_tag_disable = latency_tag_disable, ++ .output = output, ++ .timesync_enable = timesync_enable, ++ .timesync_disable = timesync_disable, ++ .timesync_authoritative = timesync_authoritative, ++ .timesync_get_last_event = timesync_get_last_event, ++}; ++ ++/* Common function to report consistent warnings based on URB status */ ++static int check_urb_status(struct urb *urb) ++{ ++ struct device *dev = &urb->dev->dev; ++ int status = urb->status; ++ ++ switch (status) { ++ case 0: ++ return 0; ++ ++ case -EOVERFLOW: ++ dev_err(dev, "%s: overflow actual length is %d\n", ++ __func__, urb->actual_length); ++ case -ECONNRESET: ++ case -ENOENT: ++ case -ESHUTDOWN: ++ case -EILSEQ: ++ case -EPROTO: ++ /* device is gone, stop sending */ ++ return status; ++ } ++ dev_err(dev, "%s: unknown status %d\n", __func__, status); ++ ++ return -EAGAIN; ++} ++ ++static void es2_destroy(struct es2_ap_dev *es2) ++{ ++ struct usb_device *udev; ++ struct urb *urb; ++ int i; ++ ++ debugfs_remove(es2->apb_log_enable_dentry); ++ usb_log_disable(es2); ++ ++ /* Tear down everything! */ ++ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { ++ urb = es2->cport_out_urb[i]; ++ usb_kill_urb(urb); ++ usb_free_urb(urb); ++ es2->cport_out_urb[i] = NULL; ++ es2->cport_out_urb_busy[i] = false; /* just to be anal */ ++ } ++ ++ for (i = 0; i < NUM_ARPC_IN_URB; ++i) { ++ usb_free_urb(es2->arpc_urb[i]); ++ kfree(es2->arpc_buffer[i]); ++ es2->arpc_buffer[i] = NULL; ++ } ++ ++ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { ++ usb_free_urb(es2->cport_in.urb[i]); ++ kfree(es2->cport_in.buffer[i]); ++ es2->cport_in.buffer[i] = NULL; ++ } ++ ++ /* release reserved CDSI0 and CDSI1 cports */ ++ gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI1); ++ gb_hd_cport_release_reserved(es2->hd, ES2_CPORT_CDSI0); ++ ++ udev = es2->usb_dev; ++ gb_hd_put(es2->hd); ++ ++ usb_put_dev(udev); ++} ++ ++static void cport_in_callback(struct urb *urb) ++{ ++ struct gb_host_device *hd = urb->context; ++ struct device *dev = &urb->dev->dev; ++ struct gb_operation_msg_hdr *header; ++ int status = check_urb_status(urb); ++ int retval; ++ u16 cport_id; ++ ++ if (status) { ++ if ((status == -EAGAIN) || (status == -EPROTO)) ++ goto exit; ++ ++ /* The urb is being unlinked */ ++ if (status == -ENOENT || status == -ESHUTDOWN) ++ return; ++ ++ dev_err(dev, "urb cport in error %d (dropped)\n", status); ++ return; ++ } ++ ++ if (urb->actual_length < sizeof(*header)) { ++ dev_err(dev, "short message received\n"); ++ goto exit; ++ } ++ ++ /* Extract the CPort id, which is packed in the message header */ ++ header = urb->transfer_buffer; ++ cport_id = gb_message_cport_unpack(header); ++ ++ if (cport_id_valid(hd, cport_id)) { ++ greybus_data_rcvd(hd, cport_id, urb->transfer_buffer, ++ urb->actual_length); ++ } else { ++ dev_err(dev, "invalid cport id %u received\n", cport_id); ++ } ++exit: ++ /* put our urb back in the request pool */ ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ dev_err(dev, "failed to resubmit in-urb: %d\n", retval); ++} ++ ++static void cport_out_callback(struct urb *urb) ++{ ++ struct gb_message *message = urb->context; ++ struct gb_host_device *hd = message->operation->connection->hd; ++ struct es2_ap_dev *es2 = hd_to_es2(hd); ++ int status = check_urb_status(urb); ++ unsigned long flags; ++ ++ gb_message_cport_clear(message->header); ++ ++ spin_lock_irqsave(&es2->cport_out_urb_lock, flags); ++ message->hcpriv = NULL; ++ spin_unlock_irqrestore(&es2->cport_out_urb_lock, flags); ++ ++ /* ++ * Tell the submitter that the message send (attempt) is ++ * complete, and report the status. ++ */ ++ greybus_message_sent(hd, message, status); ++ ++ free_urb(es2, urb); ++} ++ ++static struct arpc *arpc_alloc(void *payload, u16 size, u8 type) ++{ ++ struct arpc *rpc; ++ ++ if (size + sizeof(*rpc->req) > ARPC_OUT_SIZE_MAX) ++ return NULL; ++ ++ rpc = kzalloc(sizeof(*rpc), GFP_KERNEL); ++ if (!rpc) ++ return NULL; ++ ++ INIT_LIST_HEAD(&rpc->list); ++ rpc->req = kzalloc(sizeof(*rpc->req) + size, GFP_KERNEL); ++ if (!rpc->req) ++ goto err_free_rpc; ++ ++ rpc->resp = kzalloc(sizeof(*rpc->resp), GFP_KERNEL); ++ if (!rpc->resp) ++ goto err_free_req; ++ ++ rpc->req->type = type; ++ rpc->req->size = cpu_to_le16(sizeof(rpc->req) + size); ++ memcpy(rpc->req->data, payload, size); ++ ++ init_completion(&rpc->response_received); ++ ++ return rpc; ++ ++err_free_req: ++ kfree(rpc->req); ++err_free_rpc: ++ kfree(rpc); ++ ++ return NULL; ++} ++ ++static void arpc_free(struct arpc *rpc) ++{ ++ kfree(rpc->req); ++ kfree(rpc->resp); ++ kfree(rpc); ++} ++ ++static struct arpc *arpc_find(struct es2_ap_dev *es2, __le16 id) ++{ ++ struct arpc *rpc; ++ ++ list_for_each_entry(rpc, &es2->arpcs, list) { ++ if (rpc->req->id == id) ++ return rpc; ++ } ++ ++ return NULL; ++} ++ ++static void arpc_add(struct es2_ap_dev *es2, struct arpc *rpc) ++{ ++ rpc->active = true; ++ rpc->req->id = cpu_to_le16(es2->arpc_id_cycle++); ++ list_add_tail(&rpc->list, &es2->arpcs); ++} ++ ++static void arpc_del(struct es2_ap_dev *es2, struct arpc *rpc) ++{ ++ if (rpc->active) { ++ rpc->active = false; ++ list_del(&rpc->list); ++ } ++} ++ ++static int arpc_send(struct es2_ap_dev *es2, struct arpc *rpc, int timeout) ++{ ++ struct usb_device *udev = es2->usb_dev; ++ int retval; ++ ++ retval = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), ++ GB_APB_REQUEST_ARPC_RUN, ++ USB_DIR_OUT | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, ++ 0, 0, ++ rpc->req, le16_to_cpu(rpc->req->size), ++ ES2_USB_CTRL_TIMEOUT); ++ if (retval != le16_to_cpu(rpc->req->size)) { ++ dev_err(&udev->dev, ++ "failed to send ARPC request %d: %d\n", ++ rpc->req->type, retval); ++ if (retval > 0) ++ retval = -EIO; ++ return retval; ++ } ++ ++ return 0; ++} ++ ++static int arpc_sync(struct es2_ap_dev *es2, u8 type, void *payload, ++ size_t size, int *result, unsigned int timeout) ++{ ++ struct arpc *rpc; ++ unsigned long flags; ++ int retval; ++ ++ if (result) ++ *result = 0; ++ ++ rpc = arpc_alloc(payload, size, type); ++ if (!rpc) ++ return -ENOMEM; ++ ++ spin_lock_irqsave(&es2->arpc_lock, flags); ++ arpc_add(es2, rpc); ++ spin_unlock_irqrestore(&es2->arpc_lock, flags); ++ ++ retval = arpc_send(es2, rpc, timeout); ++ if (retval) ++ goto out_arpc_del; ++ ++ retval = wait_for_completion_interruptible_timeout( ++ &rpc->response_received, ++ msecs_to_jiffies(timeout)); ++ if (retval <= 0) { ++ if (!retval) ++ retval = -ETIMEDOUT; ++ goto out_arpc_del; ++ } ++ ++ if (rpc->resp->result) { ++ retval = -EREMOTEIO; ++ if (result) ++ *result = rpc->resp->result; ++ } else { ++ retval = 0; ++ } ++ ++out_arpc_del: ++ spin_lock_irqsave(&es2->arpc_lock, flags); ++ arpc_del(es2, rpc); ++ spin_unlock_irqrestore(&es2->arpc_lock, flags); ++ arpc_free(rpc); ++ ++ if (retval < 0 && retval != -EREMOTEIO) { ++ dev_err(&es2->usb_dev->dev, ++ "failed to execute ARPC: %d\n", retval); ++ } ++ ++ return retval; ++} ++ ++static void arpc_in_callback(struct urb *urb) ++{ ++ struct es2_ap_dev *es2 = urb->context; ++ struct device *dev = &urb->dev->dev; ++ int status = check_urb_status(urb); ++ struct arpc *rpc; ++ struct arpc_response_message *resp; ++ unsigned long flags; ++ int retval; ++ ++ if (status) { ++ if ((status == -EAGAIN) || (status == -EPROTO)) ++ goto exit; ++ ++ /* The urb is being unlinked */ ++ if (status == -ENOENT || status == -ESHUTDOWN) ++ return; ++ ++ dev_err(dev, "arpc in-urb error %d (dropped)\n", status); ++ return; ++ } ++ ++ if (urb->actual_length < sizeof(*resp)) { ++ dev_err(dev, "short aprc response received\n"); ++ goto exit; ++ } ++ ++ resp = urb->transfer_buffer; ++ spin_lock_irqsave(&es2->arpc_lock, flags); ++ rpc = arpc_find(es2, resp->id); ++ if (!rpc) { ++ dev_err(dev, "invalid arpc response id received: %u\n", ++ le16_to_cpu(resp->id)); ++ spin_unlock_irqrestore(&es2->arpc_lock, flags); ++ goto exit; ++ } ++ ++ arpc_del(es2, rpc); ++ memcpy(rpc->resp, resp, sizeof(*resp)); ++ complete(&rpc->response_received); ++ spin_unlock_irqrestore(&es2->arpc_lock, flags); ++ ++exit: ++ /* put our urb back in the request pool */ ++ retval = usb_submit_urb(urb, GFP_ATOMIC); ++ if (retval) ++ dev_err(dev, "failed to resubmit arpc in-urb: %d\n", retval); ++} ++ ++#define APB1_LOG_MSG_SIZE 64 ++static void apb_log_get(struct es2_ap_dev *es2, char *buf) ++{ ++ int retval; ++ ++ do { ++ retval = usb_control_msg(es2->usb_dev, ++ usb_rcvctrlpipe(es2->usb_dev, 0), ++ GB_APB_REQUEST_LOG, ++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE, ++ 0x00, 0x00, ++ buf, ++ APB1_LOG_MSG_SIZE, ++ ES2_USB_CTRL_TIMEOUT); ++ if (retval > 0) ++ kfifo_in(&es2->apb_log_fifo, buf, retval); ++ } while (retval > 0); ++} ++ ++static int apb_log_poll(void *data) ++{ ++ struct es2_ap_dev *es2 = data; ++ char *buf; ++ ++ buf = kmalloc(APB1_LOG_MSG_SIZE, GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ while (!kthread_should_stop()) { ++ msleep(1000); ++ apb_log_get(es2, buf); ++ } ++ ++ kfree(buf); ++ ++ return 0; ++} ++ ++static ssize_t apb_log_read(struct file *f, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct es2_ap_dev *es2 = f->f_inode->i_private; ++ ssize_t ret; ++ size_t copied; ++ char *tmp_buf; ++ ++ if (count > APB1_LOG_SIZE) ++ count = APB1_LOG_SIZE; ++ ++ tmp_buf = kmalloc(count, GFP_KERNEL); ++ if (!tmp_buf) ++ return -ENOMEM; ++ ++ copied = kfifo_out(&es2->apb_log_fifo, tmp_buf, count); ++ ret = simple_read_from_buffer(buf, count, ppos, tmp_buf, copied); ++ ++ kfree(tmp_buf); ++ ++ return ret; ++} ++ ++static const struct file_operations apb_log_fops = { ++ .read = apb_log_read, ++}; ++ ++static void usb_log_enable(struct es2_ap_dev *es2) ++{ ++ if (!IS_ERR_OR_NULL(es2->apb_log_task)) ++ return; ++ ++ /* get log from APB1 */ ++ es2->apb_log_task = kthread_run(apb_log_poll, es2, "apb_log"); ++ if (IS_ERR(es2->apb_log_task)) ++ return; ++ /* XXX We will need to rename this per APB */ ++ es2->apb_log_dentry = debugfs_create_file("apb_log", S_IRUGO, ++ gb_debugfs_get(), es2, ++ &apb_log_fops); ++} ++ ++static void usb_log_disable(struct es2_ap_dev *es2) ++{ ++ if (IS_ERR_OR_NULL(es2->apb_log_task)) ++ return; ++ ++ debugfs_remove(es2->apb_log_dentry); ++ es2->apb_log_dentry = NULL; ++ ++ kthread_stop(es2->apb_log_task); ++ es2->apb_log_task = NULL; ++} ++ ++static ssize_t apb_log_enable_read(struct file *f, char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct es2_ap_dev *es2 = f->f_inode->i_private; ++ int enable = !IS_ERR_OR_NULL(es2->apb_log_task); ++ char tmp_buf[3]; ++ ++ sprintf(tmp_buf, "%d\n", enable); ++ return simple_read_from_buffer(buf, count, ppos, tmp_buf, 3); ++} ++ ++static ssize_t apb_log_enable_write(struct file *f, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ int enable; ++ ssize_t retval; ++ struct es2_ap_dev *es2 = f->f_inode->i_private; ++ ++ retval = kstrtoint_from_user(buf, count, 10, &enable); ++ if (retval) ++ return retval; ++ ++ if (enable) ++ usb_log_enable(es2); ++ else ++ usb_log_disable(es2); ++ ++ return count; ++} ++ ++static const struct file_operations apb_log_enable_fops = { ++ .read = apb_log_enable_read, ++ .write = apb_log_enable_write, ++}; ++ ++static int apb_get_cport_count(struct usb_device *udev) ++{ ++ int retval; ++ __le16 *cport_count; ++ ++ cport_count = kzalloc(sizeof(*cport_count), GFP_KERNEL); ++ if (!cport_count) ++ return -ENOMEM; ++ ++ retval = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), ++ GB_APB_REQUEST_CPORT_COUNT, ++ USB_DIR_IN | USB_TYPE_VENDOR | ++ USB_RECIP_INTERFACE, 0, 0, cport_count, ++ sizeof(*cport_count), ES2_USB_CTRL_TIMEOUT); ++ if (retval != sizeof(*cport_count)) { ++ dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", ++ retval); ++ ++ if (retval >= 0) ++ retval = -EIO; ++ ++ goto out; ++ } ++ ++ retval = le16_to_cpu(*cport_count); ++ ++ /* We need to fit a CPort ID in one byte of a message header */ ++ if (retval > U8_MAX) { ++ retval = U8_MAX; ++ dev_warn(&udev->dev, "Limiting number of CPorts to U8_MAX\n"); ++ } ++ ++out: ++ kfree(cport_count); ++ return retval; ++} ++ ++/* ++ * The ES2 USB Bridge device has 15 endpoints ++ * 1 Control - usual USB stuff + AP -> APBridgeA messages ++ * 7 Bulk IN - CPort data in ++ * 7 Bulk OUT - CPort data out ++ */ ++static int ap_probe(struct usb_interface *interface, ++ const struct usb_device_id *id) ++{ ++ struct es2_ap_dev *es2; ++ struct gb_host_device *hd; ++ struct usb_device *udev; ++ struct usb_host_interface *iface_desc; ++ struct usb_endpoint_descriptor *endpoint; ++ __u8 ep_addr; ++ int retval; ++ int i; ++ int num_cports; ++ bool bulk_out_found = false; ++ bool bulk_in_found = false; ++ bool arpc_in_found = false; ++ ++ udev = usb_get_dev(interface_to_usbdev(interface)); ++ ++ num_cports = apb_get_cport_count(udev); ++ if (num_cports < 0) { ++ usb_put_dev(udev); ++ dev_err(&udev->dev, "Cannot retrieve CPort count: %d\n", ++ num_cports); ++ return num_cports; ++ } ++ ++ hd = gb_hd_create(&es2_driver, &udev->dev, ES2_GBUF_MSG_SIZE_MAX, ++ num_cports); ++ if (IS_ERR(hd)) { ++ usb_put_dev(udev); ++ return PTR_ERR(hd); ++ } ++ ++ es2 = hd_to_es2(hd); ++ es2->hd = hd; ++ es2->usb_intf = interface; ++ es2->usb_dev = udev; ++ spin_lock_init(&es2->cport_out_urb_lock); ++ INIT_KFIFO(es2->apb_log_fifo); ++ usb_set_intfdata(interface, es2); ++ ++ /* ++ * Reserve the CDSI0 and CDSI1 CPorts so they won't be allocated ++ * dynamically. ++ */ ++ retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI0); ++ if (retval) ++ goto error; ++ retval = gb_hd_cport_reserve(hd, ES2_CPORT_CDSI1); ++ if (retval) ++ goto error; ++ ++ /* find all bulk endpoints */ ++ iface_desc = interface->cur_altsetting; ++ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { ++ endpoint = &iface_desc->endpoint[i].desc; ++ ep_addr = endpoint->bEndpointAddress; ++ ++ if (usb_endpoint_is_bulk_in(endpoint)) { ++ if (!bulk_in_found) { ++ es2->cport_in.endpoint = ep_addr; ++ bulk_in_found = true; ++ } else if (!arpc_in_found) { ++ es2->arpc_endpoint_in = ep_addr; ++ arpc_in_found = true; ++ } else { ++ dev_warn(&udev->dev, ++ "Unused bulk IN endpoint found: 0x%02x\n", ++ ep_addr); ++ } ++ continue; ++ } ++ if (usb_endpoint_is_bulk_out(endpoint)) { ++ if (!bulk_out_found) { ++ es2->cport_out_endpoint = ep_addr; ++ bulk_out_found = true; ++ } else { ++ dev_warn(&udev->dev, ++ "Unused bulk OUT endpoint found: 0x%02x\n", ++ ep_addr); ++ } ++ continue; ++ } ++ dev_warn(&udev->dev, ++ "Unknown endpoint type found, address 0x%02x\n", ++ ep_addr); ++ } ++ if (!bulk_in_found || !arpc_in_found || !bulk_out_found) { ++ dev_err(&udev->dev, "Not enough endpoints found in device, aborting!\n"); ++ retval = -ENODEV; ++ goto error; ++ } ++ ++ /* Allocate buffers for our cport in messages */ ++ for (i = 0; i < NUM_CPORT_IN_URB; ++i) { ++ struct urb *urb; ++ u8 *buffer; ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ es2->cport_in.urb[i] = urb; ++ ++ buffer = kmalloc(ES2_GBUF_MSG_SIZE_MAX, GFP_KERNEL); ++ if (!buffer) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ usb_fill_bulk_urb(urb, udev, ++ usb_rcvbulkpipe(udev, es2->cport_in.endpoint), ++ buffer, ES2_GBUF_MSG_SIZE_MAX, ++ cport_in_callback, hd); ++ ++ es2->cport_in.buffer[i] = buffer; ++ } ++ ++ /* Allocate buffers for ARPC in messages */ ++ for (i = 0; i < NUM_ARPC_IN_URB; ++i) { ++ struct urb *urb; ++ u8 *buffer; ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ es2->arpc_urb[i] = urb; ++ ++ buffer = kmalloc(ARPC_IN_SIZE_MAX, GFP_KERNEL); ++ if (!buffer) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ usb_fill_bulk_urb(urb, udev, ++ usb_rcvbulkpipe(udev, ++ es2->arpc_endpoint_in), ++ buffer, ARPC_IN_SIZE_MAX, ++ arpc_in_callback, es2); ++ ++ es2->arpc_buffer[i] = buffer; ++ } ++ ++ /* Allocate urbs for our CPort OUT messages */ ++ for (i = 0; i < NUM_CPORT_OUT_URB; ++i) { ++ struct urb *urb; ++ ++ urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!urb) { ++ retval = -ENOMEM; ++ goto error; ++ } ++ ++ es2->cport_out_urb[i] = urb; ++ es2->cport_out_urb_busy[i] = false; /* just to be anal */ ++ } ++ ++ /* XXX We will need to rename this per APB */ ++ es2->apb_log_enable_dentry = debugfs_create_file("apb_log_enable", ++ (S_IWUSR | S_IRUGO), ++ gb_debugfs_get(), es2, ++ &apb_log_enable_fops); ++ ++ INIT_LIST_HEAD(&es2->arpcs); ++ spin_lock_init(&es2->arpc_lock); ++ ++ if (es2_arpc_in_enable(es2)) ++ goto error; ++ ++ retval = gb_hd_add(hd); ++ if (retval) ++ goto err_disable_arpc_in; ++ ++ retval = es2_cport_in_enable(es2, &es2->cport_in); ++ if (retval) ++ goto err_hd_del; ++ ++ return 0; ++ ++err_hd_del: ++ gb_hd_del(hd); ++err_disable_arpc_in: ++ es2_arpc_in_disable(es2); ++error: ++ es2_destroy(es2); ++ ++ return retval; ++} ++ ++static void ap_disconnect(struct usb_interface *interface) ++{ ++ struct es2_ap_dev *es2 = usb_get_intfdata(interface); ++ ++ gb_hd_del(es2->hd); ++ ++ es2_cport_in_disable(es2, &es2->cport_in); ++ es2_arpc_in_disable(es2); ++ ++ es2_destroy(es2); ++} ++ ++static struct usb_driver es2_ap_driver = { ++ .name = "es2_ap_driver", ++ .probe = ap_probe, ++ .disconnect = ap_disconnect, ++ .id_table = id_table, ++ .soft_unbind = 1, ++}; ++ ++module_usb_driver(es2_ap_driver); ++ ++MODULE_LICENSE("GPL v2"); ++MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>"); diff --git a/greybus_firmware.patch b/greybus_firmware.patch new file mode 100644 index 00000000000000..aa4c4f041c4464 --- /dev/null +++ b/greybus_firmware.patch @@ -0,0 +1,2429 @@ +--- + drivers/greybus/Documentation/firmware/authenticate.c | 139 ++ + drivers/greybus/Documentation/firmware/firmware-management | 333 ++++++ + drivers/greybus/Documentation/firmware/firmware.c | 262 ++++ + drivers/greybus/firmware.h | 42 + drivers/greybus/fw-core.c | 312 +++++ + drivers/greybus/fw-download.c | 465 ++++++++ + drivers/greybus/fw-management.c | 721 +++++++++++++ + drivers/greybus/greybus_firmware.h | 120 ++ + 8 files changed, 2394 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/Documentation/firmware/authenticate.c +@@ -0,0 +1,139 @@ ++/* ++ * Sample code to test CAP protocol ++ * ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include <stdio.h> ++#include <string.h> ++#include <unistd.h> ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++ ++#include "../../greybus_authentication.h" ++ ++struct cap_ioc_get_endpoint_uid uid; ++struct cap_ioc_get_ims_certificate cert = { ++ .certificate_class = 0, ++ .certificate_id = 0, ++}; ++ ++struct cap_ioc_authenticate authenticate = { ++ .auth_type = 0, ++ .challenge = {0}, ++}; ++ ++int main(int argc, char *argv[]) ++{ ++ unsigned int timeout = 10000; ++ char *capdev; ++ int fd, ret; ++ ++ /* Make sure arguments are correct */ ++ if (argc != 2) { ++ printf("\nUsage: ./firmware <Path of the gb-cap-X dev>\n"); ++ return 0; ++ } ++ ++ capdev = argv[1]; ++ ++ printf("Opening %s authentication device\n", capdev); ++ ++ fd = open(capdev, O_RDWR); ++ if (fd < 0) { ++ printf("Failed to open: %s\n", capdev); ++ return -1; ++ } ++ ++ /* Get UID */ ++ printf("Get UID\n"); ++ ++ ret = ioctl(fd, CAP_IOC_GET_ENDPOINT_UID, &uid); ++ if (ret < 0) { ++ printf("Failed to get UID: %s (%d)\n", capdev, ret); ++ ret = -1; ++ goto close_fd; ++ } ++ ++ printf("UID received: 0x%llx\n", *(long long unsigned int *)(uid.uid)); ++ ++ /* Get certificate */ ++ printf("Get IMS certificate\n"); ++ ++ ret = ioctl(fd, CAP_IOC_GET_IMS_CERTIFICATE, &cert); ++ if (ret < 0) { ++ printf("Failed to get IMS certificate: %s (%d)\n", capdev, ret); ++ ret = -1; ++ goto close_fd; ++ } ++ ++ printf("IMS Certificate size: %d\n", cert.cert_size); ++ ++ /* Authenticate */ ++ printf("Authenticate module\n"); ++ ++ memcpy(authenticate.uid, uid.uid, 8); ++ ++ ret = ioctl(fd, CAP_IOC_AUTHENTICATE, &authenticate); ++ if (ret < 0) { ++ printf("Failed to authenticate module: %s (%d)\n", capdev, ret); ++ ret = -1; ++ goto close_fd; ++ } ++ ++ printf("Authenticated, result (%02x), sig-size (%02x)\n", ++ authenticate.result_code, authenticate.signature_size); ++ ++close_fd: ++ close(fd); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/greybus/Documentation/firmware/firmware-management +@@ -0,0 +1,333 @@ ++ ++Firmware Management ++------------------- ++ Copyright 2016 Google Inc. ++ Copyright 2016 Linaro Ltd. ++ ++Interface-Manifest ++------------------ ++ ++All firmware packages on the Modules or Interfaces are managed by a special ++Firmware Management Protocol. To support Firmware Management by the AP, the ++Interface Manifest shall at least contain the Firmware Management Bundle and a ++Firmware Management Protocol CPort within it. ++ ++The bundle may contain additional CPorts based on the extra functionality ++required to manage firmware packages. ++ ++For example, this is how the Firmware Management part of the Interface Manifest ++may look like: ++ ++ ; Firmware Management Bundle (Bundle 1): ++ [bundle-descriptor 1] ++ class = 0x16 ++ ++ ; (Mandatory) Firmware Management Protocol on CPort 1 ++ [cport-descriptor 2] ++ bundle = 1 ++ protocol = 0x18 ++ ++ ; (Optional) Firmware Download Protocol on CPort 2 ++ [cport-descriptor 1] ++ bundle = 1 ++ protocol = 0x17 ++ ++ ; (Optional) SPI protocol on CPort 3 ++ [cport-descriptor 3] ++ bundle = 1 ++ protocol = 0x0b ++ ++ ; (Optional) Component Authentication Protocol (CAP) on CPort 4 ++ [cport-descriptor 4] ++ bundle = 1 ++ protocol = 0x19 ++ ++ ++Sysfs Interfaces - Firmware Management ++-------------------------------------- ++ ++The Firmware Management Protocol interacts with Userspace using the character ++device interface. The character device will be present in /dev/ directory ++and will be named gb-fw-mgmt-<N>. The number <N> is assigned at runtime. ++ ++Identifying the Character Device ++================================ ++ ++There can be multiple devices present in /dev/ directory with name gb-fw-mgmt-N ++and user first needs to identify the character device used for ++firmware-management for a particular interface. ++ ++The Firmware Management core creates a device of class 'gb_fw_mgmt', which shall ++be used by the user to identify the right character device for it. The class ++device is created within the Bundle directory for a particular Interface. ++ ++For example this is how the class-device can be present: ++ ++/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_fw_mgmt/gb-fw-mgmt-0 ++ ++The last name in this path: gb-fw-mgmt-0 is precisely the name of the char ++device and so the device in this case will be: ++ ++/dev/gb-fw-mgmt-0. ++ ++Operations on the Char device ++============================= ++ ++The Character device (gb-fw-mgmt-0 in example) can be opened by the userspace ++application and it can perform various 'ioctl' operations on the device. The ++device doesn't support any read/write operations. ++ ++Following are the IOCTLs and their data structures available to the user: ++ ++/* IOCTL support */ ++#define GB_FW_LOAD_METHOD_UNIPRO 0x01 ++#define GB_FW_LOAD_METHOD_INTERNAL 0x02 ++ ++#define GB_FW_LOAD_STATUS_FAILED 0x00 ++#define GB_FW_LOAD_STATUS_UNVALIDATED 0x01 ++#define GB_FW_LOAD_STATUS_VALIDATED 0x02 ++#define GB_FW_LOAD_STATUS_VALIDATION_FAILED 0x03 ++ ++#define GB_FW_BACKEND_FW_STATUS_SUCCESS 0x01 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND 0x02 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 ++#define GB_FW_BACKEND_FW_STATUS_INT 0x05 ++#define GB_FW_BACKEND_FW_STATUS_RETRY 0x06 ++#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 ++ ++#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS 0x01 ++#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 ++#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 ++#define GB_FW_BACKEND_VERSION_STATUS_RETRY 0x04 ++#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT 0x05 ++ ++ ++struct fw_mgmt_ioc_get_intf_version { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u16 major; ++ __u16 minor; ++} __attribute__ ((__packed__)); ++ ++struct fw_mgmt_ioc_get_backend_version { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u16 major; ++ __u16 minor; ++ __u8 status; ++} __attribute__ ((__packed__)); ++ ++struct fw_mgmt_ioc_intf_load_and_validate { ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++ __u8 load_method; ++ __u8 status; ++ __u16 major; ++ __u16 minor; ++} __packed; ++ ++struct fw_mgmt_ioc_backend_fw_update { ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++ __u8 status; ++} __packed; ++ ++#define FW_MGMT_IOCTL_BASE 'S' ++#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version) ++#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version) ++#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) ++#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) ++#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) ++#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5) ++ ++1. FW_MGMT_IOC_GET_INTF_FW: ++ ++ This ioctl shall be used by the user to get the version and firmware-tag of ++ the currently running Interface Firmware. All the fields of the 'struct ++ fw_mgmt_ioc_get_fw' are filled by the kernel. ++ ++2. FW_MGMT_IOC_GET_BACKEND_FW: ++ ++ This ioctl shall be used by the user to get the version of a currently ++ running Backend Interface Firmware identified by a firmware-tag. The user is ++ required to fill the 'firmware_tag' field of the 'struct fw_mgmt_ioc_get_fw' ++ in this case. The 'major' and 'minor' fields are set by the kernel in ++ response. ++ ++3. FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE: ++ ++ This ioctl shall be used by the user to load an Interface Firmware package on ++ an Interface. The user needs to fill the 'firmware_tag' and 'load_method' ++ fields of the 'struct fw_mgmt_ioc_intf_load_and_validate'. The 'status', ++ 'major' and 'minor' fields are set by the kernel in response. ++ ++4. FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE: ++ ++ This ioctl shall be used by the user to request an Interface to update a ++ Backend Interface Firmware. The user is required to fill the 'firmware_tag' ++ field of the 'struct fw_mgmt_ioc_get_fw' in this case. The 'status' field is ++ set by the kernel in response. ++ ++5. FW_MGMT_IOC_SET_TIMEOUT_MS: ++ ++ This ioctl shall be used by the user to increase the timeout interval within ++ which the firmware must get loaded by the Module. The default timeout is 1 ++ second. The user needs to pass the timeout in milliseconds. ++ ++6. FW_MGMT_IOC_MODE_SWITCH: ++ ++ This ioctl shall be used by the user to mode-switch the module to the ++ previously loaded interface firmware. If the interface firmware isn't loaded ++ previously, or if another unsuccessful FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE ++ operation is started after loading interface firmware, then the firmware core ++ wouldn't allow mode-switch. ++ ++ ++Sysfs Interfaces - Authentication ++--------------------------------- ++ ++The Component Authentication Protocol interacts with Userspace using the ++character device interface. The character device will be present in /dev/ ++directory and will be named gb-authenticate-<N>. The number <N> is assigned at ++runtime. ++ ++Identifying the Character Device ++================================ ++ ++There can be multiple devices present in /dev/ directory with name ++gb-authenticate-N and user first needs to identify the character device used for ++authentication a of particular interface. ++ ++The Authentication core creates a device of class 'gb_authenticate', which shall ++be used by the user to identify the right character device for it. The class ++device is created within the Bundle directory for a particular Interface. ++ ++For example this is how the class-device can be present: ++ ++/sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/gb_authenticate/gb-authenticate-0 ++ ++The last name in this path: gb-authenticate-0 is precisely the name of the char ++device and so the device in this case will be: ++ ++/dev/gb-authenticate-0. ++ ++Operations on the Char device ++============================= ++ ++The Character device (/dev/gb-authenticate-0 in above example) can be opened by ++the userspace application and it can perform various 'ioctl' operations on the ++device. The device doesn't support any read/write operations. ++ ++Following are the IOCTLs and their data structures available to the user: ++ ++#define CAP_CERTIFICATE_MAX_SIZE 1600 ++#define CAP_SIGNATURE_MAX_SIZE 320 ++ ++/* Certificate class types */ ++#define CAP_CERT_IMS_EAPC 0x00000001 ++#define CAP_CERT_IMS_EASC 0x00000002 ++#define CAP_CERT_IMS_EARC 0x00000003 ++#define CAP_CERT_IMS_IAPC 0x00000004 ++#define CAP_CERT_IMS_IASC 0x00000005 ++#define CAP_CERT_IMS_IARC 0x00000006 ++ ++/* IMS Certificate response result codes */ ++#define CAP_IMS_RESULT_CERT_FOUND 0x00 ++#define CAP_IMS_RESULT_CERT_CLASS_INVAL 0x01 ++#define CAP_IMS_RESULT_CERT_CORRUPT 0x02 ++#define CAP_IMS_RESULT_CERT_NOT_FOUND 0x03 ++ ++/* Authentication types */ ++#define CAP_AUTH_IMS_PRI 0x00000001 ++#define CAP_AUTH_IMS_SEC 0x00000002 ++#define CAP_AUTH_IMS_RSA 0x00000003 ++ ++/* Authenticate response result codes */ ++#define CAP_AUTH_RESULT_CR_SUCCESS 0x00 ++#define CAP_AUTH_RESULT_CR_BAD_TYPE 0x01 ++#define CAP_AUTH_RESULT_CR_WRONG_EP 0x02 ++#define CAP_AUTH_RESULT_CR_NO_KEY 0x03 ++#define CAP_AUTH_RESULT_CR_SIG_FAIL 0x04 ++ ++ ++/* IOCTL support */ ++struct cap_ioc_get_endpoint_uid { ++ __u8 uid[8]; ++} __attribute__ ((__packed__)); ++ ++struct cap_ioc_get_ims_certificate { ++ __u32 certificate_class; ++ __u32 certificate_id; ++ ++ __u8 result_code; ++ __u32 cert_size; ++ __u8 certificate[CAP_CERTIFICATE_MAX_SIZE]; ++} __attribute__ ((__packed__)); ++ ++struct cap_ioc_authenticate { ++ __u32 auth_type; ++ __u8 uid[8]; ++ __u8 challenge[32]; ++ ++ __u8 result_code; ++ __u8 response[64]; ++ __u32 signature_size; ++ __u8 signature[CAP_SIGNATURE_MAX_SIZE]; ++} __attribute__ ((__packed__)); ++ ++#define CAP_IOCTL_BASE 'C' ++#define CAP_IOC_GET_ENDPOINT_UID _IOR(CAP_IOCTL_BASE, 0, struct cap_ioc_get_endpoint_uid) ++#define CAP_IOC_GET_IMS_CERTIFICATE _IOWR(CAP_IOCTL_BASE, 1, struct cap_ioc_get_ims_certificate) ++#define CAP_IOC_AUTHENTICATE _IOWR(CAP_IOCTL_BASE, 2, struct cap_ioc_authenticate) ++ ++ ++1. CAP_IOC_GET_ENDPOINT_UID: ++ ++ This ioctl shall be used by the user to get the endpoint UID associated with ++ the Interface. All the fields of the 'struct cap_ioc_get_endpoint_uid' are ++ filled by the kernel. ++ ++2. CAP_IOC_GET_IMS_CERTIFICATE: ++ ++ This ioctl shall be used by the user to retrieve one of the available ++ cryptographic certificates held by the Interface for use in Component ++ Authentication. The user is required to fill the 'certificate_class' and ++ 'certificate_id' field of the 'struct cap_ioc_get_ims_certificate' in this ++ case. The other fields will be set by the kernel in response. The first ++ 'cert_size' bytes of the 'certificate' shall be read by the user and others ++ must be discarded. ++ ++3. CAP_IOC_AUTHENTICATE: ++ ++ This ioctl shall be used by the user to authenticate the Module attached to ++ an Interface. The user needs to fill the 'auth_type', 'uid', and 'challenge' ++ fields of the 'struct cap_ioc_authenticate'. The other fields will be set by ++ the kernel in response. The first 'signature_size' bytes of the 'signature' ++ shall be read by the user and others must be discarded. ++ ++ ++Sysfs Interfaces - Firmware Download ++------------------------------------ ++ ++The Firmware Download Protocol uses the existing Linux Kernel's Firmware class ++and the interface provided to userspace are described in: ++Documentation/firmware_class/. ++ ++ ++Sysfs Interfaces - SPI Flash ++---------------------------- ++ ++The SPI flash is exposed in userspace as a MTD device and is created ++within the Bundle directory. For example, this is how the path may look like: ++ ++$ ls /sys/bus/greybus/devices/1-1/1-1.1/1-1.1.1/spi_master/spi32766/spi32766.0/mtd ++mtd0 mtd0ro ++ ++ ++Sample Applications ++------------------- ++ ++The current directory also provides a firmware.c test application, which can be ++referenced while developing userspace application to talk to firmware-management ++protocol. ++ ++The current directory also provides a authenticate.c test application, which can ++be referenced while developing userspace application to talk to ++component authentication protocol. +--- /dev/null ++++ b/drivers/greybus/Documentation/firmware/firmware.c +@@ -0,0 +1,262 @@ ++/* ++ * Sample code to test firmware-management protocol ++ * ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#include <stdio.h> ++#include <string.h> ++#include <unistd.h> ++#include <sys/ioctl.h> ++#include <sys/stat.h> ++#include <fcntl.h> ++ ++#include "../../greybus_firmware.h" ++ ++#define FW_DEV_DEFAULT "/dev/gb-fw-mgmt-0" ++#define FW_TAG_INT_DEFAULT "s3f" ++#define FW_TAG_BCND_DEFAULT "bf_01" ++#define FW_UPDATE_TYPE_DEFAULT 0 ++#define FW_TIMEOUT_DEFAULT 10000; ++ ++static const char *firmware_tag; ++static const char *fwdev = FW_DEV_DEFAULT; ++static int fw_update_type = FW_UPDATE_TYPE_DEFAULT; ++static int fw_timeout = FW_TIMEOUT_DEFAULT; ++ ++static struct fw_mgmt_ioc_get_intf_version intf_fw_info; ++static struct fw_mgmt_ioc_get_backend_version backend_fw_info; ++static struct fw_mgmt_ioc_intf_load_and_validate intf_load; ++static struct fw_mgmt_ioc_backend_fw_update backend_update; ++ ++static void usage(void) ++{ ++ printf("\nUsage: ./firmware <gb-fw-mgmt-X (default: gb-fw-mgmt-0)> <interface: 0, backend: 1 (default: 0)> <firmware-tag> (default: \"s3f\"/\"bf_01\") <timeout (default: 10000 ms)>\n"); ++} ++ ++static int update_intf_firmware(int fd) ++{ ++ int ret; ++ ++ /* Get Interface Firmware Version */ ++ printf("Get Interface Firmware Version\n"); ++ ++ ret = ioctl(fd, FW_MGMT_IOC_GET_INTF_FW, &intf_fw_info); ++ if (ret < 0) { ++ printf("Failed to get interface firmware version: %s (%d)\n", ++ fwdev, ret); ++ return -1; ++ } ++ ++ printf("Interface Firmware tag (%s), major (%d), minor (%d)\n", ++ intf_fw_info.firmware_tag, intf_fw_info.major, ++ intf_fw_info.minor); ++ ++ /* Try Interface Firmware load over Unipro */ ++ printf("Loading Interface Firmware\n"); ++ ++ intf_load.load_method = GB_FW_U_LOAD_METHOD_UNIPRO; ++ intf_load.status = 0; ++ intf_load.major = 0; ++ intf_load.minor = 0; ++ ++ strncpy((char *)&intf_load.firmware_tag, firmware_tag, ++ GB_FIRMWARE_U_TAG_MAX_SIZE); ++ ++ ret = ioctl(fd, FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE, &intf_load); ++ if (ret < 0) { ++ printf("Failed to load interface firmware: %s (%d)\n", fwdev, ++ ret); ++ return -1; ++ } ++ ++ if (intf_load.status != GB_FW_U_LOAD_STATUS_VALIDATED && ++ intf_load.status != GB_FW_U_LOAD_STATUS_UNVALIDATED) { ++ printf("Load status says loading failed: %d\n", ++ intf_load.status); ++ return -1; ++ } ++ ++ printf("Interface Firmware (%s) Load done: major: %d, minor: %d, status: %d\n", ++ firmware_tag, intf_load.major, intf_load.minor, ++ intf_load.status); ++ ++ /* Initiate Mode-switch to the newly loaded firmware */ ++ printf("Initiate Mode switch\n"); ++ ++ ret = ioctl(fd, FW_MGMT_IOC_MODE_SWITCH); ++ if (ret < 0) ++ printf("Failed to initiate mode-switch (%d)\n", ret); ++ ++ return ret; ++} ++ ++static int update_backend_firmware(int fd) ++{ ++ int ret; ++ ++ /* Get Backend Firmware Version */ ++ printf("Getting Backend Firmware Version\n"); ++ ++ strncpy((char *)&backend_fw_info.firmware_tag, firmware_tag, ++ GB_FIRMWARE_U_TAG_MAX_SIZE); ++ ++retry_fw_version: ++ ret = ioctl(fd, FW_MGMT_IOC_GET_BACKEND_FW, &backend_fw_info); ++ if (ret < 0) { ++ printf("Failed to get backend firmware version: %s (%d)\n", ++ fwdev, ret); ++ return -1; ++ } ++ ++ printf("Backend Firmware tag (%s), major (%d), minor (%d), status (%d)\n", ++ backend_fw_info.firmware_tag, backend_fw_info.major, ++ backend_fw_info.minor, backend_fw_info.status); ++ ++ if (backend_fw_info.status == GB_FW_U_BACKEND_VERSION_STATUS_RETRY) ++ goto retry_fw_version; ++ ++ if ((backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS) ++ && (backend_fw_info.status != GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE)) { ++ printf("Failed to get backend firmware version: %s (%d)\n", ++ fwdev, backend_fw_info.status); ++ return -1; ++ } ++ ++ /* Try Backend Firmware Update over Unipro */ ++ printf("Updating Backend Firmware\n"); ++ ++ strncpy((char *)&backend_update.firmware_tag, firmware_tag, ++ GB_FIRMWARE_U_TAG_MAX_SIZE); ++ ++retry_fw_update: ++ backend_update.status = 0; ++ ++ ret = ioctl(fd, FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE, &backend_update); ++ if (ret < 0) { ++ printf("Failed to load backend firmware: %s (%d)\n", fwdev, ret); ++ return -1; ++ } ++ ++ if (backend_update.status == GB_FW_U_BACKEND_FW_STATUS_RETRY) { ++ printf("Retrying firmware update: %d\n", backend_update.status); ++ goto retry_fw_update; ++ } ++ ++ if (backend_update.status != GB_FW_U_BACKEND_FW_STATUS_SUCCESS) { ++ printf("Load status says loading failed: %d\n", ++ backend_update.status); ++ } else { ++ printf("Backend Firmware (%s) Load done: status: %d\n", ++ firmware_tag, backend_update.status); ++ } ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int fd, ret; ++ ++ if (argc > 1 && ++ (!strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))) { ++ usage(); ++ return -1; ++ } ++ ++ if (argc > 1) ++ fwdev = argv[1]; ++ ++ if (argc > 2) ++ sscanf(argv[2], "%u", &fw_update_type); ++ ++ if (argc > 3) { ++ firmware_tag = argv[3]; ++ } else if (!fw_update_type) { ++ firmware_tag = FW_TAG_INT_DEFAULT; ++ } else { ++ firmware_tag = FW_TAG_BCND_DEFAULT; ++ } ++ ++ if (argc > 4) ++ sscanf(argv[4], "%u", &fw_timeout); ++ ++ printf("Trying Firmware update: fwdev: %s, type: %s, tag: %s, timeout: %d\n", ++ fwdev, fw_update_type == 0 ? "interface" : "backend", ++ firmware_tag, fw_timeout); ++ ++ printf("Opening %s firmware management device\n", fwdev); ++ ++ fd = open(fwdev, O_RDWR); ++ if (fd < 0) { ++ printf("Failed to open: %s\n", fwdev); ++ return -1; ++ } ++ ++ /* Set Timeout */ ++ printf("Setting timeout to %u ms\n", fw_timeout); ++ ++ ret = ioctl(fd, FW_MGMT_IOC_SET_TIMEOUT_MS, &fw_timeout); ++ if (ret < 0) { ++ printf("Failed to set timeout: %s (%d)\n", fwdev, ret); ++ ret = -1; ++ goto close_fd; ++ } ++ ++ if (!fw_update_type) ++ ret = update_intf_firmware(fd); ++ else ++ ret = update_backend_firmware(fd); ++ ++close_fd: ++ close(fd); ++ ++ return ret; ++} +--- /dev/null ++++ b/drivers/greybus/firmware.h +@@ -0,0 +1,42 @@ ++/* ++ * Greybus Firmware Management Header ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __FIRMWARE_H ++#define __FIRMWARE_H ++ ++#include "greybus.h" ++ ++#define FW_NAME_PREFIX "gmp_" ++ ++/* ++ * Length of the string in format: "FW_NAME_PREFIX""%08x_%08x_%08x_%08x_%s.tftf" ++ * (3 + 1 + 4 * (8 + 1) + 10 + 1 + 4 + 1) ++ */ ++#define FW_NAME_SIZE 56 ++ ++/* Firmware Management Protocol specific functions */ ++int fw_mgmt_init(void); ++void fw_mgmt_exit(void); ++struct gb_connection *to_fw_mgmt_connection(struct device *dev); ++int gb_fw_mgmt_request_handler(struct gb_operation *op); ++int gb_fw_mgmt_connection_init(struct gb_connection *connection); ++void gb_fw_mgmt_connection_exit(struct gb_connection *connection); ++ ++/* Firmware Download Protocol specific functions */ ++int gb_fw_download_request_handler(struct gb_operation *op); ++int gb_fw_download_connection_init(struct gb_connection *connection); ++void gb_fw_download_connection_exit(struct gb_connection *connection); ++ ++/* CAP Protocol specific functions */ ++int cap_init(void); ++void cap_exit(void); ++int gb_cap_connection_init(struct gb_connection *connection); ++void gb_cap_connection_exit(struct gb_connection *connection); ++ ++#endif /* __FIRMWARE_H */ +--- /dev/null ++++ b/drivers/greybus/fw-core.c +@@ -0,0 +1,312 @@ ++/* ++ * Greybus Firmware Core Bundle Driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/firmware.h> ++#include "firmware.h" ++#include "greybus.h" ++#include "spilib.h" ++ ++struct gb_fw_core { ++ struct gb_connection *download_connection; ++ struct gb_connection *mgmt_connection; ++ struct gb_connection *spi_connection; ++ struct gb_connection *cap_connection; ++}; ++ ++static struct spilib_ops *spilib_ops; ++ ++struct gb_connection *to_fw_mgmt_connection(struct device *dev) ++{ ++ struct gb_fw_core *fw_core = dev_get_drvdata(dev); ++ ++ return fw_core->mgmt_connection; ++} ++ ++static int gb_fw_spi_connection_init(struct gb_connection *connection) ++{ ++ int ret; ++ ++ if (!connection) ++ return 0; ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ return ret; ++ ++ ret = gb_spilib_master_init(connection, &connection->bundle->dev, ++ spilib_ops); ++ if (ret) { ++ gb_connection_disable(connection); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static void gb_fw_spi_connection_exit(struct gb_connection *connection) ++{ ++ if (!connection) ++ return; ++ ++ gb_spilib_master_exit(connection); ++ gb_connection_disable(connection); ++} ++ ++static int gb_fw_core_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_fw_core *fw_core; ++ int ret, i; ++ u16 cport_id; ++ u8 protocol_id; ++ ++ fw_core = kzalloc(sizeof(*fw_core), GFP_KERNEL); ++ if (!fw_core) ++ return -ENOMEM; ++ ++ /* Parse CPorts and create connections */ ++ for (i = 0; i < bundle->num_cports; i++) { ++ cport_desc = &bundle->cport_desc[i]; ++ cport_id = le16_to_cpu(cport_desc->id); ++ protocol_id = cport_desc->protocol_id; ++ ++ switch (protocol_id) { ++ case GREYBUS_PROTOCOL_FW_MANAGEMENT: ++ /* Disallow multiple Firmware Management CPorts */ ++ if (fw_core->mgmt_connection) { ++ dev_err(&bundle->dev, ++ "multiple management CPorts found\n"); ++ ret = -EINVAL; ++ goto err_destroy_connections; ++ } ++ ++ connection = gb_connection_create(bundle, cport_id, ++ gb_fw_mgmt_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ dev_err(&bundle->dev, ++ "failed to create management connection (%d)\n", ++ ret); ++ goto err_destroy_connections; ++ } ++ ++ fw_core->mgmt_connection = connection; ++ break; ++ case GREYBUS_PROTOCOL_FW_DOWNLOAD: ++ /* Disallow multiple Firmware Download CPorts */ ++ if (fw_core->download_connection) { ++ dev_err(&bundle->dev, ++ "multiple download CPorts found\n"); ++ ret = -EINVAL; ++ goto err_destroy_connections; ++ } ++ ++ connection = gb_connection_create(bundle, cport_id, ++ gb_fw_download_request_handler); ++ if (IS_ERR(connection)) { ++ dev_err(&bundle->dev, "failed to create download connection (%ld)\n", ++ PTR_ERR(connection)); ++ } else { ++ fw_core->download_connection = connection; ++ } ++ ++ break; ++ case GREYBUS_PROTOCOL_SPI: ++ /* Disallow multiple SPI CPorts */ ++ if (fw_core->spi_connection) { ++ dev_err(&bundle->dev, ++ "multiple SPI CPorts found\n"); ++ ret = -EINVAL; ++ goto err_destroy_connections; ++ } ++ ++ connection = gb_connection_create(bundle, cport_id, ++ NULL); ++ if (IS_ERR(connection)) { ++ dev_err(&bundle->dev, "failed to create SPI connection (%ld)\n", ++ PTR_ERR(connection)); ++ } else { ++ fw_core->spi_connection = connection; ++ } ++ ++ break; ++ case GREYBUS_PROTOCOL_AUTHENTICATION: ++ /* Disallow multiple CAP CPorts */ ++ if (fw_core->cap_connection) { ++ dev_err(&bundle->dev, "multiple Authentication CPorts found\n"); ++ ret = -EINVAL; ++ goto err_destroy_connections; ++ } ++ ++ connection = gb_connection_create(bundle, cport_id, ++ NULL); ++ if (IS_ERR(connection)) { ++ dev_err(&bundle->dev, "failed to create Authentication connection (%ld)\n", ++ PTR_ERR(connection)); ++ } else { ++ fw_core->cap_connection = connection; ++ } ++ ++ break; ++ default: ++ dev_err(&bundle->dev, "invalid protocol id (0x%02x)\n", ++ protocol_id); ++ ret = -EINVAL; ++ goto err_destroy_connections; ++ } ++ } ++ ++ /* Firmware Management connection is mandatory */ ++ if (!fw_core->mgmt_connection) { ++ dev_err(&bundle->dev, "missing management connection\n"); ++ ret = -ENODEV; ++ goto err_destroy_connections; ++ } ++ ++ ret = gb_fw_download_connection_init(fw_core->download_connection); ++ if (ret) { ++ /* We may still be able to work with the Interface */ ++ dev_err(&bundle->dev, "failed to initialize firmware download connection, disable it (%d)\n", ++ ret); ++ gb_connection_destroy(fw_core->download_connection); ++ fw_core->download_connection = NULL; ++ } ++ ++ ret = gb_fw_spi_connection_init(fw_core->spi_connection); ++ if (ret) { ++ /* We may still be able to work with the Interface */ ++ dev_err(&bundle->dev, "failed to initialize SPI connection, disable it (%d)\n", ++ ret); ++ gb_connection_destroy(fw_core->spi_connection); ++ fw_core->spi_connection = NULL; ++ } ++ ++ ret = gb_cap_connection_init(fw_core->cap_connection); ++ if (ret) { ++ /* We may still be able to work with the Interface */ ++ dev_err(&bundle->dev, "failed to initialize CAP connection, disable it (%d)\n", ++ ret); ++ gb_connection_destroy(fw_core->cap_connection); ++ fw_core->cap_connection = NULL; ++ } ++ ++ ret = gb_fw_mgmt_connection_init(fw_core->mgmt_connection); ++ if (ret) { ++ /* We may still be able to work with the Interface */ ++ dev_err(&bundle->dev, "failed to initialize firmware management connection, disable it (%d)\n", ++ ret); ++ goto err_exit_connections; ++ } ++ ++ greybus_set_drvdata(bundle, fw_core); ++ ++ /* FIXME: Remove this after S2 Loader gets runtime PM support */ ++ if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++err_exit_connections: ++ gb_cap_connection_exit(fw_core->cap_connection); ++ gb_fw_spi_connection_exit(fw_core->spi_connection); ++ gb_fw_download_connection_exit(fw_core->download_connection); ++err_destroy_connections: ++ gb_connection_destroy(fw_core->mgmt_connection); ++ gb_connection_destroy(fw_core->cap_connection); ++ gb_connection_destroy(fw_core->spi_connection); ++ gb_connection_destroy(fw_core->download_connection); ++ kfree(fw_core); ++ ++ return ret; ++} ++ ++static void gb_fw_core_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_fw_core *fw_core = greybus_get_drvdata(bundle); ++ int ret; ++ ++ /* FIXME: Remove this after S2 Loader gets runtime PM support */ ++ if (!(bundle->intf->quirks & GB_INTERFACE_QUIRK_NO_PM)) { ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ gb_pm_runtime_get_noresume(bundle); ++ } ++ ++ gb_fw_mgmt_connection_exit(fw_core->mgmt_connection); ++ gb_cap_connection_exit(fw_core->cap_connection); ++ gb_fw_spi_connection_exit(fw_core->spi_connection); ++ gb_fw_download_connection_exit(fw_core->download_connection); ++ ++ gb_connection_destroy(fw_core->mgmt_connection); ++ gb_connection_destroy(fw_core->cap_connection); ++ gb_connection_destroy(fw_core->spi_connection); ++ gb_connection_destroy(fw_core->download_connection); ++ ++ kfree(fw_core); ++} ++ ++static const struct greybus_bundle_id gb_fw_core_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FW_MANAGEMENT) }, ++ { } ++}; ++ ++static struct greybus_driver gb_fw_core_driver = { ++ .name = "gb-firmware", ++ .probe = gb_fw_core_probe, ++ .disconnect = gb_fw_core_disconnect, ++ .id_table = gb_fw_core_id_table, ++}; ++ ++static int fw_core_init(void) ++{ ++ int ret; ++ ++ ret = fw_mgmt_init(); ++ if (ret) { ++ pr_err("Failed to initialize fw-mgmt core (%d)\n", ret); ++ return ret; ++ } ++ ++ ret = cap_init(); ++ if (ret) { ++ pr_err("Failed to initialize component authentication core (%d)\n", ++ ret); ++ goto fw_mgmt_exit; ++ } ++ ++ ret = greybus_register(&gb_fw_core_driver); ++ if (ret) ++ goto cap_exit; ++ ++ return 0; ++ ++cap_exit: ++ cap_exit(); ++fw_mgmt_exit: ++ fw_mgmt_exit(); ++ ++ return ret; ++} ++module_init(fw_core_init); ++ ++static void __exit fw_core_exit(void) ++{ ++ greybus_deregister(&gb_fw_core_driver); ++ cap_exit(); ++ fw_mgmt_exit(); ++} ++module_exit(fw_core_exit); ++ ++MODULE_ALIAS("greybus:firmware"); ++MODULE_AUTHOR("Viresh Kumar <viresh.kumar@linaro.org>"); ++MODULE_DESCRIPTION("Greybus Firmware Bundle Driver"); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/fw-download.c +@@ -0,0 +1,465 @@ ++/* ++ * Greybus Firmware Download Protocol Driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/firmware.h> ++#include <linux/jiffies.h> ++#include <linux/mutex.h> ++#include <linux/workqueue.h> ++#include "firmware.h" ++#include "greybus.h" ++ ++/* Estimated minimum buffer size, actual size can be smaller than this */ ++#define MIN_FETCH_SIZE 512 ++/* Timeout, in jiffies, within which fetch or release firmware must be called */ ++#define NEXT_REQ_TIMEOUT_J msecs_to_jiffies(1000) ++ ++struct fw_request { ++ u8 firmware_id; ++ bool disabled; ++ bool timedout; ++ char name[FW_NAME_SIZE]; ++ const struct firmware *fw; ++ struct list_head node; ++ ++ struct delayed_work dwork; ++ /* Timeout, in jiffies, within which the firmware shall download */ ++ unsigned long release_timeout_j; ++ struct kref kref; ++ struct fw_download *fw_download; ++}; ++ ++struct fw_download { ++ struct device *parent; ++ struct gb_connection *connection; ++ struct list_head fw_requests; ++ struct ida id_map; ++ struct mutex mutex; ++}; ++ ++static void fw_req_release(struct kref *kref) ++{ ++ struct fw_request *fw_req = container_of(kref, struct fw_request, kref); ++ ++ dev_dbg(fw_req->fw_download->parent, "firmware %s released\n", ++ fw_req->name); ++ ++ release_firmware(fw_req->fw); ++ ++ /* ++ * The request timed out and the module may send a fetch-fw or ++ * release-fw request later. Lets block the id we allocated for this ++ * request, so that the AP doesn't refer to a later fw-request (with ++ * same firmware_id) for the old timedout fw-request. ++ * ++ * NOTE: ++ * ++ * This also means that after 255 timeouts we will fail to service new ++ * firmware downloads. But what else can we do in that case anyway? Lets ++ * just hope that it never happens. ++ */ ++ if (!fw_req->timedout) ++ ida_simple_remove(&fw_req->fw_download->id_map, ++ fw_req->firmware_id); ++ ++ kfree(fw_req); ++} ++ ++/* ++ * Incoming requests are serialized for a connection, and the only race possible ++ * is between the timeout handler freeing this and an incoming request. ++ * ++ * The operations on the fw-request list are protected by the mutex and ++ * get_fw_req() increments the reference count before returning a fw_req pointer ++ * to the users. ++ * ++ * free_firmware() also takes the mutex while removing an entry from the list, ++ * it guarantees that every user of fw_req has taken a kref-reference by now and ++ * we wouldn't have any new users. ++ * ++ * Once the last user drops the reference, the fw_req structure is freed. ++ */ ++static void put_fw_req(struct fw_request *fw_req) ++{ ++ kref_put(&fw_req->kref, fw_req_release); ++} ++ ++/* Caller must call put_fw_req() after using struct fw_request */ ++static struct fw_request *get_fw_req(struct fw_download *fw_download, ++ u8 firmware_id) ++{ ++ struct fw_request *fw_req; ++ ++ mutex_lock(&fw_download->mutex); ++ ++ list_for_each_entry(fw_req, &fw_download->fw_requests, node) { ++ if (fw_req->firmware_id == firmware_id) { ++ kref_get(&fw_req->kref); ++ goto unlock; ++ } ++ } ++ ++ fw_req = NULL; ++ ++unlock: ++ mutex_unlock(&fw_download->mutex); ++ ++ return fw_req; ++} ++ ++static void free_firmware(struct fw_download *fw_download, ++ struct fw_request *fw_req) ++{ ++ /* Already disabled from timeout handlers */ ++ if (fw_req->disabled) ++ return; ++ ++ mutex_lock(&fw_download->mutex); ++ list_del(&fw_req->node); ++ mutex_unlock(&fw_download->mutex); ++ ++ fw_req->disabled = true; ++ put_fw_req(fw_req); ++} ++ ++static void fw_request_timedout(struct work_struct *work) ++{ ++ struct delayed_work *dwork = to_delayed_work(work); ++ struct fw_request *fw_req = container_of(dwork, struct fw_request, dwork); ++ struct fw_download *fw_download = fw_req->fw_download; ++ ++ dev_err(fw_download->parent, ++ "Timed out waiting for fetch / release firmware requests: %u\n", ++ fw_req->firmware_id); ++ ++ fw_req->timedout = true; ++ free_firmware(fw_download, fw_req); ++} ++ ++static int exceeds_release_timeout(struct fw_request *fw_req) ++{ ++ struct fw_download *fw_download = fw_req->fw_download; ++ ++ if (time_before(jiffies, fw_req->release_timeout_j)) ++ return 0; ++ ++ dev_err(fw_download->parent, ++ "Firmware download didn't finish in time, abort: %d\n", ++ fw_req->firmware_id); ++ ++ fw_req->timedout = true; ++ free_firmware(fw_download, fw_req); ++ ++ return -ETIMEDOUT; ++} ++ ++/* This returns path of the firmware blob on the disk */ ++static struct fw_request *find_firmware(struct fw_download *fw_download, ++ const char *tag) ++{ ++ struct gb_interface *intf = fw_download->connection->bundle->intf; ++ struct fw_request *fw_req; ++ int ret, req_count; ++ ++ fw_req = kzalloc(sizeof(*fw_req), GFP_KERNEL); ++ if (!fw_req) ++ return ERR_PTR(-ENOMEM); ++ ++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ ++ ret = ida_simple_get(&fw_download->id_map, 1, 256, GFP_KERNEL); ++ if (ret < 0) { ++ dev_err(fw_download->parent, ++ "failed to allocate firmware id (%d)\n", ret); ++ goto err_free_req; ++ } ++ fw_req->firmware_id = ret; ++ ++ snprintf(fw_req->name, sizeof(fw_req->name), ++ FW_NAME_PREFIX "%08x_%08x_%08x_%08x_%s.tftf", ++ intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, ++ intf->vendor_id, intf->product_id, tag); ++ ++ dev_info(fw_download->parent, "Requested firmware package '%s'\n", ++ fw_req->name); ++ ++ ret = request_firmware(&fw_req->fw, fw_req->name, fw_download->parent); ++ if (ret) { ++ dev_err(fw_download->parent, ++ "firmware request failed for %s (%d)\n", fw_req->name, ++ ret); ++ goto err_free_id; ++ } ++ ++ fw_req->fw_download = fw_download; ++ kref_init(&fw_req->kref); ++ ++ mutex_lock(&fw_download->mutex); ++ list_add(&fw_req->node, &fw_download->fw_requests); ++ mutex_unlock(&fw_download->mutex); ++ ++ /* Timeout, in jiffies, within which firmware should get loaded */ ++ req_count = DIV_ROUND_UP(fw_req->fw->size, MIN_FETCH_SIZE); ++ fw_req->release_timeout_j = jiffies + req_count * NEXT_REQ_TIMEOUT_J; ++ ++ INIT_DELAYED_WORK(&fw_req->dwork, fw_request_timedout); ++ schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); ++ ++ return fw_req; ++ ++err_free_id: ++ ida_simple_remove(&fw_download->id_map, fw_req->firmware_id); ++err_free_req: ++ kfree(fw_req); ++ ++ return ERR_PTR(ret); ++} ++ ++static int fw_download_find_firmware(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct fw_download *fw_download = gb_connection_get_data(connection); ++ struct gb_fw_download_find_firmware_request *request; ++ struct gb_fw_download_find_firmware_response *response; ++ struct fw_request *fw_req; ++ const char *tag; ++ ++ if (op->request->payload_size != sizeof(*request)) { ++ dev_err(fw_download->parent, ++ "illegal size of find firmware request (%zu != %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ tag = (const char *)request->firmware_tag; ++ ++ /* firmware_tag must be null-terminated */ ++ if (strnlen(tag, GB_FIRMWARE_TAG_MAX_SIZE) == GB_FIRMWARE_TAG_MAX_SIZE) { ++ dev_err(fw_download->parent, ++ "firmware-tag is not null-terminated\n"); ++ return -EINVAL; ++ } ++ ++ fw_req = find_firmware(fw_download, tag); ++ if (IS_ERR(fw_req)) ++ return PTR_ERR(fw_req); ++ ++ if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) { ++ dev_err(fw_download->parent, "error allocating response\n"); ++ free_firmware(fw_download, fw_req); ++ return -ENOMEM; ++ } ++ ++ response = op->response->payload; ++ response->firmware_id = fw_req->firmware_id; ++ response->size = cpu_to_le32(fw_req->fw->size); ++ ++ dev_dbg(fw_download->parent, ++ "firmware size is %zu bytes\n", fw_req->fw->size); ++ ++ return 0; ++} ++ ++static int fw_download_fetch_firmware(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct fw_download *fw_download = gb_connection_get_data(connection); ++ struct gb_fw_download_fetch_firmware_request *request; ++ struct gb_fw_download_fetch_firmware_response *response; ++ struct fw_request *fw_req; ++ const struct firmware *fw; ++ unsigned int offset, size; ++ u8 firmware_id; ++ int ret = 0; ++ ++ if (op->request->payload_size != sizeof(*request)) { ++ dev_err(fw_download->parent, ++ "Illegal size of fetch firmware request (%zu %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ offset = le32_to_cpu(request->offset); ++ size = le32_to_cpu(request->size); ++ firmware_id = request->firmware_id; ++ ++ fw_req = get_fw_req(fw_download, firmware_id); ++ if (!fw_req) { ++ dev_err(fw_download->parent, ++ "firmware not available for id: %02u\n", firmware_id); ++ return -EINVAL; ++ } ++ ++ /* Make sure work handler isn't running in parallel */ ++ cancel_delayed_work_sync(&fw_req->dwork); ++ ++ /* We timed-out before reaching here ? */ ++ if (fw_req->disabled) { ++ ret = -ETIMEDOUT; ++ goto put_fw; ++ } ++ ++ /* ++ * Firmware download must finish within a limited time interval. If it ++ * doesn't, then we might have a buggy Module on the other side. Abort ++ * download. ++ */ ++ ret = exceeds_release_timeout(fw_req); ++ if (ret) ++ goto put_fw; ++ ++ fw = fw_req->fw; ++ ++ if (offset >= fw->size || size > fw->size - offset) { ++ dev_err(fw_download->parent, ++ "bad fetch firmware request (offs = %u, size = %u)\n", ++ offset, size); ++ ret = -EINVAL; ++ goto put_fw; ++ } ++ ++ if (!gb_operation_response_alloc(op, sizeof(*response) + size, ++ GFP_KERNEL)) { ++ dev_err(fw_download->parent, ++ "error allocating fetch firmware response\n"); ++ ret = -ENOMEM; ++ goto put_fw; ++ } ++ ++ response = op->response->payload; ++ memcpy(response->data, fw->data + offset, size); ++ ++ dev_dbg(fw_download->parent, ++ "responding with firmware (offs = %u, size = %u)\n", offset, ++ size); ++ ++ /* Refresh timeout */ ++ schedule_delayed_work(&fw_req->dwork, NEXT_REQ_TIMEOUT_J); ++ ++put_fw: ++ put_fw_req(fw_req); ++ ++ return ret; ++} ++ ++static int fw_download_release_firmware(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct fw_download *fw_download = gb_connection_get_data(connection); ++ struct gb_fw_download_release_firmware_request *request; ++ struct fw_request *fw_req; ++ u8 firmware_id; ++ ++ if (op->request->payload_size != sizeof(*request)) { ++ dev_err(fw_download->parent, ++ "Illegal size of release firmware request (%zu %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ firmware_id = request->firmware_id; ++ ++ fw_req = get_fw_req(fw_download, firmware_id); ++ if (!fw_req) { ++ dev_err(fw_download->parent, ++ "firmware not available for id: %02u\n", firmware_id); ++ return -EINVAL; ++ } ++ ++ cancel_delayed_work_sync(&fw_req->dwork); ++ ++ free_firmware(fw_download, fw_req); ++ put_fw_req(fw_req); ++ ++ dev_dbg(fw_download->parent, "release firmware\n"); ++ ++ return 0; ++} ++ ++int gb_fw_download_request_handler(struct gb_operation *op) ++{ ++ u8 type = op->type; ++ ++ switch (type) { ++ case GB_FW_DOWNLOAD_TYPE_FIND_FIRMWARE: ++ return fw_download_find_firmware(op); ++ case GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE: ++ return fw_download_fetch_firmware(op); ++ case GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE: ++ return fw_download_release_firmware(op); ++ default: ++ dev_err(&op->connection->bundle->dev, ++ "unsupported request: %u\n", type); ++ return -EINVAL; ++ } ++} ++ ++int gb_fw_download_connection_init(struct gb_connection *connection) ++{ ++ struct fw_download *fw_download; ++ int ret; ++ ++ if (!connection) ++ return 0; ++ ++ fw_download = kzalloc(sizeof(*fw_download), GFP_KERNEL); ++ if (!fw_download) ++ return -ENOMEM; ++ ++ fw_download->parent = &connection->bundle->dev; ++ INIT_LIST_HEAD(&fw_download->fw_requests); ++ ida_init(&fw_download->id_map); ++ gb_connection_set_data(connection, fw_download); ++ fw_download->connection = connection; ++ mutex_init(&fw_download->mutex); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto err_destroy_id_map; ++ ++ return 0; ++ ++err_destroy_id_map: ++ ida_destroy(&fw_download->id_map); ++ kfree(fw_download); ++ ++ return ret; ++} ++ ++void gb_fw_download_connection_exit(struct gb_connection *connection) ++{ ++ struct fw_download *fw_download; ++ struct fw_request *fw_req, *tmp; ++ ++ if (!connection) ++ return; ++ ++ fw_download = gb_connection_get_data(connection); ++ gb_connection_disable(fw_download->connection); ++ ++ /* ++ * Make sure we have a reference to the pending requests, before they ++ * are freed from the timeout handler. ++ */ ++ mutex_lock(&fw_download->mutex); ++ list_for_each_entry(fw_req, &fw_download->fw_requests, node) ++ kref_get(&fw_req->kref); ++ mutex_unlock(&fw_download->mutex); ++ ++ /* Release pending firmware packages */ ++ list_for_each_entry_safe(fw_req, tmp, &fw_download->fw_requests, node) { ++ cancel_delayed_work_sync(&fw_req->dwork); ++ free_firmware(fw_download, fw_req); ++ put_fw_req(fw_req); ++ } ++ ++ ida_destroy(&fw_download->id_map); ++ kfree(fw_download); ++} +--- /dev/null ++++ b/drivers/greybus/fw-management.c +@@ -0,0 +1,721 @@ ++/* ++ * Greybus Firmware Management Protocol Driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/cdev.h> ++#include <linux/completion.h> ++#include <linux/firmware.h> ++#include <linux/fs.h> ++#include <linux/idr.h> ++#include <linux/ioctl.h> ++#include <linux/uaccess.h> ++ ++#include "firmware.h" ++#include "greybus_firmware.h" ++#include "greybus.h" ++ ++#define FW_MGMT_TIMEOUT_MS 1000 ++ ++struct fw_mgmt { ++ struct device *parent; ++ struct gb_connection *connection; ++ struct kref kref; ++ struct list_head node; ++ ++ /* Common id-map for interface and backend firmware requests */ ++ struct ida id_map; ++ struct mutex mutex; ++ struct completion completion; ++ struct cdev cdev; ++ struct device *class_device; ++ dev_t dev_num; ++ unsigned int timeout_jiffies; ++ bool disabled; /* connection getting disabled */ ++ ++ /* Interface Firmware specific fields */ ++ bool mode_switch_started; ++ bool intf_fw_loaded; ++ u8 intf_fw_request_id; ++ u8 intf_fw_status; ++ u16 intf_fw_major; ++ u16 intf_fw_minor; ++ ++ /* Backend Firmware specific fields */ ++ u8 backend_fw_request_id; ++ u8 backend_fw_status; ++}; ++ ++/* ++ * Number of minor devices this driver supports. ++ * There will be exactly one required per Interface. ++ */ ++#define NUM_MINORS U8_MAX ++ ++static struct class *fw_mgmt_class; ++static dev_t fw_mgmt_dev_num; ++static DEFINE_IDA(fw_mgmt_minors_map); ++static LIST_HEAD(fw_mgmt_list); ++static DEFINE_MUTEX(list_mutex); ++ ++static void fw_mgmt_kref_release(struct kref *kref) ++{ ++ struct fw_mgmt *fw_mgmt = container_of(kref, struct fw_mgmt, kref); ++ ++ ida_destroy(&fw_mgmt->id_map); ++ kfree(fw_mgmt); ++} ++ ++/* ++ * All users of fw_mgmt take a reference (from within list_mutex lock), before ++ * they get a pointer to play with. And the structure will be freed only after ++ * the last user has put the reference to it. ++ */ ++static void put_fw_mgmt(struct fw_mgmt *fw_mgmt) ++{ ++ kref_put(&fw_mgmt->kref, fw_mgmt_kref_release); ++} ++ ++/* Caller must call put_fw_mgmt() after using struct fw_mgmt */ ++static struct fw_mgmt *get_fw_mgmt(struct cdev *cdev) ++{ ++ struct fw_mgmt *fw_mgmt; ++ ++ mutex_lock(&list_mutex); ++ ++ list_for_each_entry(fw_mgmt, &fw_mgmt_list, node) { ++ if (&fw_mgmt->cdev == cdev) { ++ kref_get(&fw_mgmt->kref); ++ goto unlock; ++ } ++ } ++ ++ fw_mgmt = NULL; ++ ++unlock: ++ mutex_unlock(&list_mutex); ++ ++ return fw_mgmt; ++} ++ ++static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, ++ struct fw_mgmt_ioc_get_intf_version *fw_info) ++{ ++ struct gb_connection *connection = fw_mgmt->connection; ++ struct gb_fw_mgmt_interface_fw_version_response response; ++ int ret; ++ ++ ret = gb_operation_sync(connection, ++ GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION, NULL, 0, ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(fw_mgmt->parent, ++ "failed to get interface firmware version (%d)\n", ret); ++ return ret; ++ } ++ ++ fw_info->major = le16_to_cpu(response.major); ++ fw_info->minor = le16_to_cpu(response.minor); ++ ++ strncpy(fw_info->firmware_tag, response.firmware_tag, ++ GB_FIRMWARE_TAG_MAX_SIZE); ++ ++ /* ++ * The firmware-tag should be NULL terminated, otherwise throw error but ++ * don't fail. ++ */ ++ if (fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { ++ dev_err(fw_mgmt->parent, ++ "fw-version: firmware-tag is not NULL terminated\n"); ++ fw_info->firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] = '\0'; ++ } ++ ++ return 0; ++} ++ ++static int fw_mgmt_load_and_validate_operation(struct fw_mgmt *fw_mgmt, ++ u8 load_method, const char *tag) ++{ ++ struct gb_fw_mgmt_load_and_validate_fw_request request; ++ int ret; ++ ++ if (load_method != GB_FW_LOAD_METHOD_UNIPRO && ++ load_method != GB_FW_LOAD_METHOD_INTERNAL) { ++ dev_err(fw_mgmt->parent, ++ "invalid load-method (%d)\n", load_method); ++ return -EINVAL; ++ } ++ ++ request.load_method = load_method; ++ strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE); ++ ++ /* ++ * The firmware-tag should be NULL terminated, otherwise throw error and ++ * fail. ++ */ ++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { ++ dev_err(fw_mgmt->parent, "load-and-validate: firmware-tag is not NULL terminated\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ ++ ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL); ++ if (ret < 0) { ++ dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ fw_mgmt->intf_fw_request_id = ret; ++ fw_mgmt->intf_fw_loaded = false; ++ request.request_id = ret; ++ ++ ret = gb_operation_sync(fw_mgmt->connection, ++ GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW, &request, ++ sizeof(request), NULL, 0); ++ if (ret) { ++ ida_simple_remove(&fw_mgmt->id_map, ++ fw_mgmt->intf_fw_request_id); ++ fw_mgmt->intf_fw_request_id = 0; ++ dev_err(fw_mgmt->parent, ++ "load and validate firmware request failed (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection); ++ struct gb_fw_mgmt_loaded_fw_request *request; ++ ++ /* No pending load and validate request ? */ ++ if (!fw_mgmt->intf_fw_request_id) { ++ dev_err(fw_mgmt->parent, ++ "unexpected firmware loaded request received\n"); ++ return -ENODEV; ++ } ++ ++ if (op->request->payload_size != sizeof(*request)) { ++ dev_err(fw_mgmt->parent, "illegal size of firmware loaded request (%zu != %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ /* Invalid request-id ? */ ++ if (request->request_id != fw_mgmt->intf_fw_request_id) { ++ dev_err(fw_mgmt->parent, "invalid request id for firmware loaded request (%02u != %02u)\n", ++ fw_mgmt->intf_fw_request_id, request->request_id); ++ return -ENODEV; ++ } ++ ++ ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->intf_fw_request_id); ++ fw_mgmt->intf_fw_request_id = 0; ++ fw_mgmt->intf_fw_status = request->status; ++ fw_mgmt->intf_fw_major = le16_to_cpu(request->major); ++ fw_mgmt->intf_fw_minor = le16_to_cpu(request->minor); ++ ++ if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_FAILED) ++ dev_err(fw_mgmt->parent, ++ "failed to load interface firmware, status:%02x\n", ++ fw_mgmt->intf_fw_status); ++ else if (fw_mgmt->intf_fw_status == GB_FW_LOAD_STATUS_VALIDATION_FAILED) ++ dev_err(fw_mgmt->parent, ++ "failed to validate interface firmware, status:%02x\n", ++ fw_mgmt->intf_fw_status); ++ else ++ fw_mgmt->intf_fw_loaded = true; ++ ++ complete(&fw_mgmt->completion); ++ ++ return 0; ++} ++ ++static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, ++ struct fw_mgmt_ioc_get_backend_version *fw_info) ++{ ++ struct gb_connection *connection = fw_mgmt->connection; ++ struct gb_fw_mgmt_backend_fw_version_request request; ++ struct gb_fw_mgmt_backend_fw_version_response response; ++ int ret; ++ ++ strncpy(request.firmware_tag, fw_info->firmware_tag, ++ GB_FIRMWARE_TAG_MAX_SIZE); ++ ++ /* ++ * The firmware-tag should be NULL terminated, otherwise throw error and ++ * fail. ++ */ ++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { ++ dev_err(fw_mgmt->parent, "backend-version: firmware-tag is not NULL terminated\n"); ++ return -EINVAL; ++ } ++ ++ ret = gb_operation_sync(connection, ++ GB_FW_MGMT_TYPE_BACKEND_FW_VERSION, &request, ++ sizeof(request), &response, sizeof(response)); ++ if (ret) { ++ dev_err(fw_mgmt->parent, "failed to get version of %s backend firmware (%d)\n", ++ fw_info->firmware_tag, ret); ++ return ret; ++ } ++ ++ fw_info->status = response.status; ++ ++ /* Reset version as that should be non-zero only for success case */ ++ fw_info->major = 0; ++ fw_info->minor = 0; ++ ++ switch (fw_info->status) { ++ case GB_FW_BACKEND_VERSION_STATUS_SUCCESS: ++ fw_info->major = le16_to_cpu(response.major); ++ fw_info->minor = le16_to_cpu(response.minor); ++ break; ++ case GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE: ++ case GB_FW_BACKEND_VERSION_STATUS_RETRY: ++ break; ++ case GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED: ++ dev_err(fw_mgmt->parent, ++ "Firmware with tag %s is not supported by Interface\n", ++ fw_info->firmware_tag); ++ break; ++ default: ++ dev_err(fw_mgmt->parent, "Invalid status received: %u\n", ++ fw_info->status); ++ } ++ ++ return 0; ++} ++ ++static int fw_mgmt_backend_fw_update_operation(struct fw_mgmt *fw_mgmt, ++ char *tag) ++{ ++ struct gb_fw_mgmt_backend_fw_update_request request; ++ int ret; ++ ++ strncpy(request.firmware_tag, tag, GB_FIRMWARE_TAG_MAX_SIZE); ++ ++ /* ++ * The firmware-tag should be NULL terminated, otherwise throw error and ++ * fail. ++ */ ++ if (request.firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE - 1] != '\0') { ++ dev_err(fw_mgmt->parent, "backend-update: firmware-tag is not NULL terminated\n"); ++ return -EINVAL; ++ } ++ ++ /* Allocate ids from 1 to 255 (u8-max), 0 is an invalid id */ ++ ret = ida_simple_get(&fw_mgmt->id_map, 1, 256, GFP_KERNEL); ++ if (ret < 0) { ++ dev_err(fw_mgmt->parent, "failed to allocate request id (%d)\n", ++ ret); ++ return ret; ++ } ++ ++ fw_mgmt->backend_fw_request_id = ret; ++ request.request_id = ret; ++ ++ ret = gb_operation_sync(fw_mgmt->connection, ++ GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE, &request, ++ sizeof(request), NULL, 0); ++ if (ret) { ++ ida_simple_remove(&fw_mgmt->id_map, ++ fw_mgmt->backend_fw_request_id); ++ fw_mgmt->backend_fw_request_id = 0; ++ dev_err(fw_mgmt->parent, ++ "backend %s firmware update request failed (%d)\n", tag, ++ ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int fw_mgmt_backend_fw_updated_operation(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct fw_mgmt *fw_mgmt = gb_connection_get_data(connection); ++ struct gb_fw_mgmt_backend_fw_updated_request *request; ++ ++ /* No pending load and validate request ? */ ++ if (!fw_mgmt->backend_fw_request_id) { ++ dev_err(fw_mgmt->parent, "unexpected backend firmware updated request received\n"); ++ return -ENODEV; ++ } ++ ++ if (op->request->payload_size != sizeof(*request)) { ++ dev_err(fw_mgmt->parent, "illegal size of backend firmware updated request (%zu != %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ /* Invalid request-id ? */ ++ if (request->request_id != fw_mgmt->backend_fw_request_id) { ++ dev_err(fw_mgmt->parent, "invalid request id for backend firmware updated request (%02u != %02u)\n", ++ fw_mgmt->backend_fw_request_id, request->request_id); ++ return -ENODEV; ++ } ++ ++ ida_simple_remove(&fw_mgmt->id_map, fw_mgmt->backend_fw_request_id); ++ fw_mgmt->backend_fw_request_id = 0; ++ fw_mgmt->backend_fw_status = request->status; ++ ++ if ((fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_SUCCESS) && ++ (fw_mgmt->backend_fw_status != GB_FW_BACKEND_FW_STATUS_RETRY)) ++ dev_err(fw_mgmt->parent, ++ "failed to load backend firmware: %02x\n", ++ fw_mgmt->backend_fw_status); ++ ++ complete(&fw_mgmt->completion); ++ ++ return 0; ++} ++ ++/* Char device fops */ ++ ++static int fw_mgmt_open(struct inode *inode, struct file *file) ++{ ++ struct fw_mgmt *fw_mgmt = get_fw_mgmt(inode->i_cdev); ++ ++ /* fw_mgmt structure can't get freed until file descriptor is closed */ ++ if (fw_mgmt) { ++ file->private_data = fw_mgmt; ++ return 0; ++ } ++ ++ return -ENODEV; ++} ++ ++static int fw_mgmt_release(struct inode *inode, struct file *file) ++{ ++ struct fw_mgmt *fw_mgmt = file->private_data; ++ ++ put_fw_mgmt(fw_mgmt); ++ return 0; ++} ++ ++static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, ++ void __user *buf) ++{ ++ struct fw_mgmt_ioc_get_intf_version intf_fw_info; ++ struct fw_mgmt_ioc_get_backend_version backend_fw_info; ++ struct fw_mgmt_ioc_intf_load_and_validate intf_load; ++ struct fw_mgmt_ioc_backend_fw_update backend_update; ++ unsigned int timeout; ++ int ret; ++ ++ /* Reject any operations after mode-switch has started */ ++ if (fw_mgmt->mode_switch_started) ++ return -EBUSY; ++ ++ switch (cmd) { ++ case FW_MGMT_IOC_GET_INTF_FW: ++ ret = fw_mgmt_interface_fw_version_operation(fw_mgmt, ++ &intf_fw_info); ++ if (ret) ++ return ret; ++ ++ if (copy_to_user(buf, &intf_fw_info, sizeof(intf_fw_info))) ++ return -EFAULT; ++ ++ return 0; ++ case FW_MGMT_IOC_GET_BACKEND_FW: ++ if (copy_from_user(&backend_fw_info, buf, ++ sizeof(backend_fw_info))) ++ return -EFAULT; ++ ++ ret = fw_mgmt_backend_fw_version_operation(fw_mgmt, ++ &backend_fw_info); ++ if (ret) ++ return ret; ++ ++ if (copy_to_user(buf, &backend_fw_info, ++ sizeof(backend_fw_info))) ++ return -EFAULT; ++ ++ return 0; ++ case FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE: ++ if (copy_from_user(&intf_load, buf, sizeof(intf_load))) ++ return -EFAULT; ++ ++ ret = fw_mgmt_load_and_validate_operation(fw_mgmt, ++ intf_load.load_method, intf_load.firmware_tag); ++ if (ret) ++ return ret; ++ ++ if (!wait_for_completion_timeout(&fw_mgmt->completion, ++ fw_mgmt->timeout_jiffies)) { ++ dev_err(fw_mgmt->parent, "timed out waiting for firmware load and validation to finish\n"); ++ return -ETIMEDOUT; ++ } ++ ++ intf_load.status = fw_mgmt->intf_fw_status; ++ intf_load.major = fw_mgmt->intf_fw_major; ++ intf_load.minor = fw_mgmt->intf_fw_minor; ++ ++ if (copy_to_user(buf, &intf_load, sizeof(intf_load))) ++ return -EFAULT; ++ ++ return 0; ++ case FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE: ++ if (copy_from_user(&backend_update, buf, ++ sizeof(backend_update))) ++ return -EFAULT; ++ ++ ret = fw_mgmt_backend_fw_update_operation(fw_mgmt, ++ backend_update.firmware_tag); ++ if (ret) ++ return ret; ++ ++ if (!wait_for_completion_timeout(&fw_mgmt->completion, ++ fw_mgmt->timeout_jiffies)) { ++ dev_err(fw_mgmt->parent, "timed out waiting for backend firmware update to finish\n"); ++ return -ETIMEDOUT; ++ } ++ ++ backend_update.status = fw_mgmt->backend_fw_status; ++ ++ if (copy_to_user(buf, &backend_update, sizeof(backend_update))) ++ return -EFAULT; ++ ++ return 0; ++ case FW_MGMT_IOC_SET_TIMEOUT_MS: ++ if (get_user(timeout, (unsigned int __user *)buf)) ++ return -EFAULT; ++ ++ if (!timeout) { ++ dev_err(fw_mgmt->parent, "timeout can't be zero\n"); ++ return -EINVAL; ++ } ++ ++ fw_mgmt->timeout_jiffies = msecs_to_jiffies(timeout); ++ ++ return 0; ++ case FW_MGMT_IOC_MODE_SWITCH: ++ if (!fw_mgmt->intf_fw_loaded) { ++ dev_err(fw_mgmt->parent, ++ "Firmware not loaded for mode-switch\n"); ++ return -EPERM; ++ } ++ ++ /* ++ * Disallow new ioctls as the fw-core bundle driver is going to ++ * get disconnected soon and the character device will get ++ * removed. ++ */ ++ fw_mgmt->mode_switch_started = true; ++ ++ ret = gb_interface_request_mode_switch(fw_mgmt->connection->intf); ++ if (ret) { ++ dev_err(fw_mgmt->parent, "Mode-switch failed: %d\n", ++ ret); ++ fw_mgmt->mode_switch_started = false; ++ return ret; ++ } ++ ++ return 0; ++ default: ++ return -ENOTTY; ++ } ++} ++ ++static long fw_mgmt_ioctl_unlocked(struct file *file, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct fw_mgmt *fw_mgmt = file->private_data; ++ struct gb_bundle *bundle = fw_mgmt->connection->bundle; ++ int ret = -ENODEV; ++ ++ /* ++ * Serialize ioctls. ++ * ++ * We don't want the user to do few operations in parallel. For example, ++ * updating Interface firmware in parallel for the same Interface. There ++ * is no need to do things in parallel for speed and we can avoid having ++ * complicated code for now. ++ * ++ * This is also used to protect ->disabled, which is used to check if ++ * the connection is getting disconnected, so that we don't start any ++ * new operations. ++ */ ++ mutex_lock(&fw_mgmt->mutex); ++ if (!fw_mgmt->disabled) { ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (!ret) { ++ ret = fw_mgmt_ioctl(fw_mgmt, cmd, (void __user *)arg); ++ gb_pm_runtime_put_autosuspend(bundle); ++ } ++ } ++ mutex_unlock(&fw_mgmt->mutex); ++ ++ return ret; ++} ++ ++static const struct file_operations fw_mgmt_fops = { ++ .owner = THIS_MODULE, ++ .open = fw_mgmt_open, ++ .release = fw_mgmt_release, ++ .unlocked_ioctl = fw_mgmt_ioctl_unlocked, ++}; ++ ++int gb_fw_mgmt_request_handler(struct gb_operation *op) ++{ ++ u8 type = op->type; ++ ++ switch (type) { ++ case GB_FW_MGMT_TYPE_LOADED_FW: ++ return fw_mgmt_interface_fw_loaded_operation(op); ++ case GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED: ++ return fw_mgmt_backend_fw_updated_operation(op); ++ default: ++ dev_err(&op->connection->bundle->dev, ++ "unsupported request: %u\n", type); ++ return -EINVAL; ++ } ++} ++ ++int gb_fw_mgmt_connection_init(struct gb_connection *connection) ++{ ++ struct fw_mgmt *fw_mgmt; ++ int ret, minor; ++ ++ if (!connection) ++ return 0; ++ ++ fw_mgmt = kzalloc(sizeof(*fw_mgmt), GFP_KERNEL); ++ if (!fw_mgmt) ++ return -ENOMEM; ++ ++ fw_mgmt->parent = &connection->bundle->dev; ++ fw_mgmt->timeout_jiffies = msecs_to_jiffies(FW_MGMT_TIMEOUT_MS); ++ fw_mgmt->connection = connection; ++ ++ gb_connection_set_data(connection, fw_mgmt); ++ init_completion(&fw_mgmt->completion); ++ ida_init(&fw_mgmt->id_map); ++ mutex_init(&fw_mgmt->mutex); ++ kref_init(&fw_mgmt->kref); ++ ++ mutex_lock(&list_mutex); ++ list_add(&fw_mgmt->node, &fw_mgmt_list); ++ mutex_unlock(&list_mutex); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto err_list_del; ++ ++ minor = ida_simple_get(&fw_mgmt_minors_map, 0, NUM_MINORS, GFP_KERNEL); ++ if (minor < 0) { ++ ret = minor; ++ goto err_connection_disable; ++ } ++ ++ /* Add a char device to allow userspace to interact with fw-mgmt */ ++ fw_mgmt->dev_num = MKDEV(MAJOR(fw_mgmt_dev_num), minor); ++ cdev_init(&fw_mgmt->cdev, &fw_mgmt_fops); ++ ++ ret = cdev_add(&fw_mgmt->cdev, fw_mgmt->dev_num, 1); ++ if (ret) ++ goto err_remove_ida; ++ ++ /* Add a soft link to the previously added char-dev within the bundle */ ++ fw_mgmt->class_device = device_create(fw_mgmt_class, fw_mgmt->parent, ++ fw_mgmt->dev_num, NULL, ++ "gb-fw-mgmt-%d", minor); ++ if (IS_ERR(fw_mgmt->class_device)) { ++ ret = PTR_ERR(fw_mgmt->class_device); ++ goto err_del_cdev; ++ } ++ ++ return 0; ++ ++err_del_cdev: ++ cdev_del(&fw_mgmt->cdev); ++err_remove_ida: ++ ida_simple_remove(&fw_mgmt_minors_map, minor); ++err_connection_disable: ++ gb_connection_disable(connection); ++err_list_del: ++ mutex_lock(&list_mutex); ++ list_del(&fw_mgmt->node); ++ mutex_unlock(&list_mutex); ++ ++ put_fw_mgmt(fw_mgmt); ++ ++ return ret; ++} ++ ++void gb_fw_mgmt_connection_exit(struct gb_connection *connection) ++{ ++ struct fw_mgmt *fw_mgmt; ++ ++ if (!connection) ++ return; ++ ++ fw_mgmt = gb_connection_get_data(connection); ++ ++ device_destroy(fw_mgmt_class, fw_mgmt->dev_num); ++ cdev_del(&fw_mgmt->cdev); ++ ida_simple_remove(&fw_mgmt_minors_map, MINOR(fw_mgmt->dev_num)); ++ ++ /* ++ * Disallow any new ioctl operations on the char device and wait for ++ * existing ones to finish. ++ */ ++ mutex_lock(&fw_mgmt->mutex); ++ fw_mgmt->disabled = true; ++ mutex_unlock(&fw_mgmt->mutex); ++ ++ /* All pending greybus operations should have finished by now */ ++ gb_connection_disable(fw_mgmt->connection); ++ ++ /* Disallow new users to get access to the fw_mgmt structure */ ++ mutex_lock(&list_mutex); ++ list_del(&fw_mgmt->node); ++ mutex_unlock(&list_mutex); ++ ++ /* ++ * All current users of fw_mgmt would have taken a reference to it by ++ * now, we can drop our reference and wait the last user will get ++ * fw_mgmt freed. ++ */ ++ put_fw_mgmt(fw_mgmt); ++} ++ ++int fw_mgmt_init(void) ++{ ++ int ret; ++ ++ fw_mgmt_class = class_create(THIS_MODULE, "gb_fw_mgmt"); ++ if (IS_ERR(fw_mgmt_class)) ++ return PTR_ERR(fw_mgmt_class); ++ ++ ret = alloc_chrdev_region(&fw_mgmt_dev_num, 0, NUM_MINORS, ++ "gb_fw_mgmt"); ++ if (ret) ++ goto err_remove_class; ++ ++ return 0; ++ ++err_remove_class: ++ class_destroy(fw_mgmt_class); ++ return ret; ++} ++ ++void fw_mgmt_exit(void) ++{ ++ unregister_chrdev_region(fw_mgmt_dev_num, NUM_MINORS); ++ class_destroy(fw_mgmt_class); ++ ida_destroy(&fw_mgmt_minors_map); ++} +--- /dev/null ++++ b/drivers/greybus/greybus_firmware.h +@@ -0,0 +1,120 @@ ++/* ++ * Greybus Firmware Management User Header ++ * ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2016 Google Inc. All rights reserved. ++ * Copyright(c) 2016 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef __GREYBUS_FIRMWARE_USER_H ++#define __GREYBUS_FIRMWARE_USER_H ++ ++#include <linux/ioctl.h> ++#include <linux/types.h> ++ ++#define GB_FIRMWARE_U_TAG_MAX_SIZE 10 ++ ++#define GB_FW_U_LOAD_METHOD_UNIPRO 0x01 ++#define GB_FW_U_LOAD_METHOD_INTERNAL 0x02 ++ ++#define GB_FW_U_LOAD_STATUS_FAILED 0x00 ++#define GB_FW_U_LOAD_STATUS_UNVALIDATED 0x01 ++#define GB_FW_U_LOAD_STATUS_VALIDATED 0x02 ++#define GB_FW_U_LOAD_STATUS_VALIDATION_FAILED 0x03 ++ ++#define GB_FW_U_BACKEND_FW_STATUS_SUCCESS 0x01 ++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FIND 0x02 ++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_FETCH 0x03 ++#define GB_FW_U_BACKEND_FW_STATUS_FAIL_WRITE 0x04 ++#define GB_FW_U_BACKEND_FW_STATUS_INT 0x05 ++#define GB_FW_U_BACKEND_FW_STATUS_RETRY 0x06 ++#define GB_FW_U_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 ++ ++#define GB_FW_U_BACKEND_VERSION_STATUS_SUCCESS 0x01 ++#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 ++#define GB_FW_U_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 ++#define GB_FW_U_BACKEND_VERSION_STATUS_RETRY 0x04 ++#define GB_FW_U_BACKEND_VERSION_STATUS_FAIL_INT 0x05 ++ ++/* IOCTL support */ ++struct fw_mgmt_ioc_get_intf_version { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u16 major; ++ __u16 minor; ++} __attribute__ ((__packed__)); ++ ++struct fw_mgmt_ioc_get_backend_version { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u16 major; ++ __u16 minor; ++ __u8 status; ++} __attribute__ ((__packed__)); ++ ++struct fw_mgmt_ioc_intf_load_and_validate { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u8 load_method; ++ __u8 status; ++ __u16 major; ++ __u16 minor; ++} __attribute__ ((__packed__)); ++ ++struct fw_mgmt_ioc_backend_fw_update { ++ __u8 firmware_tag[GB_FIRMWARE_U_TAG_MAX_SIZE]; ++ __u8 status; ++} __attribute__ ((__packed__)); ++ ++#define FW_MGMT_IOCTL_BASE 'F' ++#define FW_MGMT_IOC_GET_INTF_FW _IOR(FW_MGMT_IOCTL_BASE, 0, struct fw_mgmt_ioc_get_intf_version) ++#define FW_MGMT_IOC_GET_BACKEND_FW _IOWR(FW_MGMT_IOCTL_BASE, 1, struct fw_mgmt_ioc_get_backend_version) ++#define FW_MGMT_IOC_INTF_LOAD_AND_VALIDATE _IOWR(FW_MGMT_IOCTL_BASE, 2, struct fw_mgmt_ioc_intf_load_and_validate) ++#define FW_MGMT_IOC_INTF_BACKEND_FW_UPDATE _IOWR(FW_MGMT_IOCTL_BASE, 3, struct fw_mgmt_ioc_backend_fw_update) ++#define FW_MGMT_IOC_SET_TIMEOUT_MS _IOW(FW_MGMT_IOCTL_BASE, 4, unsigned int) ++#define FW_MGMT_IOC_MODE_SWITCH _IO(FW_MGMT_IOCTL_BASE, 5) ++ ++#endif /* __GREYBUS_FIRMWARE_USER_H */ ++ diff --git a/greybus_gbphy.patch b/greybus_gbphy.patch new file mode 100644 index 00000000000000..932216ea2e3a65 --- /dev/null +++ b/greybus_gbphy.patch @@ -0,0 +1,481 @@ +--- + drivers/greybus/gbphy.c | 360 ++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/gbphy.h | 110 ++++++++++++++ + 2 files changed, 470 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/gbphy.c +@@ -0,0 +1,360 @@ ++/* ++ * Greybus Bridged-Phy Bus driver ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++#define GB_GBPHY_AUTOSUSPEND_MS 3000 ++ ++struct gbphy_host { ++ struct gb_bundle *bundle; ++ struct list_head devices; ++}; ++ ++static DEFINE_IDA(gbphy_id); ++ ++static ssize_t protocol_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ ++ return sprintf(buf, "0x%02x\n", gbphy_dev->cport_desc->protocol_id); ++} ++static DEVICE_ATTR_RO(protocol_id); ++ ++static struct attribute *gbphy_dev_attrs[] = { ++ &dev_attr_protocol_id.attr, ++ NULL, ++}; ++ ++ATTRIBUTE_GROUPS(gbphy_dev); ++ ++static void gbphy_dev_release(struct device *dev) ++{ ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ ++ ida_simple_remove(&gbphy_id, gbphy_dev->id); ++ kfree(gbphy_dev); ++} ++ ++#ifdef CONFIG_PM ++static int gb_gbphy_idle(struct device *dev) ++{ ++ pm_runtime_mark_last_busy(dev); ++ pm_request_autosuspend(dev); ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_gbphy_pm_ops = { ++ SET_RUNTIME_PM_OPS(pm_generic_runtime_suspend, ++ pm_generic_runtime_resume, ++ gb_gbphy_idle) ++}; ++ ++static struct device_type greybus_gbphy_dev_type = { ++ .name = "gbphy_device", ++ .release = gbphy_dev_release, ++ .pm = &gb_gbphy_pm_ops, ++}; ++ ++static int gbphy_dev_uevent(struct device *dev, struct kobj_uevent_env *env) ++{ ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ struct greybus_descriptor_cport *cport_desc = gbphy_dev->cport_desc; ++ struct gb_bundle *bundle = gbphy_dev->bundle; ++ struct gb_interface *intf = bundle->intf; ++ struct gb_module *module = intf->module; ++ struct gb_host_device *hd = intf->hd; ++ ++ if (add_uevent_var(env, "BUS=%u", hd->bus_id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "MODULE=%u", module->module_id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x", ++ intf->vendor_id, intf->product_id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "BUNDLE=%u", gbphy_dev->bundle->id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "GBPHY=%u", gbphy_dev->id)) ++ return -ENOMEM; ++ if (add_uevent_var(env, "PROTOCOL_ID=%02x", cport_desc->protocol_id)) ++ return -ENOMEM; ++ ++ return 0; ++} ++ ++static const struct gbphy_device_id * ++gbphy_dev_match_id(struct gbphy_device *gbphy_dev, struct gbphy_driver *gbphy_drv) ++{ ++ const struct gbphy_device_id *id = gbphy_drv->id_table; ++ ++ if (!id) ++ return NULL; ++ ++ for (; id->protocol_id; id++) ++ if (id->protocol_id == gbphy_dev->cport_desc->protocol_id) ++ return id; ++ ++ return NULL; ++} ++ ++static int gbphy_dev_match(struct device *dev, struct device_driver *drv) ++{ ++ struct gbphy_driver *gbphy_drv = to_gbphy_driver(drv); ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ const struct gbphy_device_id *id; ++ ++ id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); ++ if (id) ++ return 1; ++ ++ return 0; ++} ++ ++static int gbphy_dev_probe(struct device *dev) ++{ ++ struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ const struct gbphy_device_id *id; ++ int ret; ++ ++ id = gbphy_dev_match_id(gbphy_dev, gbphy_drv); ++ if (!id) ++ return -ENODEV; ++ ++ /* for old kernels we need get_sync to resume parent devices */ ++ ret = gb_pm_runtime_get_sync(gbphy_dev->bundle); ++ if (ret < 0) ++ return ret; ++ ++ pm_runtime_set_autosuspend_delay(dev, GB_GBPHY_AUTOSUSPEND_MS); ++ pm_runtime_use_autosuspend(dev); ++ pm_runtime_get_noresume(dev); ++ pm_runtime_set_active(dev); ++ pm_runtime_enable(dev); ++ ++ /* ++ * Drivers should call put on the gbphy dev before returning ++ * from probe if they support runtime pm. ++ */ ++ ret = gbphy_drv->probe(gbphy_dev, id); ++ if (ret) { ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ pm_runtime_dont_use_autosuspend(dev); ++ } ++ ++ gb_pm_runtime_put_autosuspend(gbphy_dev->bundle); ++ ++ return ret; ++} ++ ++static int gbphy_dev_remove(struct device *dev) ++{ ++ struct gbphy_driver *gbphy_drv = to_gbphy_driver(dev->driver); ++ struct gbphy_device *gbphy_dev = to_gbphy_dev(dev); ++ ++ gbphy_drv->remove(gbphy_dev); ++ ++ pm_runtime_disable(dev); ++ pm_runtime_set_suspended(dev); ++ pm_runtime_put_noidle(dev); ++ pm_runtime_dont_use_autosuspend(dev); ++ ++ return 0; ++} ++ ++static struct bus_type gbphy_bus_type = { ++ .name = "gbphy", ++ .match = gbphy_dev_match, ++ .probe = gbphy_dev_probe, ++ .remove = gbphy_dev_remove, ++ .uevent = gbphy_dev_uevent, ++}; ++ ++int gb_gbphy_register_driver(struct gbphy_driver *driver, ++ struct module *owner, const char *mod_name) ++{ ++ int retval; ++ ++ if (greybus_disabled()) ++ return -ENODEV; ++ ++ driver->driver.bus = &gbphy_bus_type; ++ driver->driver.name = driver->name; ++ driver->driver.owner = owner; ++ driver->driver.mod_name = mod_name; ++ ++ retval = driver_register(&driver->driver); ++ if (retval) ++ return retval; ++ ++ pr_info("registered new driver %s\n", driver->name); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_gbphy_register_driver); ++ ++void gb_gbphy_deregister_driver(struct gbphy_driver *driver) ++{ ++ driver_unregister(&driver->driver); ++} ++EXPORT_SYMBOL_GPL(gb_gbphy_deregister_driver); ++ ++static struct gbphy_device *gb_gbphy_create_dev(struct gb_bundle *bundle, ++ struct greybus_descriptor_cport *cport_desc) ++{ ++ struct gbphy_device *gbphy_dev; ++ int retval; ++ int id; ++ ++ id = ida_simple_get(&gbphy_id, 1, 0, GFP_KERNEL); ++ if (id < 0) ++ return ERR_PTR(id); ++ ++ gbphy_dev = kzalloc(sizeof(*gbphy_dev), GFP_KERNEL); ++ if (!gbphy_dev) { ++ ida_simple_remove(&gbphy_id, id); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ gbphy_dev->id = id; ++ gbphy_dev->bundle = bundle; ++ gbphy_dev->cport_desc = cport_desc; ++ gbphy_dev->dev.parent = &bundle->dev; ++ gbphy_dev->dev.bus = &gbphy_bus_type; ++ gbphy_dev->dev.type = &greybus_gbphy_dev_type; ++ gbphy_dev->dev.groups = gbphy_dev_groups; ++ gbphy_dev->dev.dma_mask = bundle->dev.dma_mask; ++ dev_set_name(&gbphy_dev->dev, "gbphy%d", id); ++ ++ retval = device_register(&gbphy_dev->dev); ++ if (retval) { ++ put_device(&gbphy_dev->dev); ++ return ERR_PTR(retval); ++ } ++ ++ return gbphy_dev; ++} ++ ++static void gb_gbphy_disconnect(struct gb_bundle *bundle) ++{ ++ struct gbphy_host *gbphy_host = greybus_get_drvdata(bundle); ++ struct gbphy_device *gbphy_dev, *temp; ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ list_for_each_entry_safe(gbphy_dev, temp, &gbphy_host->devices, list) { ++ list_del(&gbphy_dev->list); ++ device_unregister(&gbphy_dev->dev); ++ } ++ ++ kfree(gbphy_host); ++} ++ ++static int gb_gbphy_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct gbphy_host *gbphy_host; ++ struct gbphy_device *gbphy_dev; ++ int i; ++ ++ if (bundle->num_cports == 0) ++ return -ENODEV; ++ ++ gbphy_host = kzalloc(sizeof(*gbphy_host), GFP_KERNEL); ++ if (!gbphy_host) ++ return -ENOMEM; ++ ++ gbphy_host->bundle = bundle; ++ INIT_LIST_HEAD(&gbphy_host->devices); ++ greybus_set_drvdata(bundle, gbphy_host); ++ ++ /* ++ * Create a bunch of children devices, one per cport, and bind the ++ * bridged phy drivers to them. ++ */ ++ for (i = 0; i < bundle->num_cports; ++i) { ++ gbphy_dev = gb_gbphy_create_dev(bundle, &bundle->cport_desc[i]); ++ if (IS_ERR(gbphy_dev)) { ++ gb_gbphy_disconnect(bundle); ++ return PTR_ERR(gbphy_dev); ++ } ++ list_add(&gbphy_dev->list, &gbphy_host->devices); ++ } ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++} ++ ++static const struct greybus_bundle_id gb_gbphy_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BRIDGED_PHY) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(greybus, gb_gbphy_id_table); ++ ++static struct greybus_driver gb_gbphy_driver = { ++ .name = "gbphy", ++ .probe = gb_gbphy_probe, ++ .disconnect = gb_gbphy_disconnect, ++ .id_table = gb_gbphy_id_table, ++}; ++ ++static int __init gbphy_init(void) ++{ ++ int retval; ++ ++ retval = bus_register(&gbphy_bus_type); ++ if (retval) { ++ pr_err("gbphy bus register failed (%d)\n", retval); ++ return retval; ++ } ++ ++ retval = greybus_register(&gb_gbphy_driver); ++ if (retval) { ++ pr_err("error registering greybus driver\n"); ++ goto error_gbphy; ++ } ++ ++ return 0; ++ ++error_gbphy: ++ bus_unregister(&gbphy_bus_type); ++ ida_destroy(&gbphy_id); ++ return retval; ++} ++module_init(gbphy_init); ++ ++static void __exit gbphy_exit(void) ++{ ++ greybus_deregister(&gb_gbphy_driver); ++ bus_unregister(&gbphy_bus_type); ++ ida_destroy(&gbphy_id); ++} ++module_exit(gbphy_exit); ++ ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/gbphy.h +@@ -0,0 +1,110 @@ ++/* ++ * Greybus Bridged-Phy Bus driver ++ * ++ * Copyright 2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __GBPHY_H ++#define __GBPHY_H ++ ++struct gbphy_device { ++ u32 id; ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_bundle *bundle; ++ struct list_head list; ++ struct device dev; ++}; ++#define to_gbphy_dev(d) container_of(d, struct gbphy_device, dev) ++ ++static inline void *gb_gbphy_get_data(struct gbphy_device *gdev) ++{ ++ return dev_get_drvdata(&gdev->dev); ++} ++ ++static inline void gb_gbphy_set_data(struct gbphy_device *gdev, void *data) ++{ ++ dev_set_drvdata(&gdev->dev, data); ++} ++ ++struct gbphy_device_id { ++ __u8 protocol_id; ++}; ++ ++#define GBPHY_PROTOCOL(p) \ ++ .protocol_id = (p), ++ ++struct gbphy_driver { ++ const char *name; ++ int (*probe)(struct gbphy_device *, ++ const struct gbphy_device_id *id); ++ void (*remove)(struct gbphy_device *); ++ const struct gbphy_device_id *id_table; ++ ++ struct device_driver driver; ++}; ++#define to_gbphy_driver(d) container_of(d, struct gbphy_driver, driver) ++ ++int gb_gbphy_register_driver(struct gbphy_driver *driver, ++ struct module *owner, const char *mod_name); ++void gb_gbphy_deregister_driver(struct gbphy_driver *driver); ++ ++#define gb_gbphy_register(driver) \ ++ gb_gbphy_register_driver(driver, THIS_MODULE, KBUILD_MODNAME) ++#define gb_gbphy_deregister(driver) \ ++ gb_gbphy_deregister_driver(driver) ++ ++/** ++ * module_gbphy_driver() - Helper macro for registering a gbphy driver ++ * @__gbphy_driver: gbphy_driver structure ++ * ++ * Helper macro for gbphy drivers to set up proper module init / exit ++ * functions. Replaces module_init() and module_exit() and keeps people from ++ * printing pointless things to the kernel log when their driver is loaded. ++ */ ++#define module_gbphy_driver(__gbphy_driver) \ ++ module_driver(__gbphy_driver, gb_gbphy_register, gb_gbphy_deregister) ++ ++#ifdef CONFIG_PM ++static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) ++{ ++ struct device *dev = &gbphy_dev->dev; ++ int ret; ++ ++ ret = pm_runtime_get_sync(dev); ++ if (ret < 0) { ++ dev_err(dev, "pm_runtime_get_sync failed: %d\n", ret); ++ pm_runtime_put_noidle(dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev) ++{ ++ struct device *dev = &gbphy_dev->dev; ++ ++ pm_runtime_mark_last_busy(dev); ++ pm_runtime_put_autosuspend(dev); ++} ++ ++static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) ++{ ++ pm_runtime_get_noresume(&gbphy_dev->dev); ++} ++ ++static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) ++{ ++ pm_runtime_put_noidle(&gbphy_dev->dev); ++} ++#else ++static inline int gbphy_runtime_get_sync(struct gbphy_device *gbphy_dev) { return 0; } ++static inline void gbphy_runtime_put_autosuspend(struct gbphy_device *gbphy_dev) {} ++static inline void gbphy_runtime_get_noresume(struct gbphy_device *gbphy_dev) {} ++static inline void gbphy_runtime_put_noidle(struct gbphy_device *gbphy_dev) {} ++#endif ++ ++#endif /* __GBPHY_H */ ++ diff --git a/greybus_gpio.patch b/greybus_gpio.patch new file mode 100644 index 00000000000000..743837044b239d --- /dev/null +++ b/greybus_gpio.patch @@ -0,0 +1,774 @@ +--- + drivers/greybus/gpio.c | 767 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 767 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/gpio.c +@@ -0,0 +1,767 @@ ++/* ++ * GPIO Greybus driver. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/gpio.h> ++#include <linux/irq.h> ++#include <linux/irqdomain.h> ++#include <linux/mutex.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++struct gb_gpio_line { ++ /* The following has to be an array of line_max entries */ ++ /* --> make them just a flags field */ ++ u8 active: 1, ++ direction: 1, /* 0 = output, 1 = input */ ++ value: 1; /* 0 = low, 1 = high */ ++ u16 debounce_usec; ++ ++ u8 irq_type; ++ bool irq_type_pending; ++ bool masked; ++ bool masked_pending; ++}; ++ ++struct gb_gpio_controller { ++ struct gbphy_device *gbphy_dev; ++ struct gb_connection *connection; ++ u8 line_max; /* max line number */ ++ struct gb_gpio_line *lines; ++ ++ struct gpio_chip chip; ++ struct irq_chip irqc; ++ struct irq_chip *irqchip; ++ struct irq_domain *irqdomain; ++ unsigned int irq_base; ++ irq_flow_handler_t irq_handler; ++ unsigned int irq_default_type; ++ struct mutex irq_lock; ++}; ++#define gpio_chip_to_gb_gpio_controller(chip) \ ++ container_of(chip, struct gb_gpio_controller, chip) ++#define irq_data_to_gpio_chip(d) (d->domain->host_data) ++ ++static int gb_gpio_line_count_operation(struct gb_gpio_controller *ggc) ++{ ++ struct gb_gpio_line_count_response response; ++ int ret; ++ ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_LINE_COUNT, ++ NULL, 0, &response, sizeof(response)); ++ if (!ret) ++ ggc->line_max = response.count; ++ return ret; ++} ++ ++static int gb_gpio_activate_operation(struct gb_gpio_controller *ggc, u8 which) ++{ ++ struct gb_gpio_activate_request request; ++ struct gbphy_device *gbphy_dev = ggc->gbphy_dev; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ request.which = which; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_ACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) { ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return ret; ++ } ++ ++ ggc->lines[which].active = true; ++ ++ return 0; ++} ++ ++static void gb_gpio_deactivate_operation(struct gb_gpio_controller *ggc, ++ u8 which) ++{ ++ struct gbphy_device *gbphy_dev = ggc->gbphy_dev; ++ struct device *dev = &gbphy_dev->dev; ++ struct gb_gpio_deactivate_request request; ++ int ret; ++ ++ request.which = which; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DEACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) { ++ dev_err(dev, "failed to deactivate gpio %u\n", which); ++ goto out_pm_put; ++ } ++ ++ ggc->lines[which].active = false; ++ ++out_pm_put: ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++} ++ ++static int gb_gpio_get_direction_operation(struct gb_gpio_controller *ggc, ++ u8 which) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_get_direction_request request; ++ struct gb_gpio_get_direction_response response; ++ int ret; ++ u8 direction; ++ ++ request.which = which; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_DIRECTION, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) ++ return ret; ++ ++ direction = response.direction; ++ if (direction && direction != 1) { ++ dev_warn(dev, "gpio %u direction was %u (should be 0 or 1)\n", ++ which, direction); ++ } ++ ggc->lines[which].direction = direction ? 1 : 0; ++ return 0; ++} ++ ++static int gb_gpio_direction_in_operation(struct gb_gpio_controller *ggc, ++ u8 which) ++{ ++ struct gb_gpio_direction_in_request request; ++ int ret; ++ ++ request.which = which; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_IN, ++ &request, sizeof(request), NULL, 0); ++ if (!ret) ++ ggc->lines[which].direction = 1; ++ return ret; ++} ++ ++static int gb_gpio_direction_out_operation(struct gb_gpio_controller *ggc, ++ u8 which, bool value_high) ++{ ++ struct gb_gpio_direction_out_request request; ++ int ret; ++ ++ request.which = which; ++ request.value = value_high ? 1 : 0; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_DIRECTION_OUT, ++ &request, sizeof(request), NULL, 0); ++ if (!ret) ++ ggc->lines[which].direction = 0; ++ return ret; ++} ++ ++static int gb_gpio_get_value_operation(struct gb_gpio_controller *ggc, ++ u8 which) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_get_value_request request; ++ struct gb_gpio_get_value_response response; ++ int ret; ++ u8 value; ++ ++ request.which = which; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_GET_VALUE, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(dev, "failed to get value of gpio %u\n", which); ++ return ret; ++ } ++ ++ value = response.value; ++ if (value && value != 1) { ++ dev_warn(dev, "gpio %u value was %u (should be 0 or 1)\n", ++ which, value); ++ } ++ ggc->lines[which].value = value ? 1 : 0; ++ return 0; ++} ++ ++static void gb_gpio_set_value_operation(struct gb_gpio_controller *ggc, ++ u8 which, bool value_high) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_set_value_request request; ++ int ret; ++ ++ if (ggc->lines[which].direction == 1) { ++ dev_warn(dev, "refusing to set value of input gpio %u\n", ++ which); ++ return; ++ } ++ ++ request.which = which; ++ request.value = value_high ? 1 : 0; ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_VALUE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) { ++ dev_err(dev, "failed to set value of gpio %u\n", which); ++ return; ++ } ++ ++ ggc->lines[which].value = request.value; ++} ++ ++static int gb_gpio_set_debounce_operation(struct gb_gpio_controller *ggc, ++ u8 which, u16 debounce_usec) ++{ ++ struct gb_gpio_set_debounce_request request; ++ int ret; ++ ++ request.which = which; ++ request.usec = cpu_to_le16(debounce_usec); ++ ret = gb_operation_sync(ggc->connection, GB_GPIO_TYPE_SET_DEBOUNCE, ++ &request, sizeof(request), NULL, 0); ++ if (!ret) ++ ggc->lines[which].debounce_usec = debounce_usec; ++ return ret; ++} ++ ++static void _gb_gpio_irq_mask(struct gb_gpio_controller *ggc, u8 hwirq) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_irq_mask_request request; ++ int ret; ++ ++ request.which = hwirq; ++ ret = gb_operation_sync(ggc->connection, ++ GB_GPIO_TYPE_IRQ_MASK, ++ &request, sizeof(request), NULL, 0); ++ if (ret) ++ dev_err(dev, "failed to mask irq: %d\n", ret); ++} ++ ++static void _gb_gpio_irq_unmask(struct gb_gpio_controller *ggc, u8 hwirq) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_irq_unmask_request request; ++ int ret; ++ ++ request.which = hwirq; ++ ret = gb_operation_sync(ggc->connection, ++ GB_GPIO_TYPE_IRQ_UNMASK, ++ &request, sizeof(request), NULL, 0); ++ if (ret) ++ dev_err(dev, "failed to unmask irq: %d\n", ret); ++} ++ ++static void _gb_gpio_irq_set_type(struct gb_gpio_controller *ggc, ++ u8 hwirq, u8 type) ++{ ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_gpio_irq_type_request request; ++ int ret; ++ ++ request.which = hwirq; ++ request.type = type; ++ ++ ret = gb_operation_sync(ggc->connection, ++ GB_GPIO_TYPE_IRQ_TYPE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) ++ dev_err(dev, "failed to set irq type: %d\n", ret); ++} ++ ++static void gb_gpio_irq_mask(struct irq_data *d) ++{ ++ struct gpio_chip *chip = irq_data_to_gpio_chip(d); ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ struct gb_gpio_line *line = &ggc->lines[d->hwirq]; ++ ++ line->masked = true; ++ line->masked_pending = true; ++} ++ ++static void gb_gpio_irq_unmask(struct irq_data *d) ++{ ++ struct gpio_chip *chip = irq_data_to_gpio_chip(d); ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ struct gb_gpio_line *line = &ggc->lines[d->hwirq]; ++ ++ line->masked = false; ++ line->masked_pending = true; ++} ++ ++static int gb_gpio_irq_set_type(struct irq_data *d, unsigned int type) ++{ ++ struct gpio_chip *chip = irq_data_to_gpio_chip(d); ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ struct gb_gpio_line *line = &ggc->lines[d->hwirq]; ++ struct device *dev = &ggc->gbphy_dev->dev; ++ u8 irq_type; ++ ++ switch (type) { ++ case IRQ_TYPE_NONE: ++ irq_type = GB_GPIO_IRQ_TYPE_NONE; ++ break; ++ case IRQ_TYPE_EDGE_RISING: ++ irq_type = GB_GPIO_IRQ_TYPE_EDGE_RISING; ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ irq_type = GB_GPIO_IRQ_TYPE_EDGE_FALLING; ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ irq_type = GB_GPIO_IRQ_TYPE_EDGE_BOTH; ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ irq_type = GB_GPIO_IRQ_TYPE_LEVEL_LOW; ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ irq_type = GB_GPIO_IRQ_TYPE_LEVEL_HIGH; ++ break; ++ default: ++ dev_err(dev, "unsupported irq type: %u\n", type); ++ return -EINVAL; ++ } ++ ++ line->irq_type = irq_type; ++ line->irq_type_pending = true; ++ ++ return 0; ++} ++ ++static void gb_gpio_irq_bus_lock(struct irq_data *d) ++{ ++ struct gpio_chip *chip = irq_data_to_gpio_chip(d); ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ mutex_lock(&ggc->irq_lock); ++} ++ ++static void gb_gpio_irq_bus_sync_unlock(struct irq_data *d) ++{ ++ struct gpio_chip *chip = irq_data_to_gpio_chip(d); ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ struct gb_gpio_line *line = &ggc->lines[d->hwirq]; ++ ++ if (line->irq_type_pending) { ++ _gb_gpio_irq_set_type(ggc, d->hwirq, line->irq_type); ++ line->irq_type_pending = false; ++ } ++ ++ if (line->masked_pending) { ++ if (line->masked) ++ _gb_gpio_irq_mask(ggc, d->hwirq); ++ else ++ _gb_gpio_irq_unmask(ggc, d->hwirq); ++ line->masked_pending = false; ++ } ++ ++ mutex_unlock(&ggc->irq_lock); ++} ++ ++static int gb_gpio_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_gpio_controller *ggc = gb_connection_get_data(connection); ++ struct device *dev = &ggc->gbphy_dev->dev; ++ struct gb_message *request; ++ struct gb_gpio_irq_event_request *event; ++ u8 type = op->type; ++ int irq; ++ struct irq_desc *desc; ++ ++ if (type != GB_GPIO_TYPE_IRQ_EVENT) { ++ dev_err(dev, "unsupported unsolicited request: %u\n", type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*event)) { ++ dev_err(dev, "short event received (%zu < %zu)\n", ++ request->payload_size, sizeof(*event)); ++ return -EINVAL; ++ } ++ ++ event = request->payload; ++ if (event->which > ggc->line_max) { ++ dev_err(dev, "invalid hw irq: %d\n", event->which); ++ return -EINVAL; ++ } ++ ++ irq = irq_find_mapping(ggc->irqdomain, event->which); ++ if (!irq) { ++ dev_err(dev, "failed to find IRQ\n"); ++ return -EINVAL; ++ } ++ desc = irq_to_desc(irq); ++ if (!desc) { ++ dev_err(dev, "failed to look up irq\n"); ++ return -EINVAL; ++ } ++ ++ local_irq_disable(); ++ generic_handle_irq_desc(desc); ++ local_irq_enable(); ++ ++ return 0; ++} ++ ++static int gb_gpio_request(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ return gb_gpio_activate_operation(ggc, (u8)offset); ++} ++ ++static void gb_gpio_free(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ gb_gpio_deactivate_operation(ggc, (u8)offset); ++} ++ ++static int gb_gpio_get_direction(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ u8 which; ++ int ret; ++ ++ which = (u8)offset; ++ ret = gb_gpio_get_direction_operation(ggc, which); ++ if (ret) ++ return ret; ++ ++ return ggc->lines[which].direction ? 1 : 0; ++} ++ ++static int gb_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ return gb_gpio_direction_in_operation(ggc, (u8)offset); ++} ++ ++static int gb_gpio_direction_output(struct gpio_chip *chip, unsigned offset, ++ int value) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ return gb_gpio_direction_out_operation(ggc, (u8)offset, !!value); ++} ++ ++static int gb_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ u8 which; ++ int ret; ++ ++ which = (u8)offset; ++ ret = gb_gpio_get_value_operation(ggc, which); ++ if (ret) ++ return ret; ++ ++ return ggc->lines[which].value; ++} ++ ++static void gb_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ gb_gpio_set_value_operation(ggc, (u8)offset, !!value); ++} ++ ++static int gb_gpio_set_debounce(struct gpio_chip *chip, unsigned offset, ++ unsigned debounce) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ u16 usec; ++ ++ if (debounce > U16_MAX) ++ return -EINVAL; ++ usec = (u16)debounce; ++ ++ return gb_gpio_set_debounce_operation(ggc, (u8)offset, usec); ++} ++ ++static int gb_gpio_controller_setup(struct gb_gpio_controller *ggc) ++{ ++ int ret; ++ ++ /* Now find out how many lines there are */ ++ ret = gb_gpio_line_count_operation(ggc); ++ if (ret) ++ return ret; ++ ++ ggc->lines = kcalloc(ggc->line_max + 1, sizeof(*ggc->lines), ++ GFP_KERNEL); ++ if (!ggc->lines) ++ return -ENOMEM; ++ ++ return ret; ++} ++ ++/** ++ * gb_gpio_irq_map() - maps an IRQ into a GB gpio irqchip ++ * @d: the irqdomain used by this irqchip ++ * @irq: the global irq number used by this GB gpio irqchip irq ++ * @hwirq: the local IRQ/GPIO line offset on this GB gpio ++ * ++ * This function will set up the mapping for a certain IRQ line on a ++ * GB gpio by assigning the GB gpio as chip data, and using the irqchip ++ * stored inside the GB gpio. ++ */ ++static int gb_gpio_irq_map(struct irq_domain *domain, unsigned int irq, ++ irq_hw_number_t hwirq) ++{ ++ struct gpio_chip *chip = domain->host_data; ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ irq_set_chip_data(irq, ggc); ++ irq_set_chip_and_handler(irq, ggc->irqchip, ggc->irq_handler); ++ irq_set_noprobe(irq); ++ /* ++ * No set-up of the hardware will happen if IRQ_TYPE_NONE ++ * is passed as default type. ++ */ ++ if (ggc->irq_default_type != IRQ_TYPE_NONE) ++ irq_set_irq_type(irq, ggc->irq_default_type); ++ ++ return 0; ++} ++ ++static void gb_gpio_irq_unmap(struct irq_domain *d, unsigned int irq) ++{ ++ irq_set_chip_and_handler(irq, NULL, NULL); ++ irq_set_chip_data(irq, NULL); ++} ++ ++static const struct irq_domain_ops gb_gpio_domain_ops = { ++ .map = gb_gpio_irq_map, ++ .unmap = gb_gpio_irq_unmap, ++}; ++ ++/** ++ * gb_gpio_irqchip_remove() - removes an irqchip added to a gb_gpio_controller ++ * @ggc: the gb_gpio_controller to remove the irqchip from ++ * ++ * This is called only from gb_gpio_remove() ++ */ ++static void gb_gpio_irqchip_remove(struct gb_gpio_controller *ggc) ++{ ++ unsigned int offset; ++ ++ /* Remove all IRQ mappings and delete the domain */ ++ if (ggc->irqdomain) { ++ for (offset = 0; offset < (ggc->line_max + 1); offset++) ++ irq_dispose_mapping(irq_find_mapping(ggc->irqdomain, offset)); ++ irq_domain_remove(ggc->irqdomain); ++ } ++ ++ if (ggc->irqchip) { ++ ggc->irqchip = NULL; ++ } ++} ++ ++/** ++ * gb_gpio_irqchip_add() - adds an irqchip to a gpio chip ++ * @chip: the gpio chip to add the irqchip to ++ * @irqchip: the irqchip to add to the adapter ++ * @first_irq: if not dynamically assigned, the base (first) IRQ to ++ * allocate gpio irqs from ++ * @handler: the irq handler to use (often a predefined irq core function) ++ * @type: the default type for IRQs on this irqchip, pass IRQ_TYPE_NONE ++ * to have the core avoid setting up any default type in the hardware. ++ * ++ * This function closely associates a certain irqchip with a certain ++ * gpio chip, providing an irq domain to translate the local IRQs to ++ * global irqs, and making sure that the gpio chip ++ * is passed as chip data to all related functions. Driver callbacks ++ * need to use container_of() to get their local state containers back ++ * from the gpio chip passed as chip data. An irqdomain will be stored ++ * in the gpio chip that shall be used by the driver to handle IRQ number ++ * translation. The gpio chip will need to be initialized and registered ++ * before calling this function. ++ */ ++static int gb_gpio_irqchip_add(struct gpio_chip *chip, ++ struct irq_chip *irqchip, ++ unsigned int first_irq, ++ irq_flow_handler_t handler, ++ unsigned int type) ++{ ++ struct gb_gpio_controller *ggc; ++ unsigned int offset; ++ unsigned irq_base; ++ ++ if (!chip || !irqchip) ++ return -EINVAL; ++ ++ ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ ggc->irqchip = irqchip; ++ ggc->irq_handler = handler; ++ ggc->irq_default_type = type; ++ ggc->irqdomain = irq_domain_add_simple(NULL, ++ ggc->line_max + 1, first_irq, ++ &gb_gpio_domain_ops, chip); ++ if (!ggc->irqdomain) { ++ ggc->irqchip = NULL; ++ return -EINVAL; ++ } ++ ++ /* ++ * Prepare the mapping since the irqchip shall be orthogonal to ++ * any gpio calls. If the first_irq was zero, this is ++ * necessary to allocate descriptors for all IRQs. ++ */ ++ for (offset = 0; offset < (ggc->line_max + 1); offset++) { ++ irq_base = irq_create_mapping(ggc->irqdomain, offset); ++ if (offset == 0) ++ ggc->irq_base = irq_base; ++ } ++ ++ return 0; ++} ++ ++static int gb_gpio_to_irq(struct gpio_chip *chip, unsigned offset) ++{ ++ struct gb_gpio_controller *ggc = gpio_chip_to_gb_gpio_controller(chip); ++ ++ return irq_find_mapping(ggc->irqdomain, offset); ++} ++ ++static int gb_gpio_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct gb_gpio_controller *ggc; ++ struct gpio_chip *gpio; ++ struct irq_chip *irqc; ++ int ret; ++ ++ ggc = kzalloc(sizeof(*ggc), GFP_KERNEL); ++ if (!ggc) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ gb_gpio_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto exit_ggc_free; ++ } ++ ++ ggc->connection = connection; ++ gb_connection_set_data(connection, ggc); ++ ggc->gbphy_dev = gbphy_dev; ++ gb_gbphy_set_data(gbphy_dev, ggc); ++ ++ ret = gb_connection_enable_tx(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ ret = gb_gpio_controller_setup(ggc); ++ if (ret) ++ goto exit_connection_disable; ++ ++ irqc = &ggc->irqc; ++ irqc->irq_mask = gb_gpio_irq_mask; ++ irqc->irq_unmask = gb_gpio_irq_unmask; ++ irqc->irq_set_type = gb_gpio_irq_set_type; ++ irqc->irq_bus_lock = gb_gpio_irq_bus_lock; ++ irqc->irq_bus_sync_unlock = gb_gpio_irq_bus_sync_unlock; ++ irqc->name = "greybus_gpio"; ++ ++ mutex_init(&ggc->irq_lock); ++ ++ gpio = &ggc->chip; ++ ++ gpio->label = "greybus_gpio"; ++ gpio->parent = &gbphy_dev->dev; ++ gpio->owner = THIS_MODULE; ++ ++ gpio->request = gb_gpio_request; ++ gpio->free = gb_gpio_free; ++ gpio->get_direction = gb_gpio_get_direction; ++ gpio->direction_input = gb_gpio_direction_input; ++ gpio->direction_output = gb_gpio_direction_output; ++ gpio->get = gb_gpio_get; ++ gpio->set = gb_gpio_set; ++ gpio->set_debounce = gb_gpio_set_debounce; ++ gpio->to_irq = gb_gpio_to_irq; ++ gpio->base = -1; /* Allocate base dynamically */ ++ gpio->ngpio = ggc->line_max + 1; ++ gpio->can_sleep = true; ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_line_free; ++ ++ ret = gpiochip_add(gpio); ++ if (ret) { ++ dev_err(&connection->bundle->dev, ++ "failed to add gpio chip: %d\n", ret); ++ goto exit_line_free; ++ } ++ ++ ret = gb_gpio_irqchip_add(gpio, irqc, 0, ++ handle_level_irq, IRQ_TYPE_NONE); ++ if (ret) { ++ dev_err(&connection->bundle->dev, ++ "failed to add irq chip: %d\n", ret); ++ goto exit_gpiochip_remove; ++ } ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_gpiochip_remove: ++ gpiochip_remove(gpio); ++exit_line_free: ++ kfree(ggc->lines); ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_ggc_free: ++ kfree(ggc); ++ return ret; ++} ++ ++static void gb_gpio_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_gpio_controller *ggc = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = ggc->connection; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ gb_connection_disable_rx(connection); ++ gb_gpio_irqchip_remove(ggc); ++ gpiochip_remove(&ggc->chip); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ kfree(ggc->lines); ++ kfree(ggc); ++} ++ ++static const struct gbphy_device_id gb_gpio_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_GPIO) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_gpio_id_table); ++ ++static struct gbphy_driver gpio_driver = { ++ .name = "gpio", ++ .probe = gb_gpio_probe, ++ .remove = gb_gpio_remove, ++ .id_table = gb_gpio_id_table, ++}; ++ ++module_gbphy_driver(gpio_driver); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_hd.patch b/greybus_hd.patch new file mode 100644 index 00000000000000..2f5d5629cc5878 --- /dev/null +++ b/greybus_hd.patch @@ -0,0 +1,358 @@ +--- + drivers/greybus/hd.c | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/hd.h | 90 +++++++++++++++++ + 2 files changed, 347 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/hd.c +@@ -0,0 +1,257 @@ ++/* ++ * Greybus Host Device ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_create); ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_release); ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_add); ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_del); ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_hd_in); ++EXPORT_TRACEPOINT_SYMBOL_GPL(gb_message_submit); ++ ++static struct ida gb_hd_bus_id_map; ++ ++int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, ++ bool async) ++{ ++ if (!hd || !hd->driver || !hd->driver->output) ++ return -EINVAL; ++ return hd->driver->output(hd, req, size, cmd, async); ++} ++EXPORT_SYMBOL_GPL(gb_hd_output); ++ ++static ssize_t bus_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_host_device *hd = to_gb_host_device(dev); ++ ++ return sprintf(buf, "%d\n", hd->bus_id); ++} ++static DEVICE_ATTR_RO(bus_id); ++ ++static struct attribute *bus_attrs[] = { ++ &dev_attr_bus_id.attr, ++ NULL ++}; ++ATTRIBUTE_GROUPS(bus); ++ ++int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct ida *id_map = &hd->cport_id_map; ++ int ret; ++ ++ ret = ida_simple_get(id_map, cport_id, cport_id + 1, GFP_KERNEL); ++ if (ret < 0) { ++ dev_err(&hd->dev, "failed to reserve cport %u\n", cport_id); ++ return ret; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_hd_cport_reserve); ++ ++void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id) ++{ ++ struct ida *id_map = &hd->cport_id_map; ++ ++ ida_simple_remove(id_map, cport_id); ++} ++EXPORT_SYMBOL_GPL(gb_hd_cport_release_reserved); ++ ++/* Locking: Caller guarantees serialisation */ ++int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, ++ unsigned long flags) ++{ ++ struct ida *id_map = &hd->cport_id_map; ++ int ida_start, ida_end; ++ ++ if (hd->driver->cport_allocate) ++ return hd->driver->cport_allocate(hd, cport_id, flags); ++ ++ if (cport_id < 0) { ++ ida_start = 0; ++ ida_end = hd->num_cports; ++ } else if (cport_id < hd->num_cports) { ++ ida_start = cport_id; ++ ida_end = cport_id + 1; ++ } else { ++ dev_err(&hd->dev, "cport %d not available\n", cport_id); ++ return -EINVAL; ++ } ++ ++ return ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL); ++} ++ ++/* Locking: Caller guarantees serialisation */ ++void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id) ++{ ++ if (hd->driver->cport_release) { ++ hd->driver->cport_release(hd, cport_id); ++ return; ++ } ++ ++ ida_simple_remove(&hd->cport_id_map, cport_id); ++} ++ ++static void gb_hd_release(struct device *dev) ++{ ++ struct gb_host_device *hd = to_gb_host_device(dev); ++ ++ trace_gb_hd_release(hd); ++ ++ if (hd->svc) ++ gb_svc_put(hd->svc); ++ ida_simple_remove(&gb_hd_bus_id_map, hd->bus_id); ++ ida_destroy(&hd->cport_id_map); ++ kfree(hd); ++} ++ ++struct device_type greybus_hd_type = { ++ .name = "greybus_host_device", ++ .release = gb_hd_release, ++}; ++ ++struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, ++ struct device *parent, ++ size_t buffer_size_max, ++ size_t num_cports) ++{ ++ struct gb_host_device *hd; ++ int ret; ++ ++ /* ++ * Validate that the driver implements all of the callbacks ++ * so that we don't have to every time we make them. ++ */ ++ if ((!driver->message_send) || (!driver->message_cancel)) { ++ dev_err(parent, "mandatory hd-callbacks missing\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) { ++ dev_err(parent, "greybus host-device buffers too small\n"); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ if (num_cports == 0 || num_cports > CPORT_ID_MAX + 1) { ++ dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports); ++ return ERR_PTR(-EINVAL); ++ } ++ ++ /* ++ * Make sure to never allocate messages larger than what the Greybus ++ * protocol supports. ++ */ ++ if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) { ++ dev_warn(parent, "limiting buffer size to %u\n", ++ GB_OPERATION_MESSAGE_SIZE_MAX); ++ buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX; ++ } ++ ++ hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL); ++ if (!hd) ++ return ERR_PTR(-ENOMEM); ++ ++ ret = ida_simple_get(&gb_hd_bus_id_map, 1, 0, GFP_KERNEL); ++ if (ret < 0) { ++ kfree(hd); ++ return ERR_PTR(ret); ++ } ++ hd->bus_id = ret; ++ ++ hd->driver = driver; ++ INIT_LIST_HEAD(&hd->modules); ++ INIT_LIST_HEAD(&hd->connections); ++ ida_init(&hd->cport_id_map); ++ hd->buffer_size_max = buffer_size_max; ++ hd->num_cports = num_cports; ++ ++ hd->dev.parent = parent; ++ hd->dev.bus = &greybus_bus_type; ++ hd->dev.type = &greybus_hd_type; ++ hd->dev.groups = bus_groups; ++ hd->dev.dma_mask = hd->dev.parent->dma_mask; ++ device_initialize(&hd->dev); ++ dev_set_name(&hd->dev, "greybus%d", hd->bus_id); ++ ++ trace_gb_hd_create(hd); ++ ++ hd->svc = gb_svc_create(hd); ++ if (!hd->svc) { ++ dev_err(&hd->dev, "failed to create svc\n"); ++ put_device(&hd->dev); ++ return ERR_PTR(-ENOMEM); ++ } ++ ++ return hd; ++} ++EXPORT_SYMBOL_GPL(gb_hd_create); ++ ++int gb_hd_add(struct gb_host_device *hd) ++{ ++ int ret; ++ ++ ret = device_add(&hd->dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_svc_add(hd->svc); ++ if (ret) { ++ device_del(&hd->dev); ++ return ret; ++ } ++ ++ trace_gb_hd_add(hd); ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_hd_add); ++ ++void gb_hd_del(struct gb_host_device *hd) ++{ ++ trace_gb_hd_del(hd); ++ ++ /* ++ * Tear down the svc and flush any on-going hotplug processing before ++ * removing the remaining interfaces. ++ */ ++ gb_svc_del(hd->svc); ++ ++ device_del(&hd->dev); ++} ++EXPORT_SYMBOL_GPL(gb_hd_del); ++ ++void gb_hd_shutdown(struct gb_host_device *hd) ++{ ++ gb_svc_del(hd->svc); ++} ++EXPORT_SYMBOL_GPL(gb_hd_shutdown); ++ ++void gb_hd_put(struct gb_host_device *hd) ++{ ++ put_device(&hd->dev); ++} ++EXPORT_SYMBOL_GPL(gb_hd_put); ++ ++int __init gb_hd_init(void) ++{ ++ ida_init(&gb_hd_bus_id_map); ++ ++ return 0; ++} ++ ++void gb_hd_exit(void) ++{ ++ ida_destroy(&gb_hd_bus_id_map); ++} +--- /dev/null ++++ b/drivers/greybus/hd.h +@@ -0,0 +1,90 @@ ++/* ++ * Greybus Host Device ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __HD_H ++#define __HD_H ++ ++struct gb_host_device; ++struct gb_message; ++ ++struct gb_hd_driver { ++ size_t hd_priv_size; ++ ++ int (*cport_allocate)(struct gb_host_device *hd, int cport_id, ++ unsigned long flags); ++ void (*cport_release)(struct gb_host_device *hd, u16 cport_id); ++ int (*cport_enable)(struct gb_host_device *hd, u16 cport_id, ++ unsigned long flags); ++ int (*cport_disable)(struct gb_host_device *hd, u16 cport_id); ++ int (*cport_connected)(struct gb_host_device *hd, u16 cport_id); ++ int (*cport_flush)(struct gb_host_device *hd, u16 cport_id); ++ int (*cport_shutdown)(struct gb_host_device *hd, u16 cport_id, ++ u8 phase, unsigned int timeout); ++ int (*cport_quiesce)(struct gb_host_device *hd, u16 cport_id, ++ size_t peer_space, unsigned int timeout); ++ int (*cport_clear)(struct gb_host_device *hd, u16 cport_id); ++ ++ int (*message_send)(struct gb_host_device *hd, u16 dest_cport_id, ++ struct gb_message *message, gfp_t gfp_mask); ++ void (*message_cancel)(struct gb_message *message); ++ int (*latency_tag_enable)(struct gb_host_device *hd, u16 cport_id); ++ int (*latency_tag_disable)(struct gb_host_device *hd, u16 cport_id); ++ int (*output)(struct gb_host_device *hd, void *req, u16 size, u8 cmd, ++ bool async); ++ int (*timesync_enable)(struct gb_host_device *hd, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk); ++ int (*timesync_disable)(struct gb_host_device *hd); ++ int (*timesync_authoritative)(struct gb_host_device *hd, ++ u64 *frame_time); ++ int (*timesync_get_last_event)(struct gb_host_device *hd, ++ u64 *frame_time); ++}; ++ ++struct gb_host_device { ++ struct device dev; ++ int bus_id; ++ const struct gb_hd_driver *driver; ++ ++ struct list_head modules; ++ struct list_head connections; ++ struct ida cport_id_map; ++ ++ /* Number of CPorts supported by the UniPro IP */ ++ size_t num_cports; ++ ++ /* Host device buffer constraints */ ++ size_t buffer_size_max; ++ ++ struct gb_svc *svc; ++ /* Private data for the host driver */ ++ unsigned long hd_priv[0] __aligned(sizeof(s64)); ++}; ++#define to_gb_host_device(d) container_of(d, struct gb_host_device, dev) ++ ++int gb_hd_cport_reserve(struct gb_host_device *hd, u16 cport_id); ++void gb_hd_cport_release_reserved(struct gb_host_device *hd, u16 cport_id); ++int gb_hd_cport_allocate(struct gb_host_device *hd, int cport_id, ++ unsigned long flags); ++void gb_hd_cport_release(struct gb_host_device *hd, u16 cport_id); ++ ++struct gb_host_device *gb_hd_create(struct gb_hd_driver *driver, ++ struct device *parent, ++ size_t buffer_size_max, ++ size_t num_cports); ++int gb_hd_add(struct gb_host_device *hd); ++void gb_hd_del(struct gb_host_device *hd); ++void gb_hd_shutdown(struct gb_host_device *hd); ++void gb_hd_put(struct gb_host_device *hd); ++int gb_hd_output(struct gb_host_device *hd, void *req, u16 size, u8 cmd, ++ bool in_irq); ++ ++int gb_hd_init(void); ++void gb_hd_exit(void); ++ ++#endif /* __HD_H */ diff --git a/greybus_hid.patch b/greybus_hid.patch new file mode 100644 index 00000000000000..c449705c314f0e --- /dev/null +++ b/greybus_hid.patch @@ -0,0 +1,543 @@ +--- + drivers/greybus/hid.c | 536 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 536 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/hid.c +@@ -0,0 +1,536 @@ ++/* ++ * HID class driver for the Greybus. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/hid.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/slab.h> ++ ++#include "greybus.h" ++ ++/* Greybus HID device's structure */ ++struct gb_hid { ++ struct gb_bundle *bundle; ++ struct gb_connection *connection; ++ ++ struct hid_device *hid; ++ struct gb_hid_desc_response hdesc; ++ ++ unsigned long flags; ++#define GB_HID_STARTED 0x01 ++#define GB_HID_READ_PENDING 0x04 ++ ++ unsigned int bufsize; ++ char *inbuf; ++}; ++ ++static DEFINE_MUTEX(gb_hid_open_mutex); ++ ++/* Routines to get controller's information over greybus */ ++ ++/* Operations performed on greybus */ ++static int gb_hid_get_desc(struct gb_hid *ghid) ++{ ++ return gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_DESC, NULL, ++ 0, &ghid->hdesc, sizeof(ghid->hdesc)); ++} ++ ++static int gb_hid_get_report_desc(struct gb_hid *ghid, char *rdesc) ++{ ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(ghid->bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT_DESC, ++ NULL, 0, rdesc, ++ le16_to_cpu(ghid->hdesc.wReportDescLength)); ++ ++ gb_pm_runtime_put_autosuspend(ghid->bundle); ++ ++ return ret; ++} ++ ++static int gb_hid_set_power(struct gb_hid *ghid, int type) ++{ ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(ghid->bundle); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(ghid->connection, type, NULL, 0, NULL, 0); ++ ++ gb_pm_runtime_put_autosuspend(ghid->bundle); ++ ++ return ret; ++} ++ ++static int gb_hid_get_report(struct gb_hid *ghid, u8 report_type, u8 report_id, ++ unsigned char *buf, int len) ++{ ++ struct gb_hid_get_report_request request; ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(ghid->bundle); ++ if (ret) ++ return ret; ++ ++ request.report_type = report_type; ++ request.report_id = report_id; ++ ++ ret = gb_operation_sync(ghid->connection, GB_HID_TYPE_GET_REPORT, ++ &request, sizeof(request), buf, len); ++ ++ gb_pm_runtime_put_autosuspend(ghid->bundle); ++ ++ return ret; ++} ++ ++static int gb_hid_set_report(struct gb_hid *ghid, u8 report_type, u8 report_id, ++ unsigned char *buf, int len) ++{ ++ struct gb_hid_set_report_request *request; ++ struct gb_operation *operation; ++ int ret, size = sizeof(*request) + len - 1; ++ ++ ret = gb_pm_runtime_get_sync(ghid->bundle); ++ if (ret) ++ return ret; ++ ++ operation = gb_operation_create(ghid->connection, ++ GB_HID_TYPE_SET_REPORT, size, 0, ++ GFP_KERNEL); ++ if (!operation) { ++ gb_pm_runtime_put_autosuspend(ghid->bundle); ++ return -ENOMEM; ++ } ++ ++ request = operation->request->payload; ++ request->report_type = report_type; ++ request->report_id = report_id; ++ memcpy(request->report, buf, len); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) { ++ dev_err(&operation->connection->bundle->dev, ++ "failed to set report: %d\n", ret); ++ } else { ++ ret = len; ++ } ++ ++ gb_operation_put(operation); ++ gb_pm_runtime_put_autosuspend(ghid->bundle); ++ ++ return ret; ++} ++ ++static int gb_hid_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_hid *ghid = gb_connection_get_data(connection); ++ struct gb_hid_input_report_request *request = op->request->payload; ++ ++ if (op->type != GB_HID_TYPE_IRQ_EVENT) { ++ dev_err(&connection->bundle->dev, ++ "unsupported unsolicited request\n"); ++ return -EINVAL; ++ } ++ ++ if (test_bit(GB_HID_STARTED, &ghid->flags)) ++ hid_input_report(ghid->hid, HID_INPUT_REPORT, ++ request->report, op->request->payload_size, 1); ++ ++ return 0; ++} ++ ++static int gb_hid_report_len(struct hid_report *report) ++{ ++ return ((report->size - 1) >> 3) + 1 + ++ report->device->report_enum[report->type].numbered; ++} ++ ++static void gb_hid_find_max_report(struct hid_device *hid, unsigned int type, ++ unsigned int *max) ++{ ++ struct hid_report *report; ++ unsigned int size; ++ ++ list_for_each_entry(report, &hid->report_enum[type].report_list, list) { ++ size = gb_hid_report_len(report); ++ if (*max < size) ++ *max = size; ++ } ++} ++ ++static void gb_hid_free_buffers(struct gb_hid *ghid) ++{ ++ kfree(ghid->inbuf); ++ ghid->inbuf = NULL; ++ ghid->bufsize = 0; ++} ++ ++static int gb_hid_alloc_buffers(struct gb_hid *ghid, size_t bufsize) ++{ ++ ghid->inbuf = kzalloc(bufsize, GFP_KERNEL); ++ if (!ghid->inbuf) ++ return -ENOMEM; ++ ++ ghid->bufsize = bufsize; ++ ++ return 0; ++} ++ ++/* Routines dealing with reports */ ++static void gb_hid_init_report(struct gb_hid *ghid, struct hid_report *report) ++{ ++ unsigned int size; ++ ++ size = gb_hid_report_len(report); ++ if (gb_hid_get_report(ghid, report->type, report->id, ghid->inbuf, ++ size)) ++ return; ++ ++ /* ++ * hid->driver_lock is held as we are in probe function, ++ * we just need to setup the input fields, so using ++ * hid_report_raw_event is safe. ++ */ ++ hid_report_raw_event(ghid->hid, report->type, ghid->inbuf, size, 1); ++} ++ ++static void gb_hid_init_reports(struct gb_hid *ghid) ++{ ++ struct hid_device *hid = ghid->hid; ++ struct hid_report *report; ++ ++ list_for_each_entry(report, ++ &hid->report_enum[HID_INPUT_REPORT].report_list, list) ++ gb_hid_init_report(ghid, report); ++ ++ list_for_each_entry(report, ++ &hid->report_enum[HID_FEATURE_REPORT].report_list, list) ++ gb_hid_init_report(ghid, report); ++} ++ ++static int __gb_hid_get_raw_report(struct hid_device *hid, ++ unsigned char report_number, __u8 *buf, size_t count, ++ unsigned char report_type) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ int ret; ++ ++ if (report_type == HID_OUTPUT_REPORT) ++ return -EINVAL; ++ ++ ret = gb_hid_get_report(ghid, report_type, report_number, buf, count); ++ if (!ret) ++ ret = count; ++ ++ return ret; ++} ++ ++static int __gb_hid_output_raw_report(struct hid_device *hid, __u8 *buf, ++ size_t len, unsigned char report_type) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ int report_id = buf[0]; ++ int ret; ++ ++ if (report_type == HID_INPUT_REPORT) ++ return -EINVAL; ++ ++ if (report_id) { ++ buf++; ++ len--; ++ } ++ ++ ret = gb_hid_set_report(ghid, report_type, report_id, buf, len); ++ if (report_id && ret >= 0) ++ ret++; /* add report_id to the number of transfered bytes */ ++ ++ return 0; ++} ++ ++static int gb_hid_raw_request(struct hid_device *hid, unsigned char reportnum, ++ __u8 *buf, size_t len, unsigned char rtype, ++ int reqtype) ++{ ++ switch (reqtype) { ++ case HID_REQ_GET_REPORT: ++ return __gb_hid_get_raw_report(hid, reportnum, buf, len, rtype); ++ case HID_REQ_SET_REPORT: ++ if (buf[0] != reportnum) ++ return -EINVAL; ++ return __gb_hid_output_raw_report(hid, buf, len, rtype); ++ default: ++ return -EIO; ++ } ++} ++ ++/* HID Callbacks */ ++static int gb_hid_parse(struct hid_device *hid) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ unsigned int rsize; ++ char *rdesc; ++ int ret; ++ ++ rsize = le16_to_cpu(ghid->hdesc.wReportDescLength); ++ if (!rsize || rsize > HID_MAX_DESCRIPTOR_SIZE) { ++ dbg_hid("weird size of report descriptor (%u)\n", rsize); ++ return -EINVAL; ++ } ++ ++ rdesc = kzalloc(rsize, GFP_KERNEL); ++ if (!rdesc) { ++ dbg_hid("couldn't allocate rdesc memory\n"); ++ return -ENOMEM; ++ } ++ ++ ret = gb_hid_get_report_desc(ghid, rdesc); ++ if (ret) { ++ hid_err(hid, "reading report descriptor failed\n"); ++ goto free_rdesc; ++ } ++ ++ ret = hid_parse_report(hid, rdesc, rsize); ++ if (ret) ++ dbg_hid("parsing report descriptor failed\n"); ++ ++free_rdesc: ++ kfree(rdesc); ++ ++ return ret; ++} ++ ++static int gb_hid_start(struct hid_device *hid) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ unsigned int bufsize = HID_MIN_BUFFER_SIZE; ++ int ret; ++ ++ gb_hid_find_max_report(hid, HID_INPUT_REPORT, &bufsize); ++ gb_hid_find_max_report(hid, HID_OUTPUT_REPORT, &bufsize); ++ gb_hid_find_max_report(hid, HID_FEATURE_REPORT, &bufsize); ++ ++ if (bufsize > HID_MAX_BUFFER_SIZE) ++ bufsize = HID_MAX_BUFFER_SIZE; ++ ++ ret = gb_hid_alloc_buffers(ghid, bufsize); ++ if (ret) ++ return ret; ++ ++ if (!(hid->quirks & HID_QUIRK_NO_INIT_REPORTS)) ++ gb_hid_init_reports(ghid); ++ ++ return 0; ++} ++ ++static void gb_hid_stop(struct hid_device *hid) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ ++ gb_hid_free_buffers(ghid); ++} ++ ++static int gb_hid_open(struct hid_device *hid) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ int ret = 0; ++ ++ mutex_lock(&gb_hid_open_mutex); ++ if (!hid->open++) { ++ ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); ++ if (ret < 0) ++ hid->open--; ++ else ++ set_bit(GB_HID_STARTED, &ghid->flags); ++ } ++ mutex_unlock(&gb_hid_open_mutex); ++ ++ return ret; ++} ++ ++static void gb_hid_close(struct hid_device *hid) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ int ret; ++ ++ /* ++ * Protecting hid->open to make sure we don't restart data acquistion ++ * due to a resumption we no longer care about.. ++ */ ++ mutex_lock(&gb_hid_open_mutex); ++ if (!--hid->open) { ++ clear_bit(GB_HID_STARTED, &ghid->flags); ++ ++ /* Save some power */ ++ ret = gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); ++ if (ret) ++ dev_err(&ghid->connection->bundle->dev, ++ "failed to power off (%d)\n", ret); ++ } ++ mutex_unlock(&gb_hid_open_mutex); ++} ++ ++static int gb_hid_power(struct hid_device *hid, int lvl) ++{ ++ struct gb_hid *ghid = hid->driver_data; ++ ++ switch (lvl) { ++ case PM_HINT_FULLON: ++ return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_ON); ++ case PM_HINT_NORMAL: ++ return gb_hid_set_power(ghid, GB_HID_TYPE_PWR_OFF); ++ } ++ ++ return 0; ++} ++ ++/* HID structure to pass callbacks */ ++static struct hid_ll_driver gb_hid_ll_driver = { ++ .parse = gb_hid_parse, ++ .start = gb_hid_start, ++ .stop = gb_hid_stop, ++ .open = gb_hid_open, ++ .close = gb_hid_close, ++ .power = gb_hid_power, ++ .raw_request = gb_hid_raw_request, ++}; ++ ++static int gb_hid_init(struct gb_hid *ghid) ++{ ++ struct hid_device *hid = ghid->hid; ++ int ret; ++ ++ ret = gb_hid_get_desc(ghid); ++ if (ret) ++ return ret; ++ ++ hid->version = le16_to_cpu(ghid->hdesc.bcdHID); ++ hid->vendor = le16_to_cpu(ghid->hdesc.wVendorID); ++ hid->product = le16_to_cpu(ghid->hdesc.wProductID); ++ hid->country = ghid->hdesc.bCountryCode; ++ ++ hid->driver_data = ghid; ++ hid->ll_driver = &gb_hid_ll_driver; ++ hid->dev.parent = &ghid->connection->bundle->dev; ++// hid->bus = BUS_GREYBUS; /* Need a bustype for GREYBUS in <linux/input.h> */ ++ ++ /* Set HID device's name */ ++ snprintf(hid->name, sizeof(hid->name), "%s %04X:%04X", ++ dev_name(&ghid->connection->bundle->dev), ++ hid->vendor, hid->product); ++ ++ return 0; ++} ++ ++static int gb_hid_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct hid_device *hid; ++ struct gb_hid *ghid; ++ int ret; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_HID) ++ return -ENODEV; ++ ++ ghid = kzalloc(sizeof(*ghid), GFP_KERNEL); ++ if (!ghid) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_hid_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto err_free_ghid; ++ } ++ ++ gb_connection_set_data(connection, ghid); ++ ghid->connection = connection; ++ ++ hid = hid_allocate_device(); ++ if (IS_ERR(hid)) { ++ ret = PTR_ERR(hid); ++ goto err_connection_destroy; ++ } ++ ++ ghid->hid = hid; ++ ghid->bundle = bundle; ++ ++ greybus_set_drvdata(bundle, ghid); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto err_destroy_hid; ++ ++ ret = gb_hid_init(ghid); ++ if (ret) ++ goto err_connection_disable; ++ ++ ret = hid_add_device(hid); ++ if (ret) { ++ hid_err(hid, "can't add hid device: %d\n", ret); ++ goto err_connection_disable; ++ } ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++err_connection_disable: ++ gb_connection_disable(connection); ++err_destroy_hid: ++ hid_destroy_device(hid); ++err_connection_destroy: ++ gb_connection_destroy(connection); ++err_free_ghid: ++ kfree(ghid); ++ ++ return ret; ++} ++ ++static void gb_hid_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_hid *ghid = greybus_get_drvdata(bundle); ++ ++ if (gb_pm_runtime_get_sync(bundle)) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ hid_destroy_device(ghid->hid); ++ gb_connection_disable(ghid->connection); ++ gb_connection_destroy(ghid->connection); ++ kfree(ghid); ++} ++ ++static const struct greybus_bundle_id gb_hid_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_HID) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_hid_id_table); ++ ++static struct greybus_driver gb_hid_driver = { ++ .name = "hid", ++ .probe = gb_hid_probe, ++ .disconnect = gb_hid_disconnect, ++ .id_table = gb_hid_id_table, ++}; ++module_greybus_driver(gb_hid_driver); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_i2c.patch b/greybus_i2c.patch new file mode 100644 index 00000000000000..e739a058e339b5 --- /dev/null +++ b/greybus_i2c.patch @@ -0,0 +1,350 @@ +--- + drivers/greybus/i2c.c | 343 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 343 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/i2c.c +@@ -0,0 +1,343 @@ ++/* ++ * I2C bridge driver for the Greybus "generic" I2C module. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/i2c.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++struct gb_i2c_device { ++ struct gb_connection *connection; ++ struct gbphy_device *gbphy_dev; ++ ++ u32 functionality; ++ ++ struct i2c_adapter adapter; ++}; ++ ++/* ++ * Map Greybus i2c functionality bits into Linux ones ++ */ ++static u32 gb_i2c_functionality_map(u32 gb_i2c_functionality) ++{ ++ return gb_i2c_functionality; /* All bits the same for now */ ++} ++ ++static int gb_i2c_functionality_operation(struct gb_i2c_device *gb_i2c_dev) ++{ ++ struct gb_i2c_functionality_response response; ++ u32 functionality; ++ int ret; ++ ++ ret = gb_operation_sync(gb_i2c_dev->connection, ++ GB_I2C_TYPE_FUNCTIONALITY, ++ NULL, 0, &response, sizeof(response)); ++ if (ret) ++ return ret; ++ ++ functionality = le32_to_cpu(response.functionality); ++ gb_i2c_dev->functionality = gb_i2c_functionality_map(functionality); ++ ++ return 0; ++} ++ ++/* ++ * Map Linux i2c_msg flags into Greybus i2c transfer op flags. ++ */ ++static u16 gb_i2c_transfer_op_flags_map(u16 flags) ++{ ++ return flags; /* All flags the same for now */ ++} ++ ++static void ++gb_i2c_fill_transfer_op(struct gb_i2c_transfer_op *op, struct i2c_msg *msg) ++{ ++ u16 flags = gb_i2c_transfer_op_flags_map(msg->flags); ++ ++ op->addr = cpu_to_le16(msg->addr); ++ op->flags = cpu_to_le16(flags); ++ op->size = cpu_to_le16(msg->len); ++} ++ ++static struct gb_operation * ++gb_i2c_operation_create(struct gb_connection *connection, ++ struct i2c_msg *msgs, u32 msg_count) ++{ ++ struct gb_i2c_device *gb_i2c_dev = gb_connection_get_data(connection); ++ struct gb_i2c_transfer_request *request; ++ struct gb_operation *operation; ++ struct gb_i2c_transfer_op *op; ++ struct i2c_msg *msg; ++ u32 data_out_size = 0; ++ u32 data_in_size = 0; ++ size_t request_size; ++ void *data; ++ u16 op_count; ++ u32 i; ++ ++ if (msg_count > (u32)U16_MAX) { ++ dev_err(&gb_i2c_dev->gbphy_dev->dev, "msg_count (%u) too big\n", ++ msg_count); ++ return NULL; ++ } ++ op_count = (u16)msg_count; ++ ++ /* ++ * In addition to space for all message descriptors we need ++ * to have enough to hold all outbound message data. ++ */ ++ msg = msgs; ++ for (i = 0; i < msg_count; i++, msg++) ++ if (msg->flags & I2C_M_RD) ++ data_in_size += (u32)msg->len; ++ else ++ data_out_size += (u32)msg->len; ++ ++ request_size = sizeof(*request); ++ request_size += msg_count * sizeof(*op); ++ request_size += data_out_size; ++ ++ /* Response consists only of incoming data */ ++ operation = gb_operation_create(connection, GB_I2C_TYPE_TRANSFER, ++ request_size, data_in_size, GFP_KERNEL); ++ if (!operation) ++ return NULL; ++ ++ request = operation->request->payload; ++ request->op_count = cpu_to_le16(op_count); ++ /* Fill in the ops array */ ++ op = &request->ops[0]; ++ msg = msgs; ++ for (i = 0; i < msg_count; i++) ++ gb_i2c_fill_transfer_op(op++, msg++); ++ ++ if (!data_out_size) ++ return operation; ++ ++ /* Copy over the outgoing data; it starts after the last op */ ++ data = op; ++ msg = msgs; ++ for (i = 0; i < msg_count; i++) { ++ if (!(msg->flags & I2C_M_RD)) { ++ memcpy(data, msg->buf, msg->len); ++ data += msg->len; ++ } ++ msg++; ++ } ++ ++ return operation; ++} ++ ++static void gb_i2c_decode_response(struct i2c_msg *msgs, u32 msg_count, ++ struct gb_i2c_transfer_response *response) ++{ ++ struct i2c_msg *msg = msgs; ++ u8 *data; ++ u32 i; ++ ++ if (!response) ++ return; ++ data = response->data; ++ for (i = 0; i < msg_count; i++) { ++ if (msg->flags & I2C_M_RD) { ++ memcpy(msg->buf, data, msg->len); ++ data += msg->len; ++ } ++ msg++; ++ } ++} ++ ++/* ++ * Some i2c transfer operations return results that are expected. ++ */ ++static bool gb_i2c_expected_transfer_error(int errno) ++{ ++ return errno == -EAGAIN || errno == -ENODEV; ++} ++ ++static int gb_i2c_transfer_operation(struct gb_i2c_device *gb_i2c_dev, ++ struct i2c_msg *msgs, u32 msg_count) ++{ ++ struct gb_connection *connection = gb_i2c_dev->connection; ++ struct device *dev = &gb_i2c_dev->gbphy_dev->dev; ++ struct gb_operation *operation; ++ int ret; ++ ++ operation = gb_i2c_operation_create(connection, msgs, msg_count); ++ if (!operation) ++ return -ENOMEM; ++ ++ ret = gbphy_runtime_get_sync(gb_i2c_dev->gbphy_dev); ++ if (ret) ++ goto exit_operation_put; ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (!ret) { ++ struct gb_i2c_transfer_response *response; ++ ++ response = operation->response->payload; ++ gb_i2c_decode_response(msgs, msg_count, response); ++ ret = msg_count; ++ } else if (!gb_i2c_expected_transfer_error(ret)) { ++ dev_err(dev, "transfer operation failed (%d)\n", ret); ++ } ++ ++ gbphy_runtime_put_autosuspend(gb_i2c_dev->gbphy_dev); ++ ++exit_operation_put: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static int gb_i2c_master_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, ++ int msg_count) ++{ ++ struct gb_i2c_device *gb_i2c_dev; ++ ++ gb_i2c_dev = i2c_get_adapdata(adap); ++ ++ return gb_i2c_transfer_operation(gb_i2c_dev, msgs, msg_count); ++} ++ ++#if 0 ++/* Later */ ++static int gb_i2c_smbus_xfer(struct i2c_adapter *adap, ++ u16 addr, unsigned short flags, char read_write, ++ u8 command, int size, union i2c_smbus_data *data) ++{ ++ struct gb_i2c_device *gb_i2c_dev; ++ ++ gb_i2c_dev = i2c_get_adapdata(adap); ++ ++ return 0; ++} ++#endif ++ ++static u32 gb_i2c_functionality(struct i2c_adapter *adap) ++{ ++ struct gb_i2c_device *gb_i2c_dev = i2c_get_adapdata(adap); ++ ++ return gb_i2c_dev->functionality; ++} ++ ++static const struct i2c_algorithm gb_i2c_algorithm = { ++ .master_xfer = gb_i2c_master_xfer, ++ /* .smbus_xfer = gb_i2c_smbus_xfer, */ ++ .functionality = gb_i2c_functionality, ++}; ++ ++/* ++ * Do initial setup of the i2c device. This includes verifying we ++ * can support it (based on the protocol version it advertises). ++ * If that's OK, we get and cached its functionality bits. ++ * ++ * Note: gb_i2c_dev->connection is assumed to have been valid. ++ */ ++static int gb_i2c_device_setup(struct gb_i2c_device *gb_i2c_dev) ++{ ++ /* Assume the functionality never changes, just get it once */ ++ return gb_i2c_functionality_operation(gb_i2c_dev); ++} ++ ++static int gb_i2c_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct gb_i2c_device *gb_i2c_dev; ++ struct i2c_adapter *adapter; ++ int ret; ++ ++ gb_i2c_dev = kzalloc(sizeof(*gb_i2c_dev), GFP_KERNEL); ++ if (!gb_i2c_dev) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto exit_i2cdev_free; ++ } ++ ++ gb_i2c_dev->connection = connection; ++ gb_connection_set_data(connection, gb_i2c_dev); ++ gb_i2c_dev->gbphy_dev = gbphy_dev; ++ gb_gbphy_set_data(gbphy_dev, gb_i2c_dev); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ ret = gb_i2c_device_setup(gb_i2c_dev); ++ if (ret) ++ goto exit_connection_disable; ++ ++ /* Looks good; up our i2c adapter */ ++ adapter = &gb_i2c_dev->adapter; ++ adapter->owner = THIS_MODULE; ++ adapter->class = I2C_CLASS_HWMON | I2C_CLASS_SPD; ++ adapter->algo = &gb_i2c_algorithm; ++ /* adapter->algo_data = what? */ ++ ++ adapter->dev.parent = &gbphy_dev->dev; ++ snprintf(adapter->name, sizeof(adapter->name), "Greybus i2c adapter"); ++ i2c_set_adapdata(adapter, gb_i2c_dev); ++ ++ ret = i2c_add_adapter(adapter); ++ if (ret) ++ goto exit_connection_disable; ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_i2cdev_free: ++ kfree(gb_i2c_dev); ++ ++ return ret; ++} ++ ++static void gb_i2c_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_i2c_device *gb_i2c_dev = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = gb_i2c_dev->connection; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ i2c_del_adapter(&gb_i2c_dev->adapter); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ kfree(gb_i2c_dev); ++} ++ ++static const struct gbphy_device_id gb_i2c_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_I2C) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_i2c_id_table); ++ ++static struct gbphy_driver i2c_driver = { ++ .name = "i2c", ++ .probe = gb_i2c_probe, ++ .remove = gb_i2c_remove, ++ .id_table = gb_i2c_id_table, ++}; ++ ++module_gbphy_driver(i2c_driver); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_interface.patch b/greybus_interface.patch new file mode 100644 index 00000000000000..4285ce9f26969f --- /dev/null +++ b/greybus_interface.patch @@ -0,0 +1,1415 @@ +--- + drivers/greybus/interface.c | 1316 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/interface.h | 88 ++ + 2 files changed, 1404 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/interface.c +@@ -0,0 +1,1316 @@ ++/* ++ * Greybus interface code ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/delay.h> ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++#define GB_INTERFACE_MODE_SWITCH_TIMEOUT 2000 ++ ++#define GB_INTERFACE_DEVICE_ID_BAD 0xff ++ ++#define GB_INTERFACE_AUTOSUSPEND_MS 3000 ++ ++/* Time required for interface to enter standby before disabling REFCLK */ ++#define GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS 20 ++ ++/* Don't-care selector index */ ++#define DME_SELECTOR_INDEX_NULL 0 ++ ++/* DME attributes */ ++/* FIXME: remove ES2 support and DME_T_TST_SRC_INCREMENT */ ++#define DME_T_TST_SRC_INCREMENT 0x4083 ++ ++#define DME_DDBL1_MANUFACTURERID 0x5003 ++#define DME_DDBL1_PRODUCTID 0x5004 ++ ++#define DME_TOSHIBA_GMP_VID 0x6000 ++#define DME_TOSHIBA_GMP_PID 0x6001 ++#define DME_TOSHIBA_GMP_SN0 0x6002 ++#define DME_TOSHIBA_GMP_SN1 0x6003 ++#define DME_TOSHIBA_GMP_INIT_STATUS 0x6101 ++ ++/* DDBL1 Manufacturer and Product ids */ ++#define TOSHIBA_DMID 0x0126 ++#define TOSHIBA_ES2_BRIDGE_DPID 0x1000 ++#define TOSHIBA_ES3_APBRIDGE_DPID 0x1001 ++#define TOSHIBA_ES3_GBPHY_DPID 0x1002 ++ ++static int gb_interface_hibernate_link(struct gb_interface *intf); ++static int gb_interface_refclk_set(struct gb_interface *intf, bool enable); ++ ++static int gb_interface_dme_attr_get(struct gb_interface *intf, ++ u16 attr, u32 *val) ++{ ++ return gb_svc_dme_peer_get(intf->hd->svc, intf->interface_id, ++ attr, DME_SELECTOR_INDEX_NULL, val); ++} ++ ++static int gb_interface_read_ara_dme(struct gb_interface *intf) ++{ ++ u32 sn0, sn1; ++ int ret; ++ ++ /* ++ * Unless this is a Toshiba bridge, bail out until we have defined ++ * standard GMP attributes. ++ */ ++ if (intf->ddbl1_manufacturer_id != TOSHIBA_DMID) { ++ dev_err(&intf->dev, "unknown manufacturer %08x\n", ++ intf->ddbl1_manufacturer_id); ++ return -ENODEV; ++ } ++ ++ ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_VID, ++ &intf->vendor_id); ++ if (ret) ++ return ret; ++ ++ ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_PID, ++ &intf->product_id); ++ if (ret) ++ return ret; ++ ++ ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN0, &sn0); ++ if (ret) ++ return ret; ++ ++ ret = gb_interface_dme_attr_get(intf, DME_TOSHIBA_GMP_SN1, &sn1); ++ if (ret) ++ return ret; ++ ++ intf->serial_number = (u64)sn1 << 32 | sn0; ++ ++ return 0; ++} ++ ++static int gb_interface_read_dme(struct gb_interface *intf) ++{ ++ int ret; ++ ++ /* DME attributes have already been read */ ++ if (intf->dme_read) ++ return 0; ++ ++ ret = gb_interface_dme_attr_get(intf, DME_DDBL1_MANUFACTURERID, ++ &intf->ddbl1_manufacturer_id); ++ if (ret) ++ return ret; ++ ++ ret = gb_interface_dme_attr_get(intf, DME_DDBL1_PRODUCTID, ++ &intf->ddbl1_product_id); ++ if (ret) ++ return ret; ++ ++ if (intf->ddbl1_manufacturer_id == TOSHIBA_DMID && ++ intf->ddbl1_product_id == TOSHIBA_ES2_BRIDGE_DPID) { ++ intf->quirks |= GB_INTERFACE_QUIRK_NO_GMP_IDS; ++ intf->quirks |= GB_INTERFACE_QUIRK_NO_INIT_STATUS; ++ } ++ ++ ret = gb_interface_read_ara_dme(intf); ++ if (ret) ++ return ret; ++ ++ intf->dme_read = true; ++ ++ return 0; ++} ++ ++static int gb_interface_route_create(struct gb_interface *intf) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ u8 intf_id = intf->interface_id; ++ u8 device_id; ++ int ret; ++ ++ /* Allocate an interface device id. */ ++ ret = ida_simple_get(&svc->device_id_map, ++ GB_SVC_DEVICE_ID_MIN, GB_SVC_DEVICE_ID_MAX + 1, ++ GFP_KERNEL); ++ if (ret < 0) { ++ dev_err(&intf->dev, "failed to allocate device id: %d\n", ret); ++ return ret; ++ } ++ device_id = ret; ++ ++ ret = gb_svc_intf_device_id(svc, intf_id, device_id); ++ if (ret) { ++ dev_err(&intf->dev, "failed to set device id %u: %d\n", ++ device_id, ret); ++ goto err_ida_remove; ++ } ++ ++ /* FIXME: Hard-coded AP device id. */ ++ ret = gb_svc_route_create(svc, svc->ap_intf_id, GB_SVC_DEVICE_ID_AP, ++ intf_id, device_id); ++ if (ret) { ++ dev_err(&intf->dev, "failed to create route: %d\n", ret); ++ goto err_svc_id_free; ++ } ++ ++ intf->device_id = device_id; ++ ++ return 0; ++ ++err_svc_id_free: ++ /* ++ * XXX Should we tell SVC that this id doesn't belong to interface ++ * XXX anymore. ++ */ ++err_ida_remove: ++ ida_simple_remove(&svc->device_id_map, device_id); ++ ++ return ret; ++} ++ ++static void gb_interface_route_destroy(struct gb_interface *intf) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ ++ if (intf->device_id == GB_INTERFACE_DEVICE_ID_BAD) ++ return; ++ ++ gb_svc_route_destroy(svc, svc->ap_intf_id, intf->interface_id); ++ ida_simple_remove(&svc->device_id_map, intf->device_id); ++ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; ++} ++ ++/* Locking: Caller holds the interface mutex. */ ++static int gb_interface_legacy_mode_switch(struct gb_interface *intf) ++{ ++ int ret; ++ ++ dev_info(&intf->dev, "legacy mode switch detected\n"); ++ ++ /* Mark as disconnected to prevent I/O during disable. */ ++ intf->disconnected = true; ++ gb_interface_disable(intf); ++ intf->disconnected = false; ++ ++ ret = gb_interface_enable(intf); ++ if (ret) { ++ dev_err(&intf->dev, "failed to re-enable interface: %d\n", ret); ++ gb_interface_deactivate(intf); ++ } ++ ++ return ret; ++} ++ ++void gb_interface_mailbox_event(struct gb_interface *intf, u16 result, ++ u32 mailbox) ++{ ++ mutex_lock(&intf->mutex); ++ ++ if (result) { ++ dev_warn(&intf->dev, ++ "mailbox event with UniPro error: 0x%04x\n", ++ result); ++ goto err_disable; ++ } ++ ++ if (mailbox != GB_SVC_INTF_MAILBOX_GREYBUS) { ++ dev_warn(&intf->dev, ++ "mailbox event with unexpected value: 0x%08x\n", ++ mailbox); ++ goto err_disable; ++ } ++ ++ if (intf->quirks & GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH) { ++ gb_interface_legacy_mode_switch(intf); ++ goto out_unlock; ++ } ++ ++ if (!intf->mode_switch) { ++ dev_warn(&intf->dev, "unexpected mailbox event: 0x%08x\n", ++ mailbox); ++ goto err_disable; ++ } ++ ++ dev_info(&intf->dev, "mode switch detected\n"); ++ ++ complete(&intf->mode_switch_completion); ++ ++out_unlock: ++ mutex_unlock(&intf->mutex); ++ ++ return; ++ ++err_disable: ++ gb_interface_disable(intf); ++ gb_interface_deactivate(intf); ++ mutex_unlock(&intf->mutex); ++} ++ ++static void gb_interface_mode_switch_work(struct work_struct *work) ++{ ++ struct gb_interface *intf; ++ struct gb_control *control; ++ unsigned long timeout; ++ int ret; ++ ++ intf = container_of(work, struct gb_interface, mode_switch_work); ++ ++ mutex_lock(&intf->mutex); ++ /* Make sure interface is still enabled. */ ++ if (!intf->enabled) { ++ dev_dbg(&intf->dev, "mode switch aborted\n"); ++ intf->mode_switch = false; ++ mutex_unlock(&intf->mutex); ++ goto out_interface_put; ++ } ++ ++ /* ++ * Prepare the control device for mode switch and make sure to get an ++ * extra reference before it goes away during interface disable. ++ */ ++ control = gb_control_get(intf->control); ++ gb_control_mode_switch_prepare(control); ++ gb_interface_disable(intf); ++ mutex_unlock(&intf->mutex); ++ ++ timeout = msecs_to_jiffies(GB_INTERFACE_MODE_SWITCH_TIMEOUT); ++ ret = wait_for_completion_interruptible_timeout( ++ &intf->mode_switch_completion, timeout); ++ ++ /* Finalise control-connection mode switch. */ ++ gb_control_mode_switch_complete(control); ++ gb_control_put(control); ++ ++ if (ret < 0) { ++ dev_err(&intf->dev, "mode switch interrupted\n"); ++ goto err_deactivate; ++ } else if (ret == 0) { ++ dev_err(&intf->dev, "mode switch timed out\n"); ++ goto err_deactivate; ++ } ++ ++ /* Re-enable (re-enumerate) interface if still active. */ ++ mutex_lock(&intf->mutex); ++ intf->mode_switch = false; ++ if (intf->active) { ++ ret = gb_interface_enable(intf); ++ if (ret) { ++ dev_err(&intf->dev, "failed to re-enable interface: %d\n", ++ ret); ++ gb_interface_deactivate(intf); ++ } ++ } ++ mutex_unlock(&intf->mutex); ++ ++out_interface_put: ++ gb_interface_put(intf); ++ ++ return; ++ ++err_deactivate: ++ mutex_lock(&intf->mutex); ++ intf->mode_switch = false; ++ gb_interface_deactivate(intf); ++ mutex_unlock(&intf->mutex); ++ ++ gb_interface_put(intf); ++} ++ ++int gb_interface_request_mode_switch(struct gb_interface *intf) ++{ ++ int ret = 0; ++ ++ mutex_lock(&intf->mutex); ++ if (intf->mode_switch) { ++ ret = -EBUSY; ++ goto out_unlock; ++ } ++ ++ intf->mode_switch = true; ++ reinit_completion(&intf->mode_switch_completion); ++ ++ /* ++ * Get a reference to the interface device, which will be put once the ++ * mode switch is complete. ++ */ ++ get_device(&intf->dev); ++ ++ if (!queue_work(system_long_wq, &intf->mode_switch_work)) { ++ put_device(&intf->dev); ++ ret = -EBUSY; ++ goto out_unlock; ++ } ++ ++out_unlock: ++ mutex_unlock(&intf->mutex); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_interface_request_mode_switch); ++ ++/* ++ * T_TstSrcIncrement is written by the module on ES2 as a stand-in for the ++ * init-status attribute DME_TOSHIBA_INIT_STATUS. The AP needs to read and ++ * clear it after reading a non-zero value from it. ++ * ++ * FIXME: This is module-hardware dependent and needs to be extended for every ++ * type of module we want to support. ++ */ ++static int gb_interface_read_and_clear_init_status(struct gb_interface *intf) ++{ ++ struct gb_host_device *hd = intf->hd; ++ unsigned long bootrom_quirks; ++ unsigned long s2l_quirks; ++ int ret; ++ u32 value; ++ u16 attr; ++ u8 init_status; ++ ++ /* ++ * ES2 bridges use T_TstSrcIncrement for the init status. ++ * ++ * FIXME: Remove ES2 support ++ */ ++ if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) ++ attr = DME_T_TST_SRC_INCREMENT; ++ else ++ attr = DME_TOSHIBA_GMP_INIT_STATUS; ++ ++ ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id, attr, ++ DME_SELECTOR_INDEX_NULL, &value); ++ if (ret) ++ return ret; ++ ++ /* ++ * A nonzero init status indicates the module has finished ++ * initializing. ++ */ ++ if (!value) { ++ dev_err(&intf->dev, "invalid init status\n"); ++ return -ENODEV; ++ } ++ ++ /* ++ * Extract the init status. ++ * ++ * For ES2: We need to check lowest 8 bits of 'value'. ++ * For ES3: We need to check highest 8 bits out of 32 of 'value'. ++ * ++ * FIXME: Remove ES2 support ++ */ ++ if (intf->quirks & GB_INTERFACE_QUIRK_NO_INIT_STATUS) ++ init_status = value & 0xff; ++ else ++ init_status = value >> 24; ++ ++ /* ++ * Check if the interface is executing the quirky ES3 bootrom that, ++ * for example, requires E2EFC, CSD and CSV to be disabled. ++ */ ++ bootrom_quirks = GB_INTERFACE_QUIRK_NO_CPORT_FEATURES | ++ GB_INTERFACE_QUIRK_FORCED_DISABLE | ++ GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH | ++ GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE; ++ ++ s2l_quirks = GB_INTERFACE_QUIRK_NO_PM; ++ ++ switch (init_status) { ++ case GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED: ++ case GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED: ++ intf->quirks |= bootrom_quirks; ++ break; ++ case GB_INIT_S2_LOADER_BOOT_STARTED: ++ /* S2 Loader doesn't support runtime PM */ ++ intf->quirks &= ~bootrom_quirks; ++ intf->quirks |= s2l_quirks; ++ break; ++ default: ++ intf->quirks &= ~bootrom_quirks; ++ intf->quirks &= ~s2l_quirks; ++ } ++ ++ /* Clear the init status. */ ++ return gb_svc_dme_peer_set(hd->svc, intf->interface_id, attr, ++ DME_SELECTOR_INDEX_NULL, 0); ++} ++ ++/* interface sysfs attributes */ ++#define gb_interface_attr(field, type) \ ++static ssize_t field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_interface *intf = to_gb_interface(dev); \ ++ return scnprintf(buf, PAGE_SIZE, type"\n", intf->field); \ ++} \ ++static DEVICE_ATTR_RO(field) ++ ++gb_interface_attr(ddbl1_manufacturer_id, "0x%08x"); ++gb_interface_attr(ddbl1_product_id, "0x%08x"); ++gb_interface_attr(interface_id, "%u"); ++gb_interface_attr(vendor_id, "0x%08x"); ++gb_interface_attr(product_id, "0x%08x"); ++gb_interface_attr(serial_number, "0x%016llx"); ++ ++static ssize_t voltage_now_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ int ret; ++ u32 measurement; ++ ++ ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, ++ GB_SVC_PWRMON_TYPE_VOL, ++ &measurement); ++ if (ret) { ++ dev_err(&intf->dev, "failed to get voltage sample (%d)\n", ret); ++ return ret; ++ } ++ ++ return sprintf(buf, "%u\n", measurement); ++} ++static DEVICE_ATTR_RO(voltage_now); ++ ++static ssize_t current_now_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ int ret; ++ u32 measurement; ++ ++ ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, ++ GB_SVC_PWRMON_TYPE_CURR, ++ &measurement); ++ if (ret) { ++ dev_err(&intf->dev, "failed to get current sample (%d)\n", ret); ++ return ret; ++ } ++ ++ return sprintf(buf, "%u\n", measurement); ++} ++static DEVICE_ATTR_RO(current_now); ++ ++static ssize_t power_now_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ int ret; ++ u32 measurement; ++ ++ ret = gb_svc_pwrmon_intf_sample_get(intf->hd->svc, intf->interface_id, ++ GB_SVC_PWRMON_TYPE_PWR, ++ &measurement); ++ if (ret) { ++ dev_err(&intf->dev, "failed to get power sample (%d)\n", ret); ++ return ret; ++ } ++ ++ return sprintf(buf, "%u\n", measurement); ++} ++static DEVICE_ATTR_RO(power_now); ++ ++static ssize_t power_state_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ if (intf->active) ++ return scnprintf(buf, PAGE_SIZE, "on\n"); ++ else ++ return scnprintf(buf, PAGE_SIZE, "off\n"); ++} ++ ++static ssize_t power_state_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ bool activate; ++ int ret = 0; ++ ++ if (kstrtobool(buf, &activate)) ++ return -EINVAL; ++ ++ mutex_lock(&intf->mutex); ++ ++ if (activate == intf->active) ++ goto unlock; ++ ++ if (activate) { ++ ret = gb_interface_activate(intf); ++ if (ret) { ++ dev_err(&intf->dev, ++ "failed to activate interface: %d\n", ret); ++ goto unlock; ++ } ++ ++ ret = gb_interface_enable(intf); ++ if (ret) { ++ dev_err(&intf->dev, ++ "failed to enable interface: %d\n", ret); ++ gb_interface_deactivate(intf); ++ goto unlock; ++ } ++ } else { ++ gb_interface_disable(intf); ++ gb_interface_deactivate(intf); ++ } ++ ++unlock: ++ mutex_unlock(&intf->mutex); ++ ++ if (ret) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR_RW(power_state); ++ ++static const char *gb_interface_type_string(struct gb_interface *intf) ++{ ++ static const char * const types[] = { ++ [GB_INTERFACE_TYPE_INVALID] = "invalid", ++ [GB_INTERFACE_TYPE_UNKNOWN] = "unknown", ++ [GB_INTERFACE_TYPE_DUMMY] = "dummy", ++ [GB_INTERFACE_TYPE_UNIPRO] = "unipro", ++ [GB_INTERFACE_TYPE_GREYBUS] = "greybus", ++ }; ++ ++ return types[intf->type]; ++} ++ ++static ssize_t interface_type_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ return sprintf(buf, "%s\n", gb_interface_type_string(intf)); ++} ++static DEVICE_ATTR_RO(interface_type); ++ ++static struct attribute *interface_unipro_attrs[] = { ++ &dev_attr_ddbl1_manufacturer_id.attr, ++ &dev_attr_ddbl1_product_id.attr, ++ NULL ++}; ++ ++static struct attribute *interface_greybus_attrs[] = { ++ &dev_attr_vendor_id.attr, ++ &dev_attr_product_id.attr, ++ &dev_attr_serial_number.attr, ++ NULL ++}; ++ ++static struct attribute *interface_power_attrs[] = { ++ &dev_attr_voltage_now.attr, ++ &dev_attr_current_now.attr, ++ &dev_attr_power_now.attr, ++ &dev_attr_power_state.attr, ++ NULL ++}; ++ ++static struct attribute *interface_common_attrs[] = { ++ &dev_attr_interface_id.attr, ++ &dev_attr_interface_type.attr, ++ NULL ++}; ++ ++static umode_t interface_unipro_is_visible(struct kobject *kobj, ++ struct attribute *attr, int n) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ switch (intf->type) { ++ case GB_INTERFACE_TYPE_UNIPRO: ++ case GB_INTERFACE_TYPE_GREYBUS: ++ return attr->mode; ++ default: ++ return 0; ++ } ++} ++ ++static umode_t interface_greybus_is_visible(struct kobject *kobj, ++ struct attribute *attr, int n) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ switch (intf->type) { ++ case GB_INTERFACE_TYPE_GREYBUS: ++ return attr->mode; ++ default: ++ return 0; ++ } ++} ++ ++static umode_t interface_power_is_visible(struct kobject *kobj, ++ struct attribute *attr, int n) ++{ ++ struct device *dev = container_of(kobj, struct device, kobj); ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ switch (intf->type) { ++ case GB_INTERFACE_TYPE_UNIPRO: ++ case GB_INTERFACE_TYPE_GREYBUS: ++ return attr->mode; ++ default: ++ return 0; ++ } ++} ++ ++static const struct attribute_group interface_unipro_group = { ++ .is_visible = interface_unipro_is_visible, ++ .attrs = interface_unipro_attrs, ++}; ++ ++static const struct attribute_group interface_greybus_group = { ++ .is_visible = interface_greybus_is_visible, ++ .attrs = interface_greybus_attrs, ++}; ++ ++static const struct attribute_group interface_power_group = { ++ .is_visible = interface_power_is_visible, ++ .attrs = interface_power_attrs, ++}; ++ ++static const struct attribute_group interface_common_group = { ++ .attrs = interface_common_attrs, ++}; ++ ++static const struct attribute_group *interface_groups[] = { ++ &interface_unipro_group, ++ &interface_greybus_group, ++ &interface_power_group, ++ &interface_common_group, ++ NULL ++}; ++ ++static void gb_interface_release(struct device *dev) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ ++ trace_gb_interface_release(intf); ++ ++ kfree(intf); ++} ++ ++#ifdef CONFIG_PM ++static int gb_interface_suspend(struct device *dev) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ int ret, timesync_ret; ++ ++ ret = gb_control_interface_suspend_prepare(intf->control); ++ if (ret) ++ return ret; ++ ++ gb_timesync_interface_remove(intf); ++ ++ ret = gb_control_suspend(intf->control); ++ if (ret) ++ goto err_hibernate_abort; ++ ++ ret = gb_interface_hibernate_link(intf); ++ if (ret) ++ return ret; ++ ++ /* Delay to allow interface to enter standby before disabling refclk */ ++ msleep(GB_INTERFACE_SUSPEND_HIBERNATE_DELAY_MS); ++ ++ ret = gb_interface_refclk_set(intf, false); ++ if (ret) ++ return ret; ++ ++ return 0; ++ ++err_hibernate_abort: ++ gb_control_interface_hibernate_abort(intf->control); ++ ++ timesync_ret = gb_timesync_interface_add(intf); ++ if (timesync_ret) { ++ dev_err(dev, "failed to add to timesync: %d\n", timesync_ret); ++ return timesync_ret; ++ } ++ ++ return ret; ++} ++ ++static int gb_interface_resume(struct device *dev) ++{ ++ struct gb_interface *intf = to_gb_interface(dev); ++ struct gb_svc *svc = intf->hd->svc; ++ int ret; ++ ++ ret = gb_interface_refclk_set(intf, true); ++ if (ret) ++ return ret; ++ ++ ret = gb_svc_intf_resume(svc, intf->interface_id); ++ if (ret) ++ return ret; ++ ++ ret = gb_control_resume(intf->control); ++ if (ret) ++ return ret; ++ ++ ret = gb_timesync_interface_add(intf); ++ if (ret) { ++ dev_err(dev, "failed to add to timesync: %d\n", ret); ++ return ret; ++ } ++ ++ ret = gb_timesync_schedule_synchronous(intf); ++ if (ret) { ++ dev_err(dev, "failed to synchronize FrameTime: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_interface_runtime_idle(struct device *dev) ++{ ++ pm_runtime_mark_last_busy(dev); ++ pm_request_autosuspend(dev); ++ ++ return 0; ++} ++#endif ++ ++static const struct dev_pm_ops gb_interface_pm_ops = { ++ SET_RUNTIME_PM_OPS(gb_interface_suspend, gb_interface_resume, ++ gb_interface_runtime_idle) ++}; ++ ++struct device_type greybus_interface_type = { ++ .name = "greybus_interface", ++ .release = gb_interface_release, ++ .pm = &gb_interface_pm_ops, ++}; ++ ++/* ++ * A Greybus module represents a user-replaceable component on a GMP ++ * phone. An interface is the physical connection on that module. A ++ * module may have more than one interface. ++ * ++ * Create a gb_interface structure to represent a discovered interface. ++ * The position of interface within the Endo is encoded in "interface_id" ++ * argument. ++ * ++ * Returns a pointer to the new interfce or a null pointer if a ++ * failure occurs due to memory exhaustion. ++ */ ++struct gb_interface *gb_interface_create(struct gb_module *module, ++ u8 interface_id) ++{ ++ struct gb_host_device *hd = module->hd; ++ struct gb_interface *intf; ++ ++ intf = kzalloc(sizeof(*intf), GFP_KERNEL); ++ if (!intf) ++ return NULL; ++ ++ intf->hd = hd; /* XXX refcount? */ ++ intf->module = module; ++ intf->interface_id = interface_id; ++ INIT_LIST_HEAD(&intf->bundles); ++ INIT_LIST_HEAD(&intf->manifest_descs); ++ mutex_init(&intf->mutex); ++ INIT_WORK(&intf->mode_switch_work, gb_interface_mode_switch_work); ++ init_completion(&intf->mode_switch_completion); ++ ++ /* Invalid device id to start with */ ++ intf->device_id = GB_INTERFACE_DEVICE_ID_BAD; ++ ++ intf->dev.parent = &module->dev; ++ intf->dev.bus = &greybus_bus_type; ++ intf->dev.type = &greybus_interface_type; ++ intf->dev.groups = interface_groups; ++ intf->dev.dma_mask = module->dev.dma_mask; ++ device_initialize(&intf->dev); ++ dev_set_name(&intf->dev, "%s.%u", dev_name(&module->dev), ++ interface_id); ++ ++ pm_runtime_set_autosuspend_delay(&intf->dev, ++ GB_INTERFACE_AUTOSUSPEND_MS); ++ ++ trace_gb_interface_create(intf); ++ ++ return intf; ++} ++ ++static int gb_interface_vsys_set(struct gb_interface *intf, bool enable) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ int ret; ++ ++ dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); ++ ++ ret = gb_svc_intf_vsys_set(svc, intf->interface_id, enable); ++ if (ret) { ++ dev_err(&intf->dev, "failed to set v_sys: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_interface_refclk_set(struct gb_interface *intf, bool enable) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ int ret; ++ ++ dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); ++ ++ ret = gb_svc_intf_refclk_set(svc, intf->interface_id, enable); ++ if (ret) { ++ dev_err(&intf->dev, "failed to set refclk: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_interface_unipro_set(struct gb_interface *intf, bool enable) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ int ret; ++ ++ dev_dbg(&intf->dev, "%s - %d\n", __func__, enable); ++ ++ ret = gb_svc_intf_unipro_set(svc, intf->interface_id, enable); ++ if (ret) { ++ dev_err(&intf->dev, "failed to set UniPro: %d\n", ret); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_interface_activate_operation(struct gb_interface *intf, ++ enum gb_interface_type *intf_type) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ u8 type; ++ int ret; ++ ++ dev_dbg(&intf->dev, "%s\n", __func__); ++ ++ ret = gb_svc_intf_activate(svc, intf->interface_id, &type); ++ if (ret) { ++ dev_err(&intf->dev, "failed to activate: %d\n", ret); ++ return ret; ++ } ++ ++ switch (type) { ++ case GB_SVC_INTF_TYPE_DUMMY: ++ *intf_type = GB_INTERFACE_TYPE_DUMMY; ++ /* FIXME: handle as an error for now */ ++ return -ENODEV; ++ case GB_SVC_INTF_TYPE_UNIPRO: ++ *intf_type = GB_INTERFACE_TYPE_UNIPRO; ++ dev_err(&intf->dev, "interface type UniPro not supported\n"); ++ /* FIXME: handle as an error for now */ ++ return -ENODEV; ++ case GB_SVC_INTF_TYPE_GREYBUS: ++ *intf_type = GB_INTERFACE_TYPE_GREYBUS; ++ break; ++ default: ++ dev_err(&intf->dev, "unknown interface type: %u\n", type); ++ *intf_type = GB_INTERFACE_TYPE_UNKNOWN; ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++static int gb_interface_hibernate_link(struct gb_interface *intf) ++{ ++ struct gb_svc *svc = intf->hd->svc; ++ ++ return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id); ++} ++ ++static int _gb_interface_activate(struct gb_interface *intf, ++ enum gb_interface_type *type) ++{ ++ int ret; ++ ++ *type = GB_INTERFACE_TYPE_UNKNOWN; ++ ++ if (intf->ejected || intf->removed) ++ return -ENODEV; ++ ++ ret = gb_interface_vsys_set(intf, true); ++ if (ret) ++ return ret; ++ ++ ret = gb_interface_refclk_set(intf, true); ++ if (ret) ++ goto err_vsys_disable; ++ ++ ret = gb_interface_unipro_set(intf, true); ++ if (ret) ++ goto err_refclk_disable; ++ ++ ret = gb_interface_activate_operation(intf, type); ++ if (ret) { ++ switch (*type) { ++ case GB_INTERFACE_TYPE_UNIPRO: ++ case GB_INTERFACE_TYPE_GREYBUS: ++ goto err_hibernate_link; ++ default: ++ goto err_unipro_disable; ++ } ++ } ++ ++ ret = gb_interface_read_dme(intf); ++ if (ret) ++ goto err_hibernate_link; ++ ++ ret = gb_interface_route_create(intf); ++ if (ret) ++ goto err_hibernate_link; ++ ++ intf->active = true; ++ ++ trace_gb_interface_activate(intf); ++ ++ return 0; ++ ++err_hibernate_link: ++ gb_interface_hibernate_link(intf); ++err_unipro_disable: ++ gb_interface_unipro_set(intf, false); ++err_refclk_disable: ++ gb_interface_refclk_set(intf, false); ++err_vsys_disable: ++ gb_interface_vsys_set(intf, false); ++ ++ return ret; ++} ++ ++/* ++ * At present, we assume a UniPro-only module to be a Greybus module that ++ * failed to send its mailbox poke. There is some reason to believe that this ++ * is because of a bug in the ES3 bootrom. ++ * ++ * FIXME: Check if this is a Toshiba bridge before retrying? ++ */ ++static int _gb_interface_activate_es3_hack(struct gb_interface *intf, ++ enum gb_interface_type *type) ++{ ++ int retries = 3; ++ int ret; ++ ++ while (retries--) { ++ ret = _gb_interface_activate(intf, type); ++ if (ret == -ENODEV && *type == GB_INTERFACE_TYPE_UNIPRO) ++ continue; ++ ++ break; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Activate an interface. ++ * ++ * Locking: Caller holds the interface mutex. ++ */ ++int gb_interface_activate(struct gb_interface *intf) ++{ ++ enum gb_interface_type type; ++ int ret; ++ ++ switch (intf->type) { ++ case GB_INTERFACE_TYPE_INVALID: ++ case GB_INTERFACE_TYPE_GREYBUS: ++ ret = _gb_interface_activate_es3_hack(intf, &type); ++ break; ++ default: ++ ret = _gb_interface_activate(intf, &type); ++ } ++ ++ /* Make sure type is detected correctly during reactivation. */ ++ if (intf->type != GB_INTERFACE_TYPE_INVALID) { ++ if (type != intf->type) { ++ dev_err(&intf->dev, "failed to detect interface type\n"); ++ ++ if (!ret) ++ gb_interface_deactivate(intf); ++ ++ return -EIO; ++ } ++ } else { ++ intf->type = type; ++ } ++ ++ return ret; ++} ++ ++/* ++ * Deactivate an interface. ++ * ++ * Locking: Caller holds the interface mutex. ++ */ ++void gb_interface_deactivate(struct gb_interface *intf) ++{ ++ if (!intf->active) ++ return; ++ ++ trace_gb_interface_deactivate(intf); ++ ++ /* Abort any ongoing mode switch. */ ++ if (intf->mode_switch) ++ complete(&intf->mode_switch_completion); ++ ++ gb_interface_route_destroy(intf); ++ gb_interface_hibernate_link(intf); ++ gb_interface_unipro_set(intf, false); ++ gb_interface_refclk_set(intf, false); ++ gb_interface_vsys_set(intf, false); ++ ++ intf->active = false; ++} ++ ++/* ++ * Enable an interface by enabling its control connection, fetching the ++ * manifest and other information over it, and finally registering its child ++ * devices. ++ * ++ * Locking: Caller holds the interface mutex. ++ */ ++int gb_interface_enable(struct gb_interface *intf) ++{ ++ struct gb_control *control; ++ struct gb_bundle *bundle, *tmp; ++ int ret, size; ++ void *manifest; ++ ++ ret = gb_interface_read_and_clear_init_status(intf); ++ if (ret) { ++ dev_err(&intf->dev, "failed to clear init status: %d\n", ret); ++ return ret; ++ } ++ ++ /* Establish control connection */ ++ control = gb_control_create(intf); ++ if (IS_ERR(control)) { ++ dev_err(&intf->dev, "failed to create control device: %ld\n", ++ PTR_ERR(control)); ++ return PTR_ERR(control); ++ } ++ intf->control = control; ++ ++ ret = gb_control_enable(intf->control); ++ if (ret) ++ goto err_put_control; ++ ++ /* Get manifest size using control protocol on CPort */ ++ size = gb_control_get_manifest_size_operation(intf); ++ if (size <= 0) { ++ dev_err(&intf->dev, "failed to get manifest size: %d\n", size); ++ ++ if (size) ++ ret = size; ++ else ++ ret = -EINVAL; ++ ++ goto err_disable_control; ++ } ++ ++ manifest = kmalloc(size, GFP_KERNEL); ++ if (!manifest) { ++ ret = -ENOMEM; ++ goto err_disable_control; ++ } ++ ++ /* Get manifest using control protocol on CPort */ ++ ret = gb_control_get_manifest_operation(intf, manifest, size); ++ if (ret) { ++ dev_err(&intf->dev, "failed to get manifest: %d\n", ret); ++ goto err_free_manifest; ++ } ++ ++ /* ++ * Parse the manifest and build up our data structures representing ++ * what's in it. ++ */ ++ if (!gb_manifest_parse(intf, manifest, size)) { ++ dev_err(&intf->dev, "failed to parse manifest\n"); ++ ret = -EINVAL; ++ goto err_destroy_bundles; ++ } ++ ++ ret = gb_control_get_bundle_versions(intf->control); ++ if (ret) ++ goto err_destroy_bundles; ++ ++ ret = gb_timesync_interface_add(intf); ++ if (ret) { ++ dev_err(&intf->dev, "failed to add to timesync: %d\n", ret); ++ goto err_destroy_bundles; ++ } ++ ++ /* Register the control device and any bundles */ ++ ret = gb_control_add(intf->control); ++ if (ret) ++ goto err_remove_timesync; ++ ++ pm_runtime_use_autosuspend(&intf->dev); ++ pm_runtime_get_noresume(&intf->dev); ++ pm_runtime_set_active(&intf->dev); ++ pm_runtime_enable(&intf->dev); ++ ++ list_for_each_entry_safe_reverse(bundle, tmp, &intf->bundles, links) { ++ ret = gb_bundle_add(bundle); ++ if (ret) { ++ gb_bundle_destroy(bundle); ++ continue; ++ } ++ } ++ ++ kfree(manifest); ++ ++ intf->enabled = true; ++ ++ pm_runtime_put(&intf->dev); ++ ++ trace_gb_interface_enable(intf); ++ ++ return 0; ++ ++err_remove_timesync: ++ gb_timesync_interface_remove(intf); ++err_destroy_bundles: ++ list_for_each_entry_safe(bundle, tmp, &intf->bundles, links) ++ gb_bundle_destroy(bundle); ++err_free_manifest: ++ kfree(manifest); ++err_disable_control: ++ gb_control_disable(intf->control); ++err_put_control: ++ gb_control_put(intf->control); ++ intf->control = NULL; ++ ++ return ret; ++} ++ ++/* ++ * Disable an interface and destroy its bundles. ++ * ++ * Locking: Caller holds the interface mutex. ++ */ ++void gb_interface_disable(struct gb_interface *intf) ++{ ++ struct gb_bundle *bundle; ++ struct gb_bundle *next; ++ ++ if (!intf->enabled) ++ return; ++ ++ trace_gb_interface_disable(intf); ++ ++ pm_runtime_get_sync(&intf->dev); ++ ++ /* Set disconnected flag to avoid I/O during connection tear down. */ ++ if (intf->quirks & GB_INTERFACE_QUIRK_FORCED_DISABLE) ++ intf->disconnected = true; ++ ++ list_for_each_entry_safe(bundle, next, &intf->bundles, links) ++ gb_bundle_destroy(bundle); ++ ++ if (!intf->mode_switch && !intf->disconnected) ++ gb_control_interface_deactivate_prepare(intf->control); ++ ++ gb_control_del(intf->control); ++ gb_timesync_interface_remove(intf); ++ gb_control_disable(intf->control); ++ gb_control_put(intf->control); ++ intf->control = NULL; ++ ++ intf->enabled = false; ++ ++ pm_runtime_disable(&intf->dev); ++ pm_runtime_set_suspended(&intf->dev); ++ pm_runtime_dont_use_autosuspend(&intf->dev); ++ pm_runtime_put_noidle(&intf->dev); ++} ++ ++/* Enable TimeSync on an Interface control connection. */ ++int gb_interface_timesync_enable(struct gb_interface *intf, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk) ++{ ++ return gb_control_timesync_enable(intf->control, count, ++ frame_time, strobe_delay, ++ refclk); ++} ++ ++/* Disable TimeSync on an Interface control connection. */ ++int gb_interface_timesync_disable(struct gb_interface *intf) ++{ ++ return gb_control_timesync_disable(intf->control); ++} ++ ++/* Transmit the Authoritative FrameTime via an Interface control connection. */ ++int gb_interface_timesync_authoritative(struct gb_interface *intf, ++ u64 *frame_time) ++{ ++ return gb_control_timesync_authoritative(intf->control, ++ frame_time); ++} ++ ++/* Register an interface. */ ++int gb_interface_add(struct gb_interface *intf) ++{ ++ int ret; ++ ++ ret = device_add(&intf->dev); ++ if (ret) { ++ dev_err(&intf->dev, "failed to register interface: %d\n", ret); ++ return ret; ++ } ++ ++ trace_gb_interface_add(intf); ++ ++ dev_info(&intf->dev, "Interface added (%s)\n", ++ gb_interface_type_string(intf)); ++ ++ switch (intf->type) { ++ case GB_INTERFACE_TYPE_GREYBUS: ++ dev_info(&intf->dev, "GMP VID=0x%08x, PID=0x%08x\n", ++ intf->vendor_id, intf->product_id); ++ /* fall-through */ ++ case GB_INTERFACE_TYPE_UNIPRO: ++ dev_info(&intf->dev, "DDBL1 Manufacturer=0x%08x, Product=0x%08x\n", ++ intf->ddbl1_manufacturer_id, ++ intf->ddbl1_product_id); ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++/* Deregister an interface. */ ++void gb_interface_del(struct gb_interface *intf) ++{ ++ if (device_is_registered(&intf->dev)) { ++ trace_gb_interface_del(intf); ++ ++ device_del(&intf->dev); ++ dev_info(&intf->dev, "Interface removed\n"); ++ } ++} ++ ++void gb_interface_put(struct gb_interface *intf) ++{ ++ put_device(&intf->dev); ++} +--- /dev/null ++++ b/drivers/greybus/interface.h +@@ -0,0 +1,88 @@ ++/* ++ * Greybus Interface Block code ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __INTERFACE_H ++#define __INTERFACE_H ++ ++enum gb_interface_type { ++ GB_INTERFACE_TYPE_INVALID = 0, ++ GB_INTERFACE_TYPE_UNKNOWN, ++ GB_INTERFACE_TYPE_DUMMY, ++ GB_INTERFACE_TYPE_UNIPRO, ++ GB_INTERFACE_TYPE_GREYBUS, ++}; ++ ++#define GB_INTERFACE_QUIRK_NO_CPORT_FEATURES BIT(0) ++#define GB_INTERFACE_QUIRK_NO_INIT_STATUS BIT(1) ++#define GB_INTERFACE_QUIRK_NO_GMP_IDS BIT(2) ++#define GB_INTERFACE_QUIRK_FORCED_DISABLE BIT(3) ++#define GB_INTERFACE_QUIRK_LEGACY_MODE_SWITCH BIT(4) ++#define GB_INTERFACE_QUIRK_NO_BUNDLE_ACTIVATE BIT(5) ++#define GB_INTERFACE_QUIRK_NO_PM BIT(6) ++ ++struct gb_interface { ++ struct device dev; ++ struct gb_control *control; ++ ++ struct list_head bundles; ++ struct list_head module_node; ++ struct list_head manifest_descs; ++ u8 interface_id; /* Physical location within the Endo */ ++ u8 device_id; ++ u8 features; /* Feature flags set in the manifest */ ++ ++ enum gb_interface_type type; ++ ++ u32 ddbl1_manufacturer_id; ++ u32 ddbl1_product_id; ++ u32 vendor_id; ++ u32 product_id; ++ u64 serial_number; ++ ++ struct gb_host_device *hd; ++ struct gb_module *module; ++ ++ unsigned long quirks; ++ ++ struct mutex mutex; ++ ++ bool disconnected; ++ ++ bool ejected; ++ bool removed; ++ bool active; ++ bool enabled; ++ bool mode_switch; ++ bool dme_read; ++ ++ struct work_struct mode_switch_work; ++ struct completion mode_switch_completion; ++}; ++#define to_gb_interface(d) container_of(d, struct gb_interface, dev) ++ ++struct gb_interface *gb_interface_create(struct gb_module *module, ++ u8 interface_id); ++int gb_interface_activate(struct gb_interface *intf); ++void gb_interface_deactivate(struct gb_interface *intf); ++int gb_interface_enable(struct gb_interface *intf); ++void gb_interface_disable(struct gb_interface *intf); ++int gb_interface_timesync_enable(struct gb_interface *intf, u8 count, ++ u64 frame_time, u32 strobe_delay, u32 refclk); ++int gb_interface_timesync_authoritative(struct gb_interface *intf, ++ u64 *frame_time); ++int gb_interface_timesync_disable(struct gb_interface *intf); ++int gb_interface_add(struct gb_interface *intf); ++void gb_interface_del(struct gb_interface *intf); ++void gb_interface_put(struct gb_interface *intf); ++void gb_interface_mailbox_event(struct gb_interface *intf, u16 result, ++ u32 mailbox); ++ ++int gb_interface_request_mode_switch(struct gb_interface *intf); ++ ++#endif /* __INTERFACE_H */ diff --git a/greybus_light.patch b/greybus_light.patch new file mode 100644 index 00000000000000..152c14ce826f62 --- /dev/null +++ b/greybus_light.patch @@ -0,0 +1,1366 @@ +--- + drivers/greybus/light.c | 1359 ++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1359 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/light.c +@@ -0,0 +1,1359 @@ ++/* ++ * Greybus Lights protocol driver. ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/leds.h> ++#include <linux/led-class-flash.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/version.h> ++#include <media/v4l2-flash-led-class.h> ++ ++#include "greybus.h" ++#include "greybus_protocols.h" ++ ++#define NAMES_MAX 32 ++ ++struct gb_channel { ++ u8 id; ++ u32 flags; ++ u32 color; ++ char *color_name; ++ u8 fade_in; ++ u8 fade_out; ++ u32 mode; ++ char *mode_name; ++ struct attribute **attrs; ++ struct attribute_group *attr_group; ++ const struct attribute_group **attr_groups; ++ struct led_classdev *led; ++#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) ++ struct led_classdev_flash fled; ++ struct led_flash_setting intensity_uA; ++ struct led_flash_setting timeout_us; ++#else ++ struct led_classdev cled; ++#endif ++ struct gb_light *light; ++ bool is_registered; ++ bool releasing; ++ bool strobe_state; ++ bool active; ++ struct mutex lock; ++}; ++ ++struct gb_light { ++ u8 id; ++ char *name; ++ struct gb_lights *glights; ++ u32 flags; ++ u8 channels_count; ++ struct gb_channel *channels; ++ bool has_flash; ++ bool ready; ++#if IS_REACHABLE(CONFIG_V4L2_FLASH_LED_CLASS) ++ struct v4l2_flash *v4l2_flash; ++#endif ++}; ++ ++struct gb_lights { ++ struct gb_connection *connection; ++ u8 lights_count; ++ struct gb_light *lights; ++ struct mutex lights_lock; ++}; ++ ++static void gb_lights_channel_free(struct gb_channel *channel); ++ ++static struct gb_connection *get_conn_from_channel(struct gb_channel *channel) ++{ ++ return channel->light->glights->connection; ++} ++ ++static struct gb_connection *get_conn_from_light(struct gb_light *light) ++{ ++ return light->glights->connection; ++} ++ ++static bool is_channel_flash(struct gb_channel *channel) ++{ ++ return !!(channel->mode & (GB_CHANNEL_MODE_FLASH | GB_CHANNEL_MODE_TORCH ++ | GB_CHANNEL_MODE_INDICATOR)); ++} ++ ++#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) ++static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) ++{ ++ struct led_classdev_flash *fled_cdev = lcdev_to_flcdev(cdev); ++ ++ return container_of(fled_cdev, struct gb_channel, fled); ++} ++ ++static struct led_classdev *get_channel_cdev(struct gb_channel *channel) ++{ ++ return &channel->fled.led_cdev; ++} ++ ++static struct gb_channel *get_channel_from_mode(struct gb_light *light, ++ u32 mode) ++{ ++ struct gb_channel *channel = NULL; ++ int i; ++ ++ for (i = 0; i < light->channels_count; i++) { ++ channel = &light->channels[i]; ++ if (channel && channel->mode == mode) ++ break; ++ } ++ return channel; ++} ++ ++static int __gb_lights_flash_intensity_set(struct gb_channel *channel, ++ u32 intensity) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_set_flash_intensity_request req; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.intensity_uA = cpu_to_le32(intensity); ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_INTENSITY, ++ &req, sizeof(req), NULL, 0); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int __gb_lights_flash_brightness_set(struct gb_channel *channel) ++{ ++ u32 intensity; ++ ++ /* If the channel is flash we need to get the attached torch channel */ ++ if (channel->mode & GB_CHANNEL_MODE_FLASH) ++ channel = get_channel_from_mode(channel->light, ++ GB_CHANNEL_MODE_TORCH); ++ ++ /* For not flash we need to convert brightness to intensity */ ++ intensity = channel->intensity_uA.min + ++ (channel->intensity_uA.step * channel->led->brightness); ++ ++ return __gb_lights_flash_intensity_set(channel, intensity); ++} ++#else ++static struct gb_channel *get_channel_from_cdev(struct led_classdev *cdev) ++{ ++ return container_of(cdev, struct gb_channel, cled); ++} ++ ++static struct led_classdev *get_channel_cdev(struct gb_channel *channel) ++{ ++ return &channel->cled; ++} ++ ++static int __gb_lights_flash_brightness_set(struct gb_channel *channel) ++{ ++ return 0; ++} ++#endif ++ ++static int gb_lights_color_set(struct gb_channel *channel, u32 color); ++static int gb_lights_fade_set(struct gb_channel *channel); ++ ++static void led_lock(struct led_classdev *cdev) ++{ ++ mutex_lock(&cdev->led_access); ++} ++ ++static void led_unlock(struct led_classdev *cdev) ++{ ++ mutex_unlock(&cdev->led_access); ++} ++ ++#define gb_lights_fade_attr(__dir) \ ++static ssize_t fade_##__dir##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct led_classdev *cdev = dev_get_drvdata(dev); \ ++ struct gb_channel *channel = get_channel_from_cdev(cdev); \ ++ \ ++ return sprintf(buf, "%u\n", channel->fade_##__dir); \ ++} \ ++ \ ++static ssize_t fade_##__dir##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, size_t size) \ ++{ \ ++ struct led_classdev *cdev = dev_get_drvdata(dev); \ ++ struct gb_channel *channel = get_channel_from_cdev(cdev); \ ++ u8 fade; \ ++ int ret; \ ++ \ ++ led_lock(cdev); \ ++ if (led_sysfs_is_disabled(cdev)) { \ ++ ret = -EBUSY; \ ++ goto unlock; \ ++ } \ ++ \ ++ ret = kstrtou8(buf, 0, &fade); \ ++ if (ret < 0) { \ ++ dev_err(dev, "could not parse fade value %d\n", ret); \ ++ goto unlock; \ ++ } \ ++ if (channel->fade_##__dir == fade) \ ++ goto unlock; \ ++ channel->fade_##__dir = fade; \ ++ \ ++ ret = gb_lights_fade_set(channel); \ ++ if (ret < 0) \ ++ goto unlock; \ ++ \ ++ ret = size; \ ++unlock: \ ++ led_unlock(cdev); \ ++ return ret; \ ++} \ ++static DEVICE_ATTR_RW(fade_##__dir) ++ ++gb_lights_fade_attr(in); ++gb_lights_fade_attr(out); ++ ++static ssize_t color_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct led_classdev *cdev = dev_get_drvdata(dev); ++ struct gb_channel *channel = get_channel_from_cdev(cdev); ++ ++ return sprintf(buf, "0x%08x\n", channel->color); ++} ++ ++static ssize_t color_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t size) ++{ ++ struct led_classdev *cdev = dev_get_drvdata(dev); ++ struct gb_channel *channel = get_channel_from_cdev(cdev); ++ u32 color; ++ int ret; ++ ++ led_lock(cdev); ++ if (led_sysfs_is_disabled(cdev)) { ++ ret = -EBUSY; ++ goto unlock; ++ } ++ ret = kstrtou32(buf, 0, &color); ++ if (ret < 0) { ++ dev_err(dev, "could not parse color value %d\n", ret); ++ goto unlock; ++ } ++ ++ ret = gb_lights_color_set(channel, color); ++ if (ret < 0) ++ goto unlock; ++ ++ channel->color = color; ++ ret = size; ++unlock: ++ led_unlock(cdev); ++ return ret; ++} ++static DEVICE_ATTR_RW(color); ++ ++static int channel_attr_groups_set(struct gb_channel *channel, ++ struct led_classdev *cdev) ++{ ++ int attr = 0; ++ int size = 0; ++ ++ if (channel->flags & GB_LIGHT_CHANNEL_MULTICOLOR) ++ size++; ++ if (channel->flags & GB_LIGHT_CHANNEL_FADER) ++ size += 2; ++ ++ if (!size) ++ return 0; ++ ++ /* Set attributes based in the channel flags */ ++ channel->attrs = kcalloc(size + 1, sizeof(**channel->attrs), ++ GFP_KERNEL); ++ if (!channel->attrs) ++ return -ENOMEM; ++ channel->attr_group = kcalloc(1, sizeof(*channel->attr_group), ++ GFP_KERNEL); ++ if (!channel->attr_group) ++ return -ENOMEM; ++ channel->attr_groups = kcalloc(2, sizeof(*channel->attr_groups), ++ GFP_KERNEL); ++ if (!channel->attr_groups) ++ return -ENOMEM; ++ ++ if (channel->flags & GB_LIGHT_CHANNEL_MULTICOLOR) ++ channel->attrs[attr++] = &dev_attr_color.attr; ++ if (channel->flags & GB_LIGHT_CHANNEL_FADER) { ++ channel->attrs[attr++] = &dev_attr_fade_in.attr; ++ channel->attrs[attr++] = &dev_attr_fade_out.attr; ++ } ++ ++ channel->attr_group->attrs = channel->attrs; ++ ++ channel->attr_groups[0] = channel->attr_group; ++ ++ cdev->groups = channel->attr_groups; ++ ++ return 0; ++} ++ ++static int gb_lights_fade_set(struct gb_channel *channel) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_set_fade_request req; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.fade_in = channel->fade_in; ++ req.fade_out = channel->fade_out; ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FADE, ++ &req, sizeof(req), NULL, 0); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int gb_lights_color_set(struct gb_channel *channel, u32 color) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_set_color_request req; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.color = cpu_to_le32(color); ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_COLOR, ++ &req, sizeof(req), NULL, 0); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int __gb_lights_led_brightness_set(struct gb_channel *channel) ++{ ++ struct gb_lights_set_brightness_request req; ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ bool old_active; ++ int ret; ++ ++ mutex_lock(&channel->lock); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ goto out_unlock; ++ ++ old_active = channel->active; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.brightness = (u8)channel->led->brightness; ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BRIGHTNESS, ++ &req, sizeof(req), NULL, 0); ++ if (ret < 0) ++ goto out_pm_put; ++ ++ if (channel->led->brightness) ++ channel->active = true; ++ else ++ channel->active = false; ++ ++ /* we need to keep module alive when turning to active state */ ++ if (!old_active && channel->active) ++ goto out_unlock; ++ ++ /* ++ * on the other hand if going to inactive we still hold a reference and ++ * need to put it, so we could go to suspend. ++ */ ++ if (old_active && !channel->active) ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++out_pm_put: ++ gb_pm_runtime_put_autosuspend(bundle); ++out_unlock: ++ mutex_unlock(&channel->lock); ++ ++ return ret; ++} ++ ++static int __gb_lights_brightness_set(struct gb_channel *channel) ++{ ++ int ret; ++ ++ if (channel->releasing) ++ return 0; ++ ++ if (is_channel_flash(channel)) ++ ret = __gb_lights_flash_brightness_set(channel); ++ else ++ ret = __gb_lights_led_brightness_set(channel); ++ ++ return ret; ++} ++ ++static int gb_brightness_set(struct led_classdev *cdev, ++ enum led_brightness value) ++{ ++ struct gb_channel *channel = get_channel_from_cdev(cdev); ++ ++ channel->led->brightness = value; ++ ++ return __gb_lights_brightness_set(channel); ++} ++ ++static enum led_brightness gb_brightness_get(struct led_classdev *cdev) ++ ++{ ++ struct gb_channel *channel = get_channel_from_cdev(cdev); ++ ++ return channel->led->brightness; ++} ++ ++static int gb_blink_set(struct led_classdev *cdev, unsigned long *delay_on, ++ unsigned long *delay_off) ++{ ++ struct gb_channel *channel = get_channel_from_cdev(cdev); ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_blink_request req; ++ bool old_active; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ mutex_lock(&channel->lock); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ goto out_unlock; ++ ++ old_active = channel->active; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.time_on_ms = cpu_to_le16(*delay_on); ++ req.time_off_ms = cpu_to_le16(*delay_off); ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_BLINK, &req, ++ sizeof(req), NULL, 0); ++ if (ret < 0) ++ goto out_pm_put; ++ ++ if (delay_on) ++ channel->active = true; ++ else ++ channel->active = false; ++ ++ /* we need to keep module alive when turning to active state */ ++ if (!old_active && channel->active) ++ goto out_unlock; ++ ++ /* ++ * on the other hand if going to inactive we still hold a reference and ++ * need to put it, so we could go to suspend. ++ */ ++ if (old_active && !channel->active) ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++out_pm_put: ++ gb_pm_runtime_put_autosuspend(bundle); ++out_unlock: ++ mutex_unlock(&channel->lock); ++ ++ return ret; ++} ++ ++static void gb_lights_led_operations_set(struct gb_channel *channel, ++ struct led_classdev *cdev) ++{ ++ cdev->brightness_get = gb_brightness_get; ++ cdev->brightness_set_blocking = gb_brightness_set; ++ ++ if (channel->flags & GB_LIGHT_CHANNEL_BLINK) ++ cdev->blink_set = gb_blink_set; ++} ++ ++#if IS_REACHABLE(CONFIG_V4L2_FLASH_LED_CLASS) ++/* V4L2 specific helpers */ ++static const struct v4l2_flash_ops v4l2_flash_ops; ++ ++static void __gb_lights_channel_v4l2_config(struct led_flash_setting *channel_s, ++ struct led_flash_setting *v4l2_s) ++{ ++ v4l2_s->min = channel_s->min; ++ v4l2_s->max = channel_s->max; ++ v4l2_s->step = channel_s->step; ++ /* For v4l2 val is the default value */ ++ v4l2_s->val = channel_s->max; ++} ++ ++static int gb_lights_light_v4l2_register(struct gb_light *light) ++{ ++ struct gb_connection *connection = get_conn_from_light(light); ++ struct device *dev = &connection->bundle->dev; ++ struct v4l2_flash_config *sd_cfg; ++ struct led_classdev_flash *fled; ++ struct led_classdev_flash *iled = NULL; ++ struct gb_channel *channel_torch, *channel_ind, *channel_flash; ++ int ret = 0; ++ ++ sd_cfg = kcalloc(1, sizeof(*sd_cfg), GFP_KERNEL); ++ if (!sd_cfg) ++ return -ENOMEM; ++ ++ channel_torch = get_channel_from_mode(light, GB_CHANNEL_MODE_TORCH); ++ if (channel_torch) ++ __gb_lights_channel_v4l2_config(&channel_torch->intensity_uA, ++ &sd_cfg->torch_intensity); ++ ++ channel_ind = get_channel_from_mode(light, GB_CHANNEL_MODE_INDICATOR); ++ if (channel_ind) { ++ __gb_lights_channel_v4l2_config(&channel_ind->intensity_uA, ++ &sd_cfg->indicator_intensity); ++ iled = &channel_ind->fled; ++ } ++ ++ channel_flash = get_channel_from_mode(light, GB_CHANNEL_MODE_FLASH); ++ WARN_ON(!channel_flash); ++ ++ fled = &channel_flash->fled; ++ ++ snprintf(sd_cfg->dev_name, sizeof(sd_cfg->dev_name), "%s", light->name); ++ ++ /* Set the possible values to faults, in our case all faults */ ++ sd_cfg->flash_faults = LED_FAULT_OVER_VOLTAGE | LED_FAULT_TIMEOUT | ++ LED_FAULT_OVER_TEMPERATURE | LED_FAULT_SHORT_CIRCUIT | ++ LED_FAULT_OVER_CURRENT | LED_FAULT_INDICATOR | ++ LED_FAULT_UNDER_VOLTAGE | LED_FAULT_INPUT_VOLTAGE | ++ LED_FAULT_LED_OVER_TEMPERATURE; ++ ++ light->v4l2_flash = v4l2_flash_init(dev, NULL, fled, iled, ++ &v4l2_flash_ops, sd_cfg); ++ if (IS_ERR_OR_NULL(light->v4l2_flash)) { ++ ret = PTR_ERR(light->v4l2_flash); ++ goto out_free; ++ } ++ ++ return ret; ++ ++out_free: ++ kfree(sd_cfg); ++ return ret; ++} ++ ++static void gb_lights_light_v4l2_unregister(struct gb_light *light) ++{ ++ v4l2_flash_release(light->v4l2_flash); ++} ++#else ++static int gb_lights_light_v4l2_register(struct gb_light *light) ++{ ++ struct gb_connection *connection = get_conn_from_light(light); ++ ++ dev_err(&connection->bundle->dev, "no support for v4l2 subdevices\n"); ++ return 0; ++} ++ ++static void gb_lights_light_v4l2_unregister(struct gb_light *light) ++{ ++} ++#endif ++ ++#if IS_REACHABLE(CONFIG_LEDS_CLASS_FLASH) ++/* Flash specific operations */ ++static int gb_lights_flash_intensity_set(struct led_classdev_flash *fcdev, ++ u32 brightness) ++{ ++ struct gb_channel *channel = container_of(fcdev, struct gb_channel, ++ fled); ++ int ret; ++ ++ ret = __gb_lights_flash_intensity_set(channel, brightness); ++ if (ret < 0) ++ return ret; ++ ++ fcdev->brightness.val = brightness; ++ ++ return 0; ++} ++ ++static int gb_lights_flash_intensity_get(struct led_classdev_flash *fcdev, ++ u32 *brightness) ++{ ++ *brightness = fcdev->brightness.val; ++ ++ return 0; ++} ++ ++static int gb_lights_flash_strobe_set(struct led_classdev_flash *fcdev, ++ bool state) ++{ ++ struct gb_channel *channel = container_of(fcdev, struct gb_channel, ++ fled); ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_set_flash_strobe_request req; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.state = state ? 1 : 0; ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_STROBE, ++ &req, sizeof(req), NULL, 0); ++ if (!ret) ++ channel->strobe_state = state; ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int gb_lights_flash_strobe_get(struct led_classdev_flash *fcdev, ++ bool *state) ++{ ++ struct gb_channel *channel = container_of(fcdev, struct gb_channel, ++ fled); ++ ++ *state = channel->strobe_state; ++ return 0; ++} ++ ++static int gb_lights_flash_timeout_set(struct led_classdev_flash *fcdev, ++ u32 timeout) ++{ ++ struct gb_channel *channel = container_of(fcdev, struct gb_channel, ++ fled); ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_set_flash_timeout_request req; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ req.timeout_us = cpu_to_le32(timeout); ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_SET_FLASH_TIMEOUT, ++ &req, sizeof(req), NULL, 0); ++ if (!ret) ++ fcdev->timeout.val = timeout; ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int gb_lights_flash_fault_get(struct led_classdev_flash *fcdev, ++ u32 *fault) ++{ ++ struct gb_channel *channel = container_of(fcdev, struct gb_channel, ++ fled); ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_bundle *bundle = connection->bundle; ++ struct gb_lights_get_flash_fault_request req; ++ struct gb_lights_get_flash_fault_response resp; ++ int ret; ++ ++ if (channel->releasing) ++ return -ESHUTDOWN; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret < 0) ++ return ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_GET_FLASH_FAULT, ++ &req, sizeof(req), &resp, sizeof(resp)); ++ if (!ret) ++ *fault = le32_to_cpu(resp.fault); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static const struct led_flash_ops gb_lights_flash_ops = { ++ .flash_brightness_set = gb_lights_flash_intensity_set, ++ .flash_brightness_get = gb_lights_flash_intensity_get, ++ .strobe_set = gb_lights_flash_strobe_set, ++ .strobe_get = gb_lights_flash_strobe_get, ++ .timeout_set = gb_lights_flash_timeout_set, ++ .fault_get = gb_lights_flash_fault_get, ++}; ++ ++static int __gb_lights_channel_torch_attach(struct gb_channel *channel, ++ struct gb_channel *channel_torch) ++{ ++ char *name; ++ ++ /* we can only attach torch to a flash channel */ ++ if (!(channel->mode & GB_CHANNEL_MODE_FLASH)) ++ return 0; ++ ++ /* Move torch brightness to the destination */ ++ channel->led->max_brightness = channel_torch->led->max_brightness; ++ ++ /* append mode name to flash name */ ++ name = kasprintf(GFP_KERNEL, "%s_%s", channel->led->name, ++ channel_torch->mode_name); ++ if (!name) ++ return -ENOMEM; ++ kfree(channel->led->name); ++ channel->led->name = name; ++ ++ channel_torch->led = channel->led; ++ ++ return 0; ++} ++ ++static int __gb_lights_flash_led_register(struct gb_channel *channel) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct led_classdev_flash *fled = &channel->fled; ++ struct led_flash_setting *fset; ++ struct gb_channel *channel_torch; ++ int ret; ++ ++ fled->ops = &gb_lights_flash_ops; ++ ++ fled->led_cdev.flags |= LED_DEV_CAP_FLASH; ++ ++ fset = &fled->brightness; ++ fset->min = channel->intensity_uA.min; ++ fset->max = channel->intensity_uA.max; ++ fset->step = channel->intensity_uA.step; ++ fset->val = channel->intensity_uA.max; ++ ++ /* Only the flash mode have the timeout constraints settings */ ++ if (channel->mode & GB_CHANNEL_MODE_FLASH) { ++ fset = &fled->timeout; ++ fset->min = channel->timeout_us.min; ++ fset->max = channel->timeout_us.max; ++ fset->step = channel->timeout_us.step; ++ fset->val = channel->timeout_us.max; ++ } ++ ++ /* ++ * If light have torch mode channel, this channel will be the led ++ * classdev of the registered above flash classdev ++ */ ++ channel_torch = get_channel_from_mode(channel->light, ++ GB_CHANNEL_MODE_TORCH); ++ if (channel_torch) { ++ ret = __gb_lights_channel_torch_attach(channel, channel_torch); ++ if (ret < 0) ++ goto fail; ++ } ++ ++ ret = led_classdev_flash_register(&connection->bundle->dev, fled); ++ if (ret < 0) ++ goto fail; ++ ++ channel->is_registered = true; ++ return 0; ++fail: ++ channel->led = NULL; ++ return ret; ++} ++ ++static void __gb_lights_flash_led_unregister(struct gb_channel *channel) ++{ ++ if (!channel->is_registered) ++ return; ++ ++ led_classdev_flash_unregister(&channel->fled); ++} ++ ++static int gb_lights_channel_flash_config(struct gb_channel *channel) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct gb_lights_get_channel_flash_config_request req; ++ struct gb_lights_get_channel_flash_config_response conf; ++ struct led_flash_setting *fset; ++ int ret; ++ ++ req.light_id = channel->light->id; ++ req.channel_id = channel->id; ++ ++ ret = gb_operation_sync(connection, ++ GB_LIGHTS_TYPE_GET_CHANNEL_FLASH_CONFIG, ++ &req, sizeof(req), &conf, sizeof(conf)); ++ if (ret < 0) ++ return ret; ++ ++ /* ++ * Intensity constraints for flash related modes: flash, torch, ++ * indicator. They will be needed for v4l2 registration. ++ */ ++ fset = &channel->intensity_uA; ++ fset->min = le32_to_cpu(conf.intensity_min_uA); ++ fset->max = le32_to_cpu(conf.intensity_max_uA); ++ fset->step = le32_to_cpu(conf.intensity_step_uA); ++ ++ /* ++ * On flash type, max brightness is set as the number of intensity steps ++ * available. ++ */ ++ channel->led->max_brightness = (fset->max - fset->min) / fset->step; ++ ++ /* Only the flash mode have the timeout constraints settings */ ++ if (channel->mode & GB_CHANNEL_MODE_FLASH) { ++ fset = &channel->timeout_us; ++ fset->min = le32_to_cpu(conf.timeout_min_us); ++ fset->max = le32_to_cpu(conf.timeout_max_us); ++ fset->step = le32_to_cpu(conf.timeout_step_us); ++ } ++ ++ return 0; ++} ++#else ++static int gb_lights_channel_flash_config(struct gb_channel *channel) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ ++ dev_err(&connection->bundle->dev, "no support for flash devices\n"); ++ return 0; ++} ++ ++static int __gb_lights_flash_led_register(struct gb_channel *channel) ++{ ++ return 0; ++} ++ ++static void __gb_lights_flash_led_unregister(struct gb_channel *channel) ++{ ++} ++ ++#endif ++ ++static int __gb_lights_led_register(struct gb_channel *channel) ++{ ++ struct gb_connection *connection = get_conn_from_channel(channel); ++ struct led_classdev *cdev = get_channel_cdev(channel); ++ int ret; ++ ++ ret = led_classdev_register(&connection->bundle->dev, cdev); ++ if (ret < 0) ++ channel->led = NULL; ++ else ++ channel->is_registered = true; ++ return ret; ++} ++ ++static int gb_lights_channel_register(struct gb_channel *channel) ++{ ++ /* Normal LED channel, just register in led classdev and we are done */ ++ if (!is_channel_flash(channel)) ++ return __gb_lights_led_register(channel); ++ ++ /* ++ * Flash Type need more work, register flash classdev, indicator as ++ * flash classdev, torch will be led classdev of the flash classdev. ++ */ ++ if (!(channel->mode & GB_CHANNEL_MODE_TORCH)) ++ return __gb_lights_flash_led_register(channel); ++ ++ return 0; ++} ++ ++static void __gb_lights_led_unregister(struct gb_channel *channel) ++{ ++ struct led_classdev *cdev = get_channel_cdev(channel); ++ ++ if (!channel->is_registered) ++ return; ++ ++ led_classdev_unregister(cdev); ++ channel->led = NULL; ++} ++ ++static void gb_lights_channel_unregister(struct gb_channel *channel) ++{ ++ /* The same as register, handle channels differently */ ++ if (!is_channel_flash(channel)) { ++ __gb_lights_led_unregister(channel); ++ return; ++ } ++ ++ if (channel->mode & GB_CHANNEL_MODE_TORCH) ++ __gb_lights_led_unregister(channel); ++ else ++ __gb_lights_flash_led_unregister(channel); ++} ++ ++static int gb_lights_channel_config(struct gb_light *light, ++ struct gb_channel *channel) ++{ ++ struct gb_lights_get_channel_config_response conf; ++ struct gb_lights_get_channel_config_request req; ++ struct gb_connection *connection = get_conn_from_light(light); ++ struct led_classdev *cdev = get_channel_cdev(channel); ++ char *name; ++ int ret; ++ ++ req.light_id = light->id; ++ req.channel_id = channel->id; ++ ++ ret = gb_operation_sync(connection, GB_LIGHTS_TYPE_GET_CHANNEL_CONFIG, ++ &req, sizeof(req), &conf, sizeof(conf)); ++ if (ret < 0) ++ return ret; ++ ++ channel->light = light; ++ channel->mode = le32_to_cpu(conf.mode); ++ channel->flags = le32_to_cpu(conf.flags); ++ channel->color = le32_to_cpu(conf.color); ++ channel->color_name = kstrndup(conf.color_name, NAMES_MAX, GFP_KERNEL); ++ if (!channel->color_name) ++ return -ENOMEM; ++ channel->mode_name = kstrndup(conf.mode_name, NAMES_MAX, GFP_KERNEL); ++ if (!channel->mode_name) ++ return -ENOMEM; ++ ++ channel->led = cdev; ++ ++ name = kasprintf(GFP_KERNEL, "%s:%s:%s", light->name, ++ channel->color_name, channel->mode_name); ++ if (!name) ++ return -ENOMEM; ++ ++ cdev->name = name; ++ ++ cdev->max_brightness = conf.max_brightness; ++ ++ ret = channel_attr_groups_set(channel, cdev); ++ if (ret < 0) ++ return ret; ++ ++ gb_lights_led_operations_set(channel, cdev); ++ ++ /* ++ * If it is not a flash related channel (flash, torch or indicator) we ++ * are done here. If not, continue and fetch flash related ++ * configurations. ++ */ ++ if (!is_channel_flash(channel)) ++ return ret; ++ ++ light->has_flash = true; ++ ++ ret = gb_lights_channel_flash_config(channel); ++ if (ret < 0) ++ return ret; ++ ++ return ret; ++} ++ ++static int gb_lights_light_config(struct gb_lights *glights, u8 id) ++{ ++ struct gb_light *light = &glights->lights[id]; ++ struct gb_lights_get_light_config_request req; ++ struct gb_lights_get_light_config_response conf; ++ int ret; ++ int i; ++ ++ light->glights = glights; ++ light->id = id; ++ ++ req.id = id; ++ ++ ret = gb_operation_sync(glights->connection, ++ GB_LIGHTS_TYPE_GET_LIGHT_CONFIG, ++ &req, sizeof(req), &conf, sizeof(conf)); ++ if (ret < 0) ++ return ret; ++ ++ if (!conf.channel_count) ++ return -EINVAL; ++ if (!strlen(conf.name)) ++ return -EINVAL; ++ ++ light->channels_count = conf.channel_count; ++ light->name = kstrndup(conf.name, NAMES_MAX, GFP_KERNEL); ++ ++ light->channels = kzalloc(light->channels_count * ++ sizeof(struct gb_channel), GFP_KERNEL); ++ if (!light->channels) ++ return -ENOMEM; ++ ++ /* First we collect all the configurations for all channels */ ++ for (i = 0; i < light->channels_count; i++) { ++ light->channels[i].id = i; ++ ret = gb_lights_channel_config(light, &light->channels[i]); ++ if (ret < 0) ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int gb_lights_light_register(struct gb_light *light) ++{ ++ int ret; ++ int i; ++ ++ /* ++ * Then, if everything went ok in getting configurations, we register ++ * the classdev, flash classdev and v4l2 subsystem, if a flash device is ++ * found. ++ */ ++ for (i = 0; i < light->channels_count; i++) { ++ ret = gb_lights_channel_register(&light->channels[i]); ++ if (ret < 0) ++ return ret; ++ ++ mutex_init(&light->channels[i].lock); ++ } ++ ++ light->ready = true; ++ ++ if (light->has_flash) { ++ ret = gb_lights_light_v4l2_register(light); ++ if (ret < 0) { ++ light->has_flash = false; ++ return ret; ++ } ++ } ++ ++ return 0; ++} ++ ++static void gb_lights_channel_free(struct gb_channel *channel) ++{ ++ kfree(channel->attrs); ++ kfree(channel->attr_group); ++ kfree(channel->attr_groups); ++ kfree(channel->color_name); ++ kfree(channel->mode_name); ++ mutex_destroy(&channel->lock); ++} ++ ++static void gb_lights_channel_release(struct gb_channel *channel) ++{ ++ channel->releasing = true; ++ ++ gb_lights_channel_unregister(channel); ++ ++ gb_lights_channel_free(channel); ++} ++ ++static void gb_lights_light_release(struct gb_light *light) ++{ ++ int i; ++ int count; ++ ++ light->ready = false; ++ ++ count = light->channels_count; ++ ++ if (light->has_flash) ++ gb_lights_light_v4l2_unregister(light); ++ ++ for (i = 0; i < count; i++) { ++ gb_lights_channel_release(&light->channels[i]); ++ light->channels_count--; ++ } ++ kfree(light->channels); ++ kfree(light->name); ++} ++ ++static void gb_lights_release(struct gb_lights *glights) ++{ ++ int i; ++ ++ if (!glights) ++ return; ++ ++ mutex_lock(&glights->lights_lock); ++ if (!glights->lights) ++ goto free_glights; ++ ++ for (i = 0; i < glights->lights_count; i++) ++ gb_lights_light_release(&glights->lights[i]); ++ ++ kfree(glights->lights); ++ ++free_glights: ++ mutex_unlock(&glights->lights_lock); ++ mutex_destroy(&glights->lights_lock); ++ kfree(glights); ++} ++ ++static int gb_lights_get_count(struct gb_lights *glights) ++{ ++ struct gb_lights_get_lights_response resp; ++ int ret; ++ ++ ret = gb_operation_sync(glights->connection, GB_LIGHTS_TYPE_GET_LIGHTS, ++ NULL, 0, &resp, sizeof(resp)); ++ if (ret < 0) ++ return ret; ++ ++ if (!resp.lights_count) ++ return -EINVAL; ++ ++ glights->lights_count = resp.lights_count; ++ ++ return 0; ++} ++ ++static int gb_lights_create_all(struct gb_lights *glights) ++{ ++ struct gb_connection *connection = glights->connection; ++ int ret; ++ int i; ++ ++ mutex_lock(&glights->lights_lock); ++ ret = gb_lights_get_count(glights); ++ if (ret < 0) ++ goto out; ++ ++ glights->lights = kzalloc(glights->lights_count * ++ sizeof(struct gb_light), GFP_KERNEL); ++ if (!glights->lights) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ for (i = 0; i < glights->lights_count; i++) { ++ ret = gb_lights_light_config(glights, i); ++ if (ret < 0) { ++ dev_err(&connection->bundle->dev, ++ "Fail to configure lights device\n"); ++ goto out; ++ } ++ } ++ ++out: ++ mutex_unlock(&glights->lights_lock); ++ return ret; ++} ++ ++static int gb_lights_register_all(struct gb_lights *glights) ++{ ++ struct gb_connection *connection = glights->connection; ++ int ret = 0; ++ int i; ++ ++ mutex_lock(&glights->lights_lock); ++ for (i = 0; i < glights->lights_count; i++) { ++ ret = gb_lights_light_register(&glights->lights[i]); ++ if (ret < 0) { ++ dev_err(&connection->bundle->dev, ++ "Fail to enable lights device\n"); ++ break; ++ } ++ } ++ ++ mutex_unlock(&glights->lights_lock); ++ return ret; ++} ++ ++static int gb_lights_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct device *dev = &connection->bundle->dev; ++ struct gb_lights *glights = gb_connection_get_data(connection); ++ struct gb_light *light; ++ struct gb_message *request; ++ struct gb_lights_event_request *payload; ++ int ret = 0; ++ u8 light_id; ++ u8 event; ++ ++ if (op->type != GB_LIGHTS_TYPE_EVENT) { ++ dev_err(dev, "Unsupported unsolicited event: %u\n", op->type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*payload)) { ++ dev_err(dev, "Wrong event size received (%zu < %zu)\n", ++ request->payload_size, sizeof(*payload)); ++ return -EINVAL; ++ } ++ ++ payload = request->payload; ++ light_id = payload->light_id; ++ ++ if (light_id >= glights->lights_count || ++ !glights->lights[light_id].ready) { ++ dev_err(dev, "Event received for unconfigured light id: %d\n", ++ light_id); ++ return -EINVAL; ++ } ++ ++ event = payload->event; ++ ++ if (event & GB_LIGHTS_LIGHT_CONFIG) { ++ light = &glights->lights[light_id]; ++ ++ mutex_lock(&glights->lights_lock); ++ gb_lights_light_release(light); ++ ret = gb_lights_light_config(glights, light_id); ++ if (!ret) ++ ret = gb_lights_light_register(light); ++ if (ret < 0) ++ gb_lights_light_release(light); ++ mutex_unlock(&glights->lights_lock); ++ } ++ ++ return ret; ++} ++ ++static int gb_lights_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_lights *glights; ++ int ret; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LIGHTS) ++ return -ENODEV; ++ ++ glights = kzalloc(sizeof(*glights), GFP_KERNEL); ++ if (!glights) ++ return -ENOMEM; ++ ++ mutex_init(&glights->lights_lock); ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_lights_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto out; ++ } ++ ++ glights->connection = connection; ++ gb_connection_set_data(connection, glights); ++ ++ greybus_set_drvdata(bundle, glights); ++ ++ /* We aren't ready to receive an incoming request yet */ ++ ret = gb_connection_enable_tx(connection); ++ if (ret) ++ goto error_connection_destroy; ++ ++ /* ++ * Setup all the lights devices over this connection, if anything goes ++ * wrong tear down all lights ++ */ ++ ret = gb_lights_create_all(glights); ++ if (ret < 0) ++ goto error_connection_disable; ++ ++ /* We are ready to receive an incoming request now, enable RX as well */ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto error_connection_disable; ++ ++ /* Enable & register lights */ ++ ret = gb_lights_register_all(glights); ++ if (ret < 0) ++ goto error_connection_disable; ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++error_connection_disable: ++ gb_connection_disable(connection); ++error_connection_destroy: ++ gb_connection_destroy(connection); ++out: ++ gb_lights_release(glights); ++ return ret; ++} ++ ++static void gb_lights_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_lights *glights = greybus_get_drvdata(bundle); ++ ++ if (gb_pm_runtime_get_sync(bundle)) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ gb_connection_disable(glights->connection); ++ gb_connection_destroy(glights->connection); ++ ++ gb_lights_release(glights); ++} ++ ++static const struct greybus_bundle_id gb_lights_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_lights_id_table); ++ ++static struct greybus_driver gb_lights_driver = { ++ .name = "lights", ++ .probe = gb_lights_probe, ++ .disconnect = gb_lights_disconnect, ++ .id_table = gb_lights_id_table, ++}; ++module_greybus_driver(gb_lights_driver); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_log.patch b/greybus_log.patch new file mode 100644 index 00000000000000..afde8529724f68 --- /dev/null +++ b/greybus_log.patch @@ -0,0 +1,139 @@ +--- + drivers/greybus/log.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 132 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/log.c +@@ -0,0 +1,132 @@ ++/* ++ * Greybus driver for the log protocol ++ * ++ * Copyright 2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/sizes.h> ++#include <linux/uaccess.h> ++ ++#include "greybus.h" ++ ++struct gb_log { ++ struct gb_connection *connection; ++}; ++ ++static int gb_log_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct device *dev = &connection->bundle->dev; ++ struct gb_log_send_log_request *receive; ++ u16 len; ++ ++ if (op->type != GB_LOG_TYPE_SEND_LOG) { ++ dev_err(dev, "unknown request type 0x%02x\n", op->type); ++ return -EINVAL; ++ } ++ ++ /* Verify size of payload */ ++ if (op->request->payload_size < sizeof(*receive)) { ++ dev_err(dev, "log request too small (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*receive)); ++ return -EINVAL; ++ } ++ receive = op->request->payload; ++ len = le16_to_cpu(receive->len); ++ if (len != (int)(op->request->payload_size - sizeof(*receive))) { ++ dev_err(dev, "log request wrong size %d vs %d\n", len, ++ (int)(op->request->payload_size - sizeof(*receive))); ++ return -EINVAL; ++ } ++ if (len == 0) { ++ dev_err(dev, "log request of 0 bytes?\n"); ++ return -EINVAL; ++ } ++ ++ if (len > GB_LOG_MAX_LEN) { ++ dev_err(dev, "log request too big: %d\n", len); ++ return -EINVAL; ++ } ++ ++ /* Ensure the buffer is 0 terminated */ ++ receive->msg[len - 1] = '\0'; ++ ++ /* Print with dev_dbg() so that it can be easily turned off using ++ * dynamic debugging (and prevent any DoS) */ ++ dev_dbg(dev, "%s", receive->msg); ++ ++ return 0; ++} ++ ++static int gb_log_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_log *log; ++ int retval; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOG) ++ return -ENODEV; ++ ++ log = kzalloc(sizeof(*log), GFP_KERNEL); ++ if (!log) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_log_request_handler); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto error_free; ++ } ++ ++ log->connection = connection; ++ greybus_set_drvdata(bundle, log); ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto error_connection_destroy; ++ ++ return 0; ++ ++error_connection_destroy: ++ gb_connection_destroy(connection); ++error_free: ++ kfree(log); ++ return retval; ++} ++ ++static void gb_log_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_log *log = greybus_get_drvdata(bundle); ++ struct gb_connection *connection = log->connection; ++ ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ ++ kfree(log); ++} ++ ++static const struct greybus_bundle_id gb_log_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOG) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_log_id_table); ++ ++static struct greybus_driver gb_log_driver = { ++ .name = "log", ++ .probe = gb_log_probe, ++ .disconnect = gb_log_disconnect, ++ .id_table = gb_log_id_table, ++}; ++module_greybus_driver(gb_log_driver); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_loopback.patch b/greybus_loopback.patch new file mode 100644 index 00000000000000..463a2052538bcb --- /dev/null +++ b/greybus_loopback.patch @@ -0,0 +1,1372 @@ +--- + drivers/greybus/loopback.c | 1365 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1365 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/loopback.c +@@ -0,0 +1,1365 @@ ++/* ++ * Loopback bridge driver for the Greybus loopback module. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/mutex.h> ++#include <linux/slab.h> ++#include <linux/kthread.h> ++#include <linux/delay.h> ++#include <linux/random.h> ++#include <linux/sizes.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/kfifo.h> ++#include <linux/debugfs.h> ++#include <linux/list_sort.h> ++#include <linux/spinlock.h> ++#include <linux/workqueue.h> ++#include <linux/atomic.h> ++#include <linux/pm_runtime.h> ++ ++#include <asm/div64.h> ++ ++#include "greybus.h" ++#include "connection.h" ++ ++#define NSEC_PER_DAY 86400000000000ULL ++ ++struct gb_loopback_stats { ++ u32 min; ++ u32 max; ++ u64 sum; ++ u32 count; ++}; ++ ++struct gb_loopback_device { ++ struct dentry *root; ++ u32 count; ++ size_t size_max; ++ ++ /* We need to take a lock in atomic context */ ++ spinlock_t lock; ++ struct list_head list; ++ struct list_head list_op_async; ++ wait_queue_head_t wq; ++}; ++ ++static struct gb_loopback_device gb_dev; ++ ++struct gb_loopback_async_operation { ++ struct gb_loopback *gb; ++ struct gb_operation *operation; ++ struct timeval ts; ++ struct timer_list timer; ++ struct list_head entry; ++ struct work_struct work; ++ struct kref kref; ++ bool pending; ++ int (*completion)(struct gb_loopback_async_operation *op_async); ++}; ++ ++struct gb_loopback { ++ struct gb_connection *connection; ++ ++ struct dentry *file; ++ struct kfifo kfifo_lat; ++ struct kfifo kfifo_ts; ++ struct mutex mutex; ++ struct task_struct *task; ++ struct list_head entry; ++ struct device *dev; ++ wait_queue_head_t wq; ++ wait_queue_head_t wq_completion; ++ atomic_t outstanding_operations; ++ ++ /* Per connection stats */ ++ struct timeval ts; ++ struct gb_loopback_stats latency; ++ struct gb_loopback_stats throughput; ++ struct gb_loopback_stats requests_per_second; ++ struct gb_loopback_stats apbridge_unipro_latency; ++ struct gb_loopback_stats gbphy_firmware_latency; ++ ++ int type; ++ int async; ++ int id; ++ u32 size; ++ u32 iteration_max; ++ u32 iteration_count; ++ int us_wait; ++ u32 error; ++ u32 requests_completed; ++ u32 requests_timedout; ++ u32 timeout; ++ u32 jiffy_timeout; ++ u32 timeout_min; ++ u32 timeout_max; ++ u32 outstanding_operations_max; ++ u32 lbid; ++ u64 elapsed_nsecs; ++ u32 apbridge_latency_ts; ++ u32 gbphy_latency_ts; ++ ++ u32 send_count; ++}; ++ ++static struct class loopback_class = { ++ .name = "gb_loopback", ++ .owner = THIS_MODULE, ++}; ++static DEFINE_IDA(loopback_ida); ++ ++/* Min/max values in jiffies */ ++#define GB_LOOPBACK_TIMEOUT_MIN 1 ++#define GB_LOOPBACK_TIMEOUT_MAX 10000 ++ ++#define GB_LOOPBACK_FIFO_DEFAULT 8192 ++ ++static unsigned kfifo_depth = GB_LOOPBACK_FIFO_DEFAULT; ++module_param(kfifo_depth, uint, 0444); ++ ++/* Maximum size of any one send data buffer we support */ ++#define MAX_PACKET_SIZE (PAGE_SIZE * 2) ++ ++#define GB_LOOPBACK_US_WAIT_MAX 1000000 ++ ++/* interface sysfs attributes */ ++#define gb_loopback_ro_attr(field) \ ++static ssize_t field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%u\n", gb->field); \ ++} \ ++static DEVICE_ATTR_RO(field) ++ ++#define gb_loopback_ro_stats_attr(name, field, type) \ ++static ssize_t name##_##field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ /* Report 0 for min and max if no transfer successed */ \ ++ if (!gb->requests_completed) \ ++ return sprintf(buf, "0\n"); \ ++ return sprintf(buf, "%"#type"\n", gb->name.field); \ ++} \ ++static DEVICE_ATTR_RO(name##_##field) ++ ++#define gb_loopback_ro_avg_attr(name) \ ++static ssize_t name##_avg_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback_stats *stats; \ ++ struct gb_loopback *gb; \ ++ u64 avg, rem; \ ++ u32 count; \ ++ gb = dev_get_drvdata(dev); \ ++ stats = &gb->name; \ ++ count = stats->count ? stats->count : 1; \ ++ avg = stats->sum + count / 2000000; /* round closest */ \ ++ rem = do_div(avg, count); \ ++ rem *= 1000000; \ ++ do_div(rem, count); \ ++ return sprintf(buf, "%llu.%06u\n", avg, (u32)rem); \ ++} \ ++static DEVICE_ATTR_RO(name##_avg) ++ ++#define gb_loopback_stats_attrs(field) \ ++ gb_loopback_ro_stats_attr(field, min, u); \ ++ gb_loopback_ro_stats_attr(field, max, u); \ ++ gb_loopback_ro_avg_attr(field) ++ ++#define gb_loopback_attr(field, type) \ ++static ssize_t field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%"#type"\n", gb->field); \ ++} \ ++static ssize_t field##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, \ ++ size_t len) \ ++{ \ ++ int ret; \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ mutex_lock(&gb->mutex); \ ++ ret = sscanf(buf, "%"#type, &gb->field); \ ++ if (ret != 1) \ ++ len = -EINVAL; \ ++ else \ ++ gb_loopback_check_attr(gb, bundle); \ ++ mutex_unlock(&gb->mutex); \ ++ return len; \ ++} \ ++static DEVICE_ATTR_RW(field) ++ ++#define gb_dev_loopback_ro_attr(field, conn) \ ++static ssize_t field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%u\n", gb->field); \ ++} \ ++static DEVICE_ATTR_RO(field) ++ ++#define gb_dev_loopback_rw_attr(field, type) \ ++static ssize_t field##_show(struct device *dev, \ ++ struct device_attribute *attr, \ ++ char *buf) \ ++{ \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ return sprintf(buf, "%"#type"\n", gb->field); \ ++} \ ++static ssize_t field##_store(struct device *dev, \ ++ struct device_attribute *attr, \ ++ const char *buf, \ ++ size_t len) \ ++{ \ ++ int ret; \ ++ struct gb_loopback *gb = dev_get_drvdata(dev); \ ++ mutex_lock(&gb->mutex); \ ++ ret = sscanf(buf, "%"#type, &gb->field); \ ++ if (ret != 1) \ ++ len = -EINVAL; \ ++ else \ ++ gb_loopback_check_attr(gb); \ ++ mutex_unlock(&gb->mutex); \ ++ return len; \ ++} \ ++static DEVICE_ATTR_RW(field) ++ ++static void gb_loopback_reset_stats(struct gb_loopback *gb); ++static void gb_loopback_check_attr(struct gb_loopback *gb) ++{ ++ if (gb->us_wait > GB_LOOPBACK_US_WAIT_MAX) ++ gb->us_wait = GB_LOOPBACK_US_WAIT_MAX; ++ if (gb->size > gb_dev.size_max) ++ gb->size = gb_dev.size_max; ++ gb->requests_timedout = 0; ++ gb->requests_completed = 0; ++ gb->iteration_count = 0; ++ gb->send_count = 0; ++ gb->error = 0; ++ ++ if (kfifo_depth < gb->iteration_max) { ++ dev_warn(gb->dev, ++ "cannot log bytes %u kfifo_depth %u\n", ++ gb->iteration_max, kfifo_depth); ++ } ++ kfifo_reset_out(&gb->kfifo_lat); ++ kfifo_reset_out(&gb->kfifo_ts); ++ ++ switch (gb->type) { ++ case GB_LOOPBACK_TYPE_PING: ++ case GB_LOOPBACK_TYPE_TRANSFER: ++ case GB_LOOPBACK_TYPE_SINK: ++ gb->jiffy_timeout = usecs_to_jiffies(gb->timeout); ++ if (!gb->jiffy_timeout) ++ gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MIN; ++ else if (gb->jiffy_timeout > GB_LOOPBACK_TIMEOUT_MAX) ++ gb->jiffy_timeout = GB_LOOPBACK_TIMEOUT_MAX; ++ gb_loopback_reset_stats(gb); ++ wake_up(&gb->wq); ++ break; ++ default: ++ gb->type = 0; ++ break; ++ } ++} ++ ++/* Time to send and receive one message */ ++gb_loopback_stats_attrs(latency); ++/* Number of requests sent per second on this cport */ ++gb_loopback_stats_attrs(requests_per_second); ++/* Quantity of data sent and received on this cport */ ++gb_loopback_stats_attrs(throughput); ++/* Latency across the UniPro link from APBridge's perspective */ ++gb_loopback_stats_attrs(apbridge_unipro_latency); ++/* Firmware induced overhead in the GPBridge */ ++gb_loopback_stats_attrs(gbphy_firmware_latency); ++ ++/* Number of errors encountered during loop */ ++gb_loopback_ro_attr(error); ++/* Number of requests successfully completed async */ ++gb_loopback_ro_attr(requests_completed); ++/* Number of requests timed out async */ ++gb_loopback_ro_attr(requests_timedout); ++/* Timeout minimum in useconds */ ++gb_loopback_ro_attr(timeout_min); ++/* Timeout minimum in useconds */ ++gb_loopback_ro_attr(timeout_max); ++ ++/* ++ * Type of loopback message to send based on protocol type definitions ++ * 0 => Don't send message ++ * 2 => Send ping message continuously (message without payload) ++ * 3 => Send transfer message continuously (message with payload, ++ * payload returned in response) ++ * 4 => Send a sink message (message with payload, no payload in response) ++ */ ++gb_dev_loopback_rw_attr(type, d); ++/* Size of transfer message payload: 0-4096 bytes */ ++gb_dev_loopback_rw_attr(size, u); ++/* Time to wait between two messages: 0-1000 ms */ ++gb_dev_loopback_rw_attr(us_wait, d); ++/* Maximum iterations for a given operation: 1-(2^32-1), 0 implies infinite */ ++gb_dev_loopback_rw_attr(iteration_max, u); ++/* The current index of the for (i = 0; i < iteration_max; i++) loop */ ++gb_dev_loopback_ro_attr(iteration_count, false); ++/* A flag to indicate synchronous or asynchronous operations */ ++gb_dev_loopback_rw_attr(async, u); ++/* Timeout of an individual asynchronous request */ ++gb_dev_loopback_rw_attr(timeout, u); ++/* Maximum number of in-flight operations before back-off */ ++gb_dev_loopback_rw_attr(outstanding_operations_max, u); ++ ++static struct attribute *loopback_attrs[] = { ++ &dev_attr_latency_min.attr, ++ &dev_attr_latency_max.attr, ++ &dev_attr_latency_avg.attr, ++ &dev_attr_requests_per_second_min.attr, ++ &dev_attr_requests_per_second_max.attr, ++ &dev_attr_requests_per_second_avg.attr, ++ &dev_attr_throughput_min.attr, ++ &dev_attr_throughput_max.attr, ++ &dev_attr_throughput_avg.attr, ++ &dev_attr_apbridge_unipro_latency_min.attr, ++ &dev_attr_apbridge_unipro_latency_max.attr, ++ &dev_attr_apbridge_unipro_latency_avg.attr, ++ &dev_attr_gbphy_firmware_latency_min.attr, ++ &dev_attr_gbphy_firmware_latency_max.attr, ++ &dev_attr_gbphy_firmware_latency_avg.attr, ++ &dev_attr_type.attr, ++ &dev_attr_size.attr, ++ &dev_attr_us_wait.attr, ++ &dev_attr_iteration_count.attr, ++ &dev_attr_iteration_max.attr, ++ &dev_attr_async.attr, ++ &dev_attr_error.attr, ++ &dev_attr_requests_completed.attr, ++ &dev_attr_requests_timedout.attr, ++ &dev_attr_timeout.attr, ++ &dev_attr_outstanding_operations_max.attr, ++ &dev_attr_timeout_min.attr, ++ &dev_attr_timeout_max.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(loopback); ++ ++static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error); ++ ++static u32 gb_loopback_nsec_to_usec_latency(u64 elapsed_nsecs) ++{ ++ u32 lat; ++ ++ do_div(elapsed_nsecs, NSEC_PER_USEC); ++ lat = elapsed_nsecs; ++ return lat; ++} ++ ++static u64 __gb_loopback_calc_latency(u64 t1, u64 t2) ++{ ++ if (t2 > t1) ++ return t2 - t1; ++ else ++ return NSEC_PER_DAY - t2 + t1; ++} ++ ++static u64 gb_loopback_calc_latency(struct timeval *ts, struct timeval *te) ++{ ++ u64 t1, t2; ++ ++ t1 = timeval_to_ns(ts); ++ t2 = timeval_to_ns(te); ++ ++ return __gb_loopback_calc_latency(t1, t2); ++} ++ ++static void gb_loopback_push_latency_ts(struct gb_loopback *gb, ++ struct timeval *ts, struct timeval *te) ++{ ++ kfifo_in(&gb->kfifo_ts, (unsigned char *)ts, sizeof(*ts)); ++ kfifo_in(&gb->kfifo_ts, (unsigned char *)te, sizeof(*te)); ++} ++ ++static int gb_loopback_operation_sync(struct gb_loopback *gb, int type, ++ void *request, int request_size, ++ void *response, int response_size) ++{ ++ struct gb_operation *operation; ++ struct timeval ts, te; ++ int ret; ++ ++ do_gettimeofday(&ts); ++ operation = gb_operation_create(gb->connection, type, request_size, ++ response_size, GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) { ++ dev_err(&gb->connection->bundle->dev, ++ "synchronous operation failed: %d\n", ret); ++ goto out_put_operation; ++ } else { ++ if (response_size == operation->response->payload_size) { ++ memcpy(response, operation->response->payload, ++ response_size); ++ } else { ++ dev_err(&gb->connection->bundle->dev, ++ "response size %zu expected %d\n", ++ operation->response->payload_size, ++ response_size); ++ ret = -EINVAL; ++ goto out_put_operation; ++ } ++ } ++ ++ do_gettimeofday(&te); ++ ++ /* Calculate the total time the message took */ ++ gb_loopback_push_latency_ts(gb, &ts, &te); ++ gb->elapsed_nsecs = gb_loopback_calc_latency(&ts, &te); ++ ++out_put_operation: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static void __gb_loopback_async_operation_destroy(struct kref *kref) ++{ ++ struct gb_loopback_async_operation *op_async; ++ ++ op_async = container_of(kref, struct gb_loopback_async_operation, kref); ++ ++ list_del(&op_async->entry); ++ if (op_async->operation) ++ gb_operation_put(op_async->operation); ++ atomic_dec(&op_async->gb->outstanding_operations); ++ wake_up(&op_async->gb->wq_completion); ++ kfree(op_async); ++} ++ ++static void gb_loopback_async_operation_get(struct gb_loopback_async_operation ++ *op_async) ++{ ++ kref_get(&op_async->kref); ++} ++ ++static void gb_loopback_async_operation_put(struct gb_loopback_async_operation ++ *op_async) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gb_dev.lock, flags); ++ kref_put(&op_async->kref, __gb_loopback_async_operation_destroy); ++ spin_unlock_irqrestore(&gb_dev.lock, flags); ++} ++ ++static struct gb_loopback_async_operation * ++ gb_loopback_operation_find(u16 id) ++{ ++ struct gb_loopback_async_operation *op_async; ++ bool found = false; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gb_dev.lock, flags); ++ list_for_each_entry(op_async, &gb_dev.list_op_async, entry) { ++ if (op_async->operation->id == id) { ++ gb_loopback_async_operation_get(op_async); ++ found = true; ++ break; ++ } ++ } ++ spin_unlock_irqrestore(&gb_dev.lock, flags); ++ ++ return found ? op_async : NULL; ++} ++ ++static void gb_loopback_async_wait_all(struct gb_loopback *gb) ++{ ++ wait_event(gb->wq_completion, ++ !atomic_read(&gb->outstanding_operations)); ++} ++ ++static void gb_loopback_async_operation_callback(struct gb_operation *operation) ++{ ++ struct gb_loopback_async_operation *op_async; ++ struct gb_loopback *gb; ++ struct timeval te; ++ bool err = false; ++ ++ do_gettimeofday(&te); ++ op_async = gb_loopback_operation_find(operation->id); ++ if (!op_async) ++ return; ++ ++ gb = op_async->gb; ++ mutex_lock(&gb->mutex); ++ ++ if (!op_async->pending || gb_operation_result(operation)) { ++ err = true; ++ } else { ++ if (op_async->completion) ++ if (op_async->completion(op_async)) ++ err = true; ++ } ++ ++ if (!err) { ++ gb_loopback_push_latency_ts(gb, &op_async->ts, &te); ++ gb->elapsed_nsecs = gb_loopback_calc_latency(&op_async->ts, ++ &te); ++ } ++ ++ if (op_async->pending) { ++ if (err) ++ gb->error++; ++ gb->iteration_count++; ++ op_async->pending = false; ++ del_timer_sync(&op_async->timer); ++ gb_loopback_async_operation_put(op_async); ++ gb_loopback_calculate_stats(gb, err); ++ } ++ mutex_unlock(&gb->mutex); ++ ++ dev_dbg(&gb->connection->bundle->dev, "complete operation %d\n", ++ operation->id); ++ ++ gb_loopback_async_operation_put(op_async); ++} ++ ++static void gb_loopback_async_operation_work(struct work_struct *work) ++{ ++ struct gb_loopback *gb; ++ struct gb_operation *operation; ++ struct gb_loopback_async_operation *op_async; ++ ++ op_async = container_of(work, struct gb_loopback_async_operation, work); ++ gb = op_async->gb; ++ operation = op_async->operation; ++ ++ mutex_lock(&gb->mutex); ++ if (op_async->pending) { ++ gb->requests_timedout++; ++ gb->error++; ++ gb->iteration_count++; ++ op_async->pending = false; ++ gb_loopback_async_operation_put(op_async); ++ gb_loopback_calculate_stats(gb, true); ++ } ++ mutex_unlock(&gb->mutex); ++ ++ dev_dbg(&gb->connection->bundle->dev, "timeout operation %d\n", ++ operation->id); ++ ++ gb_operation_cancel(operation, -ETIMEDOUT); ++ gb_loopback_async_operation_put(op_async); ++} ++ ++static void gb_loopback_async_operation_timeout(unsigned long data) ++{ ++ struct gb_loopback_async_operation *op_async; ++ u16 id = data; ++ ++ op_async = gb_loopback_operation_find(id); ++ if (!op_async) { ++ pr_err("operation %d not found - time out ?\n", id); ++ return; ++ } ++ schedule_work(&op_async->work); ++} ++ ++static int gb_loopback_async_operation(struct gb_loopback *gb, int type, ++ void *request, int request_size, ++ int response_size, ++ void *completion) ++{ ++ struct gb_loopback_async_operation *op_async; ++ struct gb_operation *operation; ++ int ret; ++ unsigned long flags; ++ ++ op_async = kzalloc(sizeof(*op_async), GFP_KERNEL); ++ if (!op_async) ++ return -ENOMEM; ++ ++ INIT_WORK(&op_async->work, gb_loopback_async_operation_work); ++ init_timer(&op_async->timer); ++ kref_init(&op_async->kref); ++ ++ operation = gb_operation_create(gb->connection, type, request_size, ++ response_size, GFP_KERNEL); ++ if (!operation) { ++ kfree(op_async); ++ return -ENOMEM; ++ } ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ op_async->gb = gb; ++ op_async->operation = operation; ++ op_async->completion = completion; ++ ++ spin_lock_irqsave(&gb_dev.lock, flags); ++ list_add_tail(&op_async->entry, &gb_dev.list_op_async); ++ spin_unlock_irqrestore(&gb_dev.lock, flags); ++ ++ do_gettimeofday(&op_async->ts); ++ op_async->pending = true; ++ atomic_inc(&gb->outstanding_operations); ++ mutex_lock(&gb->mutex); ++ ret = gb_operation_request_send(operation, ++ gb_loopback_async_operation_callback, ++ GFP_KERNEL); ++ if (ret) ++ goto error; ++ ++ op_async->timer.function = gb_loopback_async_operation_timeout; ++ op_async->timer.expires = jiffies + gb->jiffy_timeout; ++ op_async->timer.data = (unsigned long)operation->id; ++ add_timer(&op_async->timer); ++ ++ goto done; ++error: ++ gb_loopback_async_operation_put(op_async); ++done: ++ mutex_unlock(&gb->mutex); ++ return ret; ++} ++ ++static int gb_loopback_sync_sink(struct gb_loopback *gb, u32 len) ++{ ++ struct gb_loopback_transfer_request *request; ++ int retval; ++ ++ request = kmalloc(len + sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ request->len = cpu_to_le32(len); ++ retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_SINK, ++ request, len + sizeof(*request), ++ NULL, 0); ++ kfree(request); ++ return retval; ++} ++ ++static int gb_loopback_sync_transfer(struct gb_loopback *gb, u32 len) ++{ ++ struct gb_loopback_transfer_request *request; ++ struct gb_loopback_transfer_response *response; ++ int retval; ++ ++ gb->apbridge_latency_ts = 0; ++ gb->gbphy_latency_ts = 0; ++ ++ request = kmalloc(len + sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ response = kmalloc(len + sizeof(*response), GFP_KERNEL); ++ if (!response) { ++ kfree(request); ++ return -ENOMEM; ++ } ++ ++ memset(request->data, 0x5A, len); ++ ++ request->len = cpu_to_le32(len); ++ retval = gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_TRANSFER, ++ request, len + sizeof(*request), ++ response, len + sizeof(*response)); ++ if (retval) ++ goto gb_error; ++ ++ if (memcmp(request->data, response->data, len)) { ++ dev_err(&gb->connection->bundle->dev, ++ "Loopback Data doesn't match\n"); ++ retval = -EREMOTEIO; ++ } ++ gb->apbridge_latency_ts = (u32)__le32_to_cpu(response->reserved0); ++ gb->gbphy_latency_ts = (u32)__le32_to_cpu(response->reserved1); ++ ++gb_error: ++ kfree(request); ++ kfree(response); ++ ++ return retval; ++} ++ ++static int gb_loopback_sync_ping(struct gb_loopback *gb) ++{ ++ return gb_loopback_operation_sync(gb, GB_LOOPBACK_TYPE_PING, ++ NULL, 0, NULL, 0); ++} ++ ++static int gb_loopback_async_sink(struct gb_loopback *gb, u32 len) ++{ ++ struct gb_loopback_transfer_request *request; ++ int retval; ++ ++ request = kmalloc(len + sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ request->len = cpu_to_le32(len); ++ retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_SINK, ++ request, len + sizeof(*request), ++ 0, NULL); ++ kfree(request); ++ return retval; ++} ++ ++static int gb_loopback_async_transfer_complete( ++ struct gb_loopback_async_operation *op_async) ++{ ++ struct gb_loopback *gb; ++ struct gb_operation *operation; ++ struct gb_loopback_transfer_request *request; ++ struct gb_loopback_transfer_response *response; ++ size_t len; ++ int retval = 0; ++ ++ gb = op_async->gb; ++ operation = op_async->operation; ++ request = operation->request->payload; ++ response = operation->response->payload; ++ len = le32_to_cpu(request->len); ++ ++ if (memcmp(request->data, response->data, len)) { ++ dev_err(&gb->connection->bundle->dev, ++ "Loopback Data doesn't match operation id %d\n", ++ operation->id); ++ retval = -EREMOTEIO; ++ } else { ++ gb->apbridge_latency_ts = ++ (u32)__le32_to_cpu(response->reserved0); ++ gb->gbphy_latency_ts = ++ (u32)__le32_to_cpu(response->reserved1); ++ } ++ ++ return retval; ++} ++ ++static int gb_loopback_async_transfer(struct gb_loopback *gb, u32 len) ++{ ++ struct gb_loopback_transfer_request *request; ++ int retval, response_len; ++ ++ request = kmalloc(len + sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ memset(request->data, 0x5A, len); ++ ++ request->len = cpu_to_le32(len); ++ response_len = sizeof(struct gb_loopback_transfer_response); ++ retval = gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_TRANSFER, ++ request, len + sizeof(*request), ++ len + response_len, ++ gb_loopback_async_transfer_complete); ++ if (retval) ++ goto gb_error; ++ ++gb_error: ++ kfree(request); ++ return retval; ++} ++ ++static int gb_loopback_async_ping(struct gb_loopback *gb) ++{ ++ return gb_loopback_async_operation(gb, GB_LOOPBACK_TYPE_PING, ++ NULL, 0, 0, NULL); ++} ++ ++static int gb_loopback_request_handler(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ struct gb_loopback_transfer_request *request; ++ struct gb_loopback_transfer_response *response; ++ struct device *dev = &connection->bundle->dev; ++ size_t len; ++ ++ /* By convention, the AP initiates the version operation */ ++ switch (operation->type) { ++ case GB_LOOPBACK_TYPE_PING: ++ case GB_LOOPBACK_TYPE_SINK: ++ return 0; ++ case GB_LOOPBACK_TYPE_TRANSFER: ++ if (operation->request->payload_size < sizeof(*request)) { ++ dev_err(dev, "transfer request too small (%zu < %zu)\n", ++ operation->request->payload_size, ++ sizeof(*request)); ++ return -EINVAL; /* -EMSGSIZE */ ++ } ++ request = operation->request->payload; ++ len = le32_to_cpu(request->len); ++ if (len > gb_dev.size_max) { ++ dev_err(dev, "transfer request too large (%zu > %zu)\n", ++ len, gb_dev.size_max); ++ return -EINVAL; ++ } ++ ++ if (!gb_operation_response_alloc(operation, ++ len + sizeof(*response), GFP_KERNEL)) { ++ dev_err(dev, "error allocating response\n"); ++ return -ENOMEM; ++ } ++ response = operation->response->payload; ++ response->len = cpu_to_le32(len); ++ if (len) ++ memcpy(response->data, request->data, len); ++ ++ return 0; ++ default: ++ dev_err(dev, "unsupported request: %u\n", operation->type); ++ return -EINVAL; ++ } ++} ++ ++static void gb_loopback_reset_stats(struct gb_loopback *gb) ++{ ++ struct gb_loopback_stats reset = { ++ .min = U32_MAX, ++ }; ++ ++ /* Reset per-connection stats */ ++ memcpy(&gb->latency, &reset, ++ sizeof(struct gb_loopback_stats)); ++ memcpy(&gb->throughput, &reset, ++ sizeof(struct gb_loopback_stats)); ++ memcpy(&gb->requests_per_second, &reset, ++ sizeof(struct gb_loopback_stats)); ++ memcpy(&gb->apbridge_unipro_latency, &reset, ++ sizeof(struct gb_loopback_stats)); ++ memcpy(&gb->gbphy_firmware_latency, &reset, ++ sizeof(struct gb_loopback_stats)); ++ ++ /* Should be initialized at least once per transaction set */ ++ gb->apbridge_latency_ts = 0; ++ gb->gbphy_latency_ts = 0; ++ memset(&gb->ts, 0, sizeof(struct timeval)); ++} ++ ++static void gb_loopback_update_stats(struct gb_loopback_stats *stats, u32 val) ++{ ++ if (stats->min > val) ++ stats->min = val; ++ if (stats->max < val) ++ stats->max = val; ++ stats->sum += val; ++ stats->count++; ++} ++ ++static void gb_loopback_update_stats_window(struct gb_loopback_stats *stats, ++ u64 val, u32 count) ++{ ++ stats->sum += val; ++ stats->count += count; ++ ++ do_div(val, count); ++ if (stats->min > val) ++ stats->min = val; ++ if (stats->max < val) ++ stats->max = val; ++} ++ ++static void gb_loopback_requests_update(struct gb_loopback *gb, u32 latency) ++{ ++ u64 req = gb->requests_completed * USEC_PER_SEC; ++ ++ gb_loopback_update_stats_window(&gb->requests_per_second, req, latency); ++} ++ ++static void gb_loopback_throughput_update(struct gb_loopback *gb, u32 latency) ++{ ++ u64 aggregate_size = sizeof(struct gb_operation_msg_hdr) * 2; ++ ++ switch (gb->type) { ++ case GB_LOOPBACK_TYPE_PING: ++ break; ++ case GB_LOOPBACK_TYPE_SINK: ++ aggregate_size += sizeof(struct gb_loopback_transfer_request) + ++ gb->size; ++ break; ++ case GB_LOOPBACK_TYPE_TRANSFER: ++ aggregate_size += sizeof(struct gb_loopback_transfer_request) + ++ sizeof(struct gb_loopback_transfer_response) + ++ gb->size * 2; ++ break; ++ default: ++ return; ++ } ++ ++ aggregate_size *= gb->requests_completed; ++ aggregate_size *= USEC_PER_SEC; ++ gb_loopback_update_stats_window(&gb->throughput, aggregate_size, ++ latency); ++} ++ ++static void gb_loopback_calculate_latency_stats(struct gb_loopback *gb) ++{ ++ u32 lat; ++ ++ /* Express latency in terms of microseconds */ ++ lat = gb_loopback_nsec_to_usec_latency(gb->elapsed_nsecs); ++ ++ /* Log latency stastic */ ++ gb_loopback_update_stats(&gb->latency, lat); ++ ++ /* Raw latency log on a per thread basis */ ++ kfifo_in(&gb->kfifo_lat, (unsigned char *)&lat, sizeof(lat)); ++ ++ /* Log the firmware supplied latency values */ ++ gb_loopback_update_stats(&gb->apbridge_unipro_latency, ++ gb->apbridge_latency_ts); ++ gb_loopback_update_stats(&gb->gbphy_firmware_latency, ++ gb->gbphy_latency_ts); ++} ++ ++static void gb_loopback_calculate_stats(struct gb_loopback *gb, bool error) ++{ ++ u64 nlat; ++ u32 lat; ++ struct timeval te; ++ ++ if (!error) { ++ gb->requests_completed++; ++ gb_loopback_calculate_latency_stats(gb); ++ } ++ ++ do_gettimeofday(&te); ++ nlat = gb_loopback_calc_latency(&gb->ts, &te); ++ if (nlat >= NSEC_PER_SEC || gb->iteration_count == gb->iteration_max) { ++ lat = gb_loopback_nsec_to_usec_latency(nlat); ++ ++ gb_loopback_throughput_update(gb, lat); ++ gb_loopback_requests_update(gb, lat); ++ ++ if (gb->iteration_count != gb->iteration_max) { ++ gb->ts = te; ++ gb->requests_completed = 0; ++ } ++ } ++} ++ ++static void gb_loopback_async_wait_to_send(struct gb_loopback *gb) ++{ ++ if (!(gb->async && gb->outstanding_operations_max)) ++ return; ++ wait_event_interruptible(gb->wq_completion, ++ (atomic_read(&gb->outstanding_operations) < ++ gb->outstanding_operations_max) || ++ kthread_should_stop()); ++} ++ ++static int gb_loopback_fn(void *data) ++{ ++ int error = 0; ++ int us_wait = 0; ++ int type; ++ int ret; ++ u32 size; ++ ++ struct gb_loopback *gb = data; ++ struct gb_bundle *bundle = gb->connection->bundle; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ while (1) { ++ if (!gb->type) { ++ gb_pm_runtime_put_autosuspend(bundle); ++ wait_event_interruptible(gb->wq, gb->type || ++ kthread_should_stop()); ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ } ++ ++ if (kthread_should_stop()) ++ break; ++ ++ /* Limit the maximum number of in-flight async operations */ ++ gb_loopback_async_wait_to_send(gb); ++ if (kthread_should_stop()) ++ break; ++ ++ mutex_lock(&gb->mutex); ++ ++ /* Optionally terminate */ ++ if (gb->send_count == gb->iteration_max) { ++ if (gb->iteration_count == gb->iteration_max) { ++ gb->type = 0; ++ gb->send_count = 0; ++ sysfs_notify(&gb->dev->kobj, NULL, ++ "iteration_count"); ++ } ++ mutex_unlock(&gb->mutex); ++ continue; ++ } ++ size = gb->size; ++ us_wait = gb->us_wait; ++ type = gb->type; ++ if (gb->ts.tv_usec == 0 && gb->ts.tv_sec == 0) ++ do_gettimeofday(&gb->ts); ++ mutex_unlock(&gb->mutex); ++ ++ /* Else operations to perform */ ++ if (gb->async) { ++ if (type == GB_LOOPBACK_TYPE_PING) { ++ error = gb_loopback_async_ping(gb); ++ } else if (type == GB_LOOPBACK_TYPE_TRANSFER) { ++ error = gb_loopback_async_transfer(gb, size); ++ } else if (type == GB_LOOPBACK_TYPE_SINK) { ++ error = gb_loopback_async_sink(gb, size); ++ } ++ ++ if (error) ++ gb->error++; ++ } else { ++ /* We are effectively single threaded here */ ++ if (type == GB_LOOPBACK_TYPE_PING) ++ error = gb_loopback_sync_ping(gb); ++ else if (type == GB_LOOPBACK_TYPE_TRANSFER) ++ error = gb_loopback_sync_transfer(gb, size); ++ else if (type == GB_LOOPBACK_TYPE_SINK) ++ error = gb_loopback_sync_sink(gb, size); ++ ++ if (error) ++ gb->error++; ++ gb->iteration_count++; ++ gb_loopback_calculate_stats(gb, !!error); ++ } ++ gb->send_count++; ++ if (us_wait) ++ udelay(us_wait); ++ } ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++} ++ ++static int gb_loopback_dbgfs_latency_show_common(struct seq_file *s, ++ struct kfifo *kfifo, ++ struct mutex *mutex) ++{ ++ u32 latency; ++ int retval; ++ ++ if (kfifo_len(kfifo) == 0) { ++ retval = -EAGAIN; ++ goto done; ++ } ++ ++ mutex_lock(mutex); ++ retval = kfifo_out(kfifo, &latency, sizeof(latency)); ++ if (retval > 0) { ++ seq_printf(s, "%u", latency); ++ retval = 0; ++ } ++ mutex_unlock(mutex); ++done: ++ return retval; ++} ++ ++static int gb_loopback_dbgfs_latency_show(struct seq_file *s, void *unused) ++{ ++ struct gb_loopback *gb = s->private; ++ ++ return gb_loopback_dbgfs_latency_show_common(s, &gb->kfifo_lat, ++ &gb->mutex); ++} ++ ++static int gb_loopback_latency_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, gb_loopback_dbgfs_latency_show, ++ inode->i_private); ++} ++ ++static const struct file_operations gb_loopback_debugfs_latency_ops = { ++ .open = gb_loopback_latency_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int gb_loopback_bus_id_compare(void *priv, struct list_head *lha, ++ struct list_head *lhb) ++{ ++ struct gb_loopback *a = list_entry(lha, struct gb_loopback, entry); ++ struct gb_loopback *b = list_entry(lhb, struct gb_loopback, entry); ++ struct gb_connection *ca = a->connection; ++ struct gb_connection *cb = b->connection; ++ ++ if (ca->bundle->intf->interface_id < cb->bundle->intf->interface_id) ++ return -1; ++ if (cb->bundle->intf->interface_id < ca->bundle->intf->interface_id) ++ return 1; ++ if (ca->bundle->id < cb->bundle->id) ++ return -1; ++ if (cb->bundle->id < ca->bundle->id) ++ return 1; ++ if (ca->intf_cport_id < cb->intf_cport_id) ++ return -1; ++ else if (cb->intf_cport_id < ca->intf_cport_id) ++ return 1; ++ ++ return 0; ++} ++ ++static void gb_loopback_insert_id(struct gb_loopback *gb) ++{ ++ struct gb_loopback *gb_list; ++ u32 new_lbid = 0; ++ ++ /* perform an insertion sort */ ++ list_add_tail(&gb->entry, &gb_dev.list); ++ list_sort(NULL, &gb_dev.list, gb_loopback_bus_id_compare); ++ list_for_each_entry(gb_list, &gb_dev.list, entry) { ++ gb_list->lbid = 1 << new_lbid; ++ new_lbid++; ++ } ++} ++ ++#define DEBUGFS_NAMELEN 32 ++ ++static int gb_loopback_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_loopback *gb; ++ struct device *dev; ++ int retval; ++ char name[DEBUGFS_NAMELEN]; ++ unsigned long flags; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_LOOPBACK) ++ return -ENODEV; ++ ++ gb = kzalloc(sizeof(*gb), GFP_KERNEL); ++ if (!gb) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_loopback_request_handler); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto out_kzalloc; ++ } ++ ++ gb->connection = connection; ++ greybus_set_drvdata(bundle, gb); ++ ++ init_waitqueue_head(&gb->wq); ++ init_waitqueue_head(&gb->wq_completion); ++ atomic_set(&gb->outstanding_operations, 0); ++ gb_loopback_reset_stats(gb); ++ ++ /* Reported values to user-space for min/max timeouts */ ++ gb->timeout_min = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MIN); ++ gb->timeout_max = jiffies_to_usecs(GB_LOOPBACK_TIMEOUT_MAX); ++ ++ if (!gb_dev.count) { ++ /* Calculate maximum payload */ ++ gb_dev.size_max = gb_operation_get_payload_size_max(connection); ++ if (gb_dev.size_max <= ++ sizeof(struct gb_loopback_transfer_request)) { ++ retval = -EINVAL; ++ goto out_connection_destroy; ++ } ++ gb_dev.size_max -= sizeof(struct gb_loopback_transfer_request); ++ } ++ ++ /* Create per-connection sysfs and debugfs data-points */ ++ snprintf(name, sizeof(name), "raw_latency_%s", ++ dev_name(&connection->bundle->dev)); ++ gb->file = debugfs_create_file(name, S_IFREG | S_IRUGO, gb_dev.root, gb, ++ &gb_loopback_debugfs_latency_ops); ++ ++ gb->id = ida_simple_get(&loopback_ida, 0, 0, GFP_KERNEL); ++ if (gb->id < 0) { ++ retval = gb->id; ++ goto out_debugfs_remove; ++ } ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto out_ida_remove; ++ ++ dev = device_create_with_groups(&loopback_class, ++ &connection->bundle->dev, ++ MKDEV(0, 0), gb, loopback_groups, ++ "gb_loopback%d", gb->id); ++ if (IS_ERR(dev)) { ++ retval = PTR_ERR(dev); ++ goto out_connection_disable; ++ } ++ gb->dev = dev; ++ ++ /* Allocate kfifo */ ++ if (kfifo_alloc(&gb->kfifo_lat, kfifo_depth * sizeof(u32), ++ GFP_KERNEL)) { ++ retval = -ENOMEM; ++ goto out_conn; ++ } ++ if (kfifo_alloc(&gb->kfifo_ts, kfifo_depth * sizeof(struct timeval) * 2, ++ GFP_KERNEL)) { ++ retval = -ENOMEM; ++ goto out_kfifo0; ++ } ++ ++ /* Fork worker thread */ ++ mutex_init(&gb->mutex); ++ gb->task = kthread_run(gb_loopback_fn, gb, "gb_loopback"); ++ if (IS_ERR(gb->task)) { ++ retval = PTR_ERR(gb->task); ++ goto out_kfifo1; ++ } ++ ++ spin_lock_irqsave(&gb_dev.lock, flags); ++ gb_loopback_insert_id(gb); ++ gb_dev.count++; ++ spin_unlock_irqrestore(&gb_dev.lock, flags); ++ ++ gb_connection_latency_tag_enable(connection); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++out_kfifo1: ++ kfifo_free(&gb->kfifo_ts); ++out_kfifo0: ++ kfifo_free(&gb->kfifo_lat); ++out_conn: ++ device_unregister(dev); ++out_connection_disable: ++ gb_connection_disable(connection); ++out_ida_remove: ++ ida_simple_remove(&loopback_ida, gb->id); ++out_debugfs_remove: ++ debugfs_remove(gb->file); ++out_connection_destroy: ++ gb_connection_destroy(connection); ++out_kzalloc: ++ kfree(gb); ++ ++ return retval; ++} ++ ++static void gb_loopback_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_loopback *gb = greybus_get_drvdata(bundle); ++ unsigned long flags; ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ gb_connection_disable(gb->connection); ++ ++ if (!IS_ERR_OR_NULL(gb->task)) ++ kthread_stop(gb->task); ++ ++ kfifo_free(&gb->kfifo_lat); ++ kfifo_free(&gb->kfifo_ts); ++ gb_connection_latency_tag_disable(gb->connection); ++ debugfs_remove(gb->file); ++ ++ /* ++ * FIXME: gb_loopback_async_wait_all() is redundant now, as connection ++ * is disabled at the beginning and so we can't have any more ++ * incoming/outgoing requests. ++ */ ++ gb_loopback_async_wait_all(gb); ++ ++ spin_lock_irqsave(&gb_dev.lock, flags); ++ gb_dev.count--; ++ list_del(&gb->entry); ++ spin_unlock_irqrestore(&gb_dev.lock, flags); ++ ++ device_unregister(gb->dev); ++ ida_simple_remove(&loopback_ida, gb->id); ++ ++ gb_connection_destroy(gb->connection); ++ kfree(gb); ++} ++ ++static const struct greybus_bundle_id gb_loopback_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_loopback_id_table); ++ ++static struct greybus_driver gb_loopback_driver = { ++ .name = "loopback", ++ .probe = gb_loopback_probe, ++ .disconnect = gb_loopback_disconnect, ++ .id_table = gb_loopback_id_table, ++}; ++ ++static int loopback_init(void) ++{ ++ int retval; ++ ++ INIT_LIST_HEAD(&gb_dev.list); ++ INIT_LIST_HEAD(&gb_dev.list_op_async); ++ spin_lock_init(&gb_dev.lock); ++ gb_dev.root = debugfs_create_dir("gb_loopback", NULL); ++ ++ retval = class_register(&loopback_class); ++ if (retval) ++ goto err; ++ ++ retval = greybus_register(&gb_loopback_driver); ++ if (retval) ++ goto err_unregister; ++ ++ return 0; ++ ++err_unregister: ++ class_unregister(&loopback_class); ++err: ++ debugfs_remove_recursive(gb_dev.root); ++ return retval; ++} ++module_init(loopback_init); ++ ++static void __exit loopback_exit(void) ++{ ++ debugfs_remove_recursive(gb_dev.root); ++ greybus_deregister(&gb_loopback_driver); ++ class_unregister(&loopback_class); ++ ida_destroy(&loopback_ida); ++} ++module_exit(loopback_exit); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_operations.patch b/greybus_operations.patch new file mode 100644 index 00000000000000..6398656528a23f --- /dev/null +++ b/greybus_operations.patch @@ -0,0 +1,1460 @@ +--- + drivers/greybus/operation.c | 1239 ++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/operation.h | 210 +++++++ + 2 files changed, 1449 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/operation.c +@@ -0,0 +1,1239 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/wait.h> ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++#include "greybus_trace.h" ++ ++static struct kmem_cache *gb_operation_cache; ++static struct kmem_cache *gb_message_cache; ++ ++/* Workqueue to handle Greybus operation completions. */ ++static struct workqueue_struct *gb_operation_completion_wq; ++ ++/* Wait queue for synchronous cancellations. */ ++static DECLARE_WAIT_QUEUE_HEAD(gb_operation_cancellation_queue); ++ ++/* ++ * Protects updates to operation->errno. ++ */ ++static DEFINE_SPINLOCK(gb_operations_lock); ++ ++static int gb_operation_response_send(struct gb_operation *operation, ++ int errno); ++ ++/* ++ * Increment operation active count and add to connection list unless the ++ * connection is going away. ++ * ++ * Caller holds operation reference. ++ */ ++static int gb_operation_get_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ switch (connection->state) { ++ case GB_CONNECTION_STATE_ENABLED: ++ break; ++ case GB_CONNECTION_STATE_ENABLED_TX: ++ if (gb_operation_is_incoming(operation)) ++ goto err_unlock; ++ break; ++ case GB_CONNECTION_STATE_DISCONNECTING: ++ if (!gb_operation_is_core(operation)) ++ goto err_unlock; ++ break; ++ default: ++ goto err_unlock; ++ } ++ ++ if (operation->active++ == 0) ++ list_add_tail(&operation->links, &connection->operations); ++ ++ trace_gb_operation_get_active(operation); ++ ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return 0; ++ ++err_unlock: ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return -ENOTCONN; ++} ++ ++/* Caller holds operation reference. */ ++static void gb_operation_put_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ ++ trace_gb_operation_put_active(operation); ++ ++ if (--operation->active == 0) { ++ list_del(&operation->links); ++ if (atomic_read(&operation->waiters)) ++ wake_up(&gb_operation_cancellation_queue); ++ } ++ spin_unlock_irqrestore(&connection->lock, flags); ++} ++ ++static bool gb_operation_is_active(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ unsigned long flags; ++ bool ret; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ ret = operation->active; ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return ret; ++} ++ ++/* ++ * Set an operation's result. ++ * ++ * Initially an outgoing operation's errno value is -EBADR. ++ * If no error occurs before sending the request message the only ++ * valid value operation->errno can be set to is -EINPROGRESS, ++ * indicating the request has been (or rather is about to be) sent. ++ * At that point nobody should be looking at the result until the ++ * response arrives. ++ * ++ * The first time the result gets set after the request has been ++ * sent, that result "sticks." That is, if two concurrent threads ++ * race to set the result, the first one wins. The return value ++ * tells the caller whether its result was recorded; if not the ++ * caller has nothing more to do. ++ * ++ * The result value -EILSEQ is reserved to signal an implementation ++ * error; if it's ever observed, the code performing the request has ++ * done something fundamentally wrong. It is an error to try to set ++ * the result to -EBADR, and attempts to do so result in a warning, ++ * and -EILSEQ is used instead. Similarly, the only valid result ++ * value to set for an operation in initial state is -EINPROGRESS. ++ * Attempts to do otherwise will also record a (successful) -EILSEQ ++ * operation result. ++ */ ++static bool gb_operation_result_set(struct gb_operation *operation, int result) ++{ ++ unsigned long flags; ++ int prev; ++ ++ if (result == -EINPROGRESS) { ++ /* ++ * -EINPROGRESS is used to indicate the request is ++ * in flight. It should be the first result value ++ * set after the initial -EBADR. Issue a warning ++ * and record an implementation error if it's ++ * set at any other time. ++ */ ++ spin_lock_irqsave(&gb_operations_lock, flags); ++ prev = operation->errno; ++ if (prev == -EBADR) ++ operation->errno = result; ++ else ++ operation->errno = -EILSEQ; ++ spin_unlock_irqrestore(&gb_operations_lock, flags); ++ WARN_ON(prev != -EBADR); ++ ++ return true; ++ } ++ ++ /* ++ * The first result value set after a request has been sent ++ * will be the final result of the operation. Subsequent ++ * attempts to set the result are ignored. ++ * ++ * Note that -EBADR is a reserved "initial state" result ++ * value. Attempts to set this value result in a warning, ++ * and the result code is set to -EILSEQ instead. ++ */ ++ if (WARN_ON(result == -EBADR)) ++ result = -EILSEQ; /* Nobody should be setting -EBADR */ ++ ++ spin_lock_irqsave(&gb_operations_lock, flags); ++ prev = operation->errno; ++ if (prev == -EINPROGRESS) ++ operation->errno = result; /* First and final result */ ++ spin_unlock_irqrestore(&gb_operations_lock, flags); ++ ++ return prev == -EINPROGRESS; ++} ++ ++int gb_operation_result(struct gb_operation *operation) ++{ ++ int result = operation->errno; ++ ++ WARN_ON(result == -EBADR); ++ WARN_ON(result == -EINPROGRESS); ++ ++ return result; ++} ++EXPORT_SYMBOL_GPL(gb_operation_result); ++ ++/* ++ * Looks up an outgoing operation on a connection and returns a refcounted ++ * pointer if found, or NULL otherwise. ++ */ ++static struct gb_operation * ++gb_operation_find_outgoing(struct gb_connection *connection, u16 operation_id) ++{ ++ struct gb_operation *operation; ++ unsigned long flags; ++ bool found = false; ++ ++ spin_lock_irqsave(&connection->lock, flags); ++ list_for_each_entry(operation, &connection->operations, links) ++ if (operation->id == operation_id && ++ !gb_operation_is_incoming(operation)) { ++ gb_operation_get(operation); ++ found = true; ++ break; ++ } ++ spin_unlock_irqrestore(&connection->lock, flags); ++ ++ return found ? operation : NULL; ++} ++ ++static int gb_message_send(struct gb_message *message, gfp_t gfp) ++{ ++ struct gb_connection *connection = message->operation->connection; ++ ++ trace_gb_message_send(message); ++ return connection->hd->driver->message_send(connection->hd, ++ connection->hd_cport_id, ++ message, ++ gfp); ++} ++ ++/* ++ * Cancel a message we have passed to the host device layer to be sent. ++ */ ++static void gb_message_cancel(struct gb_message *message) ++{ ++ struct gb_host_device *hd = message->operation->connection->hd; ++ ++ hd->driver->message_cancel(message); ++} ++ ++static void gb_operation_request_handle(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ int status; ++ int ret; ++ ++ if (connection->handler) { ++ status = connection->handler(operation); ++ } else { ++ dev_err(&connection->hd->dev, ++ "%s: unexpected incoming request of type 0x%02x\n", ++ connection->name, operation->type); ++ ++ status = -EPROTONOSUPPORT; ++ } ++ ++ ret = gb_operation_response_send(operation, status); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: failed to send response %d for type 0x%02x: %d\n", ++ connection->name, status, operation->type, ret); ++ return; ++ } ++} ++ ++/* ++ * Process operation work. ++ * ++ * For incoming requests, call the protocol request handler. The operation ++ * result should be -EINPROGRESS at this point. ++ * ++ * For outgoing requests, the operation result value should have ++ * been set before queueing this. The operation callback function ++ * allows the original requester to know the request has completed ++ * and its result is available. ++ */ ++static void gb_operation_work(struct work_struct *work) ++{ ++ struct gb_operation *operation; ++ ++ operation = container_of(work, struct gb_operation, work); ++ ++ if (gb_operation_is_incoming(operation)) ++ gb_operation_request_handle(operation); ++ else ++ operation->callback(operation); ++ ++ gb_operation_put_active(operation); ++ gb_operation_put(operation); ++} ++ ++static void gb_operation_message_init(struct gb_host_device *hd, ++ struct gb_message *message, u16 operation_id, ++ size_t payload_size, u8 type) ++{ ++ struct gb_operation_msg_hdr *header; ++ ++ header = message->buffer; ++ ++ message->header = header; ++ message->payload = payload_size ? header + 1 : NULL; ++ message->payload_size = payload_size; ++ ++ /* ++ * The type supplied for incoming message buffers will be ++ * GB_REQUEST_TYPE_INVALID. Such buffers will be overwritten by ++ * arriving data so there's no need to initialize the message header. ++ */ ++ if (type != GB_REQUEST_TYPE_INVALID) { ++ u16 message_size = (u16)(sizeof(*header) + payload_size); ++ ++ /* ++ * For a request, the operation id gets filled in ++ * when the message is sent. For a response, it ++ * will be copied from the request by the caller. ++ * ++ * The result field in a request message must be ++ * zero. It will be set just prior to sending for ++ * a response. ++ */ ++ header->size = cpu_to_le16(message_size); ++ header->operation_id = 0; ++ header->type = type; ++ header->result = 0; ++ } ++} ++ ++/* ++ * Allocate a message to be used for an operation request or response. ++ * Both types of message contain a common header. The request message ++ * for an outgoing operation is outbound, as is the response message ++ * for an incoming operation. The message header for an outbound ++ * message is partially initialized here. ++ * ++ * The headers for inbound messages don't need to be initialized; ++ * they'll be filled in by arriving data. ++ * ++ * Our message buffers have the following layout: ++ * message header \_ these combined are ++ * message payload / the message size ++ */ ++static struct gb_message * ++gb_operation_message_alloc(struct gb_host_device *hd, u8 type, ++ size_t payload_size, gfp_t gfp_flags) ++{ ++ struct gb_message *message; ++ struct gb_operation_msg_hdr *header; ++ size_t message_size = payload_size + sizeof(*header); ++ ++ if (message_size > hd->buffer_size_max) { ++ dev_warn(&hd->dev, "requested message size too big (%zu > %zu)\n", ++ message_size, hd->buffer_size_max); ++ return NULL; ++ } ++ ++ /* Allocate the message structure and buffer. */ ++ message = kmem_cache_zalloc(gb_message_cache, gfp_flags); ++ if (!message) ++ return NULL; ++ ++ message->buffer = kzalloc(message_size, gfp_flags); ++ if (!message->buffer) ++ goto err_free_message; ++ ++ /* Initialize the message. Operation id is filled in later. */ ++ gb_operation_message_init(hd, message, 0, payload_size, type); ++ ++ return message; ++ ++err_free_message: ++ kmem_cache_free(gb_message_cache, message); ++ ++ return NULL; ++} ++ ++static void gb_operation_message_free(struct gb_message *message) ++{ ++ kfree(message->buffer); ++ kmem_cache_free(gb_message_cache, message); ++} ++ ++/* ++ * Map an enum gb_operation_status value (which is represented in a ++ * message as a single byte) to an appropriate Linux negative errno. ++ */ ++static int gb_operation_status_map(u8 status) ++{ ++ switch (status) { ++ case GB_OP_SUCCESS: ++ return 0; ++ case GB_OP_INTERRUPTED: ++ return -EINTR; ++ case GB_OP_TIMEOUT: ++ return -ETIMEDOUT; ++ case GB_OP_NO_MEMORY: ++ return -ENOMEM; ++ case GB_OP_PROTOCOL_BAD: ++ return -EPROTONOSUPPORT; ++ case GB_OP_OVERFLOW: ++ return -EMSGSIZE; ++ case GB_OP_INVALID: ++ return -EINVAL; ++ case GB_OP_RETRY: ++ return -EAGAIN; ++ case GB_OP_NONEXISTENT: ++ return -ENODEV; ++ case GB_OP_MALFUNCTION: ++ return -EILSEQ; ++ case GB_OP_UNKNOWN_ERROR: ++ default: ++ return -EIO; ++ } ++} ++ ++/* ++ * Map a Linux errno value (from operation->errno) into the value ++ * that should represent it in a response message status sent ++ * over the wire. Returns an enum gb_operation_status value (which ++ * is represented in a message as a single byte). ++ */ ++static u8 gb_operation_errno_map(int errno) ++{ ++ switch (errno) { ++ case 0: ++ return GB_OP_SUCCESS; ++ case -EINTR: ++ return GB_OP_INTERRUPTED; ++ case -ETIMEDOUT: ++ return GB_OP_TIMEOUT; ++ case -ENOMEM: ++ return GB_OP_NO_MEMORY; ++ case -EPROTONOSUPPORT: ++ return GB_OP_PROTOCOL_BAD; ++ case -EMSGSIZE: ++ return GB_OP_OVERFLOW; /* Could be underflow too */ ++ case -EINVAL: ++ return GB_OP_INVALID; ++ case -EAGAIN: ++ return GB_OP_RETRY; ++ case -EILSEQ: ++ return GB_OP_MALFUNCTION; ++ case -ENODEV: ++ return GB_OP_NONEXISTENT; ++ case -EIO: ++ default: ++ return GB_OP_UNKNOWN_ERROR; ++ } ++} ++ ++bool gb_operation_response_alloc(struct gb_operation *operation, ++ size_t response_size, gfp_t gfp) ++{ ++ struct gb_host_device *hd = operation->connection->hd; ++ struct gb_operation_msg_hdr *request_header; ++ struct gb_message *response; ++ u8 type; ++ ++ type = operation->type | GB_MESSAGE_TYPE_RESPONSE; ++ response = gb_operation_message_alloc(hd, type, response_size, gfp); ++ if (!response) ++ return false; ++ response->operation = operation; ++ ++ /* ++ * Size and type get initialized when the message is ++ * allocated. The errno will be set before sending. All ++ * that's left is the operation id, which we copy from the ++ * request message header (as-is, in little-endian order). ++ */ ++ request_header = operation->request->header; ++ response->header->operation_id = request_header->operation_id; ++ operation->response = response; ++ ++ return true; ++} ++EXPORT_SYMBOL_GPL(gb_operation_response_alloc); ++ ++/* ++ * Create a Greybus operation to be sent over the given connection. ++ * The request buffer will be big enough for a payload of the given ++ * size. ++ * ++ * For outgoing requests, the request message's header will be ++ * initialized with the type of the request and the message size. ++ * Outgoing operations must also specify the response buffer size, ++ * which must be sufficient to hold all expected response data. The ++ * response message header will eventually be overwritten, so there's ++ * no need to initialize it here. ++ * ++ * Request messages for incoming operations can arrive in interrupt ++ * context, so they must be allocated with GFP_ATOMIC. In this case ++ * the request buffer will be immediately overwritten, so there is ++ * no need to initialize the message header. Responsibility for ++ * allocating a response buffer lies with the incoming request ++ * handler for a protocol. So we don't allocate that here. ++ * ++ * Returns a pointer to the new operation or a null pointer if an ++ * error occurs. ++ */ ++static struct gb_operation * ++gb_operation_create_common(struct gb_connection *connection, u8 type, ++ size_t request_size, size_t response_size, ++ unsigned long op_flags, gfp_t gfp_flags) ++{ ++ struct gb_host_device *hd = connection->hd; ++ struct gb_operation *operation; ++ ++ operation = kmem_cache_zalloc(gb_operation_cache, gfp_flags); ++ if (!operation) ++ return NULL; ++ operation->connection = connection; ++ ++ operation->request = gb_operation_message_alloc(hd, type, request_size, ++ gfp_flags); ++ if (!operation->request) ++ goto err_cache; ++ operation->request->operation = operation; ++ ++ /* Allocate the response buffer for outgoing operations */ ++ if (!(op_flags & GB_OPERATION_FLAG_INCOMING)) { ++ if (!gb_operation_response_alloc(operation, response_size, ++ gfp_flags)) { ++ goto err_request; ++ } ++ } ++ ++ operation->flags = op_flags; ++ operation->type = type; ++ operation->errno = -EBADR; /* Initial value--means "never set" */ ++ ++ INIT_WORK(&operation->work, gb_operation_work); ++ init_completion(&operation->completion); ++ kref_init(&operation->kref); ++ atomic_set(&operation->waiters, 0); ++ ++ return operation; ++ ++err_request: ++ gb_operation_message_free(operation->request); ++err_cache: ++ kmem_cache_free(gb_operation_cache, operation); ++ ++ return NULL; ++} ++ ++/* ++ * Create a new operation associated with the given connection. The ++ * request and response sizes provided are the number of bytes ++ * required to hold the request/response payload only. Both of ++ * these are allowed to be 0. Note that 0x00 is reserved as an ++ * invalid operation type for all protocols, and this is enforced ++ * here. ++ */ ++struct gb_operation * ++gb_operation_create_flags(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp) ++{ ++ struct gb_operation *operation; ++ ++ if (WARN_ON_ONCE(type == GB_REQUEST_TYPE_INVALID)) ++ return NULL; ++ if (WARN_ON_ONCE(type & GB_MESSAGE_TYPE_RESPONSE)) ++ type &= ~GB_MESSAGE_TYPE_RESPONSE; ++ ++ if (WARN_ON_ONCE(flags & ~GB_OPERATION_FLAG_USER_MASK)) ++ flags &= GB_OPERATION_FLAG_USER_MASK; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, response_size, ++ flags, gfp); ++ if (operation) ++ trace_gb_operation_create(operation); ++ ++ return operation; ++} ++EXPORT_SYMBOL_GPL(gb_operation_create_flags); ++ ++struct gb_operation * ++gb_operation_create_core(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp) ++{ ++ struct gb_operation *operation; ++ ++ flags |= GB_OPERATION_FLAG_CORE; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, response_size, ++ flags, gfp); ++ if (operation) ++ trace_gb_operation_create_core(operation); ++ ++ return operation; ++} ++/* Do not export this function. */ ++ ++size_t gb_operation_get_payload_size_max(struct gb_connection *connection) ++{ ++ struct gb_host_device *hd = connection->hd; ++ ++ return hd->buffer_size_max - sizeof(struct gb_operation_msg_hdr); ++} ++EXPORT_SYMBOL_GPL(gb_operation_get_payload_size_max); ++ ++static struct gb_operation * ++gb_operation_create_incoming(struct gb_connection *connection, u16 id, ++ u8 type, void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ size_t request_size; ++ unsigned long flags = GB_OPERATION_FLAG_INCOMING; ++ ++ /* Caller has made sure we at least have a message header. */ ++ request_size = size - sizeof(struct gb_operation_msg_hdr); ++ ++ if (!id) ++ flags |= GB_OPERATION_FLAG_UNIDIRECTIONAL; ++ ++ operation = gb_operation_create_common(connection, type, ++ request_size, ++ GB_REQUEST_TYPE_INVALID, ++ flags, GFP_ATOMIC); ++ if (!operation) ++ return NULL; ++ ++ operation->id = id; ++ memcpy(operation->request->header, data, size); ++ trace_gb_operation_create_incoming(operation); ++ ++ return operation; ++} ++ ++/* ++ * Get an additional reference on an operation. ++ */ ++void gb_operation_get(struct gb_operation *operation) ++{ ++ kref_get(&operation->kref); ++} ++EXPORT_SYMBOL_GPL(gb_operation_get); ++ ++/* ++ * Destroy a previously created operation. ++ */ ++static void _gb_operation_destroy(struct kref *kref) ++{ ++ struct gb_operation *operation; ++ ++ operation = container_of(kref, struct gb_operation, kref); ++ ++ trace_gb_operation_destroy(operation); ++ ++ if (operation->response) ++ gb_operation_message_free(operation->response); ++ gb_operation_message_free(operation->request); ++ ++ kmem_cache_free(gb_operation_cache, operation); ++} ++ ++/* ++ * Drop a reference on an operation, and destroy it when the last ++ * one is gone. ++ */ ++void gb_operation_put(struct gb_operation *operation) ++{ ++ if (WARN_ON(!operation)) ++ return; ++ ++ kref_put(&operation->kref, _gb_operation_destroy); ++} ++EXPORT_SYMBOL_GPL(gb_operation_put); ++ ++/* Tell the requester we're done */ ++static void gb_operation_sync_callback(struct gb_operation *operation) ++{ ++ complete(&operation->completion); ++} ++ ++/** ++ * gb_operation_request_send() - send an operation request message ++ * @operation: the operation to initiate ++ * @callback: the operation completion callback ++ * @gfp: the memory flags to use for any allocations ++ * ++ * The caller has filled in any payload so the request message is ready to go. ++ * The callback function supplied will be called when the response message has ++ * arrived, a unidirectional request has been sent, or the operation is ++ * cancelled, indicating that the operation is complete. The callback function ++ * can fetch the result of the operation using gb_operation_result() if ++ * desired. ++ * ++ * Return: 0 if the request was successfully queued in the host-driver queues, ++ * or a negative errno. ++ */ ++int gb_operation_request_send(struct gb_operation *operation, ++ gb_operation_callback callback, ++ gfp_t gfp) ++{ ++ struct gb_connection *connection = operation->connection; ++ struct gb_operation_msg_hdr *header; ++ unsigned int cycle; ++ int ret; ++ ++ if (gb_connection_is_offloaded(connection)) ++ return -EBUSY; ++ ++ if (!callback) ++ return -EINVAL; ++ ++ /* ++ * Record the callback function, which is executed in ++ * non-atomic (workqueue) context when the final result ++ * of an operation has been set. ++ */ ++ operation->callback = callback; ++ ++ /* ++ * Assign the operation's id, and store it in the request header. ++ * Zero is a reserved operation id for unidirectional operations. ++ */ ++ if (gb_operation_is_unidirectional(operation)) { ++ operation->id = 0; ++ } else { ++ cycle = (unsigned int)atomic_inc_return(&connection->op_cycle); ++ operation->id = (u16)(cycle % U16_MAX + 1); ++ } ++ ++ header = operation->request->header; ++ header->operation_id = cpu_to_le16(operation->id); ++ ++ gb_operation_result_set(operation, -EINPROGRESS); ++ ++ /* ++ * Get an extra reference on the operation. It'll be dropped when the ++ * operation completes. ++ */ ++ gb_operation_get(operation); ++ ret = gb_operation_get_active(operation); ++ if (ret) ++ goto err_put; ++ ++ ret = gb_message_send(operation->request, gfp); ++ if (ret) ++ goto err_put_active; ++ ++ return 0; ++ ++err_put_active: ++ gb_operation_put_active(operation); ++err_put: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_request_send); ++ ++/* ++ * Send a synchronous operation. This function is expected to ++ * block, returning only when the response has arrived, (or when an ++ * error is detected. The return value is the result of the ++ * operation. ++ */ ++int gb_operation_request_send_sync_timeout(struct gb_operation *operation, ++ unsigned int timeout) ++{ ++ int ret; ++ unsigned long timeout_jiffies; ++ ++ ret = gb_operation_request_send(operation, gb_operation_sync_callback, ++ GFP_KERNEL); ++ if (ret) ++ return ret; ++ ++ if (timeout) ++ timeout_jiffies = msecs_to_jiffies(timeout); ++ else ++ timeout_jiffies = MAX_SCHEDULE_TIMEOUT; ++ ++ ret = wait_for_completion_interruptible_timeout(&operation->completion, ++ timeout_jiffies); ++ if (ret < 0) { ++ /* Cancel the operation if interrupted */ ++ gb_operation_cancel(operation, -ECANCELED); ++ } else if (ret == 0) { ++ /* Cancel the operation if op timed out */ ++ gb_operation_cancel(operation, -ETIMEDOUT); ++ } ++ ++ return gb_operation_result(operation); ++} ++EXPORT_SYMBOL_GPL(gb_operation_request_send_sync_timeout); ++ ++/* ++ * Send a response for an incoming operation request. A non-zero ++ * errno indicates a failed operation. ++ * ++ * If there is any response payload, the incoming request handler is ++ * responsible for allocating the response message. Otherwise the ++ * it can simply supply the result errno; this function will ++ * allocate the response message if necessary. ++ */ ++static int gb_operation_response_send(struct gb_operation *operation, ++ int errno) ++{ ++ struct gb_connection *connection = operation->connection; ++ int ret; ++ ++ if (!operation->response && ++ !gb_operation_is_unidirectional(operation)) { ++ if (!gb_operation_response_alloc(operation, 0, GFP_KERNEL)) ++ return -ENOMEM; ++ } ++ ++ /* Record the result */ ++ if (!gb_operation_result_set(operation, errno)) { ++ dev_err(&connection->hd->dev, "request result already set\n"); ++ return -EIO; /* Shouldn't happen */ ++ } ++ ++ /* Sender of request does not care about response. */ ++ if (gb_operation_is_unidirectional(operation)) ++ return 0; ++ ++ /* Reference will be dropped when message has been sent. */ ++ gb_operation_get(operation); ++ ret = gb_operation_get_active(operation); ++ if (ret) ++ goto err_put; ++ ++ /* Fill in the response header and send it */ ++ operation->response->header->result = gb_operation_errno_map(errno); ++ ++ ret = gb_message_send(operation->response, GFP_KERNEL); ++ if (ret) ++ goto err_put_active; ++ ++ return 0; ++ ++err_put_active: ++ gb_operation_put_active(operation); ++err_put: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++/* ++ * This function is called when a message send request has completed. ++ */ ++void greybus_message_sent(struct gb_host_device *hd, ++ struct gb_message *message, int status) ++{ ++ struct gb_operation *operation = message->operation; ++ struct gb_connection *connection = operation->connection; ++ ++ /* ++ * If the message was a response, we just need to drop our ++ * reference to the operation. If an error occurred, report ++ * it. ++ * ++ * For requests, if there's no error and the operation in not ++ * unidirectional, there's nothing more to do until the response ++ * arrives. If an error occurred attempting to send it, or if the ++ * operation is unidrectional, record the result of the operation and ++ * schedule its completion. ++ */ ++ if (message == operation->response) { ++ if (status) { ++ dev_err(&connection->hd->dev, ++ "%s: error sending response 0x%02x: %d\n", ++ connection->name, operation->type, status); ++ } ++ ++ gb_operation_put_active(operation); ++ gb_operation_put(operation); ++ } else if (status || gb_operation_is_unidirectional(operation)) { ++ if (gb_operation_result_set(operation, status)) { ++ queue_work(gb_operation_completion_wq, ++ &operation->work); ++ } ++ } ++} ++EXPORT_SYMBOL_GPL(greybus_message_sent); ++ ++/* ++ * We've received data on a connection, and it doesn't look like a ++ * response, so we assume it's a request. ++ * ++ * This is called in interrupt context, so just copy the incoming ++ * data into the request buffer and handle the rest via workqueue. ++ */ ++static void gb_connection_recv_request(struct gb_connection *connection, ++ const struct gb_operation_msg_hdr *header, ++ void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ u16 operation_id; ++ u8 type; ++ int ret; ++ ++ operation_id = le16_to_cpu(header->operation_id); ++ type = header->type; ++ ++ operation = gb_operation_create_incoming(connection, operation_id, ++ type, data, size); ++ if (!operation) { ++ dev_err(&connection->hd->dev, ++ "%s: can't create incoming operation\n", ++ connection->name); ++ return; ++ } ++ ++ ret = gb_operation_get_active(operation); ++ if (ret) { ++ gb_operation_put(operation); ++ return; ++ } ++ trace_gb_message_recv_request(operation->request); ++ ++ /* ++ * The initial reference to the operation will be dropped when the ++ * request handler returns. ++ */ ++ if (gb_operation_result_set(operation, -EINPROGRESS)) ++ queue_work(connection->wq, &operation->work); ++} ++ ++/* ++ * We've received data that appears to be an operation response ++ * message. Look up the operation, and record that we've received ++ * its response. ++ * ++ * This is called in interrupt context, so just copy the incoming ++ * data into the response buffer and handle the rest via workqueue. ++ */ ++static void gb_connection_recv_response(struct gb_connection *connection, ++ const struct gb_operation_msg_hdr *header, ++ void *data, size_t size) ++{ ++ struct gb_operation *operation; ++ struct gb_message *message; ++ size_t message_size; ++ u16 operation_id; ++ int errno; ++ ++ operation_id = le16_to_cpu(header->operation_id); ++ ++ if (!operation_id) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: invalid response id 0 received\n", ++ connection->name); ++ return; ++ } ++ ++ operation = gb_operation_find_outgoing(connection, operation_id); ++ if (!operation) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: unexpected response id 0x%04x received\n", ++ connection->name, operation_id); ++ return; ++ } ++ ++ errno = gb_operation_status_map(header->result); ++ message = operation->response; ++ message_size = sizeof(*header) + message->payload_size; ++ if (!errno && size > message_size) { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: malformed response 0x%02x received (%zu > %zu)\n", ++ connection->name, header->type, ++ size, message_size); ++ errno = -EMSGSIZE; ++ } else if (!errno && size < message_size) { ++ if (gb_operation_short_response_allowed(operation)) { ++ message->payload_size = size - sizeof(*header); ++ } else { ++ dev_err_ratelimited(&connection->hd->dev, ++ "%s: short response 0x%02x received (%zu < %zu)\n", ++ connection->name, header->type, ++ size, message_size); ++ errno = -EMSGSIZE; ++ } ++ } ++ ++ /* We must ignore the payload if a bad status is returned */ ++ if (errno) ++ size = sizeof(*header); ++ ++ /* The rest will be handled in work queue context */ ++ if (gb_operation_result_set(operation, errno)) { ++ memcpy(message->buffer, data, size); ++ ++ trace_gb_message_recv_response(message); ++ ++ queue_work(gb_operation_completion_wq, &operation->work); ++ } ++ ++ gb_operation_put(operation); ++} ++ ++/* ++ * Handle data arriving on a connection. As soon as we return the ++ * supplied data buffer will be reused (so unless we do something ++ * with, it's effectively dropped). ++ */ ++void gb_connection_recv(struct gb_connection *connection, ++ void *data, size_t size) ++{ ++ struct gb_operation_msg_hdr header; ++ struct device *dev = &connection->hd->dev; ++ size_t msg_size; ++ ++ if (connection->state == GB_CONNECTION_STATE_DISABLED || ++ gb_connection_is_offloaded(connection)) { ++ dev_warn_ratelimited(dev, "%s: dropping %zu received bytes\n", ++ connection->name, size); ++ return; ++ } ++ ++ if (size < sizeof(header)) { ++ dev_err_ratelimited(dev, "%s: short message received\n", ++ connection->name); ++ return; ++ } ++ ++ /* Use memcpy as data may be unaligned */ ++ memcpy(&header, data, sizeof(header)); ++ msg_size = le16_to_cpu(header.size); ++ if (size < msg_size) { ++ dev_err_ratelimited(dev, ++ "%s: incomplete message 0x%04x of type 0x%02x received (%zu < %zu)\n", ++ connection->name, ++ le16_to_cpu(header.operation_id), ++ header.type, size, msg_size); ++ return; /* XXX Should still complete operation */ ++ } ++ ++ if (header.type & GB_MESSAGE_TYPE_RESPONSE) { ++ gb_connection_recv_response(connection, &header, data, ++ msg_size); ++ } else { ++ gb_connection_recv_request(connection, &header, data, ++ msg_size); ++ } ++} ++ ++/* ++ * Cancel an outgoing operation synchronously, and record the given error to ++ * indicate why. ++ */ ++void gb_operation_cancel(struct gb_operation *operation, int errno) ++{ ++ if (WARN_ON(gb_operation_is_incoming(operation))) ++ return; ++ ++ if (gb_operation_result_set(operation, errno)) { ++ gb_message_cancel(operation->request); ++ queue_work(gb_operation_completion_wq, &operation->work); ++ } ++ trace_gb_message_cancel_outgoing(operation->request); ++ ++ atomic_inc(&operation->waiters); ++ wait_event(gb_operation_cancellation_queue, ++ !gb_operation_is_active(operation)); ++ atomic_dec(&operation->waiters); ++} ++EXPORT_SYMBOL_GPL(gb_operation_cancel); ++ ++/* ++ * Cancel an incoming operation synchronously. Called during connection tear ++ * down. ++ */ ++void gb_operation_cancel_incoming(struct gb_operation *operation, int errno) ++{ ++ if (WARN_ON(!gb_operation_is_incoming(operation))) ++ return; ++ ++ if (!gb_operation_is_unidirectional(operation)) { ++ /* ++ * Make sure the request handler has submitted the response ++ * before cancelling it. ++ */ ++ flush_work(&operation->work); ++ if (!gb_operation_result_set(operation, errno)) ++ gb_message_cancel(operation->response); ++ } ++ trace_gb_message_cancel_incoming(operation->response); ++ ++ atomic_inc(&operation->waiters); ++ wait_event(gb_operation_cancellation_queue, ++ !gb_operation_is_active(operation)); ++ atomic_dec(&operation->waiters); ++} ++ ++/** ++ * gb_operation_sync_timeout() - implement a "simple" synchronous operation ++ * @connection: the Greybus connection to send this to ++ * @type: the type of operation to send ++ * @request: pointer to a memory buffer to copy the request from ++ * @request_size: size of @request ++ * @response: pointer to a memory buffer to copy the response to ++ * @response_size: the size of @response. ++ * @timeout: operation timeout in milliseconds ++ * ++ * This function implements a simple synchronous Greybus operation. It sends ++ * the provided operation request and waits (sleeps) until the corresponding ++ * operation response message has been successfully received, or an error ++ * occurs. @request and @response are buffers to hold the request and response ++ * data respectively, and if they are not NULL, their size must be specified in ++ * @request_size and @response_size. ++ * ++ * If a response payload is to come back, and @response is not NULL, ++ * @response_size number of bytes will be copied into @response if the operation ++ * is successful. ++ * ++ * If there is an error, the response buffer is left alone. ++ */ ++int gb_operation_sync_timeout(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size, ++ unsigned int timeout) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ if ((response_size && !response) || ++ (request_size && !request)) ++ return -EINVAL; ++ ++ operation = gb_operation_create(connection, type, ++ request_size, response_size, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync_timeout(operation, timeout); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: synchronous operation id 0x%04x of type 0x%02x failed: %d\n", ++ connection->name, operation->id, type, ret); ++ } else { ++ if (response_size) { ++ memcpy(response, operation->response->payload, ++ response_size); ++ } ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_sync_timeout); ++ ++/** ++ * gb_operation_unidirectional_timeout() - initiate a unidirectional operation ++ * @connection: connection to use ++ * @type: type of operation to send ++ * @request: memory buffer to copy the request from ++ * @request_size: size of @request ++ * @timeout: send timeout in milliseconds ++ * ++ * Initiate a unidirectional operation by sending a request message and ++ * waiting for it to be acknowledged as sent by the host device. ++ * ++ * Note that successful send of a unidirectional operation does not imply that ++ * the request as actually reached the remote end of the connection. ++ */ ++int gb_operation_unidirectional_timeout(struct gb_connection *connection, ++ int type, void *request, int request_size, ++ unsigned int timeout) ++{ ++ struct gb_operation *operation; ++ int ret; ++ ++ if (request_size && !request) ++ return -EINVAL; ++ ++ operation = gb_operation_create_flags(connection, type, ++ request_size, 0, ++ GB_OPERATION_FLAG_UNIDIRECTIONAL, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ if (request_size) ++ memcpy(operation->request->payload, request, request_size); ++ ++ ret = gb_operation_request_send_sync_timeout(operation, timeout); ++ if (ret) { ++ dev_err(&connection->hd->dev, ++ "%s: unidirectional operation of type 0x%02x failed: %d\n", ++ connection->name, type, ret); ++ } ++ ++ gb_operation_put(operation); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_operation_unidirectional_timeout); ++ ++int __init gb_operation_init(void) ++{ ++ gb_message_cache = kmem_cache_create("gb_message_cache", ++ sizeof(struct gb_message), 0, 0, NULL); ++ if (!gb_message_cache) ++ return -ENOMEM; ++ ++ gb_operation_cache = kmem_cache_create("gb_operation_cache", ++ sizeof(struct gb_operation), 0, 0, NULL); ++ if (!gb_operation_cache) ++ goto err_destroy_message_cache; ++ ++ gb_operation_completion_wq = alloc_workqueue("greybus_completion", ++ 0, 0); ++ if (!gb_operation_completion_wq) ++ goto err_destroy_operation_cache; ++ ++ return 0; ++ ++err_destroy_operation_cache: ++ kmem_cache_destroy(gb_operation_cache); ++ gb_operation_cache = NULL; ++err_destroy_message_cache: ++ kmem_cache_destroy(gb_message_cache); ++ gb_message_cache = NULL; ++ ++ return -ENOMEM; ++} ++ ++void gb_operation_exit(void) ++{ ++ destroy_workqueue(gb_operation_completion_wq); ++ gb_operation_completion_wq = NULL; ++ kmem_cache_destroy(gb_operation_cache); ++ gb_operation_cache = NULL; ++ kmem_cache_destroy(gb_message_cache); ++ gb_message_cache = NULL; ++} +--- /dev/null ++++ b/drivers/greybus/operation.h +@@ -0,0 +1,210 @@ ++/* ++ * Greybus operations ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __OPERATION_H ++#define __OPERATION_H ++ ++#include <linux/completion.h> ++ ++struct gb_operation; ++ ++/* The default amount of time a request is given to complete */ ++#define GB_OPERATION_TIMEOUT_DEFAULT 1000 /* milliseconds */ ++ ++/* ++ * The top bit of the type in an operation message header indicates ++ * whether the message is a request (bit clear) or response (bit set) ++ */ ++#define GB_MESSAGE_TYPE_RESPONSE ((u8)0x80) ++ ++enum gb_operation_result { ++ GB_OP_SUCCESS = 0x00, ++ GB_OP_INTERRUPTED = 0x01, ++ GB_OP_TIMEOUT = 0x02, ++ GB_OP_NO_MEMORY = 0x03, ++ GB_OP_PROTOCOL_BAD = 0x04, ++ GB_OP_OVERFLOW = 0x05, ++ GB_OP_INVALID = 0x06, ++ GB_OP_RETRY = 0x07, ++ GB_OP_NONEXISTENT = 0x08, ++ GB_OP_UNKNOWN_ERROR = 0xfe, ++ GB_OP_MALFUNCTION = 0xff, ++}; ++ ++#define GB_OPERATION_MESSAGE_SIZE_MIN sizeof(struct gb_operation_msg_hdr) ++#define GB_OPERATION_MESSAGE_SIZE_MAX U16_MAX ++ ++/* ++ * Protocol code should only examine the payload and payload_size fields, and ++ * host-controller drivers may use the hcpriv field. All other fields are ++ * intended to be private to the operations core code. ++ */ ++struct gb_message { ++ struct gb_operation *operation; ++ struct gb_operation_msg_hdr *header; ++ ++ void *payload; ++ size_t payload_size; ++ ++ void *buffer; ++ ++ void *hcpriv; ++}; ++ ++#define GB_OPERATION_FLAG_INCOMING BIT(0) ++#define GB_OPERATION_FLAG_UNIDIRECTIONAL BIT(1) ++#define GB_OPERATION_FLAG_SHORT_RESPONSE BIT(2) ++#define GB_OPERATION_FLAG_CORE BIT(3) ++ ++#define GB_OPERATION_FLAG_USER_MASK (GB_OPERATION_FLAG_SHORT_RESPONSE | \ ++ GB_OPERATION_FLAG_UNIDIRECTIONAL) ++ ++/* ++ * A Greybus operation is a remote procedure call performed over a ++ * connection between two UniPro interfaces. ++ * ++ * Every operation consists of a request message sent to the other ++ * end of the connection coupled with a reply message returned to ++ * the sender. Every operation has a type, whose interpretation is ++ * dependent on the protocol associated with the connection. ++ * ++ * Only four things in an operation structure are intended to be ++ * directly usable by protocol handlers: the operation's connection ++ * pointer; the operation type; the request message payload (and ++ * size); and the response message payload (and size). Note that a ++ * message with a 0-byte payload has a null message payload pointer. ++ * ++ * In addition, every operation has a result, which is an errno ++ * value. Protocol handlers access the operation result using ++ * gb_operation_result(). ++ */ ++typedef void (*gb_operation_callback)(struct gb_operation *); ++struct gb_operation { ++ struct gb_connection *connection; ++ struct gb_message *request; ++ struct gb_message *response; ++ ++ unsigned long flags; ++ u8 type; ++ u16 id; ++ int errno; /* Operation result */ ++ ++ struct work_struct work; ++ gb_operation_callback callback; ++ struct completion completion; ++ ++ struct kref kref; ++ atomic_t waiters; ++ ++ int active; ++ struct list_head links; /* connection->operations */ ++}; ++ ++static inline bool ++gb_operation_is_incoming(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_INCOMING; ++} ++ ++static inline bool ++gb_operation_is_unidirectional(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_UNIDIRECTIONAL; ++} ++ ++static inline bool ++gb_operation_short_response_allowed(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_SHORT_RESPONSE; ++} ++ ++static inline bool gb_operation_is_core(struct gb_operation *operation) ++{ ++ return operation->flags & GB_OPERATION_FLAG_CORE; ++} ++ ++void gb_connection_recv(struct gb_connection *connection, ++ void *data, size_t size); ++ ++int gb_operation_result(struct gb_operation *operation); ++ ++size_t gb_operation_get_payload_size_max(struct gb_connection *connection); ++struct gb_operation * ++gb_operation_create_flags(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp); ++ ++static inline struct gb_operation * ++gb_operation_create(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, gfp_t gfp) ++{ ++ return gb_operation_create_flags(connection, type, request_size, ++ response_size, 0, gfp); ++} ++ ++struct gb_operation * ++gb_operation_create_core(struct gb_connection *connection, ++ u8 type, size_t request_size, ++ size_t response_size, unsigned long flags, ++ gfp_t gfp); ++ ++void gb_operation_get(struct gb_operation *operation); ++void gb_operation_put(struct gb_operation *operation); ++ ++bool gb_operation_response_alloc(struct gb_operation *operation, ++ size_t response_size, gfp_t gfp); ++ ++int gb_operation_request_send(struct gb_operation *operation, ++ gb_operation_callback callback, ++ gfp_t gfp); ++int gb_operation_request_send_sync_timeout(struct gb_operation *operation, ++ unsigned int timeout); ++static inline int ++gb_operation_request_send_sync(struct gb_operation *operation) ++{ ++ return gb_operation_request_send_sync_timeout(operation, ++ GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++void gb_operation_cancel(struct gb_operation *operation, int errno); ++void gb_operation_cancel_incoming(struct gb_operation *operation, int errno); ++ ++void greybus_message_sent(struct gb_host_device *hd, ++ struct gb_message *message, int status); ++ ++int gb_operation_sync_timeout(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size, ++ unsigned int timeout); ++int gb_operation_unidirectional_timeout(struct gb_connection *connection, ++ int type, void *request, int request_size, ++ unsigned int timeout); ++ ++static inline int gb_operation_sync(struct gb_connection *connection, int type, ++ void *request, int request_size, ++ void *response, int response_size) ++{ ++ return gb_operation_sync_timeout(connection, type, ++ request, request_size, response, response_size, ++ GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++static inline int gb_operation_unidirectional(struct gb_connection *connection, ++ int type, void *request, int request_size) ++{ ++ return gb_operation_unidirectional_timeout(connection, type, ++ request, request_size, GB_OPERATION_TIMEOUT_DEFAULT); ++} ++ ++int gb_operation_init(void); ++void gb_operation_exit(void); ++ ++#endif /* !__OPERATION_H */ diff --git a/greybus_power.patch b/greybus_power.patch new file mode 100644 index 00000000000000..d1826b130a3e83 --- /dev/null +++ b/greybus_power.patch @@ -0,0 +1,1148 @@ +--- + drivers/greybus/power_supply.c | 1141 +++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1141 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/power_supply.c +@@ -0,0 +1,1141 @@ ++/* ++ * Power Supply driver for a Greybus module. ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/power_supply.h> ++#include <linux/slab.h> ++ ++#include "greybus.h" ++ ++#define PROP_MAX 32 ++ ++struct gb_power_supply_prop { ++ enum power_supply_property prop; ++ u8 gb_prop; ++ int val; ++ int previous_val; ++ bool is_writeable; ++}; ++ ++struct gb_power_supply { ++ u8 id; ++ bool registered; ++ struct power_supply *psy; ++ struct power_supply_desc desc; ++ char name[64]; ++ struct gb_power_supplies *supplies; ++ struct delayed_work work; ++ char *manufacturer; ++ char *model_name; ++ char *serial_number; ++ u8 type; ++ u8 properties_count; ++ u8 properties_count_str; ++ unsigned long last_update; ++ u8 cache_invalid; ++ unsigned int update_interval; ++ bool changed; ++ struct gb_power_supply_prop *props; ++ enum power_supply_property *props_raw; ++ bool pm_acquired; ++ struct mutex supply_lock; ++}; ++ ++struct gb_power_supplies { ++ struct gb_connection *connection; ++ u8 supplies_count; ++ struct gb_power_supply *supply; ++ struct mutex supplies_lock; ++}; ++ ++#define to_gb_power_supply(x) power_supply_get_drvdata(x) ++ ++/* ++ * General power supply properties that could be absent from various reasons, ++ * like kernel versions or vendor specific versions ++ */ ++#ifndef POWER_SUPPLY_PROP_VOLTAGE_BOOT ++ #define POWER_SUPPLY_PROP_VOLTAGE_BOOT -1 ++#endif ++#ifndef POWER_SUPPLY_PROP_CURRENT_BOOT ++ #define POWER_SUPPLY_PROP_CURRENT_BOOT -1 ++#endif ++#ifndef POWER_SUPPLY_PROP_CALIBRATE ++ #define POWER_SUPPLY_PROP_CALIBRATE -1 ++#endif ++ ++/* cache time in milliseconds, if cache_time is set to 0 cache is disable */ ++static unsigned int cache_time = 1000; ++/* ++ * update interval initial and maximum value, between the two will ++ * back-off exponential ++ */ ++static unsigned int update_interval_init = 1 * HZ; ++static unsigned int update_interval_max = 30 * HZ; ++ ++struct gb_power_supply_changes { ++ enum power_supply_property prop; ++ u32 tolerance_change; ++ void (*prop_changed)(struct gb_power_supply *gbpsy, ++ struct gb_power_supply_prop *prop); ++}; ++ ++static void gb_power_supply_state_change(struct gb_power_supply *gbpsy, ++ struct gb_power_supply_prop *prop); ++ ++static const struct gb_power_supply_changes psy_props_changes[] = { ++ { .prop = GB_POWER_SUPPLY_PROP_STATUS, ++ .tolerance_change = 0, ++ .prop_changed = gb_power_supply_state_change, ++ }, ++ { .prop = GB_POWER_SUPPLY_PROP_TEMP, ++ .tolerance_change = 500, ++ .prop_changed = NULL, ++ }, ++ { .prop = GB_POWER_SUPPLY_PROP_ONLINE, ++ .tolerance_change = 0, ++ .prop_changed = NULL, ++ }, ++}; ++ ++static int get_psp_from_gb_prop(int gb_prop, enum power_supply_property *psp) ++{ ++ int prop; ++ ++ switch (gb_prop) { ++ case GB_POWER_SUPPLY_PROP_STATUS: ++ prop = POWER_SUPPLY_PROP_STATUS; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_TYPE: ++ prop = POWER_SUPPLY_PROP_CHARGE_TYPE; ++ break; ++ case GB_POWER_SUPPLY_PROP_HEALTH: ++ prop = POWER_SUPPLY_PROP_HEALTH; ++ break; ++ case GB_POWER_SUPPLY_PROP_PRESENT: ++ prop = POWER_SUPPLY_PROP_PRESENT; ++ break; ++ case GB_POWER_SUPPLY_PROP_ONLINE: ++ prop = POWER_SUPPLY_PROP_ONLINE; ++ break; ++ case GB_POWER_SUPPLY_PROP_AUTHENTIC: ++ prop = POWER_SUPPLY_PROP_AUTHENTIC; ++ break; ++ case GB_POWER_SUPPLY_PROP_TECHNOLOGY: ++ prop = POWER_SUPPLY_PROP_TECHNOLOGY; ++ break; ++ case GB_POWER_SUPPLY_PROP_CYCLE_COUNT: ++ prop = POWER_SUPPLY_PROP_CYCLE_COUNT; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_AVG: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_OCV: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_OCV; ++ break; ++ case GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT: ++ prop = POWER_SUPPLY_PROP_VOLTAGE_BOOT; ++ break; ++ case GB_POWER_SUPPLY_PROP_CURRENT_MAX: ++ prop = POWER_SUPPLY_PROP_CURRENT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_CURRENT_NOW: ++ prop = POWER_SUPPLY_PROP_CURRENT_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_CURRENT_AVG: ++ prop = POWER_SUPPLY_PROP_CURRENT_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_CURRENT_BOOT: ++ prop = POWER_SUPPLY_PROP_CURRENT_BOOT; ++ break; ++ case GB_POWER_SUPPLY_PROP_POWER_NOW: ++ prop = POWER_SUPPLY_PROP_POWER_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_POWER_AVG: ++ prop = POWER_SUPPLY_PROP_POWER_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN: ++ prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN: ++ prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_FULL: ++ prop = POWER_SUPPLY_PROP_CHARGE_FULL; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_EMPTY: ++ prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_NOW: ++ prop = POWER_SUPPLY_PROP_CHARGE_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_AVG: ++ prop = POWER_SUPPLY_PROP_CHARGE_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_COUNTER: ++ prop = POWER_SUPPLY_PROP_CHARGE_COUNTER; ++ break; ++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT: ++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT; ++ break; ++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX: ++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE: ++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE; ++ break; ++ case GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX: ++ prop = POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT: ++ prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX: ++ prop = POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT: ++ prop = POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN: ++ prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN: ++ prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_FULL: ++ prop = POWER_SUPPLY_PROP_ENERGY_FULL; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_EMPTY: ++ prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_NOW: ++ prop = POWER_SUPPLY_PROP_ENERGY_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_ENERGY_AVG: ++ prop = POWER_SUPPLY_PROP_ENERGY_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_CAPACITY: ++ prop = POWER_SUPPLY_PROP_CAPACITY; ++ break; ++ case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN: ++ prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN; ++ break; ++ case GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX: ++ prop = POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL: ++ prop = POWER_SUPPLY_PROP_CAPACITY_LEVEL; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP: ++ prop = POWER_SUPPLY_PROP_TEMP; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_MAX: ++ prop = POWER_SUPPLY_PROP_TEMP_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_MIN: ++ prop = POWER_SUPPLY_PROP_TEMP_MIN; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN: ++ prop = POWER_SUPPLY_PROP_TEMP_ALERT_MIN; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX: ++ prop = POWER_SUPPLY_PROP_TEMP_ALERT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT: ++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN: ++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN; ++ break; ++ case GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX: ++ prop = POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX; ++ break; ++ case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW: ++ prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG: ++ prop = POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW: ++ prop = POWER_SUPPLY_PROP_TIME_TO_FULL_NOW; ++ break; ++ case GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG: ++ prop = POWER_SUPPLY_PROP_TIME_TO_FULL_AVG; ++ break; ++ case GB_POWER_SUPPLY_PROP_TYPE: ++ prop = POWER_SUPPLY_PROP_TYPE; ++ break; ++ case GB_POWER_SUPPLY_PROP_SCOPE: ++ prop = POWER_SUPPLY_PROP_SCOPE; ++ break; ++ case GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT: ++ prop = POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT; ++ break; ++ case GB_POWER_SUPPLY_PROP_CALIBRATE: ++ prop = POWER_SUPPLY_PROP_CALIBRATE; ++ break; ++ default: ++ prop = -1; ++ break; ++ } ++ ++ if (prop < 0) ++ return prop; ++ ++ *psp = (enum power_supply_property)prop; ++ ++ return 0; ++} ++ ++static struct gb_connection *get_conn_from_psy(struct gb_power_supply *gbpsy) ++{ ++ return gbpsy->supplies->connection; ++} ++ ++static struct gb_power_supply_prop *get_psy_prop(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp) ++{ ++ int i; ++ ++ for (i = 0; i < gbpsy->properties_count; i++) ++ if (gbpsy->props[i].prop == psp) ++ return &gbpsy->props[i]; ++ return NULL; ++} ++ ++static int is_psy_prop_writeable(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp) ++{ ++ struct gb_power_supply_prop *prop; ++ ++ prop = get_psy_prop(gbpsy, psp); ++ if (!prop) ++ return -ENOENT; ++ return prop->is_writeable ? 1 : 0; ++} ++ ++static int is_prop_valint(enum power_supply_property psp) ++{ ++ return ((psp < POWER_SUPPLY_PROP_MODEL_NAME) ? 1 : 0); ++} ++ ++static void next_interval(struct gb_power_supply *gbpsy) ++{ ++ if (gbpsy->update_interval == update_interval_max) ++ return; ++ ++ /* do some exponential back-off in the update interval */ ++ gbpsy->update_interval *= 2; ++ if (gbpsy->update_interval > update_interval_max) ++ gbpsy->update_interval = update_interval_max; ++} ++ ++static void __gb_power_supply_changed(struct gb_power_supply *gbpsy) ++{ ++ power_supply_changed(gbpsy->psy); ++} ++ ++static void gb_power_supply_state_change(struct gb_power_supply *gbpsy, ++ struct gb_power_supply_prop *prop) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ int ret; ++ ++ /* ++ * Check gbpsy->pm_acquired to make sure only one pair of 'get_sync' ++ * and 'put_autosuspend' runtime pm call for state property change. ++ */ ++ mutex_lock(&gbpsy->supply_lock); ++ ++ if ((prop->val == GB_POWER_SUPPLY_STATUS_CHARGING) && ++ !gbpsy->pm_acquired) { ++ ret = gb_pm_runtime_get_sync(connection->bundle); ++ if (ret) ++ dev_err(&connection->bundle->dev, ++ "Fail to set wake lock for charging state\n"); ++ else ++ gbpsy->pm_acquired = true; ++ } else { ++ if (gbpsy->pm_acquired) { ++ ret = gb_pm_runtime_put_autosuspend(connection->bundle); ++ if (ret) ++ dev_err(&connection->bundle->dev, ++ "Fail to set wake unlock for none charging\n"); ++ else ++ gbpsy->pm_acquired = false; ++ } ++ } ++ ++ mutex_unlock(&gbpsy->supply_lock); ++} ++ ++static void check_changed(struct gb_power_supply *gbpsy, ++ struct gb_power_supply_prop *prop) ++{ ++ const struct gb_power_supply_changes *psyc; ++ int val = prop->val; ++ int prev_val = prop->previous_val; ++ bool changed = false; ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(psy_props_changes); i++) { ++ psyc = &psy_props_changes[i]; ++ if (prop->prop == psyc->prop) { ++ if (!psyc->tolerance_change) ++ changed = true; ++ else if (val < prev_val && ++ prev_val - val > psyc->tolerance_change) ++ changed = true; ++ else if (val > prev_val && ++ val - prev_val > psyc->tolerance_change) ++ changed = true; ++ ++ if (changed && psyc->prop_changed) ++ psyc->prop_changed(gbpsy, prop); ++ ++ if (changed) ++ gbpsy->changed = true; ++ break; ++ } ++ } ++} ++ ++static int total_props(struct gb_power_supply *gbpsy) ++{ ++ /* this return the intval plus the strval properties */ ++ return (gbpsy->properties_count + gbpsy->properties_count_str); ++} ++ ++static void prop_append(struct gb_power_supply *gbpsy, ++ enum power_supply_property prop) ++{ ++ enum power_supply_property *new_props_raw; ++ ++ gbpsy->properties_count_str++; ++ new_props_raw = krealloc(gbpsy->props_raw, total_props(gbpsy) * ++ sizeof(enum power_supply_property), ++ GFP_KERNEL); ++ if (!new_props_raw) ++ return; ++ gbpsy->props_raw = new_props_raw; ++ gbpsy->props_raw[total_props(gbpsy) - 1] = prop; ++} ++ ++static int __gb_power_supply_set_name(char *init_name, char *name, size_t len) ++{ ++ unsigned int i = 0; ++ int ret = 0; ++ struct power_supply *psy; ++ ++ if (!strlen(init_name)) ++ init_name = "gb_power_supply"; ++ strlcpy(name, init_name, len); ++ ++ while ((ret < len) && (psy = power_supply_get_by_name(name))) { ++ power_supply_put(psy); ++ ++ ret = snprintf(name, len, "%s_%u", init_name, ++i); ++ } ++ if (ret >= len) ++ return -ENOMEM; ++ return i; ++} ++ ++static void _gb_power_supply_append_props(struct gb_power_supply *gbpsy) ++{ ++ if (strlen(gbpsy->manufacturer)) ++ prop_append(gbpsy, POWER_SUPPLY_PROP_MANUFACTURER); ++ if (strlen(gbpsy->model_name)) ++ prop_append(gbpsy, POWER_SUPPLY_PROP_MODEL_NAME); ++ if (strlen(gbpsy->serial_number)) ++ prop_append(gbpsy, POWER_SUPPLY_PROP_SERIAL_NUMBER); ++} ++ ++static int gb_power_supply_description_get(struct gb_power_supply *gbpsy) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ struct gb_power_supply_get_description_request req; ++ struct gb_power_supply_get_description_response resp; ++ int ret; ++ ++ req.psy_id = gbpsy->id; ++ ++ ret = gb_operation_sync(connection, ++ GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION, ++ &req, sizeof(req), &resp, sizeof(resp)); ++ if (ret < 0) ++ return ret; ++ ++ gbpsy->manufacturer = kstrndup(resp.manufacturer, PROP_MAX, GFP_KERNEL); ++ if (!gbpsy->manufacturer) ++ return -ENOMEM; ++ gbpsy->model_name = kstrndup(resp.model, PROP_MAX, GFP_KERNEL); ++ if (!gbpsy->model_name) ++ return -ENOMEM; ++ gbpsy->serial_number = kstrndup(resp.serial_number, PROP_MAX, ++ GFP_KERNEL); ++ if (!gbpsy->serial_number) ++ return -ENOMEM; ++ ++ gbpsy->type = le16_to_cpu(resp.type); ++ gbpsy->properties_count = resp.properties_count; ++ ++ return 0; ++} ++ ++static int gb_power_supply_prop_descriptors_get(struct gb_power_supply *gbpsy) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ struct gb_power_supply_get_property_descriptors_request *req; ++ struct gb_power_supply_get_property_descriptors_response *resp; ++ struct gb_operation *op; ++ u8 props_count = gbpsy->properties_count; ++ enum power_supply_property psp; ++ int ret; ++ int i, r = 0; ++ ++ if (props_count == 0) ++ return 0; ++ ++ op = gb_operation_create(connection, ++ GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS, ++ sizeof(req), sizeof(*resp) + props_count * ++ sizeof(struct gb_power_supply_props_desc), ++ GFP_KERNEL); ++ if (!op) ++ return -ENOMEM; ++ ++ req = op->request->payload; ++ req->psy_id = gbpsy->id; ++ ++ ret = gb_operation_request_send_sync(op); ++ if (ret < 0) ++ goto out_put_operation; ++ ++ resp = op->response->payload; ++ ++ /* validate received properties */ ++ for (i = 0; i < props_count; i++) { ++ ret = get_psp_from_gb_prop(resp->props[i].property, &psp); ++ if (ret < 0) { ++ dev_warn(&connection->bundle->dev, ++ "greybus property %u it is not supported by this kernel, dropped\n", ++ resp->props[i].property); ++ gbpsy->properties_count--; ++ } ++ } ++ ++ gbpsy->props = kcalloc(gbpsy->properties_count, sizeof(*gbpsy->props), ++ GFP_KERNEL); ++ if (!gbpsy->props) { ++ ret = -ENOMEM; ++ goto out_put_operation; ++ } ++ ++ gbpsy->props_raw = kcalloc(gbpsy->properties_count, ++ sizeof(*gbpsy->props_raw), GFP_KERNEL); ++ if (!gbpsy->props_raw) { ++ ret = -ENOMEM; ++ goto out_put_operation; ++ } ++ ++ /* Store available properties, skip the ones we do not support */ ++ for (i = 0; i < props_count; i++) { ++ ret = get_psp_from_gb_prop(resp->props[i].property, &psp); ++ if (ret < 0) { ++ r++; ++ continue; ++ } ++ gbpsy->props[i - r].prop = psp; ++ gbpsy->props[i - r].gb_prop = resp->props[i].property; ++ gbpsy->props_raw[i - r] = psp; ++ if (resp->props[i].is_writeable) ++ gbpsy->props[i - r].is_writeable = true; ++ } ++ ++ /* ++ * now append the properties that we already got information in the ++ * get_description operation. (char * ones) ++ */ ++ _gb_power_supply_append_props(gbpsy); ++ ++ ret = 0; ++out_put_operation: ++ gb_operation_put(op); ++ ++ return ret; ++} ++ ++static int __gb_power_supply_property_update(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ struct gb_power_supply_prop *prop; ++ struct gb_power_supply_get_property_request req; ++ struct gb_power_supply_get_property_response resp; ++ int val; ++ int ret; ++ ++ prop = get_psy_prop(gbpsy, psp); ++ if (!prop) ++ return -EINVAL; ++ req.psy_id = gbpsy->id; ++ req.property = prop->gb_prop; ++ ++ ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_GET_PROPERTY, ++ &req, sizeof(req), &resp, sizeof(resp)); ++ if (ret < 0) ++ return ret; ++ ++ val = le32_to_cpu(resp.prop_val); ++ if (val == prop->val) ++ return 0; ++ ++ prop->previous_val = prop->val; ++ prop->val = val; ++ ++ check_changed(gbpsy, prop); ++ ++ return 0; ++} ++ ++static int __gb_power_supply_property_get(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct gb_power_supply_prop *prop; ++ ++ prop = get_psy_prop(gbpsy, psp); ++ if (!prop) ++ return -EINVAL; ++ ++ val->intval = prop->val; ++ return 0; ++} ++ ++static int __gb_power_supply_property_strval_get(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = gbpsy->model_name; ++ break; ++ case POWER_SUPPLY_PROP_MANUFACTURER: ++ val->strval = gbpsy->manufacturer; ++ break; ++ case POWER_SUPPLY_PROP_SERIAL_NUMBER: ++ val->strval = gbpsy->serial_number; ++ break; ++ default: ++ break; ++ } ++ ++ return 0; ++} ++ ++static int _gb_power_supply_property_get(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ int ret; ++ ++ /* ++ * Properties of type const char *, were already fetched on ++ * get_description operation and should be cached in gb ++ */ ++ if (is_prop_valint(psp)) ++ ret = __gb_power_supply_property_get(gbpsy, psp, val); ++ else ++ ret = __gb_power_supply_property_strval_get(gbpsy, psp, val); ++ ++ if (ret < 0) ++ dev_err(&connection->bundle->dev, "get property %u\n", psp); ++ ++ return 0; ++} ++ ++static int is_cache_valid(struct gb_power_supply *gbpsy) ++{ ++ /* check if cache is good enough or it has expired */ ++ if (gbpsy->cache_invalid) { ++ gbpsy->cache_invalid = 0; ++ return 0; ++ } ++ ++ if (gbpsy->last_update && ++ time_is_after_jiffies(gbpsy->last_update + ++ msecs_to_jiffies(cache_time))) ++ return 1; ++ ++ return 0; ++} ++ ++static int gb_power_supply_status_get(struct gb_power_supply *gbpsy) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ int ret = 0; ++ int i; ++ ++ if (is_cache_valid(gbpsy)) ++ return 0; ++ ++ ret = gb_pm_runtime_get_sync(connection->bundle); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < gbpsy->properties_count; i++) { ++ ret = __gb_power_supply_property_update(gbpsy, ++ gbpsy->props[i].prop); ++ if (ret < 0) ++ break; ++ } ++ ++ if (ret == 0) ++ gbpsy->last_update = jiffies; ++ ++ gb_pm_runtime_put_autosuspend(connection->bundle); ++ return ret; ++} ++ ++static void gb_power_supply_status_update(struct gb_power_supply *gbpsy) ++{ ++ /* check if there a change that need to be reported */ ++ gb_power_supply_status_get(gbpsy); ++ ++ if (!gbpsy->changed) ++ return; ++ ++ gbpsy->update_interval = update_interval_init; ++ __gb_power_supply_changed(gbpsy); ++ gbpsy->changed = false; ++} ++ ++static void gb_power_supply_work(struct work_struct *work) ++{ ++ struct gb_power_supply *gbpsy = container_of(work, ++ struct gb_power_supply, ++ work.work); ++ ++ /* ++ * if the poll interval is not set, disable polling, this is helpful ++ * specially at unregister time. ++ */ ++ if (!gbpsy->update_interval) ++ return; ++ ++ gb_power_supply_status_update(gbpsy); ++ next_interval(gbpsy); ++ schedule_delayed_work(&gbpsy->work, gbpsy->update_interval); ++} ++ ++static int get_property(struct power_supply *b, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ struct gb_power_supply *gbpsy = to_gb_power_supply(b); ++ ++ gb_power_supply_status_get(gbpsy); ++ ++ return _gb_power_supply_property_get(gbpsy, psp, val); ++} ++ ++static int gb_power_supply_property_set(struct gb_power_supply *gbpsy, ++ enum power_supply_property psp, ++ int val) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ struct gb_power_supply_prop *prop; ++ struct gb_power_supply_set_property_request req; ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(connection->bundle); ++ if (ret) ++ return ret; ++ ++ prop = get_psy_prop(gbpsy, psp); ++ if (!prop) { ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ req.psy_id = gbpsy->id; ++ req.property = prop->gb_prop; ++ req.prop_val = cpu_to_le32((s32)val); ++ ++ ret = gb_operation_sync(connection, GB_POWER_SUPPLY_TYPE_SET_PROPERTY, ++ &req, sizeof(req), NULL, 0); ++ if (ret < 0) ++ goto out; ++ ++ /* cache immediately the new value */ ++ prop->val = val; ++ ++out: ++ gb_pm_runtime_put_autosuspend(connection->bundle); ++ return ret; ++} ++ ++static int set_property(struct power_supply *b, ++ enum power_supply_property psp, ++ const union power_supply_propval *val) ++{ ++ struct gb_power_supply *gbpsy = to_gb_power_supply(b); ++ ++ return gb_power_supply_property_set(gbpsy, psp, val->intval); ++} ++ ++static int property_is_writeable(struct power_supply *b, ++ enum power_supply_property psp) ++{ ++ struct gb_power_supply *gbpsy = to_gb_power_supply(b); ++ ++ return is_psy_prop_writeable(gbpsy, psp); ++} ++ ++static int gb_power_supply_register(struct gb_power_supply *gbpsy) ++{ ++ struct gb_connection *connection = get_conn_from_psy(gbpsy); ++ struct power_supply_config cfg = {}; ++ ++ cfg.drv_data = gbpsy; ++ ++ gbpsy->desc.name = gbpsy->name; ++ gbpsy->desc.type = gbpsy->type; ++ gbpsy->desc.properties = gbpsy->props_raw; ++ gbpsy->desc.num_properties = total_props(gbpsy); ++ gbpsy->desc.get_property = get_property; ++ gbpsy->desc.set_property = set_property; ++ gbpsy->desc.property_is_writeable = property_is_writeable; ++ ++ gbpsy->psy = power_supply_register(&connection->bundle->dev, ++ &gbpsy->desc, &cfg); ++ return PTR_ERR_OR_ZERO(gbpsy->psy); ++} ++ ++static void _gb_power_supply_free(struct gb_power_supply *gbpsy) ++{ ++ kfree(gbpsy->serial_number); ++ kfree(gbpsy->model_name); ++ kfree(gbpsy->manufacturer); ++ kfree(gbpsy->props_raw); ++ kfree(gbpsy->props); ++} ++ ++static void _gb_power_supply_release(struct gb_power_supply *gbpsy) ++{ ++ gbpsy->update_interval = 0; ++ ++ cancel_delayed_work_sync(&gbpsy->work); ++ ++ if (gbpsy->registered) ++ power_supply_unregister(gbpsy->psy); ++ ++ _gb_power_supply_free(gbpsy); ++} ++ ++static void _gb_power_supplies_release(struct gb_power_supplies *supplies) ++{ ++ int i; ++ ++ if (!supplies->supply) ++ return; ++ ++ mutex_lock(&supplies->supplies_lock); ++ for (i = 0; i < supplies->supplies_count; i++) ++ _gb_power_supply_release(&supplies->supply[i]); ++ kfree(supplies->supply); ++ mutex_unlock(&supplies->supplies_lock); ++ kfree(supplies); ++} ++ ++static int gb_power_supplies_get_count(struct gb_power_supplies *supplies) ++{ ++ struct gb_power_supply_get_supplies_response resp; ++ int ret; ++ ++ ret = gb_operation_sync(supplies->connection, ++ GB_POWER_SUPPLY_TYPE_GET_SUPPLIES, ++ NULL, 0, &resp, sizeof(resp)); ++ if (ret < 0) ++ return ret; ++ ++ if (!resp.supplies_count) ++ return -EINVAL; ++ ++ supplies->supplies_count = resp.supplies_count; ++ ++ return ret; ++} ++ ++static int gb_power_supply_config(struct gb_power_supplies *supplies, int id) ++{ ++ struct gb_power_supply *gbpsy = &supplies->supply[id]; ++ int ret; ++ ++ gbpsy->supplies = supplies; ++ gbpsy->id = id; ++ ++ ret = gb_power_supply_description_get(gbpsy); ++ if (ret < 0) ++ return ret; ++ ++ return gb_power_supply_prop_descriptors_get(gbpsy); ++} ++ ++static int gb_power_supply_enable(struct gb_power_supply *gbpsy) ++{ ++ int ret; ++ ++ /* guarantee that we have an unique name, before register */ ++ ret = __gb_power_supply_set_name(gbpsy->model_name, gbpsy->name, ++ sizeof(gbpsy->name)); ++ if (ret < 0) ++ return ret; ++ ++ mutex_init(&gbpsy->supply_lock); ++ ++ ret = gb_power_supply_register(gbpsy); ++ if (ret < 0) ++ return ret; ++ ++ gbpsy->update_interval = update_interval_init; ++ INIT_DELAYED_WORK(&gbpsy->work, gb_power_supply_work); ++ schedule_delayed_work(&gbpsy->work, 0); ++ ++ /* everything went fine, mark it for release code to know */ ++ gbpsy->registered = true; ++ ++ return 0; ++} ++ ++static int gb_power_supplies_setup(struct gb_power_supplies *supplies) ++{ ++ struct gb_connection *connection = supplies->connection; ++ int ret; ++ int i; ++ ++ mutex_lock(&supplies->supplies_lock); ++ ++ ret = gb_power_supplies_get_count(supplies); ++ if (ret < 0) ++ goto out; ++ ++ supplies->supply = kzalloc(supplies->supplies_count * ++ sizeof(struct gb_power_supply), ++ GFP_KERNEL); ++ ++ if (!supplies->supply) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ for (i = 0; i < supplies->supplies_count; i++) { ++ ret = gb_power_supply_config(supplies, i); ++ if (ret < 0) { ++ dev_err(&connection->bundle->dev, ++ "Fail to configure supplies devices\n"); ++ goto out; ++ } ++ } ++out: ++ mutex_unlock(&supplies->supplies_lock); ++ return ret; ++} ++ ++static int gb_power_supplies_register(struct gb_power_supplies *supplies) ++{ ++ struct gb_connection *connection = supplies->connection; ++ int ret = 0; ++ int i; ++ ++ mutex_lock(&supplies->supplies_lock); ++ ++ for (i = 0; i < supplies->supplies_count; i++) { ++ ret = gb_power_supply_enable(&supplies->supply[i]); ++ if (ret < 0) { ++ dev_err(&connection->bundle->dev, ++ "Fail to enable supplies devices\n"); ++ break; ++ } ++ } ++ ++ mutex_unlock(&supplies->supplies_lock); ++ return ret; ++} ++ ++static int gb_supplies_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_power_supplies *supplies = gb_connection_get_data(connection); ++ struct gb_power_supply *gbpsy; ++ struct gb_message *request; ++ struct gb_power_supply_event_request *payload; ++ u8 psy_id; ++ u8 event; ++ int ret = 0; ++ ++ if (op->type != GB_POWER_SUPPLY_TYPE_EVENT) { ++ dev_err(&connection->bundle->dev, ++ "Unsupported unsolicited event: %u\n", op->type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*payload)) { ++ dev_err(&connection->bundle->dev, ++ "Wrong event size received (%zu < %zu)\n", ++ request->payload_size, sizeof(*payload)); ++ return -EINVAL; ++ } ++ ++ payload = request->payload; ++ psy_id = payload->psy_id; ++ mutex_lock(&supplies->supplies_lock); ++ if (psy_id >= supplies->supplies_count || ++ !supplies->supply[psy_id].registered) { ++ dev_err(&connection->bundle->dev, ++ "Event received for unconfigured power_supply id: %d\n", ++ psy_id); ++ ret = -EINVAL; ++ goto out_unlock; ++ } ++ ++ event = payload->event; ++ /* ++ * we will only handle events after setup is done and before release is ++ * running. For that just check update_interval. ++ */ ++ gbpsy = &supplies->supply[psy_id]; ++ if (!gbpsy->update_interval) { ++ ret = -ESHUTDOWN; ++ goto out_unlock; ++ } ++ ++ if (event & GB_POWER_SUPPLY_UPDATE) { ++ /* ++ * we need to make sure we invalidate cache, if not no new ++ * values for the properties will be fetch and the all propose ++ * of this event is missed ++ */ ++ gbpsy->cache_invalid = 1; ++ gb_power_supply_status_update(gbpsy); ++ } ++ ++out_unlock: ++ mutex_unlock(&supplies->supplies_lock); ++ return ret; ++} ++ ++static int gb_power_supply_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_power_supplies *supplies; ++ int ret; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_POWER_SUPPLY) ++ return -ENODEV; ++ ++ supplies = kzalloc(sizeof(*supplies), GFP_KERNEL); ++ if (!supplies) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_supplies_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto out; ++ } ++ ++ supplies->connection = connection; ++ gb_connection_set_data(connection, supplies); ++ ++ mutex_init(&supplies->supplies_lock); ++ ++ greybus_set_drvdata(bundle, supplies); ++ ++ /* We aren't ready to receive an incoming request yet */ ++ ret = gb_connection_enable_tx(connection); ++ if (ret) ++ goto error_connection_destroy; ++ ++ ret = gb_power_supplies_setup(supplies); ++ if (ret < 0) ++ goto error_connection_disable; ++ ++ /* We are ready to receive an incoming request now, enable RX as well */ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto error_connection_disable; ++ ++ ret = gb_power_supplies_register(supplies); ++ if (ret < 0) ++ goto error_connection_disable; ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ return 0; ++ ++error_connection_disable: ++ gb_connection_disable(connection); ++error_connection_destroy: ++ gb_connection_destroy(connection); ++out: ++ _gb_power_supplies_release(supplies); ++ return ret; ++} ++ ++static void gb_power_supply_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_power_supplies *supplies = greybus_get_drvdata(bundle); ++ ++ gb_connection_disable(supplies->connection); ++ gb_connection_destroy(supplies->connection); ++ ++ _gb_power_supplies_release(supplies); ++} ++ ++static const struct greybus_bundle_id gb_power_supply_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_power_supply_id_table); ++ ++static struct greybus_driver gb_power_supply_driver = { ++ .name = "power_supply", ++ .probe = gb_power_supply_probe, ++ .disconnect = gb_power_supply_disconnect, ++ .id_table = gb_power_supply_id_table, ++}; ++module_greybus_driver(gb_power_supply_driver); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_protocol.patch b/greybus_protocol.patch new file mode 100644 index 00000000000000..7efe2c2259c1a8 --- /dev/null +++ b/greybus_protocol.patch @@ -0,0 +1,2275 @@ +--- + drivers/greybus/greybus_protocols.h | 2268 ++++++++++++++++++++++++++++++++++++ + 1 file changed, 2268 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/greybus_protocols.h +@@ -0,0 +1,2268 @@ ++/* ++ * This file is provided under a dual BSD/GPLv2 license. When using or ++ * redistributing this file, you may do so under either license. ++ * ++ * GPL LICENSE SUMMARY ++ * ++ * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. ++ * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of version 2 of the GNU General Public License as ++ * published by the Free Software Foundation. ++ * ++ * This program is distributed in the hope that it will be useful, but ++ * WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * General Public License version 2 for more details. ++ * ++ * BSD LICENSE ++ * ++ * Copyright(c) 2014 - 2015 Google Inc. All rights reserved. ++ * Copyright(c) 2014 - 2015 Linaro Ltd. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * ++ * * Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * * Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in ++ * the documentation and/or other materials provided with the ++ * distribution. ++ * * Neither the name of Google Inc. or Linaro Ltd. nor the names of ++ * its contributors may be used to endorse or promote products ++ * derived from this software without specific prior written ++ * permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ++ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ++ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GOOGLE INC. OR ++ * LINARO LTD. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++#ifndef __GREYBUS_PROTOCOLS_H ++#define __GREYBUS_PROTOCOLS_H ++ ++/* Fixed IDs for control/svc protocols */ ++ ++/* SVC switch-port device ids */ ++#define GB_SVC_DEVICE_ID_SVC 0 ++#define GB_SVC_DEVICE_ID_AP 1 ++#define GB_SVC_DEVICE_ID_MIN 2 ++#define GB_SVC_DEVICE_ID_MAX 31 ++ ++#define GB_SVC_CPORT_ID 0 ++#define GB_CONTROL_BUNDLE_ID 0 ++#define GB_CONTROL_CPORT_ID 0 ++ ++ ++/* ++ * All operation messages (both requests and responses) begin with ++ * a header that encodes the size of the message (header included). ++ * This header also contains a unique identifier, that associates a ++ * response message with its operation. The header contains an ++ * operation type field, whose interpretation is dependent on what ++ * type of protocol is used over the connection. The high bit ++ * (0x80) of the operation type field is used to indicate whether ++ * the message is a request (clear) or a response (set). ++ * ++ * Response messages include an additional result byte, which ++ * communicates the result of the corresponding request. A zero ++ * result value means the operation completed successfully. Any ++ * other value indicates an error; in this case, the payload of the ++ * response message (if any) is ignored. The result byte must be ++ * zero in the header for a request message. ++ * ++ * The wire format for all numeric fields in the header is little ++ * endian. Any operation-specific data begins immediately after the ++ * header. ++ */ ++struct gb_operation_msg_hdr { ++ __le16 size; /* Size in bytes of header + payload */ ++ __le16 operation_id; /* Operation unique id */ ++ __u8 type; /* E.g GB_I2C_TYPE_* or GB_GPIO_TYPE_* */ ++ __u8 result; /* Result of request (in responses only) */ ++ __u8 pad[2]; /* must be zero (ignore when read) */ ++} __packed; ++ ++ ++/* Generic request types */ ++#define GB_REQUEST_TYPE_CPORT_SHUTDOWN 0x00 ++#define GB_REQUEST_TYPE_INVALID 0x7f ++ ++struct gb_cport_shutdown_request { ++ __u8 phase; ++} __packed; ++ ++ ++/* Control Protocol */ ++ ++/* Greybus control request types */ ++#define GB_CONTROL_TYPE_VERSION 0x01 ++#define GB_CONTROL_TYPE_PROBE_AP 0x02 ++#define GB_CONTROL_TYPE_GET_MANIFEST_SIZE 0x03 ++#define GB_CONTROL_TYPE_GET_MANIFEST 0x04 ++#define GB_CONTROL_TYPE_CONNECTED 0x05 ++#define GB_CONTROL_TYPE_DISCONNECTED 0x06 ++#define GB_CONTROL_TYPE_TIMESYNC_ENABLE 0x07 ++#define GB_CONTROL_TYPE_TIMESYNC_DISABLE 0x08 ++#define GB_CONTROL_TYPE_TIMESYNC_AUTHORITATIVE 0x09 ++/* Unused 0x0a */ ++#define GB_CONTROL_TYPE_BUNDLE_VERSION 0x0b ++#define GB_CONTROL_TYPE_DISCONNECTING 0x0c ++#define GB_CONTROL_TYPE_TIMESYNC_GET_LAST_EVENT 0x0d ++#define GB_CONTROL_TYPE_MODE_SWITCH 0x0e ++#define GB_CONTROL_TYPE_BUNDLE_SUSPEND 0x0f ++#define GB_CONTROL_TYPE_BUNDLE_RESUME 0x10 ++#define GB_CONTROL_TYPE_BUNDLE_DEACTIVATE 0x11 ++#define GB_CONTROL_TYPE_BUNDLE_ACTIVATE 0x12 ++#define GB_CONTROL_TYPE_INTF_SUSPEND_PREPARE 0x13 ++#define GB_CONTROL_TYPE_INTF_DEACTIVATE_PREPARE 0x14 ++#define GB_CONTROL_TYPE_INTF_HIBERNATE_ABORT 0x15 ++ ++struct gb_control_version_request { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++struct gb_control_version_response { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++struct gb_control_bundle_version_request { ++ __u8 bundle_id; ++} __packed; ++ ++struct gb_control_bundle_version_response { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++/* Control protocol manifest get size request has no payload*/ ++struct gb_control_get_manifest_size_response { ++ __le16 size; ++} __packed; ++ ++/* Control protocol manifest get request has no payload */ ++struct gb_control_get_manifest_response { ++ __u8 data[0]; ++} __packed; ++ ++/* Control protocol [dis]connected request */ ++struct gb_control_connected_request { ++ __le16 cport_id; ++} __packed; ++ ++struct gb_control_disconnecting_request { ++ __le16 cport_id; ++} __packed; ++/* disconnecting response has no payload */ ++ ++struct gb_control_disconnected_request { ++ __le16 cport_id; ++} __packed; ++/* Control protocol [dis]connected response has no payload */ ++ ++#define GB_TIMESYNC_MAX_STROBES 0x04 ++ ++struct gb_control_timesync_enable_request { ++ __u8 count; ++ __le64 frame_time; ++ __le32 strobe_delay; ++ __le32 refclk; ++} __packed; ++/* timesync enable response has no payload */ ++ ++struct gb_control_timesync_authoritative_request { ++ __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; ++} __packed; ++/* timesync authoritative response has no payload */ ++ ++/* timesync get_last_event_request has no payload */ ++struct gb_control_timesync_get_last_event_response { ++ __le64 frame_time; ++} __packed; ++ ++/* ++ * All Bundle power management operations use the same request and response ++ * layout and status codes. ++ */ ++ ++#define GB_CONTROL_BUNDLE_PM_OK 0x00 ++#define GB_CONTROL_BUNDLE_PM_INVAL 0x01 ++#define GB_CONTROL_BUNDLE_PM_BUSY 0x02 ++#define GB_CONTROL_BUNDLE_PM_FAIL 0x03 ++#define GB_CONTROL_BUNDLE_PM_NA 0x04 ++ ++struct gb_control_bundle_pm_request { ++ __u8 bundle_id; ++} __packed; ++ ++struct gb_control_bundle_pm_response { ++ __u8 status; ++} __packed; ++ ++/* ++ * Interface Suspend Prepare and Deactivate Prepare operations use the same ++ * response layout and error codes. Define a single response structure and reuse ++ * it. Both operations have no payload. ++ */ ++ ++#define GB_CONTROL_INTF_PM_OK 0x00 ++#define GB_CONTROL_INTF_PM_BUSY 0x01 ++#define GB_CONTROL_INTF_PM_NA 0x02 ++ ++struct gb_control_intf_pm_response { ++ __u8 status; ++} __packed; ++ ++/* APBridge protocol */ ++ ++/* request APB1 log */ ++#define GB_APB_REQUEST_LOG 0x02 ++ ++/* request to map a cport to bulk in and bulk out endpoints */ ++#define GB_APB_REQUEST_EP_MAPPING 0x03 ++ ++/* request to get the number of cports available */ ++#define GB_APB_REQUEST_CPORT_COUNT 0x04 ++ ++/* request to reset a cport state */ ++#define GB_APB_REQUEST_RESET_CPORT 0x05 ++ ++/* request to time the latency of messages on a given cport */ ++#define GB_APB_REQUEST_LATENCY_TAG_EN 0x06 ++#define GB_APB_REQUEST_LATENCY_TAG_DIS 0x07 ++ ++/* request to control the CSI transmitter */ ++#define GB_APB_REQUEST_CSI_TX_CONTROL 0x08 ++ ++/* request to control audio streaming */ ++#define GB_APB_REQUEST_AUDIO_CONTROL 0x09 ++ ++/* TimeSync requests */ ++#define GB_APB_REQUEST_TIMESYNC_ENABLE 0x0d ++#define GB_APB_REQUEST_TIMESYNC_DISABLE 0x0e ++#define GB_APB_REQUEST_TIMESYNC_AUTHORITATIVE 0x0f ++#define GB_APB_REQUEST_TIMESYNC_GET_LAST_EVENT 0x10 ++ ++/* requests to set Greybus CPort flags */ ++#define GB_APB_REQUEST_CPORT_FLAGS 0x11 ++ ++/* ARPC request */ ++#define GB_APB_REQUEST_ARPC_RUN 0x12 ++ ++struct gb_apb_request_cport_flags { ++ __le32 flags; ++#define GB_APB_CPORT_FLAG_CONTROL 0x01 ++#define GB_APB_CPORT_FLAG_HIGH_PRIO 0x02 ++} __packed; ++ ++ ++/* Firmware Download Protocol */ ++ ++/* Request Types */ ++#define GB_FW_DOWNLOAD_TYPE_FIND_FIRMWARE 0x01 ++#define GB_FW_DOWNLOAD_TYPE_FETCH_FIRMWARE 0x02 ++#define GB_FW_DOWNLOAD_TYPE_RELEASE_FIRMWARE 0x03 ++ ++#define GB_FIRMWARE_TAG_MAX_SIZE 10 ++ ++/* firmware download find firmware request/response */ ++struct gb_fw_download_find_firmware_request { ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++} __packed; ++ ++struct gb_fw_download_find_firmware_response { ++ __u8 firmware_id; ++ __le32 size; ++} __packed; ++ ++/* firmware download fetch firmware request/response */ ++struct gb_fw_download_fetch_firmware_request { ++ __u8 firmware_id; ++ __le32 offset; ++ __le32 size; ++} __packed; ++ ++struct gb_fw_download_fetch_firmware_response { ++ __u8 data[0]; ++} __packed; ++ ++/* firmware download release firmware request */ ++struct gb_fw_download_release_firmware_request { ++ __u8 firmware_id; ++} __packed; ++/* firmware download release firmware response has no payload */ ++ ++ ++/* Firmware Management Protocol */ ++ ++/* Request Types */ ++#define GB_FW_MGMT_TYPE_INTERFACE_FW_VERSION 0x01 ++#define GB_FW_MGMT_TYPE_LOAD_AND_VALIDATE_FW 0x02 ++#define GB_FW_MGMT_TYPE_LOADED_FW 0x03 ++#define GB_FW_MGMT_TYPE_BACKEND_FW_VERSION 0x04 ++#define GB_FW_MGMT_TYPE_BACKEND_FW_UPDATE 0x05 ++#define GB_FW_MGMT_TYPE_BACKEND_FW_UPDATED 0x06 ++ ++#define GB_FW_LOAD_METHOD_UNIPRO 0x01 ++#define GB_FW_LOAD_METHOD_INTERNAL 0x02 ++ ++#define GB_FW_LOAD_STATUS_FAILED 0x00 ++#define GB_FW_LOAD_STATUS_UNVALIDATED 0x01 ++#define GB_FW_LOAD_STATUS_VALIDATED 0x02 ++#define GB_FW_LOAD_STATUS_VALIDATION_FAILED 0x03 ++ ++#define GB_FW_BACKEND_FW_STATUS_SUCCESS 0x01 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_FIND 0x02 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_FETCH 0x03 ++#define GB_FW_BACKEND_FW_STATUS_FAIL_WRITE 0x04 ++#define GB_FW_BACKEND_FW_STATUS_INT 0x05 ++#define GB_FW_BACKEND_FW_STATUS_RETRY 0x06 ++#define GB_FW_BACKEND_FW_STATUS_NOT_SUPPORTED 0x07 ++ ++#define GB_FW_BACKEND_VERSION_STATUS_SUCCESS 0x01 ++#define GB_FW_BACKEND_VERSION_STATUS_NOT_AVAILABLE 0x02 ++#define GB_FW_BACKEND_VERSION_STATUS_NOT_SUPPORTED 0x03 ++#define GB_FW_BACKEND_VERSION_STATUS_RETRY 0x04 ++#define GB_FW_BACKEND_VERSION_STATUS_FAIL_INT 0x05 ++ ++/* firmware management interface firmware version request has no payload */ ++struct gb_fw_mgmt_interface_fw_version_response { ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++ __le16 major; ++ __le16 minor; ++} __packed; ++ ++/* firmware management load and validate firmware request/response */ ++struct gb_fw_mgmt_load_and_validate_fw_request { ++ __u8 request_id; ++ __u8 load_method; ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++} __packed; ++/* firmware management load and validate firmware response has no payload*/ ++ ++/* firmware management loaded firmware request */ ++struct gb_fw_mgmt_loaded_fw_request { ++ __u8 request_id; ++ __u8 status; ++ __le16 major; ++ __le16 minor; ++} __packed; ++/* firmware management loaded firmware response has no payload */ ++ ++/* firmware management backend firmware version request/response */ ++struct gb_fw_mgmt_backend_fw_version_request { ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++} __packed; ++ ++struct gb_fw_mgmt_backend_fw_version_response { ++ __le16 major; ++ __le16 minor; ++ __u8 status; ++} __packed; ++ ++/* firmware management backend firmware update request */ ++struct gb_fw_mgmt_backend_fw_update_request { ++ __u8 request_id; ++ __u8 firmware_tag[GB_FIRMWARE_TAG_MAX_SIZE]; ++} __packed; ++/* firmware management backend firmware update response has no payload */ ++ ++/* firmware management backend firmware updated request */ ++struct gb_fw_mgmt_backend_fw_updated_request { ++ __u8 request_id; ++ __u8 status; ++} __packed; ++/* firmware management backend firmware updated response has no payload */ ++ ++ ++/* Component Authentication Protocol (CAP) */ ++ ++/* Request Types */ ++#define GB_CAP_TYPE_GET_ENDPOINT_UID 0x01 ++#define GB_CAP_TYPE_GET_IMS_CERTIFICATE 0x02 ++#define GB_CAP_TYPE_AUTHENTICATE 0x03 ++ ++/* CAP get endpoint uid request has no payload */ ++struct gb_cap_get_endpoint_uid_response { ++ __u8 uid[8]; ++} __packed; ++ ++/* CAP get endpoint ims certificate request/response */ ++struct gb_cap_get_ims_certificate_request { ++ __le32 certificate_class; ++ __le32 certificate_id; ++} __packed; ++ ++struct gb_cap_get_ims_certificate_response { ++ __u8 result_code; ++ __u8 certificate[0]; ++} __packed; ++ ++/* CAP authenticate request/response */ ++struct gb_cap_authenticate_request { ++ __le32 auth_type; ++ __u8 uid[8]; ++ __u8 challenge[32]; ++} __packed; ++ ++struct gb_cap_authenticate_response { ++ __u8 result_code; ++ __u8 response[64]; ++ __u8 signature[0]; ++} __packed; ++ ++ ++/* Bootrom Protocol */ ++ ++/* Version of the Greybus bootrom protocol we support */ ++#define GB_BOOTROM_VERSION_MAJOR 0x00 ++#define GB_BOOTROM_VERSION_MINOR 0x01 ++ ++/* Greybus bootrom request types */ ++#define GB_BOOTROM_TYPE_VERSION 0x01 ++#define GB_BOOTROM_TYPE_FIRMWARE_SIZE 0x02 ++#define GB_BOOTROM_TYPE_GET_FIRMWARE 0x03 ++#define GB_BOOTROM_TYPE_READY_TO_BOOT 0x04 ++#define GB_BOOTROM_TYPE_AP_READY 0x05 /* Request with no-payload */ ++#define GB_BOOTROM_TYPE_GET_VID_PID 0x06 /* Request with no-payload */ ++ ++/* Greybus bootrom boot stages */ ++#define GB_BOOTROM_BOOT_STAGE_ONE 0x01 /* Reserved for the boot ROM */ ++#define GB_BOOTROM_BOOT_STAGE_TWO 0x02 /* Bootrom package to be loaded by the boot ROM */ ++#define GB_BOOTROM_BOOT_STAGE_THREE 0x03 /* Module personality package loaded by Stage 2 firmware */ ++ ++/* Greybus bootrom ready to boot status */ ++#define GB_BOOTROM_BOOT_STATUS_INVALID 0x00 /* Firmware blob could not be validated */ ++#define GB_BOOTROM_BOOT_STATUS_INSECURE 0x01 /* Firmware blob is valid but insecure */ ++#define GB_BOOTROM_BOOT_STATUS_SECURE 0x02 /* Firmware blob is valid and secure */ ++ ++/* Max bootrom data fetch size in bytes */ ++#define GB_BOOTROM_FETCH_MAX 2000 ++ ++struct gb_bootrom_version_request { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++struct gb_bootrom_version_response { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++/* Bootrom protocol firmware size request/response */ ++struct gb_bootrom_firmware_size_request { ++ __u8 stage; ++} __packed; ++ ++struct gb_bootrom_firmware_size_response { ++ __le32 size; ++} __packed; ++ ++/* Bootrom protocol get firmware request/response */ ++struct gb_bootrom_get_firmware_request { ++ __le32 offset; ++ __le32 size; ++} __packed; ++ ++struct gb_bootrom_get_firmware_response { ++ __u8 data[0]; ++} __packed; ++ ++/* Bootrom protocol Ready to boot request */ ++struct gb_bootrom_ready_to_boot_request { ++ __u8 status; ++} __packed; ++/* Bootrom protocol Ready to boot response has no payload */ ++ ++/* Bootrom protocol get VID/PID request has no payload */ ++struct gb_bootrom_get_vid_pid_response { ++ __le32 vendor_id; ++ __le32 product_id; ++} __packed; ++ ++ ++/* Power Supply */ ++ ++/* Greybus power supply request types */ ++#define GB_POWER_SUPPLY_TYPE_GET_SUPPLIES 0x02 ++#define GB_POWER_SUPPLY_TYPE_GET_DESCRIPTION 0x03 ++#define GB_POWER_SUPPLY_TYPE_GET_PROP_DESCRIPTORS 0x04 ++#define GB_POWER_SUPPLY_TYPE_GET_PROPERTY 0x05 ++#define GB_POWER_SUPPLY_TYPE_SET_PROPERTY 0x06 ++#define GB_POWER_SUPPLY_TYPE_EVENT 0x07 ++ ++/* Greybus power supply battery technologies types */ ++#define GB_POWER_SUPPLY_TECH_UNKNOWN 0x0000 ++#define GB_POWER_SUPPLY_TECH_NiMH 0x0001 ++#define GB_POWER_SUPPLY_TECH_LION 0x0002 ++#define GB_POWER_SUPPLY_TECH_LIPO 0x0003 ++#define GB_POWER_SUPPLY_TECH_LiFe 0x0004 ++#define GB_POWER_SUPPLY_TECH_NiCd 0x0005 ++#define GB_POWER_SUPPLY_TECH_LiMn 0x0006 ++ ++/* Greybus power supply types */ ++#define GB_POWER_SUPPLY_UNKNOWN_TYPE 0x0000 ++#define GB_POWER_SUPPLY_BATTERY_TYPE 0x0001 ++#define GB_POWER_SUPPLY_UPS_TYPE 0x0002 ++#define GB_POWER_SUPPLY_MAINS_TYPE 0x0003 ++#define GB_POWER_SUPPLY_USB_TYPE 0x0004 ++#define GB_POWER_SUPPLY_USB_DCP_TYPE 0x0005 ++#define GB_POWER_SUPPLY_USB_CDP_TYPE 0x0006 ++#define GB_POWER_SUPPLY_USB_ACA_TYPE 0x0007 ++ ++/* Greybus power supply health values */ ++#define GB_POWER_SUPPLY_HEALTH_UNKNOWN 0x0000 ++#define GB_POWER_SUPPLY_HEALTH_GOOD 0x0001 ++#define GB_POWER_SUPPLY_HEALTH_OVERHEAT 0x0002 ++#define GB_POWER_SUPPLY_HEALTH_DEAD 0x0003 ++#define GB_POWER_SUPPLY_HEALTH_OVERVOLTAGE 0x0004 ++#define GB_POWER_SUPPLY_HEALTH_UNSPEC_FAILURE 0x0005 ++#define GB_POWER_SUPPLY_HEALTH_COLD 0x0006 ++#define GB_POWER_SUPPLY_HEALTH_WATCHDOG_TIMER_EXPIRE 0x0007 ++#define GB_POWER_SUPPLY_HEALTH_SAFETY_TIMER_EXPIRE 0x0008 ++ ++/* Greybus power supply status values */ ++#define GB_POWER_SUPPLY_STATUS_UNKNOWN 0x0000 ++#define GB_POWER_SUPPLY_STATUS_CHARGING 0x0001 ++#define GB_POWER_SUPPLY_STATUS_DISCHARGING 0x0002 ++#define GB_POWER_SUPPLY_STATUS_NOT_CHARGING 0x0003 ++#define GB_POWER_SUPPLY_STATUS_FULL 0x0004 ++ ++/* Greybus power supply capacity level values */ ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN 0x0000 ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL 0x0001 ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_LOW 0x0002 ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_NORMAL 0x0003 ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_HIGH 0x0004 ++#define GB_POWER_SUPPLY_CAPACITY_LEVEL_FULL 0x0005 ++ ++/* Greybus power supply scope values */ ++#define GB_POWER_SUPPLY_SCOPE_UNKNOWN 0x0000 ++#define GB_POWER_SUPPLY_SCOPE_SYSTEM 0x0001 ++#define GB_POWER_SUPPLY_SCOPE_DEVICE 0x0002 ++ ++struct gb_power_supply_get_supplies_response { ++ __u8 supplies_count; ++} __packed; ++ ++struct gb_power_supply_get_description_request { ++ __u8 psy_id; ++} __packed; ++ ++struct gb_power_supply_get_description_response { ++ __u8 manufacturer[32]; ++ __u8 model[32]; ++ __u8 serial_number[32]; ++ __le16 type; ++ __u8 properties_count; ++} __packed; ++ ++struct gb_power_supply_props_desc { ++ __u8 property; ++#define GB_POWER_SUPPLY_PROP_STATUS 0x00 ++#define GB_POWER_SUPPLY_PROP_CHARGE_TYPE 0x01 ++#define GB_POWER_SUPPLY_PROP_HEALTH 0x02 ++#define GB_POWER_SUPPLY_PROP_PRESENT 0x03 ++#define GB_POWER_SUPPLY_PROP_ONLINE 0x04 ++#define GB_POWER_SUPPLY_PROP_AUTHENTIC 0x05 ++#define GB_POWER_SUPPLY_PROP_TECHNOLOGY 0x06 ++#define GB_POWER_SUPPLY_PROP_CYCLE_COUNT 0x07 ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_MAX 0x08 ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_MIN 0x09 ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN 0x0A ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN 0x0B ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_NOW 0x0C ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_AVG 0x0D ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_OCV 0x0E ++#define GB_POWER_SUPPLY_PROP_VOLTAGE_BOOT 0x0F ++#define GB_POWER_SUPPLY_PROP_CURRENT_MAX 0x10 ++#define GB_POWER_SUPPLY_PROP_CURRENT_NOW 0x11 ++#define GB_POWER_SUPPLY_PROP_CURRENT_AVG 0x12 ++#define GB_POWER_SUPPLY_PROP_CURRENT_BOOT 0x13 ++#define GB_POWER_SUPPLY_PROP_POWER_NOW 0x14 ++#define GB_POWER_SUPPLY_PROP_POWER_AVG 0x15 ++#define GB_POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN 0x16 ++#define GB_POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN 0x17 ++#define GB_POWER_SUPPLY_PROP_CHARGE_FULL 0x18 ++#define GB_POWER_SUPPLY_PROP_CHARGE_EMPTY 0x19 ++#define GB_POWER_SUPPLY_PROP_CHARGE_NOW 0x1A ++#define GB_POWER_SUPPLY_PROP_CHARGE_AVG 0x1B ++#define GB_POWER_SUPPLY_PROP_CHARGE_COUNTER 0x1C ++#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT 0x1D ++#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_CURRENT_MAX 0x1E ++#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE 0x1F ++#define GB_POWER_SUPPLY_PROP_CONSTANT_CHARGE_VOLTAGE_MAX 0x20 ++#define GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT 0x21 ++#define GB_POWER_SUPPLY_PROP_CHARGE_CONTROL_LIMIT_MAX 0x22 ++#define GB_POWER_SUPPLY_PROP_INPUT_CURRENT_LIMIT 0x23 ++#define GB_POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN 0x24 ++#define GB_POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN 0x25 ++#define GB_POWER_SUPPLY_PROP_ENERGY_FULL 0x26 ++#define GB_POWER_SUPPLY_PROP_ENERGY_EMPTY 0x27 ++#define GB_POWER_SUPPLY_PROP_ENERGY_NOW 0x28 ++#define GB_POWER_SUPPLY_PROP_ENERGY_AVG 0x29 ++#define GB_POWER_SUPPLY_PROP_CAPACITY 0x2A ++#define GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MIN 0x2B ++#define GB_POWER_SUPPLY_PROP_CAPACITY_ALERT_MAX 0x2C ++#define GB_POWER_SUPPLY_PROP_CAPACITY_LEVEL 0x2D ++#define GB_POWER_SUPPLY_PROP_TEMP 0x2E ++#define GB_POWER_SUPPLY_PROP_TEMP_MAX 0x2F ++#define GB_POWER_SUPPLY_PROP_TEMP_MIN 0x30 ++#define GB_POWER_SUPPLY_PROP_TEMP_ALERT_MIN 0x31 ++#define GB_POWER_SUPPLY_PROP_TEMP_ALERT_MAX 0x32 ++#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT 0x33 ++#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MIN 0x34 ++#define GB_POWER_SUPPLY_PROP_TEMP_AMBIENT_ALERT_MAX 0x35 ++#define GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_NOW 0x36 ++#define GB_POWER_SUPPLY_PROP_TIME_TO_EMPTY_AVG 0x37 ++#define GB_POWER_SUPPLY_PROP_TIME_TO_FULL_NOW 0x38 ++#define GB_POWER_SUPPLY_PROP_TIME_TO_FULL_AVG 0x39 ++#define GB_POWER_SUPPLY_PROP_TYPE 0x3A ++#define GB_POWER_SUPPLY_PROP_SCOPE 0x3B ++#define GB_POWER_SUPPLY_PROP_CHARGE_TERM_CURRENT 0x3C ++#define GB_POWER_SUPPLY_PROP_CALIBRATE 0x3D ++ __u8 is_writeable; ++} __packed; ++ ++struct gb_power_supply_get_property_descriptors_request { ++ __u8 psy_id; ++} __packed; ++ ++struct gb_power_supply_get_property_descriptors_response { ++ __u8 properties_count; ++ struct gb_power_supply_props_desc props[]; ++} __packed; ++ ++struct gb_power_supply_get_property_request { ++ __u8 psy_id; ++ __u8 property; ++} __packed; ++ ++struct gb_power_supply_get_property_response { ++ __le32 prop_val; ++}; ++ ++struct gb_power_supply_set_property_request { ++ __u8 psy_id; ++ __u8 property; ++ __le32 prop_val; ++} __packed; ++ ++struct gb_power_supply_event_request { ++ __u8 psy_id; ++ __u8 event; ++#define GB_POWER_SUPPLY_UPDATE 0x01 ++} __packed; ++ ++ ++/* HID */ ++ ++/* Greybus HID operation types */ ++#define GB_HID_TYPE_GET_DESC 0x02 ++#define GB_HID_TYPE_GET_REPORT_DESC 0x03 ++#define GB_HID_TYPE_PWR_ON 0x04 ++#define GB_HID_TYPE_PWR_OFF 0x05 ++#define GB_HID_TYPE_GET_REPORT 0x06 ++#define GB_HID_TYPE_SET_REPORT 0x07 ++#define GB_HID_TYPE_IRQ_EVENT 0x08 ++ ++/* Report type */ ++#define GB_HID_INPUT_REPORT 0 ++#define GB_HID_OUTPUT_REPORT 1 ++#define GB_HID_FEATURE_REPORT 2 ++ ++/* Different request/response structures */ ++/* HID get descriptor response */ ++struct gb_hid_desc_response { ++ __u8 bLength; ++ __le16 wReportDescLength; ++ __le16 bcdHID; ++ __le16 wProductID; ++ __le16 wVendorID; ++ __u8 bCountryCode; ++} __packed; ++ ++/* HID get report request/response */ ++struct gb_hid_get_report_request { ++ __u8 report_type; ++ __u8 report_id; ++} __packed; ++ ++/* HID set report request */ ++struct gb_hid_set_report_request { ++ __u8 report_type; ++ __u8 report_id; ++ __u8 report[0]; ++} __packed; ++ ++/* HID input report request, via interrupt pipe */ ++struct gb_hid_input_report_request { ++ __u8 report[0]; ++} __packed; ++ ++ ++/* I2C */ ++ ++/* Greybus i2c request types */ ++#define GB_I2C_TYPE_FUNCTIONALITY 0x02 ++#define GB_I2C_TYPE_TRANSFER 0x05 ++ ++/* functionality request has no payload */ ++struct gb_i2c_functionality_response { ++ __le32 functionality; ++} __packed; ++ ++/* ++ * Outgoing data immediately follows the op count and ops array. ++ * The data for each write (master -> slave) op in the array is sent ++ * in order, with no (e.g. pad) bytes separating them. ++ * ++ * Short reads cause the entire transfer request to fail So response ++ * payload consists only of bytes read, and the number of bytes is ++ * exactly what was specified in the corresponding op. Like ++ * outgoing data, the incoming data is in order and contiguous. ++ */ ++struct gb_i2c_transfer_op { ++ __le16 addr; ++ __le16 flags; ++ __le16 size; ++} __packed; ++ ++struct gb_i2c_transfer_request { ++ __le16 op_count; ++ struct gb_i2c_transfer_op ops[0]; /* op_count of these */ ++} __packed; ++struct gb_i2c_transfer_response { ++ __u8 data[0]; /* inbound data */ ++} __packed; ++ ++ ++/* GPIO */ ++ ++/* Greybus GPIO request types */ ++#define GB_GPIO_TYPE_LINE_COUNT 0x02 ++#define GB_GPIO_TYPE_ACTIVATE 0x03 ++#define GB_GPIO_TYPE_DEACTIVATE 0x04 ++#define GB_GPIO_TYPE_GET_DIRECTION 0x05 ++#define GB_GPIO_TYPE_DIRECTION_IN 0x06 ++#define GB_GPIO_TYPE_DIRECTION_OUT 0x07 ++#define GB_GPIO_TYPE_GET_VALUE 0x08 ++#define GB_GPIO_TYPE_SET_VALUE 0x09 ++#define GB_GPIO_TYPE_SET_DEBOUNCE 0x0a ++#define GB_GPIO_TYPE_IRQ_TYPE 0x0b ++#define GB_GPIO_TYPE_IRQ_MASK 0x0c ++#define GB_GPIO_TYPE_IRQ_UNMASK 0x0d ++#define GB_GPIO_TYPE_IRQ_EVENT 0x0e ++ ++#define GB_GPIO_IRQ_TYPE_NONE 0x00 ++#define GB_GPIO_IRQ_TYPE_EDGE_RISING 0x01 ++#define GB_GPIO_IRQ_TYPE_EDGE_FALLING 0x02 ++#define GB_GPIO_IRQ_TYPE_EDGE_BOTH 0x03 ++#define GB_GPIO_IRQ_TYPE_LEVEL_HIGH 0x04 ++#define GB_GPIO_IRQ_TYPE_LEVEL_LOW 0x08 ++ ++/* line count request has no payload */ ++struct gb_gpio_line_count_response { ++ __u8 count; ++} __packed; ++ ++struct gb_gpio_activate_request { ++ __u8 which; ++} __packed; ++/* activate response has no payload */ ++ ++struct gb_gpio_deactivate_request { ++ __u8 which; ++} __packed; ++/* deactivate response has no payload */ ++ ++struct gb_gpio_get_direction_request { ++ __u8 which; ++} __packed; ++struct gb_gpio_get_direction_response { ++ __u8 direction; ++} __packed; ++ ++struct gb_gpio_direction_in_request { ++ __u8 which; ++} __packed; ++/* direction in response has no payload */ ++ ++struct gb_gpio_direction_out_request { ++ __u8 which; ++ __u8 value; ++} __packed; ++/* direction out response has no payload */ ++ ++struct gb_gpio_get_value_request { ++ __u8 which; ++} __packed; ++struct gb_gpio_get_value_response { ++ __u8 value; ++} __packed; ++ ++struct gb_gpio_set_value_request { ++ __u8 which; ++ __u8 value; ++} __packed; ++/* set value response has no payload */ ++ ++struct gb_gpio_set_debounce_request { ++ __u8 which; ++ __le16 usec; ++} __packed; ++/* debounce response has no payload */ ++ ++struct gb_gpio_irq_type_request { ++ __u8 which; ++ __u8 type; ++} __packed; ++/* irq type response has no payload */ ++ ++struct gb_gpio_irq_mask_request { ++ __u8 which; ++} __packed; ++/* irq mask response has no payload */ ++ ++struct gb_gpio_irq_unmask_request { ++ __u8 which; ++} __packed; ++/* irq unmask response has no payload */ ++ ++/* irq event requests originate on another module and are handled on the AP */ ++struct gb_gpio_irq_event_request { ++ __u8 which; ++} __packed; ++/* irq event has no response */ ++ ++ ++/* PWM */ ++ ++/* Greybus PWM operation types */ ++#define GB_PWM_TYPE_PWM_COUNT 0x02 ++#define GB_PWM_TYPE_ACTIVATE 0x03 ++#define GB_PWM_TYPE_DEACTIVATE 0x04 ++#define GB_PWM_TYPE_CONFIG 0x05 ++#define GB_PWM_TYPE_POLARITY 0x06 ++#define GB_PWM_TYPE_ENABLE 0x07 ++#define GB_PWM_TYPE_DISABLE 0x08 ++ ++/* pwm count request has no payload */ ++struct gb_pwm_count_response { ++ __u8 count; ++} __packed; ++ ++struct gb_pwm_activate_request { ++ __u8 which; ++} __packed; ++ ++struct gb_pwm_deactivate_request { ++ __u8 which; ++} __packed; ++ ++struct gb_pwm_config_request { ++ __u8 which; ++ __le32 duty; ++ __le32 period; ++} __packed; ++ ++struct gb_pwm_polarity_request { ++ __u8 which; ++ __u8 polarity; ++} __packed; ++ ++struct gb_pwm_enable_request { ++ __u8 which; ++} __packed; ++ ++struct gb_pwm_disable_request { ++ __u8 which; ++} __packed; ++ ++/* SPI */ ++ ++/* Should match up with modes in linux/spi/spi.h */ ++#define GB_SPI_MODE_CPHA 0x01 /* clock phase */ ++#define GB_SPI_MODE_CPOL 0x02 /* clock polarity */ ++#define GB_SPI_MODE_MODE_0 (0|0) /* (original MicroWire) */ ++#define GB_SPI_MODE_MODE_1 (0|GB_SPI_MODE_CPHA) ++#define GB_SPI_MODE_MODE_2 (GB_SPI_MODE_CPOL|0) ++#define GB_SPI_MODE_MODE_3 (GB_SPI_MODE_CPOL|GB_SPI_MODE_CPHA) ++#define GB_SPI_MODE_CS_HIGH 0x04 /* chipselect active high? */ ++#define GB_SPI_MODE_LSB_FIRST 0x08 /* per-word bits-on-wire */ ++#define GB_SPI_MODE_3WIRE 0x10 /* SI/SO signals shared */ ++#define GB_SPI_MODE_LOOP 0x20 /* loopback mode */ ++#define GB_SPI_MODE_NO_CS 0x40 /* 1 dev/bus, no chipselect */ ++#define GB_SPI_MODE_READY 0x80 /* slave pulls low to pause */ ++ ++/* Should match up with flags in linux/spi/spi.h */ ++#define GB_SPI_FLAG_HALF_DUPLEX BIT(0) /* can't do full duplex */ ++#define GB_SPI_FLAG_NO_RX BIT(1) /* can't do buffer read */ ++#define GB_SPI_FLAG_NO_TX BIT(2) /* can't do buffer write */ ++ ++/* Greybus spi operation types */ ++#define GB_SPI_TYPE_MASTER_CONFIG 0x02 ++#define GB_SPI_TYPE_DEVICE_CONFIG 0x03 ++#define GB_SPI_TYPE_TRANSFER 0x04 ++ ++/* mode request has no payload */ ++struct gb_spi_master_config_response { ++ __le32 bits_per_word_mask; ++ __le32 min_speed_hz; ++ __le32 max_speed_hz; ++ __le16 mode; ++ __le16 flags; ++ __u8 num_chipselect; ++} __packed; ++ ++struct gb_spi_device_config_request { ++ __u8 chip_select; ++} __packed; ++ ++struct gb_spi_device_config_response { ++ __le16 mode; ++ __u8 bits_per_word; ++ __le32 max_speed_hz; ++ __u8 device_type; ++#define GB_SPI_SPI_DEV 0x00 ++#define GB_SPI_SPI_NOR 0x01 ++#define GB_SPI_SPI_MODALIAS 0x02 ++ __u8 name[32]; ++} __packed; ++ ++/** ++ * struct gb_spi_transfer - a read/write buffer pair ++ * @speed_hz: Select a speed other than the device default for this transfer. If ++ * 0 the default (from @spi_device) is used. ++ * @len: size of rx and tx buffers (in bytes) ++ * @delay_usecs: microseconds to delay after this transfer before (optionally) ++ * changing the chipselect status, then starting the next transfer or ++ * completing this spi_message. ++ * @cs_change: affects chipselect after this transfer completes ++ * @bits_per_word: select a bits_per_word other than the device default for this ++ * transfer. If 0 the default (from @spi_device) is used. ++ */ ++struct gb_spi_transfer { ++ __le32 speed_hz; ++ __le32 len; ++ __le16 delay_usecs; ++ __u8 cs_change; ++ __u8 bits_per_word; ++ __u8 xfer_flags; ++#define GB_SPI_XFER_READ 0x01 ++#define GB_SPI_XFER_WRITE 0x02 ++#define GB_SPI_XFER_INPROGRESS 0x04 ++} __packed; ++ ++struct gb_spi_transfer_request { ++ __u8 chip_select; /* of the spi device */ ++ __u8 mode; /* of the spi device */ ++ __le16 count; ++ struct gb_spi_transfer transfers[0]; /* count of these */ ++} __packed; ++ ++struct gb_spi_transfer_response { ++ __u8 data[0]; /* inbound data */ ++} __packed; ++ ++/* Version of the Greybus SVC protocol we support */ ++#define GB_SVC_VERSION_MAJOR 0x00 ++#define GB_SVC_VERSION_MINOR 0x01 ++ ++/* Greybus SVC request types */ ++#define GB_SVC_TYPE_PROTOCOL_VERSION 0x01 ++#define GB_SVC_TYPE_SVC_HELLO 0x02 ++#define GB_SVC_TYPE_INTF_DEVICE_ID 0x03 ++#define GB_SVC_TYPE_INTF_RESET 0x06 ++#define GB_SVC_TYPE_CONN_CREATE 0x07 ++#define GB_SVC_TYPE_CONN_DESTROY 0x08 ++#define GB_SVC_TYPE_DME_PEER_GET 0x09 ++#define GB_SVC_TYPE_DME_PEER_SET 0x0a ++#define GB_SVC_TYPE_ROUTE_CREATE 0x0b ++#define GB_SVC_TYPE_ROUTE_DESTROY 0x0c ++#define GB_SVC_TYPE_TIMESYNC_ENABLE 0x0d ++#define GB_SVC_TYPE_TIMESYNC_DISABLE 0x0e ++#define GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE 0x0f ++#define GB_SVC_TYPE_INTF_SET_PWRM 0x10 ++#define GB_SVC_TYPE_INTF_EJECT 0x11 ++#define GB_SVC_TYPE_PING 0x13 ++#define GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET 0x14 ++#define GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET 0x15 ++#define GB_SVC_TYPE_PWRMON_SAMPLE_GET 0x16 ++#define GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET 0x17 ++#define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE 0x18 ++#define GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE 0x19 ++#define GB_SVC_TYPE_TIMESYNC_PING 0x1a ++#define GB_SVC_TYPE_MODULE_INSERTED 0x1f ++#define GB_SVC_TYPE_MODULE_REMOVED 0x20 ++#define GB_SVC_TYPE_INTF_VSYS_ENABLE 0x21 ++#define GB_SVC_TYPE_INTF_VSYS_DISABLE 0x22 ++#define GB_SVC_TYPE_INTF_REFCLK_ENABLE 0x23 ++#define GB_SVC_TYPE_INTF_REFCLK_DISABLE 0x24 ++#define GB_SVC_TYPE_INTF_UNIPRO_ENABLE 0x25 ++#define GB_SVC_TYPE_INTF_UNIPRO_DISABLE 0x26 ++#define GB_SVC_TYPE_INTF_ACTIVATE 0x27 ++#define GB_SVC_TYPE_INTF_RESUME 0x28 ++#define GB_SVC_TYPE_INTF_MAILBOX_EVENT 0x29 ++#define GB_SVC_TYPE_INTF_OOPS 0x2a ++ ++/* Greybus SVC protocol status values */ ++#define GB_SVC_OP_SUCCESS 0x00 ++#define GB_SVC_OP_UNKNOWN_ERROR 0x01 ++#define GB_SVC_INTF_NOT_DETECTED 0x02 ++#define GB_SVC_INTF_NO_UPRO_LINK 0x03 ++#define GB_SVC_INTF_UPRO_NOT_DOWN 0x04 ++#define GB_SVC_INTF_UPRO_NOT_HIBERNATED 0x05 ++#define GB_SVC_INTF_NO_V_SYS 0x06 ++#define GB_SVC_INTF_V_CHG 0x07 ++#define GB_SVC_INTF_WAKE_BUSY 0x08 ++#define GB_SVC_INTF_NO_REFCLK 0x09 ++#define GB_SVC_INTF_RELEASING 0x0a ++#define GB_SVC_INTF_NO_ORDER 0x0b ++#define GB_SVC_INTF_MBOX_SET 0x0c ++#define GB_SVC_INTF_BAD_MBOX 0x0d ++#define GB_SVC_INTF_OP_TIMEOUT 0x0e ++#define GB_SVC_PWRMON_OP_NOT_PRESENT 0x0f ++ ++struct gb_svc_version_request { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++struct gb_svc_version_response { ++ __u8 major; ++ __u8 minor; ++} __packed; ++ ++/* SVC protocol hello request */ ++struct gb_svc_hello_request { ++ __le16 endo_id; ++ __u8 interface_id; ++} __packed; ++/* hello response has no payload */ ++ ++struct gb_svc_intf_device_id_request { ++ __u8 intf_id; ++ __u8 device_id; ++} __packed; ++/* device id response has no payload */ ++ ++struct gb_svc_intf_reset_request { ++ __u8 intf_id; ++} __packed; ++/* interface reset response has no payload */ ++ ++struct gb_svc_intf_eject_request { ++ __u8 intf_id; ++} __packed; ++/* interface eject response has no payload */ ++ ++struct gb_svc_conn_create_request { ++ __u8 intf1_id; ++ __le16 cport1_id; ++ __u8 intf2_id; ++ __le16 cport2_id; ++ __u8 tc; ++ __u8 flags; ++} __packed; ++/* connection create response has no payload */ ++ ++struct gb_svc_conn_destroy_request { ++ __u8 intf1_id; ++ __le16 cport1_id; ++ __u8 intf2_id; ++ __le16 cport2_id; ++} __packed; ++/* connection destroy response has no payload */ ++ ++struct gb_svc_dme_peer_get_request { ++ __u8 intf_id; ++ __le16 attr; ++ __le16 selector; ++} __packed; ++ ++struct gb_svc_dme_peer_get_response { ++ __le16 result_code; ++ __le32 attr_value; ++} __packed; ++ ++struct gb_svc_dme_peer_set_request { ++ __u8 intf_id; ++ __le16 attr; ++ __le16 selector; ++ __le32 value; ++} __packed; ++ ++struct gb_svc_dme_peer_set_response { ++ __le16 result_code; ++} __packed; ++ ++/* Greybus init-status values, currently retrieved using DME peer gets. */ ++#define GB_INIT_SPI_BOOT_STARTED 0x02 ++#define GB_INIT_TRUSTED_SPI_BOOT_FINISHED 0x03 ++#define GB_INIT_UNTRUSTED_SPI_BOOT_FINISHED 0x04 ++#define GB_INIT_BOOTROM_UNIPRO_BOOT_STARTED 0x06 ++#define GB_INIT_BOOTROM_FALLBACK_UNIPRO_BOOT_STARTED 0x09 ++#define GB_INIT_S2_LOADER_BOOT_STARTED 0x0D ++ ++struct gb_svc_route_create_request { ++ __u8 intf1_id; ++ __u8 dev1_id; ++ __u8 intf2_id; ++ __u8 dev2_id; ++} __packed; ++/* route create response has no payload */ ++ ++struct gb_svc_route_destroy_request { ++ __u8 intf1_id; ++ __u8 intf2_id; ++} __packed; ++/* route destroy response has no payload */ ++ ++/* used for svc_intf_vsys_{enable,disable} */ ++struct gb_svc_intf_vsys_request { ++ __u8 intf_id; ++} __packed; ++ ++struct gb_svc_intf_vsys_response { ++ __u8 result_code; ++#define GB_SVC_INTF_VSYS_OK 0x00 ++ /* 0x01 is reserved */ ++#define GB_SVC_INTF_VSYS_FAIL 0x02 ++} __packed; ++ ++/* used for svc_intf_refclk_{enable,disable} */ ++struct gb_svc_intf_refclk_request { ++ __u8 intf_id; ++} __packed; ++ ++struct gb_svc_intf_refclk_response { ++ __u8 result_code; ++#define GB_SVC_INTF_REFCLK_OK 0x00 ++ /* 0x01 is reserved */ ++#define GB_SVC_INTF_REFCLK_FAIL 0x02 ++} __packed; ++ ++/* used for svc_intf_unipro_{enable,disable} */ ++struct gb_svc_intf_unipro_request { ++ __u8 intf_id; ++} __packed; ++ ++struct gb_svc_intf_unipro_response { ++ __u8 result_code; ++#define GB_SVC_INTF_UNIPRO_OK 0x00 ++ /* 0x01 is reserved */ ++#define GB_SVC_INTF_UNIPRO_FAIL 0x02 ++#define GB_SVC_INTF_UNIPRO_NOT_OFF 0x03 ++} __packed; ++ ++struct gb_svc_timesync_enable_request { ++ __u8 count; ++ __le64 frame_time; ++ __le32 strobe_delay; ++ __le32 refclk; ++} __packed; ++/* timesync enable response has no payload */ ++ ++/* timesync authoritative request has no payload */ ++struct gb_svc_timesync_authoritative_response { ++ __le64 frame_time[GB_TIMESYNC_MAX_STROBES]; ++}; ++ ++struct gb_svc_timesync_wake_pins_acquire_request { ++ __le32 strobe_mask; ++}; ++ ++/* timesync wake pins acquire response has no payload */ ++ ++/* timesync wake pins release request has no payload */ ++/* timesync wake pins release response has no payload */ ++ ++/* timesync svc ping request has no payload */ ++struct gb_svc_timesync_ping_response { ++ __le64 frame_time; ++} __packed; ++ ++#define GB_SVC_UNIPRO_FAST_MODE 0x01 ++#define GB_SVC_UNIPRO_SLOW_MODE 0x02 ++#define GB_SVC_UNIPRO_FAST_AUTO_MODE 0x04 ++#define GB_SVC_UNIPRO_SLOW_AUTO_MODE 0x05 ++#define GB_SVC_UNIPRO_MODE_UNCHANGED 0x07 ++#define GB_SVC_UNIPRO_HIBERNATE_MODE 0x11 ++#define GB_SVC_UNIPRO_OFF_MODE 0x12 ++ ++#define GB_SVC_SMALL_AMPLITUDE 0x01 ++#define GB_SVC_LARGE_AMPLITUDE 0x02 ++ ++#define GB_SVC_NO_DE_EMPHASIS 0x00 ++#define GB_SVC_SMALL_DE_EMPHASIS 0x01 ++#define GB_SVC_LARGE_DE_EMPHASIS 0x02 ++ ++#define GB_SVC_PWRM_RXTERMINATION 0x01 ++#define GB_SVC_PWRM_TXTERMINATION 0x02 ++#define GB_SVC_PWRM_LINE_RESET 0x04 ++#define GB_SVC_PWRM_SCRAMBLING 0x20 ++ ++#define GB_SVC_PWRM_QUIRK_HSSER 0x00000001 ++ ++#define GB_SVC_UNIPRO_HS_SERIES_A 0x01 ++#define GB_SVC_UNIPRO_HS_SERIES_B 0x02 ++ ++#define GB_SVC_SETPWRM_PWR_OK 0x00 ++#define GB_SVC_SETPWRM_PWR_LOCAL 0x01 ++#define GB_SVC_SETPWRM_PWR_REMOTE 0x02 ++#define GB_SVC_SETPWRM_PWR_BUSY 0x03 ++#define GB_SVC_SETPWRM_PWR_ERROR_CAP 0x04 ++#define GB_SVC_SETPWRM_PWR_FATAL_ERROR 0x05 ++ ++struct gb_svc_l2_timer_cfg { ++ __le16 tsb_fc0_protection_timeout; ++ __le16 tsb_tc0_replay_timeout; ++ __le16 tsb_afc0_req_timeout; ++ __le16 tsb_fc1_protection_timeout; ++ __le16 tsb_tc1_replay_timeout; ++ __le16 tsb_afc1_req_timeout; ++ __le16 reserved_for_tc2[3]; ++ __le16 reserved_for_tc3[3]; ++} __packed; ++ ++struct gb_svc_intf_set_pwrm_request { ++ __u8 intf_id; ++ __u8 hs_series; ++ __u8 tx_mode; ++ __u8 tx_gear; ++ __u8 tx_nlanes; ++ __u8 tx_amplitude; ++ __u8 tx_hs_equalizer; ++ __u8 rx_mode; ++ __u8 rx_gear; ++ __u8 rx_nlanes; ++ __u8 flags; ++ __le32 quirks; ++ struct gb_svc_l2_timer_cfg local_l2timerdata, remote_l2timerdata; ++} __packed; ++ ++struct gb_svc_intf_set_pwrm_response { ++ __u8 result_code; ++} __packed; ++ ++struct gb_svc_key_event_request { ++ __le16 key_code; ++#define GB_KEYCODE_ARA 0x00 ++ ++ __u8 key_event; ++#define GB_SVC_KEY_RELEASED 0x00 ++#define GB_SVC_KEY_PRESSED 0x01 ++} __packed; ++ ++#define GB_SVC_PWRMON_MAX_RAIL_COUNT 254 ++ ++struct gb_svc_pwrmon_rail_count_get_response { ++ __u8 rail_count; ++} __packed; ++ ++#define GB_SVC_PWRMON_RAIL_NAME_BUFSIZE 32 ++ ++struct gb_svc_pwrmon_rail_names_get_response { ++ __u8 status; ++ __u8 name[0][GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; ++} __packed; ++ ++#define GB_SVC_PWRMON_TYPE_CURR 0x01 ++#define GB_SVC_PWRMON_TYPE_VOL 0x02 ++#define GB_SVC_PWRMON_TYPE_PWR 0x03 ++ ++#define GB_SVC_PWRMON_GET_SAMPLE_OK 0x00 ++#define GB_SVC_PWRMON_GET_SAMPLE_INVAL 0x01 ++#define GB_SVC_PWRMON_GET_SAMPLE_NOSUPP 0x02 ++#define GB_SVC_PWRMON_GET_SAMPLE_HWERR 0x03 ++ ++struct gb_svc_pwrmon_sample_get_request { ++ __u8 rail_id; ++ __u8 measurement_type; ++} __packed; ++ ++struct gb_svc_pwrmon_sample_get_response { ++ __u8 result; ++ __le32 measurement; ++} __packed; ++ ++struct gb_svc_pwrmon_intf_sample_get_request { ++ __u8 intf_id; ++ __u8 measurement_type; ++} __packed; ++ ++struct gb_svc_pwrmon_intf_sample_get_response { ++ __u8 result; ++ __le32 measurement; ++} __packed; ++ ++#define GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY 0x0001 ++ ++struct gb_svc_module_inserted_request { ++ __u8 primary_intf_id; ++ __u8 intf_count; ++ __le16 flags; ++} __packed; ++/* module_inserted response has no payload */ ++ ++struct gb_svc_module_removed_request { ++ __u8 primary_intf_id; ++} __packed; ++/* module_removed response has no payload */ ++ ++struct gb_svc_intf_activate_request { ++ __u8 intf_id; ++} __packed; ++ ++#define GB_SVC_INTF_TYPE_UNKNOWN 0x00 ++#define GB_SVC_INTF_TYPE_DUMMY 0x01 ++#define GB_SVC_INTF_TYPE_UNIPRO 0x02 ++#define GB_SVC_INTF_TYPE_GREYBUS 0x03 ++ ++struct gb_svc_intf_activate_response { ++ __u8 status; ++ __u8 intf_type; ++} __packed; ++ ++struct gb_svc_intf_resume_request { ++ __u8 intf_id; ++} __packed; ++ ++struct gb_svc_intf_resume_response { ++ __u8 status; ++} __packed; ++ ++#define GB_SVC_INTF_MAILBOX_NONE 0x00 ++#define GB_SVC_INTF_MAILBOX_AP 0x01 ++#define GB_SVC_INTF_MAILBOX_GREYBUS 0x02 ++ ++struct gb_svc_intf_mailbox_event_request { ++ __u8 intf_id; ++ __le16 result_code; ++ __le32 mailbox; ++} __packed; ++/* intf_mailbox_event response has no payload */ ++ ++struct gb_svc_intf_oops_request { ++ __u8 intf_id; ++ __u8 reason; ++} __packed; ++/* intf_oops response has no payload */ ++ ++ ++/* RAW */ ++ ++/* Greybus raw request types */ ++#define GB_RAW_TYPE_SEND 0x02 ++ ++struct gb_raw_send_request { ++ __le32 len; ++ __u8 data[0]; ++} __packed; ++ ++ ++/* UART */ ++ ++/* Greybus UART operation types */ ++#define GB_UART_TYPE_SEND_DATA 0x02 ++#define GB_UART_TYPE_RECEIVE_DATA 0x03 /* Unsolicited data */ ++#define GB_UART_TYPE_SET_LINE_CODING 0x04 ++#define GB_UART_TYPE_SET_CONTROL_LINE_STATE 0x05 ++#define GB_UART_TYPE_SEND_BREAK 0x06 ++#define GB_UART_TYPE_SERIAL_STATE 0x07 /* Unsolicited data */ ++#define GB_UART_TYPE_RECEIVE_CREDITS 0x08 ++#define GB_UART_TYPE_FLUSH_FIFOS 0x09 ++ ++/* Represents data from AP -> Module */ ++struct gb_uart_send_data_request { ++ __le16 size; ++ __u8 data[0]; ++} __packed; ++ ++/* recv-data-request flags */ ++#define GB_UART_RECV_FLAG_FRAMING 0x01 /* Framing error */ ++#define GB_UART_RECV_FLAG_PARITY 0x02 /* Parity error */ ++#define GB_UART_RECV_FLAG_OVERRUN 0x04 /* Overrun error */ ++#define GB_UART_RECV_FLAG_BREAK 0x08 /* Break */ ++ ++/* Represents data from Module -> AP */ ++struct gb_uart_recv_data_request { ++ __le16 size; ++ __u8 flags; ++ __u8 data[0]; ++} __packed; ++ ++struct gb_uart_receive_credits_request { ++ __le16 count; ++} __packed; ++ ++struct gb_uart_set_line_coding_request { ++ __le32 rate; ++ __u8 format; ++#define GB_SERIAL_1_STOP_BITS 0 ++#define GB_SERIAL_1_5_STOP_BITS 1 ++#define GB_SERIAL_2_STOP_BITS 2 ++ ++ __u8 parity; ++#define GB_SERIAL_NO_PARITY 0 ++#define GB_SERIAL_ODD_PARITY 1 ++#define GB_SERIAL_EVEN_PARITY 2 ++#define GB_SERIAL_MARK_PARITY 3 ++#define GB_SERIAL_SPACE_PARITY 4 ++ ++ __u8 data_bits; ++ ++ __u8 flow_control; ++#define GB_SERIAL_AUTO_RTSCTS_EN 0x1 ++} __packed; ++ ++/* output control lines */ ++#define GB_UART_CTRL_DTR 0x01 ++#define GB_UART_CTRL_RTS 0x02 ++ ++struct gb_uart_set_control_line_state_request { ++ __u8 control; ++} __packed; ++ ++struct gb_uart_set_break_request { ++ __u8 state; ++} __packed; ++ ++/* input control lines and line errors */ ++#define GB_UART_CTRL_DCD 0x01 ++#define GB_UART_CTRL_DSR 0x02 ++#define GB_UART_CTRL_RI 0x04 ++ ++struct gb_uart_serial_state_request { ++ __u8 control; ++} __packed; ++ ++struct gb_uart_serial_flush_request { ++ __u8 flags; ++#define GB_SERIAL_FLAG_FLUSH_TRANSMITTER 0x01 ++#define GB_SERIAL_FLAG_FLUSH_RECEIVER 0x02 ++} __packed; ++ ++/* Loopback */ ++ ++/* Greybus loopback request types */ ++#define GB_LOOPBACK_TYPE_PING 0x02 ++#define GB_LOOPBACK_TYPE_TRANSFER 0x03 ++#define GB_LOOPBACK_TYPE_SINK 0x04 ++ ++/* ++ * Loopback request/response header format should be identical ++ * to simplify bandwidth and data movement analysis. ++ */ ++struct gb_loopback_transfer_request { ++ __le32 len; ++ __le32 reserved0; ++ __le32 reserved1; ++ __u8 data[0]; ++} __packed; ++ ++struct gb_loopback_transfer_response { ++ __le32 len; ++ __le32 reserved0; ++ __le32 reserved1; ++ __u8 data[0]; ++} __packed; ++ ++/* SDIO */ ++/* Greybus SDIO operation types */ ++#define GB_SDIO_TYPE_GET_CAPABILITIES 0x02 ++#define GB_SDIO_TYPE_SET_IOS 0x03 ++#define GB_SDIO_TYPE_COMMAND 0x04 ++#define GB_SDIO_TYPE_TRANSFER 0x05 ++#define GB_SDIO_TYPE_EVENT 0x06 ++ ++/* get caps response: request has no payload */ ++struct gb_sdio_get_caps_response { ++ __le32 caps; ++#define GB_SDIO_CAP_NONREMOVABLE 0x00000001 ++#define GB_SDIO_CAP_4_BIT_DATA 0x00000002 ++#define GB_SDIO_CAP_8_BIT_DATA 0x00000004 ++#define GB_SDIO_CAP_MMC_HS 0x00000008 ++#define GB_SDIO_CAP_SD_HS 0x00000010 ++#define GB_SDIO_CAP_ERASE 0x00000020 ++#define GB_SDIO_CAP_1_2V_DDR 0x00000040 ++#define GB_SDIO_CAP_1_8V_DDR 0x00000080 ++#define GB_SDIO_CAP_POWER_OFF_CARD 0x00000100 ++#define GB_SDIO_CAP_UHS_SDR12 0x00000200 ++#define GB_SDIO_CAP_UHS_SDR25 0x00000400 ++#define GB_SDIO_CAP_UHS_SDR50 0x00000800 ++#define GB_SDIO_CAP_UHS_SDR104 0x00001000 ++#define GB_SDIO_CAP_UHS_DDR50 0x00002000 ++#define GB_SDIO_CAP_DRIVER_TYPE_A 0x00004000 ++#define GB_SDIO_CAP_DRIVER_TYPE_C 0x00008000 ++#define GB_SDIO_CAP_DRIVER_TYPE_D 0x00010000 ++#define GB_SDIO_CAP_HS200_1_2V 0x00020000 ++#define GB_SDIO_CAP_HS200_1_8V 0x00040000 ++#define GB_SDIO_CAP_HS400_1_2V 0x00080000 ++#define GB_SDIO_CAP_HS400_1_8V 0x00100000 ++ ++ /* see possible values below at vdd */ ++ __le32 ocr; ++ __le32 f_min; ++ __le32 f_max; ++ __le16 max_blk_count; ++ __le16 max_blk_size; ++} __packed; ++ ++/* set ios request: response has no payload */ ++struct gb_sdio_set_ios_request { ++ __le32 clock; ++ __le32 vdd; ++#define GB_SDIO_VDD_165_195 0x00000001 ++#define GB_SDIO_VDD_20_21 0x00000002 ++#define GB_SDIO_VDD_21_22 0x00000004 ++#define GB_SDIO_VDD_22_23 0x00000008 ++#define GB_SDIO_VDD_23_24 0x00000010 ++#define GB_SDIO_VDD_24_25 0x00000020 ++#define GB_SDIO_VDD_25_26 0x00000040 ++#define GB_SDIO_VDD_26_27 0x00000080 ++#define GB_SDIO_VDD_27_28 0x00000100 ++#define GB_SDIO_VDD_28_29 0x00000200 ++#define GB_SDIO_VDD_29_30 0x00000400 ++#define GB_SDIO_VDD_30_31 0x00000800 ++#define GB_SDIO_VDD_31_32 0x00001000 ++#define GB_SDIO_VDD_32_33 0x00002000 ++#define GB_SDIO_VDD_33_34 0x00004000 ++#define GB_SDIO_VDD_34_35 0x00008000 ++#define GB_SDIO_VDD_35_36 0x00010000 ++ ++ __u8 bus_mode; ++#define GB_SDIO_BUSMODE_OPENDRAIN 0x00 ++#define GB_SDIO_BUSMODE_PUSHPULL 0x01 ++ ++ __u8 power_mode; ++#define GB_SDIO_POWER_OFF 0x00 ++#define GB_SDIO_POWER_UP 0x01 ++#define GB_SDIO_POWER_ON 0x02 ++#define GB_SDIO_POWER_UNDEFINED 0x03 ++ ++ __u8 bus_width; ++#define GB_SDIO_BUS_WIDTH_1 0x00 ++#define GB_SDIO_BUS_WIDTH_4 0x02 ++#define GB_SDIO_BUS_WIDTH_8 0x03 ++ ++ __u8 timing; ++#define GB_SDIO_TIMING_LEGACY 0x00 ++#define GB_SDIO_TIMING_MMC_HS 0x01 ++#define GB_SDIO_TIMING_SD_HS 0x02 ++#define GB_SDIO_TIMING_UHS_SDR12 0x03 ++#define GB_SDIO_TIMING_UHS_SDR25 0x04 ++#define GB_SDIO_TIMING_UHS_SDR50 0x05 ++#define GB_SDIO_TIMING_UHS_SDR104 0x06 ++#define GB_SDIO_TIMING_UHS_DDR50 0x07 ++#define GB_SDIO_TIMING_MMC_DDR52 0x08 ++#define GB_SDIO_TIMING_MMC_HS200 0x09 ++#define GB_SDIO_TIMING_MMC_HS400 0x0A ++ ++ __u8 signal_voltage; ++#define GB_SDIO_SIGNAL_VOLTAGE_330 0x00 ++#define GB_SDIO_SIGNAL_VOLTAGE_180 0x01 ++#define GB_SDIO_SIGNAL_VOLTAGE_120 0x02 ++ ++ __u8 drv_type; ++#define GB_SDIO_SET_DRIVER_TYPE_B 0x00 ++#define GB_SDIO_SET_DRIVER_TYPE_A 0x01 ++#define GB_SDIO_SET_DRIVER_TYPE_C 0x02 ++#define GB_SDIO_SET_DRIVER_TYPE_D 0x03 ++} __packed; ++ ++/* command request */ ++struct gb_sdio_command_request { ++ __u8 cmd; ++ __u8 cmd_flags; ++#define GB_SDIO_RSP_NONE 0x00 ++#define GB_SDIO_RSP_PRESENT 0x01 ++#define GB_SDIO_RSP_136 0x02 ++#define GB_SDIO_RSP_CRC 0x04 ++#define GB_SDIO_RSP_BUSY 0x08 ++#define GB_SDIO_RSP_OPCODE 0x10 ++ ++ __u8 cmd_type; ++#define GB_SDIO_CMD_AC 0x00 ++#define GB_SDIO_CMD_ADTC 0x01 ++#define GB_SDIO_CMD_BC 0x02 ++#define GB_SDIO_CMD_BCR 0x03 ++ ++ __le32 cmd_arg; ++ __le16 data_blocks; ++ __le16 data_blksz; ++} __packed; ++ ++struct gb_sdio_command_response { ++ __le32 resp[4]; ++} __packed; ++ ++/* transfer request */ ++struct gb_sdio_transfer_request { ++ __u8 data_flags; ++#define GB_SDIO_DATA_WRITE 0x01 ++#define GB_SDIO_DATA_READ 0x02 ++#define GB_SDIO_DATA_STREAM 0x04 ++ ++ __le16 data_blocks; ++ __le16 data_blksz; ++ __u8 data[0]; ++} __packed; ++ ++struct gb_sdio_transfer_response { ++ __le16 data_blocks; ++ __le16 data_blksz; ++ __u8 data[0]; ++} __packed; ++ ++/* event request: generated by module and is defined as unidirectional */ ++struct gb_sdio_event_request { ++ __u8 event; ++#define GB_SDIO_CARD_INSERTED 0x01 ++#define GB_SDIO_CARD_REMOVED 0x02 ++#define GB_SDIO_WP 0x04 ++} __packed; ++ ++/* Camera */ ++ ++/* Greybus Camera request types */ ++#define GB_CAMERA_TYPE_CAPABILITIES 0x02 ++#define GB_CAMERA_TYPE_CONFIGURE_STREAMS 0x03 ++#define GB_CAMERA_TYPE_CAPTURE 0x04 ++#define GB_CAMERA_TYPE_FLUSH 0x05 ++#define GB_CAMERA_TYPE_METADATA 0x06 ++ ++#define GB_CAMERA_MAX_STREAMS 4 ++#define GB_CAMERA_MAX_SETTINGS_SIZE 8192 ++ ++/* Greybus Camera Configure Streams request payload */ ++struct gb_camera_stream_config_request { ++ __le16 width; ++ __le16 height; ++ __le16 format; ++ __le16 padding; ++} __packed; ++ ++struct gb_camera_configure_streams_request { ++ __u8 num_streams; ++ __u8 flags; ++#define GB_CAMERA_CONFIGURE_STREAMS_TEST_ONLY 0x01 ++ __le16 padding; ++ struct gb_camera_stream_config_request config[0]; ++} __packed; ++ ++/* Greybus Camera Configure Streams response payload */ ++struct gb_camera_stream_config_response { ++ __le16 width; ++ __le16 height; ++ __le16 format; ++ __u8 virtual_channel; ++ __u8 data_type[2]; ++ __le16 max_pkt_size; ++ __u8 padding; ++ __le32 max_size; ++} __packed; ++ ++struct gb_camera_configure_streams_response { ++ __u8 num_streams; ++#define GB_CAMERA_CONFIGURE_STREAMS_ADJUSTED 0x01 ++ __u8 flags; ++ __u8 padding[2]; ++ __le32 data_rate; ++ struct gb_camera_stream_config_response config[0]; ++}; ++ ++/* Greybus Camera Capture request payload - response has no payload */ ++struct gb_camera_capture_request { ++ __le32 request_id; ++ __u8 streams; ++ __u8 padding; ++ __le16 num_frames; ++ __u8 settings[0]; ++} __packed; ++ ++/* Greybus Camera Flush response payload - request has no payload */ ++struct gb_camera_flush_response { ++ __le32 request_id; ++} __packed; ++ ++/* Greybus Camera Metadata request payload - operation has no response */ ++struct gb_camera_metadata_request { ++ __le32 request_id; ++ __le16 frame_number; ++ __u8 stream; ++ __u8 padding; ++ __u8 metadata[0]; ++} __packed; ++ ++/* Lights */ ++ ++/* Greybus Lights request types */ ++#define GB_LIGHTS_TYPE_GET_LIGHTS 0x02 ++#define GB_LIGHTS_TYPE_GET_LIGHT_CONFIG 0x03 ++#define GB_LIGHTS_TYPE_GET_CHANNEL_CONFIG 0x04 ++#define GB_LIGHTS_TYPE_GET_CHANNEL_FLASH_CONFIG 0x05 ++#define GB_LIGHTS_TYPE_SET_BRIGHTNESS 0x06 ++#define GB_LIGHTS_TYPE_SET_BLINK 0x07 ++#define GB_LIGHTS_TYPE_SET_COLOR 0x08 ++#define GB_LIGHTS_TYPE_SET_FADE 0x09 ++#define GB_LIGHTS_TYPE_EVENT 0x0A ++#define GB_LIGHTS_TYPE_SET_FLASH_INTENSITY 0x0B ++#define GB_LIGHTS_TYPE_SET_FLASH_STROBE 0x0C ++#define GB_LIGHTS_TYPE_SET_FLASH_TIMEOUT 0x0D ++#define GB_LIGHTS_TYPE_GET_FLASH_FAULT 0x0E ++ ++/* Greybus Light modes */ ++ ++/* ++ * if you add any specific mode below, update also the ++ * GB_CHANNEL_MODE_DEFINED_RANGE value accordingly ++ */ ++#define GB_CHANNEL_MODE_NONE 0x00000000 ++#define GB_CHANNEL_MODE_BATTERY 0x00000001 ++#define GB_CHANNEL_MODE_POWER 0x00000002 ++#define GB_CHANNEL_MODE_WIRELESS 0x00000004 ++#define GB_CHANNEL_MODE_BLUETOOTH 0x00000008 ++#define GB_CHANNEL_MODE_KEYBOARD 0x00000010 ++#define GB_CHANNEL_MODE_BUTTONS 0x00000020 ++#define GB_CHANNEL_MODE_NOTIFICATION 0x00000040 ++#define GB_CHANNEL_MODE_ATTENTION 0x00000080 ++#define GB_CHANNEL_MODE_FLASH 0x00000100 ++#define GB_CHANNEL_MODE_TORCH 0x00000200 ++#define GB_CHANNEL_MODE_INDICATOR 0x00000400 ++ ++/* Lights Mode valid bit values */ ++#define GB_CHANNEL_MODE_DEFINED_RANGE 0x000004FF ++#define GB_CHANNEL_MODE_VENDOR_RANGE 0x00F00000 ++ ++/* Greybus Light Channels Flags */ ++#define GB_LIGHT_CHANNEL_MULTICOLOR 0x00000001 ++#define GB_LIGHT_CHANNEL_FADER 0x00000002 ++#define GB_LIGHT_CHANNEL_BLINK 0x00000004 ++ ++/* get count of lights in module */ ++struct gb_lights_get_lights_response { ++ __u8 lights_count; ++} __packed; ++ ++/* light config request payload */ ++struct gb_lights_get_light_config_request { ++ __u8 id; ++} __packed; ++ ++/* light config response payload */ ++struct gb_lights_get_light_config_response { ++ __u8 channel_count; ++ __u8 name[32]; ++} __packed; ++ ++/* channel config request payload */ ++struct gb_lights_get_channel_config_request { ++ __u8 light_id; ++ __u8 channel_id; ++} __packed; ++ ++/* channel flash config request payload */ ++struct gb_lights_get_channel_flash_config_request { ++ __u8 light_id; ++ __u8 channel_id; ++} __packed; ++ ++/* channel config response payload */ ++struct gb_lights_get_channel_config_response { ++ __u8 max_brightness; ++ __le32 flags; ++ __le32 color; ++ __u8 color_name[32]; ++ __le32 mode; ++ __u8 mode_name[32]; ++} __packed; ++ ++/* channel flash config response payload */ ++struct gb_lights_get_channel_flash_config_response { ++ __le32 intensity_min_uA; ++ __le32 intensity_max_uA; ++ __le32 intensity_step_uA; ++ __le32 timeout_min_us; ++ __le32 timeout_max_us; ++ __le32 timeout_step_us; ++} __packed; ++ ++/* blink request payload: response have no payload */ ++struct gb_lights_blink_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __le16 time_on_ms; ++ __le16 time_off_ms; ++} __packed; ++ ++/* set brightness request payload: response have no payload */ ++struct gb_lights_set_brightness_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __u8 brightness; ++} __packed; ++ ++/* set color request payload: response have no payload */ ++struct gb_lights_set_color_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __le32 color; ++} __packed; ++ ++/* set fade request payload: response have no payload */ ++struct gb_lights_set_fade_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __u8 fade_in; ++ __u8 fade_out; ++} __packed; ++ ++/* event request: generated by module */ ++struct gb_lights_event_request { ++ __u8 light_id; ++ __u8 event; ++#define GB_LIGHTS_LIGHT_CONFIG 0x01 ++} __packed; ++ ++/* set flash intensity request payload: response have no payload */ ++struct gb_lights_set_flash_intensity_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __le32 intensity_uA; ++} __packed; ++ ++/* set flash strobe state request payload: response have no payload */ ++struct gb_lights_set_flash_strobe_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __u8 state; ++} __packed; ++ ++/* set flash timeout request payload: response have no payload */ ++struct gb_lights_set_flash_timeout_request { ++ __u8 light_id; ++ __u8 channel_id; ++ __le32 timeout_us; ++} __packed; ++ ++/* get flash fault request payload */ ++struct gb_lights_get_flash_fault_request { ++ __u8 light_id; ++ __u8 channel_id; ++} __packed; ++ ++/* get flash fault response payload */ ++struct gb_lights_get_flash_fault_response { ++ __le32 fault; ++#define GB_LIGHTS_FLASH_FAULT_OVER_VOLTAGE 0x00000000 ++#define GB_LIGHTS_FLASH_FAULT_TIMEOUT 0x00000001 ++#define GB_LIGHTS_FLASH_FAULT_OVER_TEMPERATURE 0x00000002 ++#define GB_LIGHTS_FLASH_FAULT_SHORT_CIRCUIT 0x00000004 ++#define GB_LIGHTS_FLASH_FAULT_OVER_CURRENT 0x00000008 ++#define GB_LIGHTS_FLASH_FAULT_INDICATOR 0x00000010 ++#define GB_LIGHTS_FLASH_FAULT_UNDER_VOLTAGE 0x00000020 ++#define GB_LIGHTS_FLASH_FAULT_INPUT_VOLTAGE 0x00000040 ++#define GB_LIGHTS_FLASH_FAULT_LED_OVER_TEMPERATURE 0x00000080 ++} __packed; ++ ++/* Audio */ ++ ++#define GB_AUDIO_TYPE_GET_TOPOLOGY_SIZE 0x02 ++#define GB_AUDIO_TYPE_GET_TOPOLOGY 0x03 ++#define GB_AUDIO_TYPE_GET_CONTROL 0x04 ++#define GB_AUDIO_TYPE_SET_CONTROL 0x05 ++#define GB_AUDIO_TYPE_ENABLE_WIDGET 0x06 ++#define GB_AUDIO_TYPE_DISABLE_WIDGET 0x07 ++#define GB_AUDIO_TYPE_GET_PCM 0x08 ++#define GB_AUDIO_TYPE_SET_PCM 0x09 ++#define GB_AUDIO_TYPE_SET_TX_DATA_SIZE 0x0a ++ /* 0x0b unused */ ++#define GB_AUDIO_TYPE_ACTIVATE_TX 0x0c ++#define GB_AUDIO_TYPE_DEACTIVATE_TX 0x0d ++#define GB_AUDIO_TYPE_SET_RX_DATA_SIZE 0x0e ++ /* 0x0f unused */ ++#define GB_AUDIO_TYPE_ACTIVATE_RX 0x10 ++#define GB_AUDIO_TYPE_DEACTIVATE_RX 0x11 ++#define GB_AUDIO_TYPE_JACK_EVENT 0x12 ++#define GB_AUDIO_TYPE_BUTTON_EVENT 0x13 ++#define GB_AUDIO_TYPE_STREAMING_EVENT 0x14 ++#define GB_AUDIO_TYPE_SEND_DATA 0x15 ++ ++/* Module must be able to buffer 10ms of audio data, minimum */ ++#define GB_AUDIO_SAMPLE_BUFFER_MIN_US 10000 ++ ++#define GB_AUDIO_PCM_NAME_MAX 32 ++#define AUDIO_DAI_NAME_MAX 32 ++#define AUDIO_CONTROL_NAME_MAX 32 ++#define AUDIO_CTL_ELEM_NAME_MAX 44 ++#define AUDIO_ENUM_NAME_MAX 64 ++#define AUDIO_WIDGET_NAME_MAX 32 ++ ++/* See SNDRV_PCM_FMTBIT_* in Linux source */ ++#define GB_AUDIO_PCM_FMT_S8 BIT(0) ++#define GB_AUDIO_PCM_FMT_U8 BIT(1) ++#define GB_AUDIO_PCM_FMT_S16_LE BIT(2) ++#define GB_AUDIO_PCM_FMT_S16_BE BIT(3) ++#define GB_AUDIO_PCM_FMT_U16_LE BIT(4) ++#define GB_AUDIO_PCM_FMT_U16_BE BIT(5) ++#define GB_AUDIO_PCM_FMT_S24_LE BIT(6) ++#define GB_AUDIO_PCM_FMT_S24_BE BIT(7) ++#define GB_AUDIO_PCM_FMT_U24_LE BIT(8) ++#define GB_AUDIO_PCM_FMT_U24_BE BIT(9) ++#define GB_AUDIO_PCM_FMT_S32_LE BIT(10) ++#define GB_AUDIO_PCM_FMT_S32_BE BIT(11) ++#define GB_AUDIO_PCM_FMT_U32_LE BIT(12) ++#define GB_AUDIO_PCM_FMT_U32_BE BIT(13) ++ ++/* See SNDRV_PCM_RATE_* in Linux source */ ++#define GB_AUDIO_PCM_RATE_5512 BIT(0) ++#define GB_AUDIO_PCM_RATE_8000 BIT(1) ++#define GB_AUDIO_PCM_RATE_11025 BIT(2) ++#define GB_AUDIO_PCM_RATE_16000 BIT(3) ++#define GB_AUDIO_PCM_RATE_22050 BIT(4) ++#define GB_AUDIO_PCM_RATE_32000 BIT(5) ++#define GB_AUDIO_PCM_RATE_44100 BIT(6) ++#define GB_AUDIO_PCM_RATE_48000 BIT(7) ++#define GB_AUDIO_PCM_RATE_64000 BIT(8) ++#define GB_AUDIO_PCM_RATE_88200 BIT(9) ++#define GB_AUDIO_PCM_RATE_96000 BIT(10) ++#define GB_AUDIO_PCM_RATE_176400 BIT(11) ++#define GB_AUDIO_PCM_RATE_192000 BIT(12) ++ ++#define GB_AUDIO_STREAM_TYPE_CAPTURE 0x1 ++#define GB_AUDIO_STREAM_TYPE_PLAYBACK 0x2 ++ ++#define GB_AUDIO_CTL_ELEM_ACCESS_READ BIT(0) ++#define GB_AUDIO_CTL_ELEM_ACCESS_WRITE BIT(1) ++ ++/* See SNDRV_CTL_ELEM_TYPE_* in Linux source */ ++#define GB_AUDIO_CTL_ELEM_TYPE_BOOLEAN 0x01 ++#define GB_AUDIO_CTL_ELEM_TYPE_INTEGER 0x02 ++#define GB_AUDIO_CTL_ELEM_TYPE_ENUMERATED 0x03 ++#define GB_AUDIO_CTL_ELEM_TYPE_INTEGER64 0x06 ++ ++/* See SNDRV_CTL_ELEM_IFACE_* in Linux source */ ++#define GB_AUDIO_CTL_ELEM_IFACE_CARD 0x00 ++#define GB_AUDIO_CTL_ELEM_IFACE_HWDEP 0x01 ++#define GB_AUDIO_CTL_ELEM_IFACE_MIXER 0x02 ++#define GB_AUDIO_CTL_ELEM_IFACE_PCM 0x03 ++#define GB_AUDIO_CTL_ELEM_IFACE_RAWMIDI 0x04 ++#define GB_AUDIO_CTL_ELEM_IFACE_TIMER 0x05 ++#define GB_AUDIO_CTL_ELEM_IFACE_SEQUENCER 0x06 ++ ++/* SNDRV_CTL_ELEM_ACCESS_* in Linux source */ ++#define GB_AUDIO_ACCESS_READ BIT(0) ++#define GB_AUDIO_ACCESS_WRITE BIT(1) ++#define GB_AUDIO_ACCESS_VOLATILE BIT(2) ++#define GB_AUDIO_ACCESS_TIMESTAMP BIT(3) ++#define GB_AUDIO_ACCESS_TLV_READ BIT(4) ++#define GB_AUDIO_ACCESS_TLV_WRITE BIT(5) ++#define GB_AUDIO_ACCESS_TLV_COMMAND BIT(6) ++#define GB_AUDIO_ACCESS_INACTIVE BIT(7) ++#define GB_AUDIO_ACCESS_LOCK BIT(8) ++#define GB_AUDIO_ACCESS_OWNER BIT(9) ++ ++/* enum snd_soc_dapm_type */ ++#define GB_AUDIO_WIDGET_TYPE_INPUT 0x0 ++#define GB_AUDIO_WIDGET_TYPE_OUTPUT 0x1 ++#define GB_AUDIO_WIDGET_TYPE_MUX 0x2 ++#define GB_AUDIO_WIDGET_TYPE_VIRT_MUX 0x3 ++#define GB_AUDIO_WIDGET_TYPE_VALUE_MUX 0x4 ++#define GB_AUDIO_WIDGET_TYPE_MIXER 0x5 ++#define GB_AUDIO_WIDGET_TYPE_MIXER_NAMED_CTL 0x6 ++#define GB_AUDIO_WIDGET_TYPE_PGA 0x7 ++#define GB_AUDIO_WIDGET_TYPE_OUT_DRV 0x8 ++#define GB_AUDIO_WIDGET_TYPE_ADC 0x9 ++#define GB_AUDIO_WIDGET_TYPE_DAC 0xa ++#define GB_AUDIO_WIDGET_TYPE_MICBIAS 0xb ++#define GB_AUDIO_WIDGET_TYPE_MIC 0xc ++#define GB_AUDIO_WIDGET_TYPE_HP 0xd ++#define GB_AUDIO_WIDGET_TYPE_SPK 0xe ++#define GB_AUDIO_WIDGET_TYPE_LINE 0xf ++#define GB_AUDIO_WIDGET_TYPE_SWITCH 0x10 ++#define GB_AUDIO_WIDGET_TYPE_VMID 0x11 ++#define GB_AUDIO_WIDGET_TYPE_PRE 0x12 ++#define GB_AUDIO_WIDGET_TYPE_POST 0x13 ++#define GB_AUDIO_WIDGET_TYPE_SUPPLY 0x14 ++#define GB_AUDIO_WIDGET_TYPE_REGULATOR_SUPPLY 0x15 ++#define GB_AUDIO_WIDGET_TYPE_CLOCK_SUPPLY 0x16 ++#define GB_AUDIO_WIDGET_TYPE_AIF_IN 0x17 ++#define GB_AUDIO_WIDGET_TYPE_AIF_OUT 0x18 ++#define GB_AUDIO_WIDGET_TYPE_SIGGEN 0x19 ++#define GB_AUDIO_WIDGET_TYPE_DAI_IN 0x1a ++#define GB_AUDIO_WIDGET_TYPE_DAI_OUT 0x1b ++#define GB_AUDIO_WIDGET_TYPE_DAI_LINK 0x1c ++ ++#define GB_AUDIO_WIDGET_STATE_DISABLED 0x01 ++#define GB_AUDIO_WIDGET_STATE_ENAABLED 0x02 ++ ++#define GB_AUDIO_JACK_EVENT_INSERTION 0x1 ++#define GB_AUDIO_JACK_EVENT_REMOVAL 0x2 ++ ++#define GB_AUDIO_BUTTON_EVENT_PRESS 0x1 ++#define GB_AUDIO_BUTTON_EVENT_RELEASE 0x2 ++ ++#define GB_AUDIO_STREAMING_EVENT_UNSPECIFIED 0x1 ++#define GB_AUDIO_STREAMING_EVENT_HALT 0x2 ++#define GB_AUDIO_STREAMING_EVENT_INTERNAL_ERROR 0x3 ++#define GB_AUDIO_STREAMING_EVENT_PROTOCOL_ERROR 0x4 ++#define GB_AUDIO_STREAMING_EVENT_FAILURE 0x5 ++#define GB_AUDIO_STREAMING_EVENT_UNDERRUN 0x6 ++#define GB_AUDIO_STREAMING_EVENT_OVERRUN 0x7 ++#define GB_AUDIO_STREAMING_EVENT_CLOCKING 0x8 ++#define GB_AUDIO_STREAMING_EVENT_DATA_LEN 0x9 ++ ++#define GB_AUDIO_INVALID_INDEX 0xff ++ ++/* enum snd_jack_types */ ++#define GB_AUDIO_JACK_HEADPHONE 0x0000001 ++#define GB_AUDIO_JACK_MICROPHONE 0x0000002 ++#define GB_AUDIO_JACK_HEADSET (GB_AUDIO_JACK_HEADPHONE | \ ++ GB_AUDIO_JACK_MICROPHONE) ++#define GB_AUDIO_JACK_LINEOUT 0x0000004 ++#define GB_AUDIO_JACK_MECHANICAL 0x0000008 ++#define GB_AUDIO_JACK_VIDEOOUT 0x0000010 ++#define GB_AUDIO_JACK_AVOUT (GB_AUDIO_JACK_LINEOUT | \ ++ GB_AUDIO_JACK_VIDEOOUT) ++#define GB_AUDIO_JACK_LINEIN 0x0000020 ++#define GB_AUDIO_JACK_OC_HPHL 0x0000040 ++#define GB_AUDIO_JACK_OC_HPHR 0x0000080 ++#define GB_AUDIO_JACK_MICROPHONE2 0x0000200 ++#define GB_AUDIO_JACK_ANC_HEADPHONE (GB_AUDIO_JACK_HEADPHONE | \ ++ GB_AUDIO_JACK_MICROPHONE | \ ++ GB_AUDIO_JACK_MICROPHONE2) ++/* Kept separate from switches to facilitate implementation */ ++#define GB_AUDIO_JACK_BTN_0 0x4000000 ++#define GB_AUDIO_JACK_BTN_1 0x2000000 ++#define GB_AUDIO_JACK_BTN_2 0x1000000 ++#define GB_AUDIO_JACK_BTN_3 0x0800000 ++ ++struct gb_audio_pcm { ++ __u8 stream_name[GB_AUDIO_PCM_NAME_MAX]; ++ __le32 formats; /* GB_AUDIO_PCM_FMT_* */ ++ __le32 rates; /* GB_AUDIO_PCM_RATE_* */ ++ __u8 chan_min; ++ __u8 chan_max; ++ __u8 sig_bits; /* number of bits of content */ ++} __packed; ++ ++struct gb_audio_dai { ++ __u8 name[AUDIO_DAI_NAME_MAX]; ++ __le16 data_cport; ++ struct gb_audio_pcm capture; ++ struct gb_audio_pcm playback; ++} __packed; ++ ++struct gb_audio_integer { ++ __le32 min; ++ __le32 max; ++ __le32 step; ++} __packed; ++ ++struct gb_audio_integer64 { ++ __le64 min; ++ __le64 max; ++ __le64 step; ++} __packed; ++ ++struct gb_audio_enumerated { ++ __le32 items; ++ __le16 names_length; ++ __u8 names[0]; ++} __packed; ++ ++struct gb_audio_ctl_elem_info { /* See snd_ctl_elem_info in Linux source */ ++ __u8 type; /* GB_AUDIO_CTL_ELEM_TYPE_* */ ++ __le16 dimen[4]; ++ union { ++ struct gb_audio_integer integer; ++ struct gb_audio_integer64 integer64; ++ struct gb_audio_enumerated enumerated; ++ } value; ++} __packed; ++ ++struct gb_audio_ctl_elem_value { /* See snd_ctl_elem_value in Linux source */ ++ __le64 timestamp; /* XXX needed? */ ++ union { ++ __le32 integer_value[2]; /* consider CTL_DOUBLE_xxx */ ++ __le64 integer64_value[2]; ++ __le32 enumerated_item[2]; ++ } value; ++} __packed; ++ ++struct gb_audio_control { ++ __u8 name[AUDIO_CONTROL_NAME_MAX]; ++ __u8 id; /* 0-63 */ ++ __u8 iface; /* GB_AUDIO_IFACE_* */ ++ __le16 data_cport; ++ __le32 access; /* GB_AUDIO_ACCESS_* */ ++ __u8 count; /* count of same elements */ ++ __u8 count_values; /* count of values, max=2 for CTL_DOUBLE_xxx */ ++ struct gb_audio_ctl_elem_info info; ++} __packed; ++ ++struct gb_audio_widget { ++ __u8 name[AUDIO_WIDGET_NAME_MAX]; ++ __u8 sname[AUDIO_WIDGET_NAME_MAX]; ++ __u8 id; ++ __u8 type; /* GB_AUDIO_WIDGET_TYPE_* */ ++ __u8 state; /* GB_AUDIO_WIDGET_STATE_* */ ++ __u8 ncontrols; ++ struct gb_audio_control ctl[0]; /* 'ncontrols' entries */ ++} __packed; ++ ++struct gb_audio_route { ++ __u8 source_id; /* widget id */ ++ __u8 destination_id; /* widget id */ ++ __u8 control_id; /* 0-63 */ ++ __u8 index; /* Selection within the control */ ++} __packed; ++ ++struct gb_audio_topology { ++ __u8 num_dais; ++ __u8 num_controls; ++ __u8 num_widgets; ++ __u8 num_routes; ++ __le32 size_dais; ++ __le32 size_controls; ++ __le32 size_widgets; ++ __le32 size_routes; ++ __le32 jack_type; ++ /* ++ * struct gb_audio_dai dai[num_dais]; ++ * struct gb_audio_control controls[num_controls]; ++ * struct gb_audio_widget widgets[num_widgets]; ++ * struct gb_audio_route routes[num_routes]; ++ */ ++ __u8 data[0]; ++} __packed; ++ ++struct gb_audio_get_topology_size_response { ++ __le16 size; ++} __packed; ++ ++struct gb_audio_get_topology_response { ++ struct gb_audio_topology topology; ++} __packed; ++ ++struct gb_audio_get_control_request { ++ __u8 control_id; ++ __u8 index; ++} __packed; ++ ++struct gb_audio_get_control_response { ++ struct gb_audio_ctl_elem_value value; ++} __packed; ++ ++struct gb_audio_set_control_request { ++ __u8 control_id; ++ __u8 index; ++ struct gb_audio_ctl_elem_value value; ++} __packed; ++ ++struct gb_audio_enable_widget_request { ++ __u8 widget_id; ++} __packed; ++ ++struct gb_audio_disable_widget_request { ++ __u8 widget_id; ++} __packed; ++ ++struct gb_audio_get_pcm_request { ++ __le16 data_cport; ++} __packed; ++ ++struct gb_audio_get_pcm_response { ++ __le32 format; ++ __le32 rate; ++ __u8 channels; ++ __u8 sig_bits; ++} __packed; ++ ++struct gb_audio_set_pcm_request { ++ __le16 data_cport; ++ __le32 format; ++ __le32 rate; ++ __u8 channels; ++ __u8 sig_bits; ++} __packed; ++ ++struct gb_audio_set_tx_data_size_request { ++ __le16 data_cport; ++ __le16 size; ++} __packed; ++ ++struct gb_audio_activate_tx_request { ++ __le16 data_cport; ++} __packed; ++ ++struct gb_audio_deactivate_tx_request { ++ __le16 data_cport; ++} __packed; ++ ++struct gb_audio_set_rx_data_size_request { ++ __le16 data_cport; ++ __le16 size; ++} __packed; ++ ++struct gb_audio_activate_rx_request { ++ __le16 data_cport; ++} __packed; ++ ++struct gb_audio_deactivate_rx_request { ++ __le16 data_cport; ++} __packed; ++ ++struct gb_audio_jack_event_request { ++ __u8 widget_id; ++ __u8 jack_attribute; ++ __u8 event; ++} __packed; ++ ++struct gb_audio_button_event_request { ++ __u8 widget_id; ++ __u8 button_id; ++ __u8 event; ++} __packed; ++ ++struct gb_audio_streaming_event_request { ++ __le16 data_cport; ++ __u8 event; ++} __packed; ++ ++struct gb_audio_send_data_request { ++ __le64 timestamp; ++ __u8 data[0]; ++} __packed; ++ ++ ++/* Log */ ++ ++/* operations */ ++#define GB_LOG_TYPE_SEND_LOG 0x02 ++ ++/* length */ ++#define GB_LOG_MAX_LEN 1024 ++ ++struct gb_log_send_log_request { ++ __le16 len; ++ __u8 msg[0]; ++} __packed; ++ ++#endif /* __GREYBUS_PROTOCOLS_H */ ++ diff --git a/greybus_pwm.patch b/greybus_pwm.patch new file mode 100644 index 00000000000000..3f6ad0059d2052 --- /dev/null +++ b/greybus_pwm.patch @@ -0,0 +1,345 @@ +--- + drivers/greybus/pwm.c | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 338 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/pwm.c +@@ -0,0 +1,338 @@ ++/* ++ * PWM Greybus driver. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/pwm.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++struct gb_pwm_chip { ++ struct gb_connection *connection; ++ u8 pwm_max; /* max pwm number */ ++ ++ struct pwm_chip chip; ++ struct pwm_chip *pwm; ++}; ++#define pwm_chip_to_gb_pwm_chip(chip) \ ++ container_of(chip, struct gb_pwm_chip, chip) ++ ++ ++static int gb_pwm_count_operation(struct gb_pwm_chip *pwmc) ++{ ++ struct gb_pwm_count_response response; ++ int ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_PWM_COUNT, ++ NULL, 0, &response, sizeof(response)); ++ if (ret) ++ return ret; ++ pwmc->pwm_max = response.count; ++ return 0; ++} ++ ++static int gb_pwm_activate_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_activate_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_deactivate_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_deactivate_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DEACTIVATE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_config_operation(struct gb_pwm_chip *pwmc, ++ u8 which, u32 duty, u32 period) ++{ ++ struct gb_pwm_config_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ request.duty = cpu_to_le32(duty); ++ request.period = cpu_to_le32(period); ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_CONFIG, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_set_polarity_operation(struct gb_pwm_chip *pwmc, ++ u8 which, u8 polarity) ++{ ++ struct gb_pwm_polarity_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ request.polarity = polarity; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_POLARITY, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_enable_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_enable_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_ENABLE, ++ &request, sizeof(request), NULL, 0); ++ if (ret) ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_disable_operation(struct gb_pwm_chip *pwmc, ++ u8 which) ++{ ++ struct gb_pwm_disable_request request; ++ struct gbphy_device *gbphy_dev; ++ int ret; ++ ++ if (which > pwmc->pwm_max) ++ return -EINVAL; ++ ++ request.which = which; ++ ++ ret = gb_operation_sync(pwmc->connection, GB_PWM_TYPE_DISABLE, ++ &request, sizeof(request), NULL, 0); ++ ++ gbphy_dev = to_gbphy_dev(pwmc->chip.dev); ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++} ++ ++static int gb_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_activate_operation(pwmc, pwm->hwpwm); ++}; ++ ++static void gb_pwm_free(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ if (pwm_is_enabled(pwm)) ++ dev_warn(chip->dev, "freeing PWM device without disabling\n"); ++ ++ gb_pwm_deactivate_operation(pwmc, pwm->hwpwm); ++} ++ ++static int gb_pwm_config(struct pwm_chip *chip, struct pwm_device *pwm, ++ int duty_ns, int period_ns) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_config_operation(pwmc, pwm->hwpwm, duty_ns, period_ns); ++}; ++ ++static int gb_pwm_set_polarity(struct pwm_chip *chip, struct pwm_device *pwm, ++ enum pwm_polarity polarity) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_set_polarity_operation(pwmc, pwm->hwpwm, polarity); ++}; ++ ++static int gb_pwm_enable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ return gb_pwm_enable_operation(pwmc, pwm->hwpwm); ++}; ++ ++static void gb_pwm_disable(struct pwm_chip *chip, struct pwm_device *pwm) ++{ ++ struct gb_pwm_chip *pwmc = pwm_chip_to_gb_pwm_chip(chip); ++ ++ gb_pwm_disable_operation(pwmc, pwm->hwpwm); ++}; ++ ++static const struct pwm_ops gb_pwm_ops = { ++ .request = gb_pwm_request, ++ .free = gb_pwm_free, ++ .config = gb_pwm_config, ++ .set_polarity = gb_pwm_set_polarity, ++ .enable = gb_pwm_enable, ++ .disable = gb_pwm_disable, ++ .owner = THIS_MODULE, ++}; ++ ++static int gb_pwm_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct gb_pwm_chip *pwmc; ++ struct pwm_chip *pwm; ++ int ret; ++ ++ pwmc = kzalloc(sizeof(*pwmc), GFP_KERNEL); ++ if (!pwmc) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto exit_pwmc_free; ++ } ++ ++ pwmc->connection = connection; ++ gb_connection_set_data(connection, pwmc); ++ gb_gbphy_set_data(gbphy_dev, pwmc); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ /* Query number of pwms present */ ++ ret = gb_pwm_count_operation(pwmc); ++ if (ret) ++ goto exit_connection_disable; ++ ++ pwm = &pwmc->chip; ++ ++ pwm->dev = &gbphy_dev->dev; ++ pwm->ops = &gb_pwm_ops; ++ pwm->base = -1; /* Allocate base dynamically */ ++ pwm->npwm = pwmc->pwm_max + 1; ++ pwm->can_sleep = true; /* FIXME */ ++ ++ ret = pwmchip_add(pwm); ++ if (ret) { ++ dev_err(&gbphy_dev->dev, ++ "failed to register PWM: %d\n", ret); ++ goto exit_connection_disable; ++ } ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_pwmc_free: ++ kfree(pwmc); ++ return ret; ++} ++ ++static void gb_pwm_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_pwm_chip *pwmc = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = pwmc->connection; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ pwmchip_remove(&pwmc->chip); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ kfree(pwmc); ++} ++ ++static const struct gbphy_device_id gb_pwm_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_PWM) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_pwm_id_table); ++ ++static struct gbphy_driver pwm_driver = { ++ .name = "pwm", ++ .probe = gb_pwm_probe, ++ .remove = gb_pwm_remove, ++ .id_table = gb_pwm_id_table, ++}; ++ ++module_gbphy_driver(pwm_driver); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_raw.patch b/greybus_raw.patch new file mode 100644 index 00000000000000..ea821a4f4d541f --- /dev/null +++ b/greybus_raw.patch @@ -0,0 +1,388 @@ +--- + drivers/greybus/raw.c | 381 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 381 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/raw.c +@@ -0,0 +1,381 @@ ++/* ++ * Greybus driver for the Raw protocol ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/sizes.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/idr.h> ++#include <linux/uaccess.h> ++ ++#include "greybus.h" ++ ++struct gb_raw { ++ struct gb_connection *connection; ++ ++ struct list_head list; ++ int list_data; ++ struct mutex list_lock; ++ dev_t dev; ++ struct cdev cdev; ++ struct device *device; ++}; ++ ++struct raw_data { ++ struct list_head entry; ++ u32 len; ++ u8 data[0]; ++}; ++ ++static struct class *raw_class; ++static int raw_major; ++static const struct file_operations raw_fops; ++static DEFINE_IDA(minors); ++ ++/* Number of minor devices this driver supports */ ++#define NUM_MINORS 256 ++ ++/* Maximum size of any one send data buffer we support */ ++#define MAX_PACKET_SIZE (PAGE_SIZE * 2) ++ ++/* ++ * Maximum size of the data in the receive buffer we allow before we start to ++ * drop messages on the floor ++ */ ++#define MAX_DATA_SIZE (MAX_PACKET_SIZE * 8) ++ ++/* ++ * Add the raw data message to the list of received messages. ++ */ ++static int receive_data(struct gb_raw *raw, u32 len, u8 *data) ++{ ++ struct raw_data *raw_data; ++ struct device *dev = &raw->connection->bundle->dev; ++ int retval = 0; ++ ++ if (len > MAX_PACKET_SIZE) { ++ dev_err(dev, "Too big of a data packet, rejected\n"); ++ return -EINVAL; ++ } ++ ++ mutex_lock(&raw->list_lock); ++ if ((raw->list_data + len) > MAX_DATA_SIZE) { ++ dev_err(dev, "Too much data in receive buffer, now dropping packets\n"); ++ retval = -EINVAL; ++ goto exit; ++ } ++ ++ raw_data = kmalloc(sizeof(*raw_data) + len, GFP_KERNEL); ++ if (!raw_data) { ++ retval = -ENOMEM; ++ goto exit; ++ } ++ ++ raw->list_data += len; ++ raw_data->len = len; ++ memcpy(&raw_data->data[0], data, len); ++ ++ list_add_tail(&raw_data->entry, &raw->list); ++exit: ++ mutex_unlock(&raw->list_lock); ++ return retval; ++} ++ ++static int gb_raw_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct device *dev = &connection->bundle->dev; ++ struct gb_raw *raw = greybus_get_drvdata(connection->bundle); ++ struct gb_raw_send_request *receive; ++ u32 len; ++ ++ if (op->type != GB_RAW_TYPE_SEND) { ++ dev_err(dev, "unknown request type 0x%02x\n", op->type); ++ return -EINVAL; ++ } ++ ++ /* Verify size of payload */ ++ if (op->request->payload_size < sizeof(*receive)) { ++ dev_err(dev, "raw receive request too small (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*receive)); ++ return -EINVAL; ++ } ++ receive = op->request->payload; ++ len = le32_to_cpu(receive->len); ++ if (len != (int)(op->request->payload_size - sizeof(__le32))) { ++ dev_err(dev, "raw receive request wrong size %d vs %d\n", len, ++ (int)(op->request->payload_size - sizeof(__le32))); ++ return -EINVAL; ++ } ++ if (len == 0) { ++ dev_err(dev, "raw receive request of 0 bytes?\n"); ++ return -EINVAL; ++ } ++ ++ return receive_data(raw, len, receive->data); ++} ++ ++static int gb_raw_send(struct gb_raw *raw, u32 len, const char __user *data) ++{ ++ struct gb_connection *connection = raw->connection; ++ struct gb_raw_send_request *request; ++ int retval; ++ ++ request = kmalloc(len + sizeof(*request), GFP_KERNEL); ++ if (!request) ++ return -ENOMEM; ++ ++ if (copy_from_user(&request->data[0], data, len)) { ++ kfree(request); ++ return -EFAULT; ++ } ++ ++ request->len = cpu_to_le32(len); ++ ++ retval = gb_operation_sync(connection, GB_RAW_TYPE_SEND, ++ request, len + sizeof(*request), ++ NULL, 0); ++ ++ kfree(request); ++ return retval; ++} ++ ++static int gb_raw_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_raw *raw; ++ int retval; ++ int minor; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_RAW) ++ return -ENODEV; ++ ++ raw = kzalloc(sizeof(*raw), GFP_KERNEL); ++ if (!raw) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ gb_raw_request_handler); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto error_free; ++ } ++ ++ INIT_LIST_HEAD(&raw->list); ++ mutex_init(&raw->list_lock); ++ ++ raw->connection = connection; ++ greybus_set_drvdata(bundle, raw); ++ ++ minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); ++ if (minor < 0) { ++ retval = minor; ++ goto error_connection_destroy; ++ } ++ ++ raw->dev = MKDEV(raw_major, minor); ++ cdev_init(&raw->cdev, &raw_fops); ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto error_remove_ida; ++ ++ retval = cdev_add(&raw->cdev, raw->dev, 1); ++ if (retval) ++ goto error_connection_disable; ++ ++ raw->device = device_create(raw_class, &connection->bundle->dev, ++ raw->dev, raw, "gb!raw%d", minor); ++ if (IS_ERR(raw->device)) { ++ retval = PTR_ERR(raw->device); ++ goto error_del_cdev; ++ } ++ ++ return 0; ++ ++error_del_cdev: ++ cdev_del(&raw->cdev); ++ ++error_connection_disable: ++ gb_connection_disable(connection); ++ ++error_remove_ida: ++ ida_simple_remove(&minors, minor); ++ ++error_connection_destroy: ++ gb_connection_destroy(connection); ++ ++error_free: ++ kfree(raw); ++ return retval; ++} ++ ++static void gb_raw_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_raw *raw = greybus_get_drvdata(bundle); ++ struct gb_connection *connection = raw->connection; ++ struct raw_data *raw_data; ++ struct raw_data *temp; ++ ++ // FIXME - handle removing a connection when the char device node is open. ++ device_destroy(raw_class, raw->dev); ++ cdev_del(&raw->cdev); ++ gb_connection_disable(connection); ++ ida_simple_remove(&minors, MINOR(raw->dev)); ++ gb_connection_destroy(connection); ++ ++ mutex_lock(&raw->list_lock); ++ list_for_each_entry_safe(raw_data, temp, &raw->list, entry) { ++ list_del(&raw_data->entry); ++ kfree(raw_data); ++ } ++ mutex_unlock(&raw->list_lock); ++ ++ kfree(raw); ++} ++ ++/* ++ * Character device node interfaces. ++ * ++ * Note, we are using read/write to only allow a single read/write per message. ++ * This means for read(), you have to provide a big enough buffer for the full ++ * message to be copied into. If the buffer isn't big enough, the read() will ++ * fail with -ENOSPC. ++ */ ++ ++static int raw_open(struct inode *inode, struct file *file) ++{ ++ struct cdev *cdev = inode->i_cdev; ++ struct gb_raw *raw = container_of(cdev, struct gb_raw, cdev); ++ ++ file->private_data = raw; ++ return 0; ++} ++ ++static ssize_t raw_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ struct gb_raw *raw = file->private_data; ++ int retval; ++ ++ if (!count) ++ return 0; ++ ++ if (count > MAX_PACKET_SIZE) ++ return -E2BIG; ++ ++ retval = gb_raw_send(raw, count, buf); ++ if (retval) ++ return retval; ++ ++ return count; ++} ++ ++static ssize_t raw_read(struct file *file, char __user *buf, size_t count, ++ loff_t *ppos) ++{ ++ struct gb_raw *raw = file->private_data; ++ int retval = 0; ++ struct raw_data *raw_data; ++ ++ mutex_lock(&raw->list_lock); ++ if (list_empty(&raw->list)) ++ goto exit; ++ ++ raw_data = list_first_entry(&raw->list, struct raw_data, entry); ++ if (raw_data->len > count) { ++ retval = -ENOSPC; ++ goto exit; ++ } ++ ++ if (copy_to_user(buf, &raw_data->data[0], raw_data->len)) { ++ retval = -EFAULT; ++ goto exit; ++ } ++ ++ list_del(&raw_data->entry); ++ raw->list_data -= raw_data->len; ++ retval = raw_data->len; ++ kfree(raw_data); ++ ++exit: ++ mutex_unlock(&raw->list_lock); ++ return retval; ++} ++ ++static const struct file_operations raw_fops = { ++ .owner = THIS_MODULE, ++ .write = raw_write, ++ .read = raw_read, ++ .open = raw_open, ++ .llseek = noop_llseek, ++}; ++ ++static const struct greybus_bundle_id gb_raw_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_raw_id_table); ++ ++static struct greybus_driver gb_raw_driver = { ++ .name = "raw", ++ .probe = gb_raw_probe, ++ .disconnect = gb_raw_disconnect, ++ .id_table = gb_raw_id_table, ++}; ++ ++static int raw_init(void) ++{ ++ dev_t dev; ++ int retval; ++ ++ raw_class = class_create(THIS_MODULE, "gb_raw"); ++ if (IS_ERR(raw_class)) { ++ retval = PTR_ERR(raw_class); ++ goto error_class; ++ } ++ ++ retval = alloc_chrdev_region(&dev, 0, NUM_MINORS, "gb_raw"); ++ if (retval < 0) ++ goto error_chrdev; ++ ++ raw_major = MAJOR(dev); ++ ++ retval = greybus_register(&gb_raw_driver); ++ if (retval) ++ goto error_gb; ++ ++ return 0; ++ ++error_gb: ++ unregister_chrdev_region(dev, NUM_MINORS); ++error_chrdev: ++ class_destroy(raw_class); ++error_class: ++ return retval; ++} ++module_init(raw_init); ++ ++static void __exit raw_exit(void) ++{ ++ greybus_deregister(&gb_raw_driver); ++ unregister_chrdev_region(MKDEV(raw_major, 0), NUM_MINORS); ++ class_destroy(raw_class); ++ ida_destroy(&minors); ++} ++module_exit(raw_exit); ++ ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_sdio.patch b/greybus_sdio.patch new file mode 100644 index 00000000000000..32e0d3af6d5804 --- /dev/null +++ b/greybus_sdio.patch @@ -0,0 +1,891 @@ +--- + drivers/greybus/sdio.c | 884 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 884 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/sdio.c +@@ -0,0 +1,884 @@ ++/* ++ * SD/MMC Greybus driver. ++ * ++ * Copyright 2014-2015 Google Inc. ++ * Copyright 2014-2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/mmc/core.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/mmc.h> ++#include <linux/scatterlist.h> ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++struct gb_sdio_host { ++ struct gb_connection *connection; ++ struct gbphy_device *gbphy_dev; ++ struct mmc_host *mmc; ++ struct mmc_request *mrq; ++ struct mutex lock; /* lock for this host */ ++ size_t data_max; ++ spinlock_t xfer; /* lock to cancel ongoing transfer */ ++ bool xfer_stop; ++ struct workqueue_struct *mrq_workqueue; ++ struct work_struct mrqwork; ++ u8 queued_events; ++ bool removed; ++ bool card_present; ++ bool read_only; ++}; ++ ++ ++#define GB_SDIO_RSP_R1_R5_R6_R7 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ ++ GB_SDIO_RSP_OPCODE) ++#define GB_SDIO_RSP_R3_R4 (GB_SDIO_RSP_PRESENT) ++#define GB_SDIO_RSP_R2 (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ ++ GB_SDIO_RSP_136) ++#define GB_SDIO_RSP_R1B (GB_SDIO_RSP_PRESENT | GB_SDIO_RSP_CRC | \ ++ GB_SDIO_RSP_OPCODE | GB_SDIO_RSP_BUSY) ++ ++/* kernel vdd starts at 0x80 and we need to translate to greybus ones 0x01 */ ++#define GB_SDIO_VDD_SHIFT 8 ++ ++#ifndef MMC_CAP2_CORE_RUNTIME_PM ++#define MMC_CAP2_CORE_RUNTIME_PM 0 ++#endif ++ ++static inline bool single_op(struct mmc_command *cmd) ++{ ++ uint32_t opcode = cmd->opcode; ++ ++ return opcode == MMC_WRITE_BLOCK || ++ opcode == MMC_READ_SINGLE_BLOCK; ++} ++ ++static void _gb_sdio_set_host_caps(struct gb_sdio_host *host, u32 r) ++{ ++ u32 caps = 0; ++ u32 caps2 = 0; ++ ++ caps = ((r & GB_SDIO_CAP_NONREMOVABLE) ? MMC_CAP_NONREMOVABLE : 0) | ++ ((r & GB_SDIO_CAP_4_BIT_DATA) ? MMC_CAP_4_BIT_DATA : 0) | ++ ((r & GB_SDIO_CAP_8_BIT_DATA) ? MMC_CAP_8_BIT_DATA : 0) | ++ ((r & GB_SDIO_CAP_MMC_HS) ? MMC_CAP_MMC_HIGHSPEED : 0) | ++ ((r & GB_SDIO_CAP_SD_HS) ? MMC_CAP_SD_HIGHSPEED : 0) | ++ ((r & GB_SDIO_CAP_ERASE) ? MMC_CAP_ERASE : 0) | ++ ((r & GB_SDIO_CAP_1_2V_DDR) ? MMC_CAP_1_2V_DDR : 0) | ++ ((r & GB_SDIO_CAP_1_8V_DDR) ? MMC_CAP_1_8V_DDR : 0) | ++ ((r & GB_SDIO_CAP_POWER_OFF_CARD) ? MMC_CAP_POWER_OFF_CARD : 0) | ++ ((r & GB_SDIO_CAP_UHS_SDR12) ? MMC_CAP_UHS_SDR12 : 0) | ++ ((r & GB_SDIO_CAP_UHS_SDR25) ? MMC_CAP_UHS_SDR25 : 0) | ++ ((r & GB_SDIO_CAP_UHS_SDR50) ? MMC_CAP_UHS_SDR50 : 0) | ++ ((r & GB_SDIO_CAP_UHS_SDR104) ? MMC_CAP_UHS_SDR104 : 0) | ++ ((r & GB_SDIO_CAP_UHS_DDR50) ? MMC_CAP_UHS_DDR50 : 0) | ++ ((r & GB_SDIO_CAP_DRIVER_TYPE_A) ? MMC_CAP_DRIVER_TYPE_A : 0) | ++ ((r & GB_SDIO_CAP_DRIVER_TYPE_C) ? MMC_CAP_DRIVER_TYPE_C : 0) | ++ ((r & GB_SDIO_CAP_DRIVER_TYPE_D) ? MMC_CAP_DRIVER_TYPE_D : 0); ++ ++ caps2 = ((r & GB_SDIO_CAP_HS200_1_2V) ? MMC_CAP2_HS200_1_2V_SDR : 0) | ++ ((r & GB_SDIO_CAP_HS400_1_2V) ? MMC_CAP2_HS400_1_2V : 0) | ++ ((r & GB_SDIO_CAP_HS400_1_8V) ? MMC_CAP2_HS400_1_8V : 0) | ++ ((r & GB_SDIO_CAP_HS200_1_8V) ? MMC_CAP2_HS200_1_8V_SDR : 0); ++ ++ host->mmc->caps = caps; ++ host->mmc->caps2 = caps2 | MMC_CAP2_CORE_RUNTIME_PM; ++ ++ if (caps & MMC_CAP_NONREMOVABLE) ++ host->card_present = true; ++} ++ ++static u32 _gb_sdio_get_host_ocr(u32 ocr) ++{ ++ return (((ocr & GB_SDIO_VDD_165_195) ? MMC_VDD_165_195 : 0) | ++ ((ocr & GB_SDIO_VDD_20_21) ? MMC_VDD_20_21 : 0) | ++ ((ocr & GB_SDIO_VDD_21_22) ? MMC_VDD_21_22 : 0) | ++ ((ocr & GB_SDIO_VDD_22_23) ? MMC_VDD_22_23 : 0) | ++ ((ocr & GB_SDIO_VDD_23_24) ? MMC_VDD_23_24 : 0) | ++ ((ocr & GB_SDIO_VDD_24_25) ? MMC_VDD_24_25 : 0) | ++ ((ocr & GB_SDIO_VDD_25_26) ? MMC_VDD_25_26 : 0) | ++ ((ocr & GB_SDIO_VDD_26_27) ? MMC_VDD_26_27 : 0) | ++ ((ocr & GB_SDIO_VDD_27_28) ? MMC_VDD_27_28 : 0) | ++ ((ocr & GB_SDIO_VDD_28_29) ? MMC_VDD_28_29 : 0) | ++ ((ocr & GB_SDIO_VDD_29_30) ? MMC_VDD_29_30 : 0) | ++ ((ocr & GB_SDIO_VDD_30_31) ? MMC_VDD_30_31 : 0) | ++ ((ocr & GB_SDIO_VDD_31_32) ? MMC_VDD_31_32 : 0) | ++ ((ocr & GB_SDIO_VDD_32_33) ? MMC_VDD_32_33 : 0) | ++ ((ocr & GB_SDIO_VDD_33_34) ? MMC_VDD_33_34 : 0) | ++ ((ocr & GB_SDIO_VDD_34_35) ? MMC_VDD_34_35 : 0) | ++ ((ocr & GB_SDIO_VDD_35_36) ? MMC_VDD_35_36 : 0) ++ ); ++} ++ ++static int gb_sdio_get_caps(struct gb_sdio_host *host) ++{ ++ struct gb_sdio_get_caps_response response; ++ struct mmc_host *mmc = host->mmc; ++ u16 data_max; ++ u32 blksz; ++ u32 ocr; ++ u32 r; ++ int ret; ++ ++ ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_GET_CAPABILITIES, ++ NULL, 0, &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ r = le32_to_cpu(response.caps); ++ ++ _gb_sdio_set_host_caps(host, r); ++ ++ /* get the max block size that could fit our payload */ ++ data_max = gb_operation_get_payload_size_max(host->connection); ++ data_max = min(data_max - sizeof(struct gb_sdio_transfer_request), ++ data_max - sizeof(struct gb_sdio_transfer_response)); ++ ++ blksz = min_t(u16, le16_to_cpu(response.max_blk_size), data_max); ++ blksz = max_t(u32, 512, blksz); ++ ++ mmc->max_blk_size = rounddown_pow_of_two(blksz); ++ mmc->max_blk_count = le16_to_cpu(response.max_blk_count); ++ host->data_max = data_max; ++ ++ /* get ocr supported values */ ++ ocr = _gb_sdio_get_host_ocr(le32_to_cpu(response.ocr)); ++ mmc->ocr_avail = ocr; ++ mmc->ocr_avail_sdio = mmc->ocr_avail; ++ mmc->ocr_avail_sd = mmc->ocr_avail; ++ mmc->ocr_avail_mmc = mmc->ocr_avail; ++ ++ /* get frequency range values */ ++ mmc->f_min = le32_to_cpu(response.f_min); ++ mmc->f_max = le32_to_cpu(response.f_max); ++ ++ return 0; ++} ++ ++static void _gb_queue_event(struct gb_sdio_host *host, u8 event) ++{ ++ if (event & GB_SDIO_CARD_INSERTED) ++ host->queued_events &= ~GB_SDIO_CARD_REMOVED; ++ else if (event & GB_SDIO_CARD_REMOVED) ++ host->queued_events &= ~GB_SDIO_CARD_INSERTED; ++ ++ host->queued_events |= event; ++} ++ ++static int _gb_sdio_process_events(struct gb_sdio_host *host, u8 event) ++{ ++ u8 state_changed = 0; ++ ++ if (event & GB_SDIO_CARD_INSERTED) { ++ if (host->mmc->caps & MMC_CAP_NONREMOVABLE) ++ return 0; ++ if (host->card_present) ++ return 0; ++ host->card_present = true; ++ state_changed = 1; ++ } ++ ++ if (event & GB_SDIO_CARD_REMOVED) { ++ if (host->mmc->caps & MMC_CAP_NONREMOVABLE) ++ return 0; ++ if (!(host->card_present)) ++ return 0; ++ host->card_present = false; ++ state_changed = 1; ++ } ++ ++ if (event & GB_SDIO_WP) { ++ host->read_only = true; ++ } ++ ++ if (state_changed) { ++ dev_info(mmc_dev(host->mmc), "card %s now event\n", ++ (host->card_present ? "inserted" : "removed")); ++ mmc_detect_change(host->mmc, 0); ++ } ++ ++ return 0; ++} ++ ++static int gb_sdio_request_handler(struct gb_operation *op) ++{ ++ struct gb_sdio_host *host = gb_connection_get_data(op->connection); ++ struct gb_message *request; ++ struct gb_sdio_event_request *payload; ++ u8 type = op->type; ++ int ret = 0; ++ u8 event; ++ ++ if (type != GB_SDIO_TYPE_EVENT) { ++ dev_err(mmc_dev(host->mmc), ++ "unsupported unsolicited event: %u\n", type); ++ return -EINVAL; ++ } ++ ++ request = op->request; ++ ++ if (request->payload_size < sizeof(*payload)) { ++ dev_err(mmc_dev(host->mmc), "wrong event size received (%zu < %zu)\n", ++ request->payload_size, sizeof(*payload)); ++ return -EINVAL; ++ } ++ ++ payload = request->payload; ++ event = payload->event; ++ ++ if (host->removed) ++ _gb_queue_event(host, event); ++ else ++ ret = _gb_sdio_process_events(host, event); ++ ++ return ret; ++} ++ ++static int gb_sdio_set_ios(struct gb_sdio_host *host, ++ struct gb_sdio_set_ios_request *request) ++{ ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(host->gbphy_dev); ++ if (ret) ++ return ret; ++ ++ ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_SET_IOS, request, ++ sizeof(*request), NULL, 0); ++ ++ gbphy_runtime_put_autosuspend(host->gbphy_dev); ++ ++ return ret; ++} ++ ++static int _gb_sdio_send(struct gb_sdio_host *host, struct mmc_data *data, ++ size_t len, u16 nblocks, off_t skip) ++{ ++ struct gb_sdio_transfer_request *request; ++ struct gb_sdio_transfer_response *response; ++ struct gb_operation *operation; ++ struct scatterlist *sg = data->sg; ++ unsigned int sg_len = data->sg_len; ++ size_t copied; ++ u16 send_blksz; ++ u16 send_blocks; ++ int ret; ++ ++ WARN_ON(len > host->data_max); ++ ++ operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, ++ len + sizeof(*request), ++ sizeof(*response), GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ request = operation->request->payload; ++ request->data_flags = (data->flags >> 8); ++ request->data_blocks = cpu_to_le16(nblocks); ++ request->data_blksz = cpu_to_le16(data->blksz); ++ ++ copied = sg_pcopy_to_buffer(sg, sg_len, &request->data[0], len, skip); ++ ++ if (copied != len) { ++ ret = -EINVAL; ++ goto err_put_operation; ++ } ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret < 0) ++ goto err_put_operation; ++ ++ response = operation->response->payload; ++ ++ send_blocks = le16_to_cpu(response->data_blocks); ++ send_blksz = le16_to_cpu(response->data_blksz); ++ ++ if (len != send_blksz * send_blocks) { ++ dev_err(mmc_dev(host->mmc), "send: size received: %zu != %d\n", ++ len, send_blksz * send_blocks); ++ ret = -EINVAL; ++ } ++ ++err_put_operation: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static int _gb_sdio_recv(struct gb_sdio_host *host, struct mmc_data *data, ++ size_t len, u16 nblocks, off_t skip) ++{ ++ struct gb_sdio_transfer_request *request; ++ struct gb_sdio_transfer_response *response; ++ struct gb_operation *operation; ++ struct scatterlist *sg = data->sg; ++ unsigned int sg_len = data->sg_len; ++ size_t copied; ++ u16 recv_blksz; ++ u16 recv_blocks; ++ int ret; ++ ++ WARN_ON(len > host->data_max); ++ ++ operation = gb_operation_create(host->connection, GB_SDIO_TYPE_TRANSFER, ++ sizeof(*request), ++ len + sizeof(*response), GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ request = operation->request->payload; ++ request->data_flags = (data->flags >> 8); ++ request->data_blocks = cpu_to_le16(nblocks); ++ request->data_blksz = cpu_to_le16(data->blksz); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret < 0) ++ goto err_put_operation; ++ ++ response = operation->response->payload; ++ recv_blocks = le16_to_cpu(response->data_blocks); ++ recv_blksz = le16_to_cpu(response->data_blksz); ++ ++ if (len != recv_blksz * recv_blocks) { ++ dev_err(mmc_dev(host->mmc), "recv: size received: %d != %zu\n", ++ recv_blksz * recv_blocks, len); ++ ret = -EINVAL; ++ goto err_put_operation; ++ } ++ ++ copied = sg_pcopy_from_buffer(sg, sg_len, &response->data[0], len, ++ skip); ++ if (copied != len) ++ ret = -EINVAL; ++ ++err_put_operation: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static int gb_sdio_transfer(struct gb_sdio_host *host, struct mmc_data *data) ++{ ++ size_t left, len; ++ off_t skip = 0; ++ int ret = 0; ++ u16 nblocks; ++ ++ if (single_op(data->mrq->cmd) && data->blocks > 1) { ++ ret = -ETIMEDOUT; ++ goto out; ++ } ++ ++ left = data->blksz * data->blocks; ++ ++ while (left) { ++ /* check is a stop transmission is pending */ ++ spin_lock(&host->xfer); ++ if (host->xfer_stop) { ++ host->xfer_stop = false; ++ spin_unlock(&host->xfer); ++ ret = -EINTR; ++ goto out; ++ } ++ spin_unlock(&host->xfer); ++ len = min(left, host->data_max); ++ nblocks = len / data->blksz; ++ len = nblocks * data->blksz; ++ ++ if (data->flags & MMC_DATA_READ) { ++ ret = _gb_sdio_recv(host, data, len, nblocks, skip); ++ if (ret < 0) ++ goto out; ++ } else { ++ ret = _gb_sdio_send(host, data, len, nblocks, skip); ++ if (ret < 0) ++ goto out; ++ } ++ data->bytes_xfered += len; ++ left -= len; ++ skip += len; ++ } ++ ++out: ++ data->error = ret; ++ return ret; ++} ++ ++static int gb_sdio_command(struct gb_sdio_host *host, struct mmc_command *cmd) ++{ ++ struct gb_sdio_command_request request = {0}; ++ struct gb_sdio_command_response response; ++ struct mmc_data *data = host->mrq->data; ++ u8 cmd_flags; ++ u8 cmd_type; ++ int i; ++ int ret; ++ ++ switch (mmc_resp_type(cmd)) { ++ case MMC_RSP_NONE: ++ cmd_flags = GB_SDIO_RSP_NONE; ++ break; ++ case MMC_RSP_R1: ++ cmd_flags = GB_SDIO_RSP_R1_R5_R6_R7; ++ break; ++ case MMC_RSP_R1B: ++ cmd_flags = GB_SDIO_RSP_R1B; ++ break; ++ case MMC_RSP_R2: ++ cmd_flags = GB_SDIO_RSP_R2; ++ break; ++ case MMC_RSP_R3: ++ cmd_flags = GB_SDIO_RSP_R3_R4; ++ break; ++ default: ++ dev_err(mmc_dev(host->mmc), "cmd flag invalid 0x%04x\n", ++ mmc_resp_type(cmd)); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ switch (mmc_cmd_type(cmd)) { ++ case MMC_CMD_BC: ++ cmd_type = GB_SDIO_CMD_BC; ++ break; ++ case MMC_CMD_BCR: ++ cmd_type = GB_SDIO_CMD_BCR; ++ break; ++ case MMC_CMD_AC: ++ cmd_type = GB_SDIO_CMD_AC; ++ break; ++ case MMC_CMD_ADTC: ++ cmd_type = GB_SDIO_CMD_ADTC; ++ break; ++ default: ++ dev_err(mmc_dev(host->mmc), "cmd type invalid 0x%04x\n", ++ mmc_cmd_type(cmd)); ++ ret = -EINVAL; ++ goto out; ++ } ++ ++ request.cmd = cmd->opcode; ++ request.cmd_flags = cmd_flags; ++ request.cmd_type = cmd_type; ++ request.cmd_arg = cpu_to_le32(cmd->arg); ++ /* some controllers need to know at command time data details */ ++ if (data) { ++ request.data_blocks = cpu_to_le16(data->blocks); ++ request.data_blksz = cpu_to_le16(data->blksz); ++ } ++ ++ ret = gb_operation_sync(host->connection, GB_SDIO_TYPE_COMMAND, ++ &request, sizeof(request), &response, ++ sizeof(response)); ++ if (ret < 0) ++ goto out; ++ ++ /* no response expected */ ++ if (cmd_flags & GB_SDIO_RSP_NONE) ++ goto out; ++ ++ /* long response expected */ ++ if (cmd_flags & GB_SDIO_RSP_R2) ++ for (i = 0; i < 4; i++) ++ cmd->resp[i] = le32_to_cpu(response.resp[i]); ++ else ++ cmd->resp[0] = le32_to_cpu(response.resp[0]); ++ ++out: ++ cmd->error = ret; ++ return ret; ++} ++ ++static void gb_sdio_mrq_work(struct work_struct *work) ++{ ++ struct gb_sdio_host *host; ++ struct mmc_request *mrq; ++ int ret; ++ ++ host = container_of(work, struct gb_sdio_host, mrqwork); ++ ++ ret = gbphy_runtime_get_sync(host->gbphy_dev); ++ if (ret) ++ return; ++ ++ mutex_lock(&host->lock); ++ mrq = host->mrq; ++ if (!mrq) { ++ mutex_unlock(&host->lock); ++ gbphy_runtime_put_autosuspend(host->gbphy_dev); ++ dev_err(mmc_dev(host->mmc), "mmc request is NULL"); ++ return; ++ } ++ ++ if (host->removed) { ++ mrq->cmd->error = -ESHUTDOWN; ++ goto done; ++ } ++ ++ if (mrq->sbc) { ++ ret = gb_sdio_command(host, mrq->sbc); ++ if (ret < 0) ++ goto done; ++ } ++ ++ ret = gb_sdio_command(host, mrq->cmd); ++ if (ret < 0) ++ goto done; ++ ++ if (mrq->data) { ++ ret = gb_sdio_transfer(host, mrq->data); ++ if (ret < 0) ++ goto done; ++ } ++ ++ if (mrq->stop) { ++ ret = gb_sdio_command(host, mrq->stop); ++ if (ret < 0) ++ goto done; ++ } ++ ++done: ++ host->mrq = NULL; ++ mutex_unlock(&host->lock); ++ mmc_request_done(host->mmc, mrq); ++ gbphy_runtime_put_autosuspend(host->gbphy_dev); ++} ++ ++static void gb_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct gb_sdio_host *host = mmc_priv(mmc); ++ struct mmc_command *cmd = mrq->cmd; ++ ++ /* Check if it is a cancel to ongoing transfer */ ++ if (cmd->opcode == MMC_STOP_TRANSMISSION) { ++ spin_lock(&host->xfer); ++ host->xfer_stop = true; ++ spin_unlock(&host->xfer); ++ } ++ ++ mutex_lock(&host->lock); ++ ++ WARN_ON(host->mrq); ++ host->mrq = mrq; ++ ++ if (host->removed) { ++ mrq->cmd->error = -ESHUTDOWN; ++ goto out; ++ } ++ if (!host->card_present) { ++ mrq->cmd->error = -ENOMEDIUM; ++ goto out; ++ } ++ ++ queue_work(host->mrq_workqueue, &host->mrqwork); ++ ++ mutex_unlock(&host->lock); ++ return; ++ ++out: ++ host->mrq = NULL; ++ mutex_unlock(&host->lock); ++ mmc_request_done(mmc, mrq); ++} ++ ++static void gb_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct gb_sdio_host *host = mmc_priv(mmc); ++ struct gb_sdio_set_ios_request request; ++ int ret; ++ u8 power_mode; ++ u8 bus_width; ++ u8 timing; ++ u8 signal_voltage; ++ u8 drv_type; ++ u32 vdd = 0; ++ ++ mutex_lock(&host->lock); ++ request.clock = cpu_to_le32(ios->clock); ++ ++ if (ios->vdd) ++ vdd = 1 << (ios->vdd - GB_SDIO_VDD_SHIFT); ++ request.vdd = cpu_to_le32(vdd); ++ ++ request.bus_mode = (ios->bus_mode == MMC_BUSMODE_OPENDRAIN ? ++ GB_SDIO_BUSMODE_OPENDRAIN : ++ GB_SDIO_BUSMODE_PUSHPULL); ++ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ default: ++ power_mode = GB_SDIO_POWER_OFF; ++ break; ++ case MMC_POWER_UP: ++ power_mode = GB_SDIO_POWER_UP; ++ break; ++ case MMC_POWER_ON: ++ power_mode = GB_SDIO_POWER_ON; ++ break; ++ case MMC_POWER_UNDEFINED: ++ power_mode = GB_SDIO_POWER_UNDEFINED; ++ break; ++ } ++ request.power_mode = power_mode; ++ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_1: ++ bus_width = GB_SDIO_BUS_WIDTH_1; ++ break; ++ case MMC_BUS_WIDTH_4: ++ default: ++ bus_width = GB_SDIO_BUS_WIDTH_4; ++ break; ++ case MMC_BUS_WIDTH_8: ++ bus_width = GB_SDIO_BUS_WIDTH_8; ++ break; ++ } ++ request.bus_width = bus_width; ++ ++ switch (ios->timing) { ++ case MMC_TIMING_LEGACY: ++ default: ++ timing = GB_SDIO_TIMING_LEGACY; ++ break; ++ case MMC_TIMING_MMC_HS: ++ timing = GB_SDIO_TIMING_MMC_HS; ++ break; ++ case MMC_TIMING_SD_HS: ++ timing = GB_SDIO_TIMING_SD_HS; ++ break; ++ case MMC_TIMING_UHS_SDR12: ++ timing = GB_SDIO_TIMING_UHS_SDR12; ++ break; ++ case MMC_TIMING_UHS_SDR25: ++ timing = GB_SDIO_TIMING_UHS_SDR25; ++ break; ++ case MMC_TIMING_UHS_SDR50: ++ timing = GB_SDIO_TIMING_UHS_SDR50; ++ break; ++ case MMC_TIMING_UHS_SDR104: ++ timing = GB_SDIO_TIMING_UHS_SDR104; ++ break; ++ case MMC_TIMING_UHS_DDR50: ++ timing = GB_SDIO_TIMING_UHS_DDR50; ++ break; ++ case MMC_TIMING_MMC_DDR52: ++ timing = GB_SDIO_TIMING_MMC_DDR52; ++ break; ++ case MMC_TIMING_MMC_HS200: ++ timing = GB_SDIO_TIMING_MMC_HS200; ++ break; ++ case MMC_TIMING_MMC_HS400: ++ timing = GB_SDIO_TIMING_MMC_HS400; ++ break; ++ } ++ request.timing = timing; ++ ++ switch (ios->signal_voltage) { ++ case MMC_SIGNAL_VOLTAGE_330: ++ signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_330; ++ break; ++ case MMC_SIGNAL_VOLTAGE_180: ++ default: ++ signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_180; ++ break; ++ case MMC_SIGNAL_VOLTAGE_120: ++ signal_voltage = GB_SDIO_SIGNAL_VOLTAGE_120; ++ break; ++ } ++ request.signal_voltage = signal_voltage; ++ ++ switch (ios->drv_type) { ++ case MMC_SET_DRIVER_TYPE_A: ++ drv_type = GB_SDIO_SET_DRIVER_TYPE_A; ++ break; ++ case MMC_SET_DRIVER_TYPE_C: ++ drv_type = GB_SDIO_SET_DRIVER_TYPE_C; ++ break; ++ case MMC_SET_DRIVER_TYPE_D: ++ drv_type = GB_SDIO_SET_DRIVER_TYPE_D; ++ break; ++ case MMC_SET_DRIVER_TYPE_B: ++ default: ++ drv_type = GB_SDIO_SET_DRIVER_TYPE_B; ++ break; ++ } ++ request.drv_type = drv_type; ++ ++ ret = gb_sdio_set_ios(host, &request); ++ if (ret < 0) ++ goto out; ++ ++ memcpy(&mmc->ios, ios, sizeof(mmc->ios)); ++ ++out: ++ mutex_unlock(&host->lock); ++} ++ ++static int gb_mmc_get_ro(struct mmc_host *mmc) ++{ ++ struct gb_sdio_host *host = mmc_priv(mmc); ++ ++ mutex_lock(&host->lock); ++ if (host->removed) { ++ mutex_unlock(&host->lock); ++ return -ESHUTDOWN; ++ } ++ mutex_unlock(&host->lock); ++ ++ return host->read_only; ++} ++ ++static int gb_mmc_get_cd(struct mmc_host *mmc) ++{ ++ struct gb_sdio_host *host = mmc_priv(mmc); ++ ++ mutex_lock(&host->lock); ++ if (host->removed) { ++ mutex_unlock(&host->lock); ++ return -ESHUTDOWN; ++ } ++ mutex_unlock(&host->lock); ++ ++ return host->card_present; ++} ++ ++static int gb_mmc_switch_voltage(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ return 0; ++} ++ ++static const struct mmc_host_ops gb_sdio_ops = { ++ .request = gb_mmc_request, ++ .set_ios = gb_mmc_set_ios, ++ .get_ro = gb_mmc_get_ro, ++ .get_cd = gb_mmc_get_cd, ++ .start_signal_voltage_switch = gb_mmc_switch_voltage, ++}; ++ ++static int gb_sdio_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct mmc_host *mmc; ++ struct gb_sdio_host *host; ++ int ret = 0; ++ ++ mmc = mmc_alloc_host(sizeof(*host), &gbphy_dev->dev); ++ if (!mmc) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ gb_sdio_request_handler); ++ if (IS_ERR(connection)) { ++ ret = PTR_ERR(connection); ++ goto exit_mmc_free; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ host->removed = true; ++ ++ host->connection = connection; ++ gb_connection_set_data(connection, host); ++ host->gbphy_dev = gbphy_dev; ++ gb_gbphy_set_data(gbphy_dev, host); ++ ++ ret = gb_connection_enable_tx(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ ret = gb_sdio_get_caps(host); ++ if (ret < 0) ++ goto exit_connection_disable; ++ ++ mmc->ops = &gb_sdio_ops; ++ ++ mmc->max_segs = host->mmc->max_blk_count; ++ ++ /* for now we make a map 1:1 between max request and segment size */ ++ mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; ++ mmc->max_seg_size = mmc->max_req_size; ++ ++ mutex_init(&host->lock); ++ spin_lock_init(&host->xfer); ++ host->mrq_workqueue = alloc_workqueue("mmc-%s", 0, 1, ++ dev_name(&gbphy_dev->dev)); ++ if (!host->mrq_workqueue) { ++ ret = -ENOMEM; ++ goto exit_connection_disable; ++ } ++ INIT_WORK(&host->mrqwork, gb_sdio_mrq_work); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_wq_destroy; ++ ++ ret = mmc_add_host(mmc); ++ if (ret < 0) ++ goto exit_wq_destroy; ++ host->removed = false; ++ ret = _gb_sdio_process_events(host, host->queued_events); ++ host->queued_events = 0; ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ ++ return ret; ++ ++exit_wq_destroy: ++ destroy_workqueue(host->mrq_workqueue); ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_mmc_free: ++ mmc_free_host(mmc); ++ ++ return ret; ++} ++ ++static void gb_sdio_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_sdio_host *host = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = host->connection; ++ struct mmc_host *mmc; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ mutex_lock(&host->lock); ++ host->removed = true; ++ mmc = host->mmc; ++ gb_connection_set_data(connection, NULL); ++ mutex_unlock(&host->lock); ++ ++ flush_workqueue(host->mrq_workqueue); ++ destroy_workqueue(host->mrq_workqueue); ++ gb_connection_disable_rx(connection); ++ mmc_remove_host(mmc); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ mmc_free_host(mmc); ++} ++ ++static const struct gbphy_device_id gb_sdio_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SDIO) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_sdio_id_table); ++ ++static struct gbphy_driver sdio_driver = { ++ .name = "sdio", ++ .probe = gb_sdio_probe, ++ .remove = gb_sdio_remove, ++ .id_table = gb_sdio_id_table, ++}; ++ ++module_gbphy_driver(sdio_driver); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_spi.patch b/greybus_spi.patch new file mode 100644 index 00000000000000..eb7cd8f578b5d8 --- /dev/null +++ b/greybus_spi.patch @@ -0,0 +1,683 @@ +--- + drivers/greybus/spi.c | 79 ++++++ + drivers/greybus/spilib.c | 565 +++++++++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/spilib.h | 24 + + 3 files changed, 668 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/spi.c +@@ -0,0 +1,79 @@ ++/* ++ * SPI bridge PHY driver. ++ * ++ * Copyright 2014-2016 Google Inc. ++ * Copyright 2014-2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/module.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++#include "spilib.h" ++ ++static struct spilib_ops *spilib_ops; ++ ++static int gb_spi_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ int ret; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) ++ return PTR_ERR(connection); ++ ++ ret = gb_connection_enable(connection); ++ if (ret) ++ goto exit_connection_destroy; ++ ++ ret = gb_spilib_master_init(connection, &gbphy_dev->dev, spilib_ops); ++ if (ret) ++ goto exit_connection_disable; ++ ++ gb_gbphy_set_data(gbphy_dev, connection); ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++ ++ return ret; ++} ++ ++static void gb_spi_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_connection *connection = gb_gbphy_get_data(gbphy_dev); ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ gb_spilib_master_exit(connection); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++} ++ ++static const struct gbphy_device_id gb_spi_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_SPI) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_spi_id_table); ++ ++static struct gbphy_driver spi_driver = { ++ .name = "spi", ++ .probe = gb_spi_probe, ++ .remove = gb_spi_remove, ++ .id_table = gb_spi_id_table, ++}; ++ ++module_gbphy_driver(spi_driver); ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/spilib.c +@@ -0,0 +1,565 @@ ++/* ++ * Greybus SPI library ++ * ++ * Copyright 2014-2016 Google Inc. ++ * Copyright 2014-2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/bitops.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/spi/spi.h> ++ ++#include "greybus.h" ++#include "spilib.h" ++ ++struct gb_spilib { ++ struct gb_connection *connection; ++ struct device *parent; ++ struct spi_transfer *first_xfer; ++ struct spi_transfer *last_xfer; ++ struct spilib_ops *ops; ++ u32 rx_xfer_offset; ++ u32 tx_xfer_offset; ++ u32 last_xfer_size; ++ unsigned int op_timeout; ++ u16 mode; ++ u16 flags; ++ u32 bits_per_word_mask; ++ u8 num_chipselect; ++ u32 min_speed_hz; ++ u32 max_speed_hz; ++}; ++ ++#define GB_SPI_STATE_MSG_DONE ((void *)0) ++#define GB_SPI_STATE_MSG_IDLE ((void *)1) ++#define GB_SPI_STATE_MSG_RUNNING ((void *)2) ++#define GB_SPI_STATE_OP_READY ((void *)3) ++#define GB_SPI_STATE_OP_DONE ((void *)4) ++#define GB_SPI_STATE_MSG_ERROR ((void *)-1) ++ ++#define XFER_TIMEOUT_TOLERANCE 200 ++ ++static struct spi_master *get_master_from_spi(struct gb_spilib *spi) ++{ ++ return gb_connection_get_data(spi->connection); ++} ++ ++static int tx_header_fit_operation(u32 tx_size, u32 count, size_t data_max) ++{ ++ size_t headers_size; ++ ++ data_max -= sizeof(struct gb_spi_transfer_request); ++ headers_size = (count + 1) * sizeof(struct gb_spi_transfer); ++ ++ return tx_size + headers_size > data_max ? 0 : 1; ++} ++ ++static size_t calc_rx_xfer_size(u32 rx_size, u32 *tx_xfer_size, u32 len, ++ size_t data_max) ++{ ++ size_t rx_xfer_size; ++ ++ data_max -= sizeof(struct gb_spi_transfer_response); ++ ++ if (rx_size + len > data_max) ++ rx_xfer_size = data_max - rx_size; ++ else ++ rx_xfer_size = len; ++ ++ /* if this is a write_read, for symmetry read the same as write */ ++ if (*tx_xfer_size && rx_xfer_size > *tx_xfer_size) ++ rx_xfer_size = *tx_xfer_size; ++ if (*tx_xfer_size && rx_xfer_size < *tx_xfer_size) ++ *tx_xfer_size = rx_xfer_size; ++ ++ return rx_xfer_size; ++} ++ ++static size_t calc_tx_xfer_size(u32 tx_size, u32 count, size_t len, ++ size_t data_max) ++{ ++ size_t headers_size; ++ ++ data_max -= sizeof(struct gb_spi_transfer_request); ++ headers_size = (count + 1) * sizeof(struct gb_spi_transfer); ++ ++ if (tx_size + headers_size + len > data_max) ++ return data_max - (tx_size + sizeof(struct gb_spi_transfer)); ++ ++ return len; ++} ++ ++static void clean_xfer_state(struct gb_spilib *spi) ++{ ++ spi->first_xfer = NULL; ++ spi->last_xfer = NULL; ++ spi->rx_xfer_offset = 0; ++ spi->tx_xfer_offset = 0; ++ spi->last_xfer_size = 0; ++ spi->op_timeout = 0; ++} ++ ++static bool is_last_xfer_done(struct gb_spilib *spi) ++{ ++ struct spi_transfer *last_xfer = spi->last_xfer; ++ ++ if ((spi->tx_xfer_offset + spi->last_xfer_size == last_xfer->len) || ++ (spi->rx_xfer_offset + spi->last_xfer_size == last_xfer->len)) ++ return true; ++ ++ return false; ++} ++ ++static int setup_next_xfer(struct gb_spilib *spi, struct spi_message *msg) ++{ ++ struct spi_transfer *last_xfer = spi->last_xfer; ++ ++ if (msg->state != GB_SPI_STATE_OP_DONE) ++ return 0; ++ ++ /* ++ * if we transferred all content of the last transfer, reset values and ++ * check if this was the last transfer in the message ++ */ ++ if (is_last_xfer_done(spi)) { ++ spi->tx_xfer_offset = 0; ++ spi->rx_xfer_offset = 0; ++ spi->op_timeout = 0; ++ if (last_xfer == list_last_entry(&msg->transfers, ++ struct spi_transfer, ++ transfer_list)) ++ msg->state = GB_SPI_STATE_MSG_DONE; ++ else ++ spi->first_xfer = list_next_entry(last_xfer, ++ transfer_list); ++ return 0; ++ } ++ ++ spi->first_xfer = last_xfer; ++ if (last_xfer->tx_buf) ++ spi->tx_xfer_offset += spi->last_xfer_size; ++ ++ if (last_xfer->rx_buf) ++ spi->rx_xfer_offset += spi->last_xfer_size; ++ ++ return 0; ++} ++ ++static struct spi_transfer *get_next_xfer(struct spi_transfer *xfer, ++ struct spi_message *msg) ++{ ++ if (xfer == list_last_entry(&msg->transfers, struct spi_transfer, ++ transfer_list)) ++ return NULL; ++ ++ return list_next_entry(xfer, transfer_list); ++} ++ ++/* Routines to transfer data */ ++static struct gb_operation *gb_spi_operation_create(struct gb_spilib *spi, ++ struct gb_connection *connection, struct spi_message *msg) ++{ ++ struct gb_spi_transfer_request *request; ++ struct spi_device *dev = msg->spi; ++ struct spi_transfer *xfer; ++ struct gb_spi_transfer *gb_xfer; ++ struct gb_operation *operation; ++ u32 tx_size = 0, rx_size = 0, count = 0, xfer_len = 0, request_size; ++ u32 tx_xfer_size = 0, rx_xfer_size = 0, len; ++ u32 total_len = 0; ++ unsigned int xfer_timeout; ++ size_t data_max; ++ void *tx_data; ++ ++ data_max = gb_operation_get_payload_size_max(connection); ++ xfer = spi->first_xfer; ++ ++ /* Find number of transfers queued and tx/rx length in the message */ ++ ++ while (msg->state != GB_SPI_STATE_OP_READY) { ++ msg->state = GB_SPI_STATE_MSG_RUNNING; ++ spi->last_xfer = xfer; ++ ++ if (!xfer->tx_buf && !xfer->rx_buf) { ++ dev_err(spi->parent, ++ "bufferless transfer, length %u\n", xfer->len); ++ msg->state = GB_SPI_STATE_MSG_ERROR; ++ return NULL; ++ } ++ ++ tx_xfer_size = 0; ++ rx_xfer_size = 0; ++ ++ if (xfer->tx_buf) { ++ len = xfer->len - spi->tx_xfer_offset; ++ if (!tx_header_fit_operation(tx_size, count, data_max)) ++ break; ++ tx_xfer_size = calc_tx_xfer_size(tx_size, count, ++ len, data_max); ++ spi->last_xfer_size = tx_xfer_size; ++ } ++ ++ if (xfer->rx_buf) { ++ len = xfer->len - spi->rx_xfer_offset; ++ rx_xfer_size = calc_rx_xfer_size(rx_size, &tx_xfer_size, ++ len, data_max); ++ spi->last_xfer_size = rx_xfer_size; ++ } ++ ++ tx_size += tx_xfer_size; ++ rx_size += rx_xfer_size; ++ ++ total_len += spi->last_xfer_size; ++ count++; ++ ++ xfer = get_next_xfer(xfer, msg); ++ if (!xfer || total_len >= data_max) ++ msg->state = GB_SPI_STATE_OP_READY; ++ } ++ ++ /* ++ * In addition to space for all message descriptors we need ++ * to have enough to hold all tx data. ++ */ ++ request_size = sizeof(*request); ++ request_size += count * sizeof(*gb_xfer); ++ request_size += tx_size; ++ ++ /* Response consists only of incoming data */ ++ operation = gb_operation_create(connection, GB_SPI_TYPE_TRANSFER, ++ request_size, rx_size, GFP_KERNEL); ++ if (!operation) ++ return NULL; ++ ++ request = operation->request->payload; ++ request->count = cpu_to_le16(count); ++ request->mode = dev->mode; ++ request->chip_select = dev->chip_select; ++ ++ gb_xfer = &request->transfers[0]; ++ tx_data = gb_xfer + count; /* place tx data after last gb_xfer */ ++ ++ /* Fill in the transfers array */ ++ xfer = spi->first_xfer; ++ while (msg->state != GB_SPI_STATE_OP_DONE) { ++ if (xfer == spi->last_xfer) ++ xfer_len = spi->last_xfer_size; ++ else ++ xfer_len = xfer->len; ++ ++ /* make sure we do not timeout in a slow transfer */ ++ xfer_timeout = xfer_len * 8 * MSEC_PER_SEC / xfer->speed_hz; ++ xfer_timeout += GB_OPERATION_TIMEOUT_DEFAULT; ++ ++ if (xfer_timeout > spi->op_timeout) ++ spi->op_timeout = xfer_timeout; ++ ++ gb_xfer->speed_hz = cpu_to_le32(xfer->speed_hz); ++ gb_xfer->len = cpu_to_le32(xfer_len); ++ gb_xfer->delay_usecs = cpu_to_le16(xfer->delay_usecs); ++ gb_xfer->cs_change = xfer->cs_change; ++ gb_xfer->bits_per_word = xfer->bits_per_word; ++ ++ /* Copy tx data */ ++ if (xfer->tx_buf) { ++ gb_xfer->xfer_flags |= GB_SPI_XFER_WRITE; ++ memcpy(tx_data, xfer->tx_buf + spi->tx_xfer_offset, ++ xfer_len); ++ tx_data += xfer_len; ++ } ++ ++ if (xfer->rx_buf) ++ gb_xfer->xfer_flags |= GB_SPI_XFER_READ; ++ ++ if (xfer == spi->last_xfer) { ++ if (!is_last_xfer_done(spi)) ++ gb_xfer->xfer_flags |= GB_SPI_XFER_INPROGRESS; ++ msg->state = GB_SPI_STATE_OP_DONE; ++ continue; ++ } ++ ++ gb_xfer++; ++ xfer = get_next_xfer(xfer, msg); ++ } ++ ++ msg->actual_length += total_len; ++ ++ return operation; ++} ++ ++static void gb_spi_decode_response(struct gb_spilib *spi, ++ struct spi_message *msg, ++ struct gb_spi_transfer_response *response) ++{ ++ struct spi_transfer *xfer = spi->first_xfer; ++ void *rx_data = response->data; ++ u32 xfer_len; ++ ++ while (xfer) { ++ /* Copy rx data */ ++ if (xfer->rx_buf) { ++ if (xfer == spi->first_xfer) ++ xfer_len = xfer->len - spi->rx_xfer_offset; ++ else if (xfer == spi->last_xfer) ++ xfer_len = spi->last_xfer_size; ++ else ++ xfer_len = xfer->len; ++ ++ memcpy(xfer->rx_buf + spi->rx_xfer_offset, rx_data, ++ xfer_len); ++ rx_data += xfer_len; ++ } ++ ++ if (xfer == spi->last_xfer) ++ break; ++ ++ xfer = list_next_entry(xfer, transfer_list); ++ } ++} ++ ++static int gb_spi_transfer_one_message(struct spi_master *master, ++ struct spi_message *msg) ++{ ++ struct gb_spilib *spi = spi_master_get_devdata(master); ++ struct gb_connection *connection = spi->connection; ++ struct gb_spi_transfer_response *response; ++ struct gb_operation *operation; ++ int ret = 0; ++ ++ spi->first_xfer = list_first_entry_or_null(&msg->transfers, ++ struct spi_transfer, ++ transfer_list); ++ if (!spi->first_xfer) { ++ ret = -ENOMEM; ++ goto out; ++ } ++ ++ msg->state = GB_SPI_STATE_MSG_IDLE; ++ ++ while (msg->state != GB_SPI_STATE_MSG_DONE && ++ msg->state != GB_SPI_STATE_MSG_ERROR) { ++ operation = gb_spi_operation_create(spi, connection, msg); ++ if (!operation) { ++ msg->state = GB_SPI_STATE_MSG_ERROR; ++ ret = -EINVAL; ++ continue; ++ } ++ ++ ret = gb_operation_request_send_sync_timeout(operation, ++ spi->op_timeout); ++ if (!ret) { ++ response = operation->response->payload; ++ if (response) ++ gb_spi_decode_response(spi, msg, response); ++ } else { ++ dev_err(spi->parent, ++ "transfer operation failed: %d\n", ret); ++ msg->state = GB_SPI_STATE_MSG_ERROR; ++ } ++ ++ gb_operation_put(operation); ++ setup_next_xfer(spi, msg); ++ } ++ ++out: ++ msg->status = ret; ++ clean_xfer_state(spi); ++ spi_finalize_current_message(master); ++ ++ return ret; ++} ++ ++static int gb_spi_prepare_transfer_hardware(struct spi_master *master) ++{ ++ struct gb_spilib *spi = spi_master_get_devdata(master); ++ ++ return spi->ops->prepare_transfer_hardware(spi->parent); ++} ++ ++static int gb_spi_unprepare_transfer_hardware(struct spi_master *master) ++{ ++ struct gb_spilib *spi = spi_master_get_devdata(master); ++ ++ spi->ops->unprepare_transfer_hardware(spi->parent); ++ ++ return 0; ++} ++ ++static int gb_spi_setup(struct spi_device *spi) ++{ ++ /* Nothing to do for now */ ++ return 0; ++} ++ ++static void gb_spi_cleanup(struct spi_device *spi) ++{ ++ /* Nothing to do for now */ ++} ++ ++/* Routines to get controller information */ ++ ++/* ++ * Map Greybus spi mode bits/flags/bpw into Linux ones. ++ * All bits are same for now and so these macro's return same values. ++ */ ++#define gb_spi_mode_map(mode) mode ++#define gb_spi_flags_map(flags) flags ++ ++static int gb_spi_get_master_config(struct gb_spilib *spi) ++{ ++ struct gb_spi_master_config_response response; ++ u16 mode, flags; ++ int ret; ++ ++ ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_MASTER_CONFIG, ++ NULL, 0, &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ ++ mode = le16_to_cpu(response.mode); ++ spi->mode = gb_spi_mode_map(mode); ++ ++ flags = le16_to_cpu(response.flags); ++ spi->flags = gb_spi_flags_map(flags); ++ ++ spi->bits_per_word_mask = le32_to_cpu(response.bits_per_word_mask); ++ spi->num_chipselect = response.num_chipselect; ++ ++ spi->min_speed_hz = le32_to_cpu(response.min_speed_hz); ++ spi->max_speed_hz = le32_to_cpu(response.max_speed_hz); ++ ++ return 0; ++} ++ ++static int gb_spi_setup_device(struct gb_spilib *spi, u8 cs) ++{ ++ struct spi_master *master = get_master_from_spi(spi); ++ struct gb_spi_device_config_request request; ++ struct gb_spi_device_config_response response; ++ struct spi_board_info spi_board = { {0} }; ++ struct spi_device *spidev; ++ int ret; ++ u8 dev_type; ++ ++ request.chip_select = cs; ++ ++ ret = gb_operation_sync(spi->connection, GB_SPI_TYPE_DEVICE_CONFIG, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ ++ dev_type = response.device_type; ++ ++ if (dev_type == GB_SPI_SPI_DEV) ++ strlcpy(spi_board.modalias, "spidev", ++ sizeof(spi_board.modalias)); ++ else if (dev_type == GB_SPI_SPI_NOR) ++ strlcpy(spi_board.modalias, "spi-nor", ++ sizeof(spi_board.modalias)); ++ else if (dev_type == GB_SPI_SPI_MODALIAS) ++ memcpy(spi_board.modalias, response.name, ++ sizeof(spi_board.modalias)); ++ else ++ return -EINVAL; ++ ++ spi_board.mode = le16_to_cpu(response.mode); ++ spi_board.bus_num = master->bus_num; ++ spi_board.chip_select = cs; ++ spi_board.max_speed_hz = le32_to_cpu(response.max_speed_hz); ++ ++ spidev = spi_new_device(master, &spi_board); ++ if (!spidev) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, ++ struct spilib_ops *ops) ++{ ++ struct gb_spilib *spi; ++ struct spi_master *master; ++ int ret; ++ u8 i; ++ ++ /* Allocate master with space for data */ ++ master = spi_alloc_master(dev, sizeof(*spi)); ++ if (!master) { ++ dev_err(dev, "cannot alloc SPI master\n"); ++ return -ENOMEM; ++ } ++ ++ spi = spi_master_get_devdata(master); ++ spi->connection = connection; ++ gb_connection_set_data(connection, master); ++ spi->parent = dev; ++ spi->ops = ops; ++ ++ /* get master configuration */ ++ ret = gb_spi_get_master_config(spi); ++ if (ret) ++ goto exit_spi_put; ++ ++ master->bus_num = -1; /* Allow spi-core to allocate it dynamically */ ++ master->num_chipselect = spi->num_chipselect; ++ master->mode_bits = spi->mode; ++ master->flags = spi->flags; ++ master->bits_per_word_mask = spi->bits_per_word_mask; ++ ++ /* Attach methods */ ++ master->cleanup = gb_spi_cleanup; ++ master->setup = gb_spi_setup; ++ master->transfer_one_message = gb_spi_transfer_one_message; ++ ++ if (ops && ops->prepare_transfer_hardware) { ++ master->prepare_transfer_hardware = ++ gb_spi_prepare_transfer_hardware; ++ } ++ ++ if (ops && ops->unprepare_transfer_hardware) { ++ master->unprepare_transfer_hardware = ++ gb_spi_unprepare_transfer_hardware; ++ } ++ ++ master->auto_runtime_pm = true; ++ ++ ret = spi_register_master(master); ++ if (ret < 0) ++ goto exit_spi_put; ++ ++ /* now, fetch the devices configuration */ ++ for (i = 0; i < spi->num_chipselect; i++) { ++ ret = gb_spi_setup_device(spi, i); ++ if (ret < 0) { ++ dev_err(dev, "failed to allocate spi device %d: %d\n", ++ i, ret); ++ goto exit_spi_unregister; ++ } ++ } ++ ++ return 0; ++ ++exit_spi_unregister: ++ spi_unregister_master(master); ++exit_spi_put: ++ spi_master_put(master); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_spilib_master_init); ++ ++void gb_spilib_master_exit(struct gb_connection *connection) ++{ ++ struct spi_master *master = gb_connection_get_data(connection); ++ ++ spi_unregister_master(master); ++ spi_master_put(master); ++} ++EXPORT_SYMBOL_GPL(gb_spilib_master_exit); ++ ++MODULE_LICENSE("GPL v2"); +--- /dev/null ++++ b/drivers/greybus/spilib.h +@@ -0,0 +1,24 @@ ++/* ++ * Greybus SPI library header ++ * ++ * copyright 2016 google inc. ++ * copyright 2016 linaro ltd. ++ * ++ * released under the gplv2 only. ++ */ ++ ++#ifndef __SPILIB_H ++#define __SPILIB_H ++ ++struct device; ++struct gb_connection; ++ ++struct spilib_ops { ++ int (*prepare_transfer_hardware)(struct device *dev); ++ void (*unprepare_transfer_hardware)(struct device *dev); ++}; ++ ++int gb_spilib_master_init(struct gb_connection *connection, struct device *dev, struct spilib_ops *ops); ++void gb_spilib_master_exit(struct gb_connection *connection); ++ ++#endif /* __SPILIB_H */ diff --git a/greybus_svc.patch b/greybus_svc.patch new file mode 100644 index 00000000000000..388adcc6737733 --- /dev/null +++ b/greybus_svc.patch @@ -0,0 +1,1808 @@ +--- + drivers/greybus/svc.c | 1486 +++++++++++++++++++++++++++++++++++++++++ + drivers/greybus/svc.h | 109 +++ + drivers/greybus/svc_watchdog.c | 198 +++++ + 3 files changed, 1793 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/svc.c +@@ -0,0 +1,1486 @@ ++/* ++ * SVC Greybus driver. ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/debugfs.h> ++#include <linux/workqueue.h> ++ ++#include "greybus.h" ++ ++#define SVC_INTF_EJECT_TIMEOUT 9000 ++#define SVC_INTF_ACTIVATE_TIMEOUT 6000 ++#define SVC_INTF_RESUME_TIMEOUT 3000 ++ ++struct gb_svc_deferred_request { ++ struct work_struct work; ++ struct gb_operation *operation; ++}; ++ ++ ++static int gb_svc_queue_deferred_request(struct gb_operation *operation); ++ ++static ssize_t endo_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ return sprintf(buf, "0x%04x\n", svc->endo_id); ++} ++static DEVICE_ATTR_RO(endo_id); ++ ++static ssize_t ap_intf_id_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ return sprintf(buf, "%u\n", svc->ap_intf_id); ++} ++static DEVICE_ATTR_RO(ap_intf_id); ++ ++// FIXME ++// This is a hack, we need to do this "right" and clean the interface up ++// properly, not just forcibly yank the thing out of the system and hope for the ++// best. But for now, people want their modules to come out without having to ++// throw the thing to the ground or get out a screwdriver. ++static ssize_t intf_eject_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ unsigned short intf_id; ++ int ret; ++ ++ ret = kstrtou16(buf, 10, &intf_id); ++ if (ret < 0) ++ return ret; ++ ++ dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id); ++ ++ ret = gb_svc_intf_eject(svc, intf_id); ++ if (ret < 0) ++ return ret; ++ ++ return len; ++} ++static DEVICE_ATTR_WO(intf_eject); ++ ++static ssize_t watchdog_show(struct device *dev, struct device_attribute *attr, ++ char *buf) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ return sprintf(buf, "%s\n", ++ gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled"); ++} ++ ++static ssize_t watchdog_store(struct device *dev, ++ struct device_attribute *attr, const char *buf, ++ size_t len) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ int retval; ++ bool user_request; ++ ++ retval = strtobool(buf, &user_request); ++ if (retval) ++ return retval; ++ ++ if (user_request) ++ retval = gb_svc_watchdog_enable(svc); ++ else ++ retval = gb_svc_watchdog_disable(svc); ++ if (retval) ++ return retval; ++ return len; ++} ++static DEVICE_ATTR_RW(watchdog); ++ ++static ssize_t watchdog_action_show(struct device *dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) ++ return sprintf(buf, "panic\n"); ++ else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) ++ return sprintf(buf, "reset\n"); ++ ++ return -EINVAL; ++} ++ ++static ssize_t watchdog_action_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t len) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ if (sysfs_streq(buf, "panic")) ++ svc->action = GB_SVC_WATCHDOG_BITE_PANIC_KERNEL; ++ else if (sysfs_streq(buf, "reset")) ++ svc->action = GB_SVC_WATCHDOG_BITE_RESET_UNIPRO; ++ else ++ return -EINVAL; ++ ++ return len; ++} ++static DEVICE_ATTR_RW(watchdog_action); ++ ++static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) ++{ ++ struct gb_svc_pwrmon_rail_count_get_response response; ++ int ret; ++ ++ ret = gb_operation_sync(svc->connection, ++ GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0, ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get rail count: %d\n", ret); ++ return ret; ++ } ++ ++ *value = response.rail_count; ++ ++ return 0; ++} ++ ++static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, ++ struct gb_svc_pwrmon_rail_names_get_response *response, ++ size_t bufsize) ++{ ++ int ret; ++ ++ ret = gb_operation_sync(svc->connection, ++ GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0, ++ response, bufsize); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get rail names: %d\n", ret); ++ return ret; ++ } ++ ++ if (response->status != GB_SVC_OP_SUCCESS) { ++ dev_err(&svc->dev, ++ "SVC error while getting rail names: %u\n", ++ response->status); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, ++ u8 measurement_type, u32 *value) ++{ ++ struct gb_svc_pwrmon_sample_get_request request; ++ struct gb_svc_pwrmon_sample_get_response response; ++ int ret; ++ ++ request.rail_id = rail_id; ++ request.measurement_type = measurement_type; ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_PWRMON_SAMPLE_GET, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get rail sample: %d\n", ret); ++ return ret; ++ } ++ ++ if (response.result) { ++ dev_err(&svc->dev, ++ "UniPro error while getting rail power sample (%d %d): %d\n", ++ rail_id, measurement_type, response.result); ++ switch (response.result) { ++ case GB_SVC_PWRMON_GET_SAMPLE_INVAL: ++ return -EINVAL; ++ case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: ++ return -ENOMSG; ++ default: ++ return -EREMOTEIO; ++ } ++ } ++ ++ *value = le32_to_cpu(response.measurement); ++ ++ return 0; ++} ++ ++int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, ++ u8 measurement_type, u32 *value) ++{ ++ struct gb_svc_pwrmon_intf_sample_get_request request; ++ struct gb_svc_pwrmon_intf_sample_get_response response; ++ int ret; ++ ++ request.intf_id = intf_id; ++ request.measurement_type = measurement_type; ++ ++ ret = gb_operation_sync(svc->connection, ++ GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get intf sample: %d\n", ret); ++ return ret; ++ } ++ ++ if (response.result) { ++ dev_err(&svc->dev, ++ "UniPro error while getting intf power sample (%d %d): %d\n", ++ intf_id, measurement_type, response.result); ++ switch (response.result) { ++ case GB_SVC_PWRMON_GET_SAMPLE_INVAL: ++ return -EINVAL; ++ case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: ++ return -ENOMSG; ++ default: ++ return -EREMOTEIO; ++ } ++ } ++ ++ *value = le32_to_cpu(response.measurement); ++ ++ return 0; ++} ++ ++static struct attribute *svc_attrs[] = { ++ &dev_attr_endo_id.attr, ++ &dev_attr_ap_intf_id.attr, ++ &dev_attr_intf_eject.attr, ++ &dev_attr_watchdog.attr, ++ &dev_attr_watchdog_action.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(svc); ++ ++int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) ++{ ++ struct gb_svc_intf_device_id_request request; ++ ++ request.intf_id = intf_id; ++ request.device_id = device_id; ++ ++ return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, ++ &request, sizeof(request), NULL, 0); ++} ++ ++int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) ++{ ++ struct gb_svc_intf_eject_request request; ++ int ret; ++ ++ request.intf_id = intf_id; ++ ++ /* ++ * The pulse width for module release in svc is long so we need to ++ * increase the timeout so the operation will not return to soon. ++ */ ++ ret = gb_operation_sync_timeout(svc->connection, ++ GB_SVC_TYPE_INTF_EJECT, &request, ++ sizeof(request), NULL, 0, ++ SVC_INTF_EJECT_TIMEOUT); ++ if (ret) { ++ dev_err(&svc->dev, "failed to eject interface %u\n", intf_id); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable) ++{ ++ struct gb_svc_intf_vsys_request request; ++ struct gb_svc_intf_vsys_response response; ++ int type, ret; ++ ++ request.intf_id = intf_id; ++ ++ if (enable) ++ type = GB_SVC_TYPE_INTF_VSYS_ENABLE; ++ else ++ type = GB_SVC_TYPE_INTF_VSYS_DISABLE; ++ ++ ret = gb_operation_sync(svc->connection, type, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ if (response.result_code != GB_SVC_INTF_VSYS_OK) ++ return -EREMOTEIO; ++ return 0; ++} ++ ++int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable) ++{ ++ struct gb_svc_intf_refclk_request request; ++ struct gb_svc_intf_refclk_response response; ++ int type, ret; ++ ++ request.intf_id = intf_id; ++ ++ if (enable) ++ type = GB_SVC_TYPE_INTF_REFCLK_ENABLE; ++ else ++ type = GB_SVC_TYPE_INTF_REFCLK_DISABLE; ++ ++ ret = gb_operation_sync(svc->connection, type, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ if (response.result_code != GB_SVC_INTF_REFCLK_OK) ++ return -EREMOTEIO; ++ return 0; ++} ++ ++int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) ++{ ++ struct gb_svc_intf_unipro_request request; ++ struct gb_svc_intf_unipro_response response; ++ int type, ret; ++ ++ request.intf_id = intf_id; ++ ++ if (enable) ++ type = GB_SVC_TYPE_INTF_UNIPRO_ENABLE; ++ else ++ type = GB_SVC_TYPE_INTF_UNIPRO_DISABLE; ++ ++ ret = gb_operation_sync(svc->connection, type, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ if (response.result_code != GB_SVC_INTF_UNIPRO_OK) ++ return -EREMOTEIO; ++ return 0; ++} ++ ++int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) ++{ ++ struct gb_svc_intf_activate_request request; ++ struct gb_svc_intf_activate_response response; ++ int ret; ++ ++ request.intf_id = intf_id; ++ ++ ret = gb_operation_sync_timeout(svc->connection, ++ GB_SVC_TYPE_INTF_ACTIVATE, ++ &request, sizeof(request), ++ &response, sizeof(response), ++ SVC_INTF_ACTIVATE_TIMEOUT); ++ if (ret < 0) ++ return ret; ++ if (response.status != GB_SVC_OP_SUCCESS) { ++ dev_err(&svc->dev, "failed to activate interface %u: %u\n", ++ intf_id, response.status); ++ return -EREMOTEIO; ++ } ++ ++ *intf_type = response.intf_type; ++ ++ return 0; ++} ++ ++int gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id) ++{ ++ struct gb_svc_intf_resume_request request; ++ struct gb_svc_intf_resume_response response; ++ int ret; ++ ++ request.intf_id = intf_id; ++ ++ ret = gb_operation_sync_timeout(svc->connection, ++ GB_SVC_TYPE_INTF_RESUME, ++ &request, sizeof(request), ++ &response, sizeof(response), ++ SVC_INTF_RESUME_TIMEOUT); ++ if (ret < 0) { ++ dev_err(&svc->dev, "failed to send interface resume %u: %d\n", ++ intf_id, ret); ++ return ret; ++ } ++ ++ if (response.status != GB_SVC_OP_SUCCESS) { ++ dev_err(&svc->dev, "failed to resume interface %u: %u\n", ++ intf_id, response.status); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, ++ u32 *value) ++{ ++ struct gb_svc_dme_peer_get_request request; ++ struct gb_svc_dme_peer_get_response response; ++ u16 result; ++ int ret; ++ ++ request.intf_id = intf_id; ++ request.attr = cpu_to_le16(attr); ++ request.selector = cpu_to_le16(selector); ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get DME attribute (%u 0x%04x %u): %d\n", ++ intf_id, attr, selector, ret); ++ return ret; ++ } ++ ++ result = le16_to_cpu(response.result_code); ++ if (result) { ++ dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n", ++ intf_id, attr, selector, result); ++ return -EREMOTEIO; ++ } ++ ++ if (value) ++ *value = le32_to_cpu(response.attr_value); ++ ++ return 0; ++} ++ ++int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, ++ u32 value) ++{ ++ struct gb_svc_dme_peer_set_request request; ++ struct gb_svc_dme_peer_set_response response; ++ u16 result; ++ int ret; ++ ++ request.intf_id = intf_id; ++ request.attr = cpu_to_le16(attr); ++ request.selector = cpu_to_le16(selector); ++ request.value = cpu_to_le32(value); ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret) { ++ dev_err(&svc->dev, "failed to set DME attribute (%u 0x%04x %u %u): %d\n", ++ intf_id, attr, selector, value, ret); ++ return ret; ++ } ++ ++ result = le16_to_cpu(response.result_code); ++ if (result) { ++ dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n", ++ intf_id, attr, selector, value, result); ++ return -EREMOTEIO; ++ } ++ ++ return 0; ++} ++ ++int gb_svc_connection_create(struct gb_svc *svc, ++ u8 intf1_id, u16 cport1_id, ++ u8 intf2_id, u16 cport2_id, ++ u8 cport_flags) ++{ ++ struct gb_svc_conn_create_request request; ++ ++ request.intf1_id = intf1_id; ++ request.cport1_id = cpu_to_le16(cport1_id); ++ request.intf2_id = intf2_id; ++ request.cport2_id = cpu_to_le16(cport2_id); ++ request.tc = 0; /* TC0 */ ++ request.flags = cport_flags; ++ ++ return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, ++ &request, sizeof(request), NULL, 0); ++} ++ ++void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ++ u8 intf2_id, u16 cport2_id) ++{ ++ struct gb_svc_conn_destroy_request request; ++ struct gb_connection *connection = svc->connection; ++ int ret; ++ ++ request.intf1_id = intf1_id; ++ request.cport1_id = cpu_to_le16(cport1_id); ++ request.intf2_id = intf2_id; ++ request.cport2_id = cpu_to_le16(cport2_id); ++ ++ ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, ++ &request, sizeof(request), NULL, 0); ++ if (ret) { ++ dev_err(&svc->dev, "failed to destroy connection (%u:%u %u:%u): %d\n", ++ intf1_id, cport1_id, intf2_id, cport2_id, ret); ++ } ++} ++ ++int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, ++ u32 strobe_delay, u32 refclk) ++{ ++ struct gb_connection *connection = svc->connection; ++ struct gb_svc_timesync_enable_request request; ++ ++ request.count = count; ++ request.frame_time = cpu_to_le64(frame_time); ++ request.strobe_delay = cpu_to_le32(strobe_delay); ++ request.refclk = cpu_to_le32(refclk); ++ return gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_ENABLE, ++ &request, sizeof(request), NULL, 0); ++} ++ ++int gb_svc_timesync_disable(struct gb_svc *svc) ++{ ++ struct gb_connection *connection = svc->connection; ++ ++ return gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_DISABLE, ++ NULL, 0, NULL, 0); ++} ++ ++int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) ++{ ++ struct gb_connection *connection = svc->connection; ++ struct gb_svc_timesync_authoritative_response response; ++ int ret, i; ++ ++ ret = gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE, NULL, 0, ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) ++ frame_time[i] = le64_to_cpu(response.frame_time[i]); ++ return 0; ++} ++ ++int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) ++{ ++ struct gb_connection *connection = svc->connection; ++ struct gb_svc_timesync_ping_response response; ++ int ret; ++ ++ ret = gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_PING, ++ NULL, 0, ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ ++ *frame_time = le64_to_cpu(response.frame_time); ++ return 0; ++} ++ ++int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) ++{ ++ struct gb_connection *connection = svc->connection; ++ struct gb_svc_timesync_wake_pins_acquire_request request; ++ ++ request.strobe_mask = cpu_to_le32(strobe_mask); ++ return gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE, ++ &request, sizeof(request), ++ NULL, 0); ++} ++ ++int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) ++{ ++ struct gb_connection *connection = svc->connection; ++ ++ return gb_operation_sync(connection, ++ GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, ++ NULL, 0, NULL, 0); ++} ++ ++/* Creates bi-directional routes between the devices */ ++int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, ++ u8 intf2_id, u8 dev2_id) ++{ ++ struct gb_svc_route_create_request request; ++ ++ request.intf1_id = intf1_id; ++ request.dev1_id = dev1_id; ++ request.intf2_id = intf2_id; ++ request.dev2_id = dev2_id; ++ ++ return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, ++ &request, sizeof(request), NULL, 0); ++} ++ ++/* Destroys bi-directional routes between the devices */ ++void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) ++{ ++ struct gb_svc_route_destroy_request request; ++ int ret; ++ ++ request.intf1_id = intf1_id; ++ request.intf2_id = intf2_id; ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, ++ &request, sizeof(request), NULL, 0); ++ if (ret) { ++ dev_err(&svc->dev, "failed to destroy route (%u %u): %d\n", ++ intf1_id, intf2_id, ret); ++ } ++} ++ ++int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, ++ u8 tx_mode, u8 tx_gear, u8 tx_nlanes, ++ u8 tx_amplitude, u8 tx_hs_equalizer, ++ u8 rx_mode, u8 rx_gear, u8 rx_nlanes, ++ u8 flags, u32 quirks, ++ struct gb_svc_l2_timer_cfg *local, ++ struct gb_svc_l2_timer_cfg *remote) ++{ ++ struct gb_svc_intf_set_pwrm_request request; ++ struct gb_svc_intf_set_pwrm_response response; ++ int ret; ++ u16 result_code; ++ ++ memset(&request, 0, sizeof(request)); ++ ++ request.intf_id = intf_id; ++ request.hs_series = hs_series; ++ request.tx_mode = tx_mode; ++ request.tx_gear = tx_gear; ++ request.tx_nlanes = tx_nlanes; ++ request.tx_amplitude = tx_amplitude; ++ request.tx_hs_equalizer = tx_hs_equalizer; ++ request.rx_mode = rx_mode; ++ request.rx_gear = rx_gear; ++ request.rx_nlanes = rx_nlanes; ++ request.flags = flags; ++ request.quirks = cpu_to_le32(quirks); ++ if (local) ++ request.local_l2timerdata = *local; ++ if (remote) ++ request.remote_l2timerdata = *remote; ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) ++ return ret; ++ ++ result_code = response.result_code; ++ if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { ++ dev_err(&svc->dev, "set power mode = %d\n", result_code); ++ return -EIO; ++ } ++ ++ return 0; ++} ++EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); ++ ++int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) ++{ ++ struct gb_svc_intf_set_pwrm_request request; ++ struct gb_svc_intf_set_pwrm_response response; ++ int ret; ++ u16 result_code; ++ ++ memset(&request, 0, sizeof(request)); ++ ++ request.intf_id = intf_id; ++ request.hs_series = GB_SVC_UNIPRO_HS_SERIES_A; ++ request.tx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; ++ request.rx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; ++ ++ ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, ++ &request, sizeof(request), ++ &response, sizeof(response)); ++ if (ret < 0) { ++ dev_err(&svc->dev, ++ "failed to send set power mode operation to interface %u: %d\n", ++ intf_id, ret); ++ return ret; ++ } ++ ++ result_code = response.result_code; ++ if (result_code != GB_SVC_SETPWRM_PWR_OK) { ++ dev_err(&svc->dev, ++ "failed to hibernate the link for interface %u: %u\n", ++ intf_id, result_code); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++int gb_svc_ping(struct gb_svc *svc) ++{ ++ return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PING, ++ NULL, 0, NULL, 0, ++ GB_OPERATION_TIMEOUT_DEFAULT * 2); ++} ++ ++static int gb_svc_version_request(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_svc_version_request *request; ++ struct gb_svc_version_response *response; ++ ++ if (op->request->payload_size < sizeof(*request)) { ++ dev_err(&svc->dev, "short version request (%zu < %zu)\n", ++ op->request->payload_size, ++ sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ if (request->major > GB_SVC_VERSION_MAJOR) { ++ dev_warn(&svc->dev, "unsupported major version (%u > %u)\n", ++ request->major, GB_SVC_VERSION_MAJOR); ++ return -ENOTSUPP; ++ } ++ ++ svc->protocol_major = request->major; ++ svc->protocol_minor = request->minor; ++ ++ if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) ++ return -ENOMEM; ++ ++ response = op->response->payload; ++ response->major = svc->protocol_major; ++ response->minor = svc->protocol_minor; ++ ++ return 0; ++} ++ ++static ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; ++ struct gb_svc *svc = pwrmon_rails->svc; ++ int ret, desc; ++ u32 value; ++ char buff[16]; ++ ++ ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, ++ GB_SVC_PWRMON_TYPE_VOL, &value); ++ if (ret) { ++ dev_err(&svc->dev, ++ "failed to get voltage sample %u: %d\n", ++ pwrmon_rails->id, ret); ++ return ret; ++ } ++ ++ desc = scnprintf(buff, sizeof(buff), "%u\n", value); ++ ++ return simple_read_from_buffer(buf, len, offset, buff, desc); ++} ++ ++static ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; ++ struct gb_svc *svc = pwrmon_rails->svc; ++ int ret, desc; ++ u32 value; ++ char buff[16]; ++ ++ ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, ++ GB_SVC_PWRMON_TYPE_CURR, &value); ++ if (ret) { ++ dev_err(&svc->dev, ++ "failed to get current sample %u: %d\n", ++ pwrmon_rails->id, ret); ++ return ret; ++ } ++ ++ desc = scnprintf(buff, sizeof(buff), "%u\n", value); ++ ++ return simple_read_from_buffer(buf, len, offset, buff, desc); ++} ++ ++static ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ struct svc_debugfs_pwrmon_rail *pwrmon_rails = file->f_inode->i_private; ++ struct gb_svc *svc = pwrmon_rails->svc; ++ int ret, desc; ++ u32 value; ++ char buff[16]; ++ ++ ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, ++ GB_SVC_PWRMON_TYPE_PWR, &value); ++ if (ret) { ++ dev_err(&svc->dev, "failed to get power sample %u: %d\n", ++ pwrmon_rails->id, ret); ++ return ret; ++ } ++ ++ desc = scnprintf(buff, sizeof(buff), "%u\n", value); ++ ++ return simple_read_from_buffer(buf, len, offset, buff, desc); ++} ++ ++static const struct file_operations pwrmon_debugfs_voltage_fops = { ++ .read = pwr_debugfs_voltage_read, ++}; ++ ++static const struct file_operations pwrmon_debugfs_current_fops = { ++ .read = pwr_debugfs_current_read, ++}; ++ ++static const struct file_operations pwrmon_debugfs_power_fops = { ++ .read = pwr_debugfs_power_read, ++}; ++ ++static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) ++{ ++ int i; ++ size_t bufsize; ++ struct dentry *dent; ++ struct gb_svc_pwrmon_rail_names_get_response *rail_names; ++ u8 rail_count; ++ ++ dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry); ++ if (IS_ERR_OR_NULL(dent)) ++ return; ++ ++ if (gb_svc_pwrmon_rail_count_get(svc, &rail_count)) ++ goto err_pwrmon_debugfs; ++ ++ if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) ++ goto err_pwrmon_debugfs; ++ ++ bufsize = sizeof(*rail_names) + ++ GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; ++ ++ rail_names = kzalloc(bufsize, GFP_KERNEL); ++ if (!rail_names) ++ goto err_pwrmon_debugfs; ++ ++ svc->pwrmon_rails = kcalloc(rail_count, sizeof(*svc->pwrmon_rails), ++ GFP_KERNEL); ++ if (!svc->pwrmon_rails) ++ goto err_pwrmon_debugfs_free; ++ ++ if (gb_svc_pwrmon_rail_names_get(svc, rail_names, bufsize)) ++ goto err_pwrmon_debugfs_free; ++ ++ for (i = 0; i < rail_count; i++) { ++ struct dentry *dir; ++ struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i]; ++ char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; ++ ++ snprintf(fname, sizeof(fname), "%s", ++ (char *)&rail_names->name[i]); ++ ++ rail->id = i; ++ rail->svc = svc; ++ ++ dir = debugfs_create_dir(fname, dent); ++ debugfs_create_file("voltage_now", S_IRUGO, dir, rail, ++ &pwrmon_debugfs_voltage_fops); ++ debugfs_create_file("current_now", S_IRUGO, dir, rail, ++ &pwrmon_debugfs_current_fops); ++ debugfs_create_file("power_now", S_IRUGO, dir, rail, ++ &pwrmon_debugfs_power_fops); ++ } ++ ++ kfree(rail_names); ++ return; ++ ++err_pwrmon_debugfs_free: ++ kfree(rail_names); ++ kfree(svc->pwrmon_rails); ++ svc->pwrmon_rails = NULL; ++ ++err_pwrmon_debugfs: ++ debugfs_remove(dent); ++} ++ ++static void gb_svc_debugfs_init(struct gb_svc *svc) ++{ ++ svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev), ++ gb_debugfs_get()); ++ gb_svc_pwrmon_debugfs_init(svc); ++} ++ ++static void gb_svc_debugfs_exit(struct gb_svc *svc) ++{ ++ debugfs_remove_recursive(svc->debugfs_dentry); ++ kfree(svc->pwrmon_rails); ++ svc->pwrmon_rails = NULL; ++} ++ ++static int gb_svc_hello(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_svc_hello_request *hello_request; ++ int ret; ++ ++ if (op->request->payload_size < sizeof(*hello_request)) { ++ dev_warn(&svc->dev, "short hello request (%zu < %zu)\n", ++ op->request->payload_size, ++ sizeof(*hello_request)); ++ return -EINVAL; ++ } ++ ++ hello_request = op->request->payload; ++ svc->endo_id = le16_to_cpu(hello_request->endo_id); ++ svc->ap_intf_id = hello_request->interface_id; ++ ++ ret = device_add(&svc->dev); ++ if (ret) { ++ dev_err(&svc->dev, "failed to register svc device: %d\n", ret); ++ return ret; ++ } ++ ++ ret = gb_svc_watchdog_create(svc); ++ if (ret) { ++ dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); ++ goto err_unregister_device; ++ } ++ ++ gb_svc_debugfs_init(svc); ++ ++ ret = gb_timesync_svc_add(svc); ++ if (ret) { ++ dev_err(&svc->dev, "failed to add SVC to timesync: %d\n", ret); ++ gb_svc_debugfs_exit(svc); ++ goto err_unregister_device; ++ } ++ ++ return gb_svc_queue_deferred_request(op); ++ ++err_unregister_device: ++ gb_svc_watchdog_destroy(svc); ++ device_del(&svc->dev); ++ return ret; ++} ++ ++static struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, ++ u8 intf_id) ++{ ++ struct gb_host_device *hd = svc->hd; ++ struct gb_module *module; ++ size_t num_interfaces; ++ u8 module_id; ++ ++ list_for_each_entry(module, &hd->modules, hd_node) { ++ module_id = module->module_id; ++ num_interfaces = module->num_interfaces; ++ ++ if (intf_id >= module_id && ++ intf_id < module_id + num_interfaces) { ++ return module->interfaces[intf_id - module_id]; ++ } ++ } ++ ++ return NULL; ++} ++ ++static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) ++{ ++ struct gb_host_device *hd = svc->hd; ++ struct gb_module *module; ++ ++ list_for_each_entry(module, &hd->modules, hd_node) { ++ if (module->module_id == module_id) ++ return module; ++ } ++ ++ return NULL; ++} ++ ++static void gb_svc_process_hello_deferred(struct gb_operation *operation) ++{ ++ struct gb_connection *connection = operation->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ int ret; ++ ++ /* ++ * XXX This is a hack/work-around to reconfigure the APBridgeA-Switch ++ * link to PWM G2, 1 Lane, Slow Auto, so that it has sufficient ++ * bandwidth for 3 audio streams plus boot-over-UniPro of a hot-plugged ++ * module. ++ * ++ * The code should be removed once SW-2217, Heuristic for UniPro ++ * Power Mode Changes is resolved. ++ */ ++ ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, ++ GB_SVC_UNIPRO_HS_SERIES_A, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ GB_SVC_SMALL_AMPLITUDE, GB_SVC_NO_DE_EMPHASIS, ++ GB_SVC_UNIPRO_SLOW_AUTO_MODE, ++ 2, 1, ++ 0, 0, ++ NULL, NULL); ++ ++ if (ret) ++ dev_warn(&svc->dev, ++ "power mode change failed on AP to switch link: %d\n", ++ ret); ++} ++ ++static void gb_svc_process_module_inserted(struct gb_operation *operation) ++{ ++ struct gb_svc_module_inserted_request *request; ++ struct gb_connection *connection = operation->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_host_device *hd = svc->hd; ++ struct gb_module *module; ++ size_t num_interfaces; ++ u8 module_id; ++ u16 flags; ++ int ret; ++ ++ /* The request message size has already been verified. */ ++ request = operation->request->payload; ++ module_id = request->primary_intf_id; ++ num_interfaces = request->intf_count; ++ flags = le16_to_cpu(request->flags); ++ ++ dev_dbg(&svc->dev, "%s - id = %u, num_interfaces = %zu, flags = 0x%04x\n", ++ __func__, module_id, num_interfaces, flags); ++ ++ if (flags & GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY) { ++ dev_warn(&svc->dev, "no primary interface detected on module %u\n", ++ module_id); ++ } ++ ++ module = gb_svc_module_lookup(svc, module_id); ++ if (module) { ++ dev_warn(&svc->dev, "unexpected module-inserted event %u\n", ++ module_id); ++ return; ++ } ++ ++ module = gb_module_create(hd, module_id, num_interfaces); ++ if (!module) { ++ dev_err(&svc->dev, "failed to create module\n"); ++ return; ++ } ++ ++ ret = gb_module_add(module); ++ if (ret) { ++ gb_module_put(module); ++ return; ++ } ++ ++ list_add(&module->hd_node, &hd->modules); ++} ++ ++static void gb_svc_process_module_removed(struct gb_operation *operation) ++{ ++ struct gb_svc_module_removed_request *request; ++ struct gb_connection *connection = operation->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_module *module; ++ u8 module_id; ++ ++ /* The request message size has already been verified. */ ++ request = operation->request->payload; ++ module_id = request->primary_intf_id; ++ ++ dev_dbg(&svc->dev, "%s - id = %u\n", __func__, module_id); ++ ++ module = gb_svc_module_lookup(svc, module_id); ++ if (!module) { ++ dev_warn(&svc->dev, "unexpected module-removed event %u\n", ++ module_id); ++ return; ++ } ++ ++ module->disconnected = true; ++ ++ gb_module_del(module); ++ list_del(&module->hd_node); ++ gb_module_put(module); ++} ++ ++static void gb_svc_process_intf_oops(struct gb_operation *operation) ++{ ++ struct gb_svc_intf_oops_request *request; ++ struct gb_connection *connection = operation->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_interface *intf; ++ u8 intf_id; ++ u8 reason; ++ ++ /* The request message size has already been verified. */ ++ request = operation->request->payload; ++ intf_id = request->intf_id; ++ reason = request->reason; ++ ++ intf = gb_svc_interface_lookup(svc, intf_id); ++ if (!intf) { ++ dev_warn(&svc->dev, "unexpected interface-oops event %u\n", ++ intf_id); ++ return; ++ } ++ ++ dev_info(&svc->dev, "Deactivating interface %u, interface oops reason = %u\n", ++ intf_id, reason); ++ ++ mutex_lock(&intf->mutex); ++ intf->disconnected = true; ++ gb_interface_disable(intf); ++ gb_interface_deactivate(intf); ++ mutex_unlock(&intf->mutex); ++} ++ ++static void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) ++{ ++ struct gb_svc_intf_mailbox_event_request *request; ++ struct gb_connection *connection = operation->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ struct gb_interface *intf; ++ u8 intf_id; ++ u16 result_code; ++ u32 mailbox; ++ ++ /* The request message size has already been verified. */ ++ request = operation->request->payload; ++ intf_id = request->intf_id; ++ result_code = le16_to_cpu(request->result_code); ++ mailbox = le32_to_cpu(request->mailbox); ++ ++ dev_dbg(&svc->dev, "%s - id = %u, result = 0x%04x, mailbox = 0x%08x\n", ++ __func__, intf_id, result_code, mailbox); ++ ++ intf = gb_svc_interface_lookup(svc, intf_id); ++ if (!intf) { ++ dev_warn(&svc->dev, "unexpected mailbox event %u\n", intf_id); ++ return; ++ } ++ ++ gb_interface_mailbox_event(intf, result_code, mailbox); ++} ++ ++static void gb_svc_process_deferred_request(struct work_struct *work) ++{ ++ struct gb_svc_deferred_request *dr; ++ struct gb_operation *operation; ++ struct gb_svc *svc; ++ u8 type; ++ ++ dr = container_of(work, struct gb_svc_deferred_request, work); ++ operation = dr->operation; ++ svc = gb_connection_get_data(operation->connection); ++ type = operation->request->header->type; ++ ++ switch (type) { ++ case GB_SVC_TYPE_SVC_HELLO: ++ gb_svc_process_hello_deferred(operation); ++ break; ++ case GB_SVC_TYPE_MODULE_INSERTED: ++ gb_svc_process_module_inserted(operation); ++ break; ++ case GB_SVC_TYPE_MODULE_REMOVED: ++ gb_svc_process_module_removed(operation); ++ break; ++ case GB_SVC_TYPE_INTF_MAILBOX_EVENT: ++ gb_svc_process_intf_mailbox_event(operation); ++ break; ++ case GB_SVC_TYPE_INTF_OOPS: ++ gb_svc_process_intf_oops(operation); ++ break; ++ default: ++ dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); ++ } ++ ++ gb_operation_put(operation); ++ kfree(dr); ++} ++ ++static int gb_svc_queue_deferred_request(struct gb_operation *operation) ++{ ++ struct gb_svc *svc = gb_connection_get_data(operation->connection); ++ struct gb_svc_deferred_request *dr; ++ ++ dr = kmalloc(sizeof(*dr), GFP_KERNEL); ++ if (!dr) ++ return -ENOMEM; ++ ++ gb_operation_get(operation); ++ ++ dr->operation = operation; ++ INIT_WORK(&dr->work, gb_svc_process_deferred_request); ++ ++ queue_work(svc->wq, &dr->work); ++ ++ return 0; ++} ++ ++static int gb_svc_intf_reset_recv(struct gb_operation *op) ++{ ++ struct gb_svc *svc = gb_connection_get_data(op->connection); ++ struct gb_message *request = op->request; ++ struct gb_svc_intf_reset_request *reset; ++ u8 intf_id; ++ ++ if (request->payload_size < sizeof(*reset)) { ++ dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", ++ request->payload_size, sizeof(*reset)); ++ return -EINVAL; ++ } ++ reset = request->payload; ++ ++ intf_id = reset->intf_id; ++ ++ /* FIXME Reset the interface here */ ++ ++ return 0; ++} ++ ++static int gb_svc_module_inserted_recv(struct gb_operation *op) ++{ ++ struct gb_svc *svc = gb_connection_get_data(op->connection); ++ struct gb_svc_module_inserted_request *request; ++ ++ if (op->request->payload_size < sizeof(*request)) { ++ dev_warn(&svc->dev, "short module-inserted request received (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ dev_dbg(&svc->dev, "%s - id = %u\n", __func__, ++ request->primary_intf_id); ++ ++ return gb_svc_queue_deferred_request(op); ++} ++ ++static int gb_svc_module_removed_recv(struct gb_operation *op) ++{ ++ struct gb_svc *svc = gb_connection_get_data(op->connection); ++ struct gb_svc_module_removed_request *request; ++ ++ if (op->request->payload_size < sizeof(*request)) { ++ dev_warn(&svc->dev, "short module-removed request received (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ dev_dbg(&svc->dev, "%s - id = %u\n", __func__, ++ request->primary_intf_id); ++ ++ return gb_svc_queue_deferred_request(op); ++} ++ ++static int gb_svc_intf_oops_recv(struct gb_operation *op) ++{ ++ struct gb_svc *svc = gb_connection_get_data(op->connection); ++ struct gb_svc_intf_oops_request *request; ++ ++ if (op->request->payload_size < sizeof(*request)) { ++ dev_warn(&svc->dev, "short intf-oops request received (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ return gb_svc_queue_deferred_request(op); ++} ++ ++static int gb_svc_intf_mailbox_event_recv(struct gb_operation *op) ++{ ++ struct gb_svc *svc = gb_connection_get_data(op->connection); ++ struct gb_svc_intf_mailbox_event_request *request; ++ ++ if (op->request->payload_size < sizeof(*request)) { ++ dev_warn(&svc->dev, "short mailbox request received (%zu < %zu)\n", ++ op->request->payload_size, sizeof(*request)); ++ return -EINVAL; ++ } ++ ++ request = op->request->payload; ++ ++ dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); ++ ++ return gb_svc_queue_deferred_request(op); ++} ++ ++static int gb_svc_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_svc *svc = gb_connection_get_data(connection); ++ u8 type = op->type; ++ int ret = 0; ++ ++ /* ++ * SVC requests need to follow a specific order (at least initially) and ++ * below code takes care of enforcing that. The expected order is: ++ * - PROTOCOL_VERSION ++ * - SVC_HELLO ++ * - Any other request, but the earlier two. ++ * ++ * Incoming requests are guaranteed to be serialized and so we don't ++ * need to protect 'state' for any races. ++ */ ++ switch (type) { ++ case GB_SVC_TYPE_PROTOCOL_VERSION: ++ if (svc->state != GB_SVC_STATE_RESET) ++ ret = -EINVAL; ++ break; ++ case GB_SVC_TYPE_SVC_HELLO: ++ if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION) ++ ret = -EINVAL; ++ break; ++ default: ++ if (svc->state != GB_SVC_STATE_SVC_HELLO) ++ ret = -EINVAL; ++ break; ++ } ++ ++ if (ret) { ++ dev_warn(&svc->dev, "unexpected request 0x%02x received (state %u)\n", ++ type, svc->state); ++ return ret; ++ } ++ ++ switch (type) { ++ case GB_SVC_TYPE_PROTOCOL_VERSION: ++ ret = gb_svc_version_request(op); ++ if (!ret) ++ svc->state = GB_SVC_STATE_PROTOCOL_VERSION; ++ return ret; ++ case GB_SVC_TYPE_SVC_HELLO: ++ ret = gb_svc_hello(op); ++ if (!ret) ++ svc->state = GB_SVC_STATE_SVC_HELLO; ++ return ret; ++ case GB_SVC_TYPE_INTF_RESET: ++ return gb_svc_intf_reset_recv(op); ++ case GB_SVC_TYPE_MODULE_INSERTED: ++ return gb_svc_module_inserted_recv(op); ++ case GB_SVC_TYPE_MODULE_REMOVED: ++ return gb_svc_module_removed_recv(op); ++ case GB_SVC_TYPE_INTF_MAILBOX_EVENT: ++ return gb_svc_intf_mailbox_event_recv(op); ++ case GB_SVC_TYPE_INTF_OOPS: ++ return gb_svc_intf_oops_recv(op); ++ default: ++ dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); ++ return -EINVAL; ++ } ++} ++ ++static void gb_svc_release(struct device *dev) ++{ ++ struct gb_svc *svc = to_gb_svc(dev); ++ ++ if (svc->connection) ++ gb_connection_destroy(svc->connection); ++ ida_destroy(&svc->device_id_map); ++ destroy_workqueue(svc->wq); ++ kfree(svc); ++} ++ ++struct device_type greybus_svc_type = { ++ .name = "greybus_svc", ++ .release = gb_svc_release, ++}; ++ ++struct gb_svc *gb_svc_create(struct gb_host_device *hd) ++{ ++ struct gb_svc *svc; ++ ++ svc = kzalloc(sizeof(*svc), GFP_KERNEL); ++ if (!svc) ++ return NULL; ++ ++ svc->wq = alloc_workqueue("%s:svc", WQ_UNBOUND, 1, dev_name(&hd->dev)); ++ if (!svc->wq) { ++ kfree(svc); ++ return NULL; ++ } ++ ++ svc->dev.parent = &hd->dev; ++ svc->dev.bus = &greybus_bus_type; ++ svc->dev.type = &greybus_svc_type; ++ svc->dev.groups = svc_groups; ++ svc->dev.dma_mask = svc->dev.parent->dma_mask; ++ device_initialize(&svc->dev); ++ ++ dev_set_name(&svc->dev, "%d-svc", hd->bus_id); ++ ++ ida_init(&svc->device_id_map); ++ svc->state = GB_SVC_STATE_RESET; ++ svc->hd = hd; ++ ++ svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, ++ gb_svc_request_handler); ++ if (IS_ERR(svc->connection)) { ++ dev_err(&svc->dev, "failed to create connection: %ld\n", ++ PTR_ERR(svc->connection)); ++ goto err_put_device; ++ } ++ ++ gb_connection_set_data(svc->connection, svc); ++ ++ return svc; ++ ++err_put_device: ++ put_device(&svc->dev); ++ return NULL; ++} ++ ++int gb_svc_add(struct gb_svc *svc) ++{ ++ int ret; ++ ++ /* ++ * The SVC protocol is currently driven by the SVC, so the SVC device ++ * is added from the connection request handler when enough ++ * information has been received. ++ */ ++ ret = gb_connection_enable(svc->connection); ++ if (ret) ++ return ret; ++ ++ return 0; ++} ++ ++static void gb_svc_remove_modules(struct gb_svc *svc) ++{ ++ struct gb_host_device *hd = svc->hd; ++ struct gb_module *module, *tmp; ++ ++ list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) { ++ gb_module_del(module); ++ list_del(&module->hd_node); ++ gb_module_put(module); ++ } ++} ++ ++void gb_svc_del(struct gb_svc *svc) ++{ ++ gb_connection_disable_rx(svc->connection); ++ ++ /* ++ * The SVC device may have been registered from the request handler. ++ */ ++ if (device_is_registered(&svc->dev)) { ++ gb_timesync_svc_remove(svc); ++ gb_svc_debugfs_exit(svc); ++ gb_svc_watchdog_destroy(svc); ++ device_del(&svc->dev); ++ } ++ ++ flush_workqueue(svc->wq); ++ ++ gb_svc_remove_modules(svc); ++ ++ gb_connection_disable(svc->connection); ++} ++ ++void gb_svc_put(struct gb_svc *svc) ++{ ++ put_device(&svc->dev); ++} +--- /dev/null ++++ b/drivers/greybus/svc.h +@@ -0,0 +1,109 @@ ++/* ++ * Greybus SVC code ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __SVC_H ++#define __SVC_H ++ ++#define GB_SVC_CPORT_FLAG_E2EFC BIT(0) ++#define GB_SVC_CPORT_FLAG_CSD_N BIT(1) ++#define GB_SVC_CPORT_FLAG_CSV_N BIT(2) ++ ++enum gb_svc_state { ++ GB_SVC_STATE_RESET, ++ GB_SVC_STATE_PROTOCOL_VERSION, ++ GB_SVC_STATE_SVC_HELLO, ++}; ++ ++enum gb_svc_watchdog_bite { ++ GB_SVC_WATCHDOG_BITE_RESET_UNIPRO = 0, ++ GB_SVC_WATCHDOG_BITE_PANIC_KERNEL, ++}; ++ ++struct gb_svc_watchdog; ++ ++struct svc_debugfs_pwrmon_rail { ++ u8 id; ++ struct gb_svc *svc; ++}; ++ ++struct gb_svc { ++ struct device dev; ++ ++ struct gb_host_device *hd; ++ struct gb_connection *connection; ++ enum gb_svc_state state; ++ struct ida device_id_map; ++ struct workqueue_struct *wq; ++ ++ u16 endo_id; ++ u8 ap_intf_id; ++ ++ u8 protocol_major; ++ u8 protocol_minor; ++ ++ struct gb_svc_watchdog *watchdog; ++ enum gb_svc_watchdog_bite action; ++ ++ struct dentry *debugfs_dentry; ++ struct svc_debugfs_pwrmon_rail *pwrmon_rails; ++}; ++#define to_gb_svc(d) container_of(d, struct gb_svc, dev) ++ ++struct gb_svc *gb_svc_create(struct gb_host_device *hd); ++int gb_svc_add(struct gb_svc *svc); ++void gb_svc_del(struct gb_svc *svc); ++void gb_svc_put(struct gb_svc *svc); ++ ++int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, ++ u8 measurement_type, u32 *value); ++int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id); ++int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, ++ u8 intf2_id, u8 dev2_id); ++void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id); ++int gb_svc_connection_create(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ++ u8 intf2_id, u16 cport2_id, u8 cport_flags); ++void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, ++ u8 intf2_id, u16 cport2_id); ++int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id); ++int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable); ++int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable); ++int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable); ++int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type); ++int gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id); ++ ++int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, ++ u32 *value); ++int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, ++ u32 value); ++int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, ++ u8 tx_mode, u8 tx_gear, u8 tx_nlanes, ++ u8 tx_amplitude, u8 tx_hs_equalizer, ++ u8 rx_mode, u8 rx_gear, u8 rx_nlanes, ++ u8 flags, u32 quirks, ++ struct gb_svc_l2_timer_cfg *local, ++ struct gb_svc_l2_timer_cfg *remote); ++int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id); ++int gb_svc_ping(struct gb_svc *svc); ++int gb_svc_watchdog_create(struct gb_svc *svc); ++void gb_svc_watchdog_destroy(struct gb_svc *svc); ++bool gb_svc_watchdog_enabled(struct gb_svc *svc); ++int gb_svc_watchdog_enable(struct gb_svc *svc); ++int gb_svc_watchdog_disable(struct gb_svc *svc); ++int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, ++ u32 strobe_delay, u32 refclk); ++int gb_svc_timesync_disable(struct gb_svc *svc); ++int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time); ++int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time); ++int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask); ++int gb_svc_timesync_wake_pins_release(struct gb_svc *svc); ++ ++int gb_svc_protocol_init(void); ++void gb_svc_protocol_exit(void); ++ ++#endif /* __SVC_H */ +--- /dev/null ++++ b/drivers/greybus/svc_watchdog.c +@@ -0,0 +1,198 @@ ++/* ++ * SVC Greybus "watchdog" driver. ++ * ++ * Copyright 2016 Google Inc. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/delay.h> ++#include <linux/suspend.h> ++#include <linux/workqueue.h> ++#include "greybus.h" ++ ++#define SVC_WATCHDOG_PERIOD (2*HZ) ++ ++struct gb_svc_watchdog { ++ struct delayed_work work; ++ struct gb_svc *svc; ++ bool enabled; ++ struct notifier_block pm_notifier; ++}; ++ ++static struct delayed_work reset_work; ++ ++static int svc_watchdog_pm_notifier(struct notifier_block *notifier, ++ unsigned long pm_event, void *unused) ++{ ++ struct gb_svc_watchdog *watchdog = ++ container_of(notifier, struct gb_svc_watchdog, pm_notifier); ++ ++ switch (pm_event) { ++ case PM_SUSPEND_PREPARE: ++ gb_svc_watchdog_disable(watchdog->svc); ++ break; ++ case PM_POST_SUSPEND: ++ gb_svc_watchdog_enable(watchdog->svc); ++ break; ++ default: ++ break; ++ } ++ ++ return NOTIFY_DONE; ++} ++ ++static void greybus_reset(struct work_struct *work) ++{ ++ static char start_path[256] = "/system/bin/start"; ++ static char *envp[] = { ++ "HOME=/", ++ "PATH=/sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin", ++ NULL, ++ }; ++ static char *argv[] = { ++ start_path, ++ "unipro_reset", ++ NULL, ++ }; ++ ++ printk(KERN_ERR "svc_watchdog: calling \"%s %s\" to reset greybus network!\n", ++ argv[0], argv[1]); ++ call_usermodehelper(start_path, argv, envp, UMH_WAIT_EXEC); ++} ++ ++static void do_work(struct work_struct *work) ++{ ++ struct gb_svc_watchdog *watchdog; ++ struct gb_svc *svc; ++ int retval; ++ ++ watchdog = container_of(work, struct gb_svc_watchdog, work.work); ++ svc = watchdog->svc; ++ ++ dev_dbg(&svc->dev, "%s: ping.\n", __func__); ++ retval = gb_svc_ping(svc); ++ if (retval) { ++ /* ++ * Something went really wrong, let's warn userspace and then ++ * pull the plug and reset the whole greybus network. ++ * We need to do this outside of this workqueue as we will be ++ * tearing down the svc device itself. So queue up ++ * yet-another-callback to do that. ++ */ ++ dev_err(&svc->dev, ++ "SVC ping has returned %d, something is wrong!!!\n", ++ retval); ++ ++ if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) { ++ panic("SVC is not responding\n"); ++ } else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) { ++ dev_err(&svc->dev, "Resetting the greybus network, watch out!!!\n"); ++ ++ INIT_DELAYED_WORK(&reset_work, greybus_reset); ++ schedule_delayed_work(&reset_work, HZ / 2); ++ ++ /* ++ * Disable ourselves, we don't want to trip again unless ++ * userspace wants us to. ++ */ ++ watchdog->enabled = false; ++ } ++ } ++ ++ /* resubmit our work to happen again, if we are still "alive" */ ++ if (watchdog->enabled) ++ schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD); ++} ++ ++int gb_svc_watchdog_create(struct gb_svc *svc) ++{ ++ struct gb_svc_watchdog *watchdog; ++ int retval; ++ ++ if (svc->watchdog) ++ return 0; ++ ++ watchdog = kmalloc(sizeof(*watchdog), GFP_KERNEL); ++ if (!watchdog) ++ return -ENOMEM; ++ ++ watchdog->enabled = false; ++ watchdog->svc = svc; ++ INIT_DELAYED_WORK(&watchdog->work, do_work); ++ svc->watchdog = watchdog; ++ ++ watchdog->pm_notifier.notifier_call = svc_watchdog_pm_notifier; ++ retval = register_pm_notifier(&watchdog->pm_notifier); ++ if (retval) { ++ dev_err(&svc->dev, "error registering pm notifier(%d)\n", ++ retval); ++ goto svc_watchdog_create_err; ++ } ++ ++ retval = gb_svc_watchdog_enable(svc); ++ if (retval) { ++ dev_err(&svc->dev, "error enabling watchdog (%d)\n", retval); ++ unregister_pm_notifier(&watchdog->pm_notifier); ++ goto svc_watchdog_create_err; ++ } ++ return retval; ++ ++svc_watchdog_create_err: ++ svc->watchdog = NULL; ++ kfree(watchdog); ++ ++ return retval; ++} ++ ++void gb_svc_watchdog_destroy(struct gb_svc *svc) ++{ ++ struct gb_svc_watchdog *watchdog = svc->watchdog; ++ ++ if (!watchdog) ++ return; ++ ++ unregister_pm_notifier(&watchdog->pm_notifier); ++ gb_svc_watchdog_disable(svc); ++ svc->watchdog = NULL; ++ kfree(watchdog); ++} ++ ++bool gb_svc_watchdog_enabled(struct gb_svc *svc) ++{ ++ if (!svc || !svc->watchdog) ++ return false; ++ return svc->watchdog->enabled; ++} ++ ++int gb_svc_watchdog_enable(struct gb_svc *svc) ++{ ++ struct gb_svc_watchdog *watchdog; ++ ++ if (!svc->watchdog) ++ return -ENODEV; ++ ++ watchdog = svc->watchdog; ++ if (watchdog->enabled) ++ return 0; ++ ++ watchdog->enabled = true; ++ schedule_delayed_work(&watchdog->work, SVC_WATCHDOG_PERIOD); ++ return 0; ++} ++ ++int gb_svc_watchdog_disable(struct gb_svc *svc) ++{ ++ struct gb_svc_watchdog *watchdog; ++ ++ if (!svc->watchdog) ++ return -ENODEV; ++ ++ watchdog = svc->watchdog; ++ if (!watchdog->enabled) ++ return 0; ++ ++ watchdog->enabled = false; ++ cancel_delayed_work_sync(&watchdog->work); ++ return 0; ++} diff --git a/greybus_timesync.patch b/greybus_timesync.patch new file mode 100644 index 00000000000000..fcdacf64a88cf4 --- /dev/null +++ b/greybus_timesync.patch @@ -0,0 +1,1494 @@ +--- + drivers/greybus/timesync.c | 1357 ++++++++++++++++++++++++++++++++++++ + drivers/greybus/timesync.h | 45 + + drivers/greybus/timesync_platform.c | 77 ++ + 3 files changed, 1479 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/timesync.c +@@ -0,0 +1,1357 @@ ++/* ++ * TimeSync API driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#include <linux/debugfs.h> ++#include <linux/hrtimer.h> ++#include "greybus.h" ++#include "timesync.h" ++#include "greybus_trace.h" ++ ++/* ++ * Minimum inter-strobe value of one millisecond is chosen because it ++ * just-about fits the common definition of a jiffy. ++ * ++ * Maximum value OTOH is constrained by the number of bits the SVC can fit ++ * into a 16 bit up-counter. The SVC configures the timer in microseconds ++ * so the maximum allowable value is 65535 microseconds. We clip that value ++ * to 10000 microseconds for the sake of using nice round base 10 numbers ++ * and since right-now there's no imaginable use-case requiring anything ++ * other than a one millisecond inter-strobe time, let alone something ++ * higher than ten milliseconds. ++ */ ++#define GB_TIMESYNC_STROBE_DELAY_US 1000 ++#define GB_TIMESYNC_DEFAULT_OFFSET_US 1000 ++ ++/* Work queue timers long, short and SVC strobe timeout */ ++#define GB_TIMESYNC_DELAYED_WORK_LONG msecs_to_jiffies(10) ++#define GB_TIMESYNC_DELAYED_WORK_SHORT msecs_to_jiffies(1) ++#define GB_TIMESYNC_MAX_WAIT_SVC msecs_to_jiffies(5000) ++#define GB_TIMESYNC_KTIME_UPDATE msecs_to_jiffies(1000) ++#define GB_TIMESYNC_MAX_KTIME_CONVERSION 15 ++ ++/* Maximum number of times we'll retry a failed synchronous sync */ ++#define GB_TIMESYNC_MAX_RETRIES 5 ++ ++/* Reported nanoseconds/femtoseconds per clock */ ++static u64 gb_timesync_ns_per_clock; ++static u64 gb_timesync_fs_per_clock; ++ ++/* Maximum difference we will accept converting FrameTime to ktime */ ++static u32 gb_timesync_max_ktime_diff; ++ ++/* Reported clock rate */ ++static unsigned long gb_timesync_clock_rate; ++ ++/* Workqueue */ ++static void gb_timesync_worker(struct work_struct *work); ++ ++/* List of SVCs with one FrameTime per SVC */ ++static LIST_HEAD(gb_timesync_svc_list); ++ ++/* Synchronize parallel contexts accessing a valid timesync_svc pointer */ ++static DEFINE_MUTEX(gb_timesync_svc_list_mutex); ++ ++/* Structure to convert from FrameTime to timespec/ktime */ ++struct gb_timesync_frame_time_data { ++ u64 frame_time; ++ struct timespec ts; ++}; ++ ++struct gb_timesync_svc { ++ struct list_head list; ++ struct list_head interface_list; ++ struct gb_svc *svc; ++ struct gb_timesync_host_device *timesync_hd; ++ ++ spinlock_t spinlock; /* Per SVC spinlock to sync with ISR */ ++ struct mutex mutex; /* Per SVC mutex for regular synchronization */ ++ ++ struct dentry *frame_time_dentry; ++ struct dentry *frame_ktime_dentry; ++ struct workqueue_struct *work_queue; ++ wait_queue_head_t wait_queue; ++ struct delayed_work delayed_work; ++ struct timer_list ktime_timer; ++ ++ /* The current local FrameTime */ ++ u64 frame_time_offset; ++ struct gb_timesync_frame_time_data strobe_data[GB_TIMESYNC_MAX_STROBES]; ++ struct gb_timesync_frame_time_data ktime_data; ++ ++ /* The SVC FrameTime and relative AP FrameTime @ last TIMESYNC_PING */ ++ u64 svc_ping_frame_time; ++ u64 ap_ping_frame_time; ++ ++ /* Transitory settings */ ++ u32 strobe_mask; ++ bool offset_down; ++ bool print_ping; ++ bool capture_ping; ++ int strobe; ++ ++ /* Current state */ ++ int state; ++}; ++ ++struct gb_timesync_host_device { ++ struct list_head list; ++ struct gb_host_device *hd; ++ u64 ping_frame_time; ++}; ++ ++struct gb_timesync_interface { ++ struct list_head list; ++ struct gb_interface *interface; ++ u64 ping_frame_time; ++}; ++ ++enum gb_timesync_state { ++ GB_TIMESYNC_STATE_INVALID = 0, ++ GB_TIMESYNC_STATE_INACTIVE = 1, ++ GB_TIMESYNC_STATE_INIT = 2, ++ GB_TIMESYNC_STATE_WAIT_SVC = 3, ++ GB_TIMESYNC_STATE_AUTHORITATIVE = 4, ++ GB_TIMESYNC_STATE_PING = 5, ++ GB_TIMESYNC_STATE_ACTIVE = 6, ++}; ++ ++static void gb_timesync_ktime_timer_fn(unsigned long data); ++ ++static u64 gb_timesync_adjust_count(struct gb_timesync_svc *timesync_svc, ++ u64 counts) ++{ ++ if (timesync_svc->offset_down) ++ return counts - timesync_svc->frame_time_offset; ++ else ++ return counts + timesync_svc->frame_time_offset; ++} ++ ++/* ++ * This function provides the authoritative FrameTime to a calling function. It ++ * is designed to be lockless and should remain that way the caller is assumed ++ * to be state-aware. ++ */ ++static u64 __gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) ++{ ++ u64 clocks = gb_timesync_platform_get_counter(); ++ ++ return gb_timesync_adjust_count(timesync_svc, clocks); ++} ++ ++static void gb_timesync_schedule_svc_timeout(struct gb_timesync_svc ++ *timesync_svc) ++{ ++ queue_delayed_work(timesync_svc->work_queue, ++ ×ync_svc->delayed_work, ++ GB_TIMESYNC_MAX_WAIT_SVC); ++} ++ ++static void gb_timesync_set_state(struct gb_timesync_svc *timesync_svc, ++ int state) ++{ ++ switch (state) { ++ case GB_TIMESYNC_STATE_INVALID: ++ timesync_svc->state = state; ++ wake_up(×ync_svc->wait_queue); ++ break; ++ case GB_TIMESYNC_STATE_INACTIVE: ++ timesync_svc->state = state; ++ wake_up(×ync_svc->wait_queue); ++ break; ++ case GB_TIMESYNC_STATE_INIT: ++ if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { ++ timesync_svc->strobe = 0; ++ timesync_svc->frame_time_offset = 0; ++ timesync_svc->state = state; ++ cancel_delayed_work(×ync_svc->delayed_work); ++ queue_delayed_work(timesync_svc->work_queue, ++ ×ync_svc->delayed_work, ++ GB_TIMESYNC_DELAYED_WORK_LONG); ++ } ++ break; ++ case GB_TIMESYNC_STATE_WAIT_SVC: ++ if (timesync_svc->state == GB_TIMESYNC_STATE_INIT) ++ timesync_svc->state = state; ++ break; ++ case GB_TIMESYNC_STATE_AUTHORITATIVE: ++ if (timesync_svc->state == GB_TIMESYNC_STATE_WAIT_SVC) { ++ timesync_svc->state = state; ++ cancel_delayed_work(×ync_svc->delayed_work); ++ queue_delayed_work(timesync_svc->work_queue, ++ ×ync_svc->delayed_work, 0); ++ } ++ break; ++ case GB_TIMESYNC_STATE_PING: ++ if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) { ++ timesync_svc->state = state; ++ queue_delayed_work(timesync_svc->work_queue, ++ ×ync_svc->delayed_work, ++ GB_TIMESYNC_DELAYED_WORK_SHORT); ++ } ++ break; ++ case GB_TIMESYNC_STATE_ACTIVE: ++ if (timesync_svc->state == GB_TIMESYNC_STATE_AUTHORITATIVE || ++ timesync_svc->state == GB_TIMESYNC_STATE_PING) { ++ timesync_svc->state = state; ++ wake_up(×ync_svc->wait_queue); ++ } ++ break; ++ } ++ ++ if (WARN_ON(timesync_svc->state != state)) { ++ pr_err("Invalid state transition %d=>%d\n", ++ timesync_svc->state, state); ++ } ++} ++ ++static void gb_timesync_set_state_atomic(struct gb_timesync_svc *timesync_svc, ++ int state) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ gb_timesync_set_state(timesync_svc, state); ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++} ++ ++static u64 gb_timesync_diff(u64 x, u64 y) ++{ ++ if (x > y) ++ return x - y; ++ else ++ return y - x; ++} ++ ++static void gb_timesync_adjust_to_svc(struct gb_timesync_svc *svc, ++ u64 svc_frame_time, u64 ap_frame_time) ++{ ++ if (svc_frame_time > ap_frame_time) { ++ svc->frame_time_offset = svc_frame_time - ap_frame_time; ++ svc->offset_down = false; ++ } else { ++ svc->frame_time_offset = ap_frame_time - svc_frame_time; ++ svc->offset_down = true; ++ } ++} ++ ++/* ++ * Associate a FrameTime with a ktime timestamp represented as struct timespec ++ * Requires the calling context to hold timesync_svc->mutex ++ */ ++static void gb_timesync_store_ktime(struct gb_timesync_svc *timesync_svc, ++ struct timespec ts, u64 frame_time) ++{ ++ timesync_svc->ktime_data.ts = ts; ++ timesync_svc->ktime_data.frame_time = frame_time; ++} ++ ++/* ++ * Find the two pulses that best-match our expected inter-strobe gap and ++ * then calculate the difference between the SVC time at the second pulse ++ * to the local time at the second pulse. ++ */ ++static void gb_timesync_collate_frame_time(struct gb_timesync_svc *timesync_svc, ++ u64 *frame_time) ++{ ++ int i = 0; ++ u64 delta, ap_frame_time; ++ u64 strobe_delay_ns = GB_TIMESYNC_STROBE_DELAY_US * NSEC_PER_USEC; ++ u64 least = 0; ++ ++ for (i = 1; i < GB_TIMESYNC_MAX_STROBES; i++) { ++ delta = timesync_svc->strobe_data[i].frame_time - ++ timesync_svc->strobe_data[i - 1].frame_time; ++ delta *= gb_timesync_ns_per_clock; ++ delta = gb_timesync_diff(delta, strobe_delay_ns); ++ ++ if (!least || delta < least) { ++ least = delta; ++ gb_timesync_adjust_to_svc(timesync_svc, frame_time[i], ++ timesync_svc->strobe_data[i].frame_time); ++ ++ ap_frame_time = timesync_svc->strobe_data[i].frame_time; ++ ap_frame_time = gb_timesync_adjust_count(timesync_svc, ++ ap_frame_time); ++ gb_timesync_store_ktime(timesync_svc, ++ timesync_svc->strobe_data[i].ts, ++ ap_frame_time); ++ ++ pr_debug("adjust %s local %llu svc %llu delta %llu\n", ++ timesync_svc->offset_down ? "down" : "up", ++ timesync_svc->strobe_data[i].frame_time, ++ frame_time[i], delta); ++ } ++ } ++} ++ ++static void gb_timesync_teardown(struct gb_timesync_svc *timesync_svc) ++{ ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_interface *interface; ++ struct gb_host_device *hd; ++ int ret; ++ ++ list_for_each_entry(timesync_interface, ++ ×ync_svc->interface_list, list) { ++ interface = timesync_interface->interface; ++ ret = gb_interface_timesync_disable(interface); ++ if (ret) { ++ dev_err(&interface->dev, ++ "interface timesync_disable %d\n", ret); ++ } ++ } ++ ++ hd = timesync_svc->timesync_hd->hd; ++ ret = hd->driver->timesync_disable(hd); ++ if (ret < 0) { ++ dev_err(&hd->dev, "host timesync_disable %d\n", ++ ret); ++ } ++ ++ gb_svc_timesync_wake_pins_release(svc); ++ gb_svc_timesync_disable(svc); ++ gb_timesync_platform_unlock_bus(); ++ ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); ++} ++ ++static void gb_timesync_platform_lock_bus_fail(struct gb_timesync_svc ++ *timesync_svc, int ret) ++{ ++ if (ret == -EAGAIN) { ++ gb_timesync_set_state(timesync_svc, timesync_svc->state); ++ } else { ++ pr_err("Failed to lock timesync bus %d\n", ret); ++ gb_timesync_set_state(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); ++ } ++} ++ ++static void gb_timesync_enable(struct gb_timesync_svc *timesync_svc) ++{ ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_host_device *hd; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_interface *interface; ++ u64 init_frame_time; ++ unsigned long clock_rate = gb_timesync_clock_rate; ++ int ret; ++ ++ /* ++ * Get access to the wake pins in the AP and SVC ++ * Release these pins either in gb_timesync_teardown() or in ++ * gb_timesync_authoritative() ++ */ ++ ret = gb_timesync_platform_lock_bus(timesync_svc); ++ if (ret < 0) { ++ gb_timesync_platform_lock_bus_fail(timesync_svc, ret); ++ return; ++ } ++ ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); ++ if (ret) { ++ dev_err(&svc->dev, ++ "gb_svc_timesync_wake_pins_acquire %d\n", ret); ++ gb_timesync_teardown(timesync_svc); ++ return; ++ } ++ ++ /* Choose an initial time in the future */ ++ init_frame_time = __gb_timesync_get_frame_time(timesync_svc) + 100000UL; ++ ++ /* Send enable command to all relevant participants */ ++ list_for_each_entry(timesync_interface, ×ync_svc->interface_list, ++ list) { ++ interface = timesync_interface->interface; ++ ret = gb_interface_timesync_enable(interface, ++ GB_TIMESYNC_MAX_STROBES, ++ init_frame_time, ++ GB_TIMESYNC_STROBE_DELAY_US, ++ clock_rate); ++ if (ret) { ++ dev_err(&interface->dev, ++ "interface timesync_enable %d\n", ret); ++ } ++ } ++ ++ hd = timesync_svc->timesync_hd->hd; ++ ret = hd->driver->timesync_enable(hd, GB_TIMESYNC_MAX_STROBES, ++ init_frame_time, ++ GB_TIMESYNC_STROBE_DELAY_US, ++ clock_rate); ++ if (ret < 0) { ++ dev_err(&hd->dev, "host timesync_enable %d\n", ++ ret); ++ } ++ ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_WAIT_SVC); ++ ret = gb_svc_timesync_enable(svc, GB_TIMESYNC_MAX_STROBES, ++ init_frame_time, ++ GB_TIMESYNC_STROBE_DELAY_US, ++ clock_rate); ++ if (ret) { ++ dev_err(&svc->dev, ++ "gb_svc_timesync_enable %d\n", ret); ++ gb_timesync_teardown(timesync_svc); ++ return; ++ } ++ ++ /* Schedule a timeout waiting for SVC to complete strobing */ ++ gb_timesync_schedule_svc_timeout(timesync_svc); ++} ++ ++static void gb_timesync_authoritative(struct gb_timesync_svc *timesync_svc) ++{ ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_host_device *hd; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_interface *interface; ++ u64 svc_frame_time[GB_TIMESYNC_MAX_STROBES]; ++ int ret; ++ ++ /* Get authoritative time from SVC and adjust local clock */ ++ ret = gb_svc_timesync_authoritative(svc, svc_frame_time); ++ if (ret) { ++ dev_err(&svc->dev, ++ "gb_svc_timesync_authoritative %d\n", ret); ++ gb_timesync_teardown(timesync_svc); ++ return; ++ } ++ gb_timesync_collate_frame_time(timesync_svc, svc_frame_time); ++ ++ /* Transmit authoritative time to downstream slaves */ ++ hd = timesync_svc->timesync_hd->hd; ++ ret = hd->driver->timesync_authoritative(hd, svc_frame_time); ++ if (ret < 0) ++ dev_err(&hd->dev, "host timesync_authoritative %d\n", ret); ++ ++ list_for_each_entry(timesync_interface, ++ ×ync_svc->interface_list, list) { ++ interface = timesync_interface->interface; ++ ret = gb_interface_timesync_authoritative( ++ interface, ++ svc_frame_time); ++ if (ret) { ++ dev_err(&interface->dev, ++ "interface timesync_authoritative %d\n", ret); ++ } ++ } ++ ++ /* Release wake pins */ ++ gb_svc_timesync_wake_pins_release(svc); ++ gb_timesync_platform_unlock_bus(); ++ ++ /* Transition to state ACTIVE */ ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); ++ ++ /* Schedule a ping to verify the synchronized system time */ ++ timesync_svc->print_ping = true; ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_PING); ++} ++ ++static int __gb_timesync_get_status(struct gb_timesync_svc *timesync_svc) ++{ ++ int ret = -EINVAL; ++ ++ switch (timesync_svc->state) { ++ case GB_TIMESYNC_STATE_INVALID: ++ case GB_TIMESYNC_STATE_INACTIVE: ++ ret = -ENODEV; ++ break; ++ case GB_TIMESYNC_STATE_INIT: ++ case GB_TIMESYNC_STATE_WAIT_SVC: ++ case GB_TIMESYNC_STATE_AUTHORITATIVE: ++ ret = -EAGAIN; ++ break; ++ case GB_TIMESYNC_STATE_PING: ++ case GB_TIMESYNC_STATE_ACTIVE: ++ ret = 0; ++ break; ++ } ++ return ret; ++} ++ ++/* ++ * This routine takes a FrameTime and derives the difference with-respect ++ * to a reference FrameTime/ktime pair. It then returns the calculated ++ * ktime based on the difference between the supplied FrameTime and ++ * the reference FrameTime. ++ * ++ * The time difference is calculated to six decimal places. Taking 19.2MHz ++ * as an example this means we have 52.083333~ nanoseconds per clock or ++ * 52083333~ femtoseconds per clock. ++ * ++ * Naively taking the count difference and converting to ++ * seconds/nanoseconds would quickly see the 0.0833 component produce ++ * noticeable errors. For example a time difference of one second would ++ * loose 19200000 * 0.08333x nanoseconds or 1.59 seconds. ++ * ++ * In contrast calculating in femtoseconds the same example of 19200000 * ++ * 0.000000083333x nanoseconds per count of error is just 1.59 nanoseconds! ++ * ++ * Continuing the example of 19.2 MHz we cap the maximum error difference ++ * at a worst-case 0.3 microseconds over a potential calculation window of ++ * abount 15 seconds, meaning you can convert a FrameTime that is <= 15 ++ * seconds older/younger than the reference time with a maximum error of ++ * 0.2385 useconds. Note 19.2MHz is an example frequency not a requirement. ++ */ ++static int gb_timesync_to_timespec(struct gb_timesync_svc *timesync_svc, ++ u64 frame_time, struct timespec *ts) ++{ ++ unsigned long flags; ++ u64 delta_fs, counts, sec, nsec; ++ bool add; ++ int ret = 0; ++ ++ memset(ts, 0x00, sizeof(*ts)); ++ mutex_lock(×ync_svc->mutex); ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ ++ ret = __gb_timesync_get_status(timesync_svc); ++ if (ret) ++ goto done; ++ ++ /* Support calculating ktime upwards or downwards from the reference */ ++ if (frame_time < timesync_svc->ktime_data.frame_time) { ++ add = false; ++ counts = timesync_svc->ktime_data.frame_time - frame_time; ++ } else { ++ add = true; ++ counts = frame_time - timesync_svc->ktime_data.frame_time; ++ } ++ ++ /* Enforce the .23 of a usecond boundary @ 19.2MHz */ ++ if (counts > gb_timesync_max_ktime_diff) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ++ /* Determine the time difference in femtoseconds */ ++ delta_fs = counts * gb_timesync_fs_per_clock; ++ ++ /* Convert to seconds */ ++ sec = delta_fs; ++ do_div(sec, NSEC_PER_SEC); ++ do_div(sec, 1000000UL); ++ ++ /* Get the nanosecond remainder */ ++ nsec = do_div(delta_fs, sec); ++ do_div(nsec, 1000000UL); ++ ++ if (add) { ++ /* Add the calculated offset - overflow nanoseconds upwards */ ++ ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec + sec; ++ ts->tv_nsec = timesync_svc->ktime_data.ts.tv_nsec + nsec; ++ if (ts->tv_nsec >= NSEC_PER_SEC) { ++ ts->tv_sec++; ++ ts->tv_nsec -= NSEC_PER_SEC; ++ } ++ } else { ++ /* Subtract the difference over/underflow as necessary */ ++ if (nsec > timesync_svc->ktime_data.ts.tv_nsec) { ++ sec++; ++ nsec = nsec + timesync_svc->ktime_data.ts.tv_nsec; ++ nsec = do_div(nsec, NSEC_PER_SEC); ++ } else { ++ nsec = timesync_svc->ktime_data.ts.tv_nsec - nsec; ++ } ++ /* Cannot return a negative second value */ ++ if (sec > timesync_svc->ktime_data.ts.tv_sec) { ++ ret = -EINVAL; ++ goto done; ++ } ++ ts->tv_sec = timesync_svc->ktime_data.ts.tv_sec - sec; ++ ts->tv_nsec = nsec; ++ } ++done: ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++ mutex_unlock(×ync_svc->mutex); ++ return ret; ++} ++ ++static size_t gb_timesync_log_frame_time(struct gb_timesync_svc *timesync_svc, ++ char *buf, size_t buflen) ++{ ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_host_device *hd; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_interface *interface; ++ unsigned int len; ++ size_t off; ++ ++ /* AP/SVC */ ++ off = snprintf(buf, buflen, "%s frametime: ap=%llu %s=%llu ", ++ greybus_bus_type.name, ++ timesync_svc->ap_ping_frame_time, dev_name(&svc->dev), ++ timesync_svc->svc_ping_frame_time); ++ len = buflen - off; ++ ++ /* APB/GPB */ ++ if (len < buflen) { ++ hd = timesync_svc->timesync_hd->hd; ++ off += snprintf(&buf[off], len, "%s=%llu ", dev_name(&hd->dev), ++ timesync_svc->timesync_hd->ping_frame_time); ++ len = buflen - off; ++ } ++ ++ list_for_each_entry(timesync_interface, ++ ×ync_svc->interface_list, list) { ++ if (len < buflen) { ++ interface = timesync_interface->interface; ++ off += snprintf(&buf[off], len, "%s=%llu ", ++ dev_name(&interface->dev), ++ timesync_interface->ping_frame_time); ++ len = buflen - off; ++ } ++ } ++ if (len < buflen) ++ off += snprintf(&buf[off], len, "\n"); ++ return off; ++} ++ ++static size_t gb_timesync_log_frame_ktime(struct gb_timesync_svc *timesync_svc, ++ char *buf, size_t buflen) ++{ ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_host_device *hd; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_interface *interface; ++ struct timespec ts; ++ unsigned int len; ++ size_t off; ++ ++ /* AP */ ++ gb_timesync_to_timespec(timesync_svc, timesync_svc->ap_ping_frame_time, ++ &ts); ++ off = snprintf(buf, buflen, "%s frametime: ap=%lu.%lu ", ++ greybus_bus_type.name, ts.tv_sec, ts.tv_nsec); ++ len = buflen - off; ++ if (len >= buflen) ++ goto done; ++ ++ /* SVC */ ++ gb_timesync_to_timespec(timesync_svc, timesync_svc->svc_ping_frame_time, ++ &ts); ++ off += snprintf(&buf[off], len, "%s=%lu.%lu ", dev_name(&svc->dev), ++ ts.tv_sec, ts.tv_nsec); ++ len = buflen - off; ++ if (len >= buflen) ++ goto done; ++ ++ /* APB/GPB */ ++ hd = timesync_svc->timesync_hd->hd; ++ gb_timesync_to_timespec(timesync_svc, ++ timesync_svc->timesync_hd->ping_frame_time, ++ &ts); ++ off += snprintf(&buf[off], len, "%s=%lu.%lu ", ++ dev_name(&hd->dev), ++ ts.tv_sec, ts.tv_nsec); ++ len = buflen - off; ++ if (len >= buflen) ++ goto done; ++ ++ list_for_each_entry(timesync_interface, ++ ×ync_svc->interface_list, list) { ++ interface = timesync_interface->interface; ++ gb_timesync_to_timespec(timesync_svc, ++ timesync_interface->ping_frame_time, ++ &ts); ++ off += snprintf(&buf[off], len, "%s=%lu.%lu ", ++ dev_name(&interface->dev), ++ ts.tv_sec, ts.tv_nsec); ++ len = buflen - off; ++ if (len >= buflen) ++ goto done; ++ } ++ off += snprintf(&buf[off], len, "\n"); ++done: ++ return off; ++} ++ ++/* ++ * Send an SVC initiated wake 'ping' to each TimeSync participant. ++ * Get the FrameTime from each participant associated with the wake ++ * ping. ++ */ ++static void gb_timesync_ping(struct gb_timesync_svc *timesync_svc) ++{ ++ struct gb_svc *svc = timesync_svc->svc; ++ struct gb_host_device *hd; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_control *control; ++ u64 *ping_frame_time; ++ int ret; ++ ++ /* Get access to the wake pins in the AP and SVC */ ++ ret = gb_timesync_platform_lock_bus(timesync_svc); ++ if (ret < 0) { ++ gb_timesync_platform_lock_bus_fail(timesync_svc, ret); ++ return; ++ } ++ ret = gb_svc_timesync_wake_pins_acquire(svc, timesync_svc->strobe_mask); ++ if (ret) { ++ dev_err(&svc->dev, ++ "gb_svc_timesync_wake_pins_acquire %d\n", ret); ++ gb_timesync_teardown(timesync_svc); ++ return; ++ } ++ ++ /* Have SVC generate a timesync ping */ ++ timesync_svc->capture_ping = true; ++ timesync_svc->svc_ping_frame_time = 0; ++ ret = gb_svc_timesync_ping(svc, ×ync_svc->svc_ping_frame_time); ++ timesync_svc->capture_ping = false; ++ if (ret) { ++ dev_err(&svc->dev, ++ "gb_svc_timesync_ping %d\n", ret); ++ gb_timesync_teardown(timesync_svc); ++ return; ++ } ++ ++ /* Get the ping FrameTime from each APB/GPB */ ++ hd = timesync_svc->timesync_hd->hd; ++ timesync_svc->timesync_hd->ping_frame_time = 0; ++ ret = hd->driver->timesync_get_last_event(hd, ++ ×ync_svc->timesync_hd->ping_frame_time); ++ if (ret) ++ dev_err(&hd->dev, "host timesync_get_last_event %d\n", ret); ++ ++ list_for_each_entry(timesync_interface, ++ ×ync_svc->interface_list, list) { ++ control = timesync_interface->interface->control; ++ timesync_interface->ping_frame_time = 0; ++ ping_frame_time = ×ync_interface->ping_frame_time; ++ ret = gb_control_timesync_get_last_event(control, ++ ping_frame_time); ++ if (ret) { ++ dev_err(×ync_interface->interface->dev, ++ "gb_control_timesync_get_last_event %d\n", ret); ++ } ++ } ++ ++ /* Ping success - move to timesync active */ ++ gb_svc_timesync_wake_pins_release(svc); ++ gb_timesync_platform_unlock_bus(); ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_ACTIVE); ++} ++ ++static void gb_timesync_log_ping_time(struct gb_timesync_svc *timesync_svc) ++{ ++ char *buf; ++ ++ if (!timesync_svc->print_ping) ++ return; ++ ++ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ if (buf) { ++ gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); ++ dev_dbg(×ync_svc->svc->dev, "%s", buf); ++ kfree(buf); ++ } ++} ++ ++/* ++ * Perform the actual work of scheduled TimeSync logic. ++ */ ++static void gb_timesync_worker(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = to_delayed_work(work); ++ struct gb_timesync_svc *timesync_svc = ++ container_of(delayed_work, struct gb_timesync_svc, delayed_work); ++ ++ mutex_lock(×ync_svc->mutex); ++ ++ switch (timesync_svc->state) { ++ case GB_TIMESYNC_STATE_INIT: ++ gb_timesync_enable(timesync_svc); ++ break; ++ ++ case GB_TIMESYNC_STATE_WAIT_SVC: ++ dev_err(×ync_svc->svc->dev, ++ "timeout SVC strobe completion %d/%d\n", ++ timesync_svc->strobe, GB_TIMESYNC_MAX_STROBES); ++ gb_timesync_teardown(timesync_svc); ++ break; ++ ++ case GB_TIMESYNC_STATE_AUTHORITATIVE: ++ gb_timesync_authoritative(timesync_svc); ++ break; ++ ++ case GB_TIMESYNC_STATE_PING: ++ gb_timesync_ping(timesync_svc); ++ gb_timesync_log_ping_time(timesync_svc); ++ break; ++ ++ default: ++ pr_err("Invalid state %d for delayed work\n", ++ timesync_svc->state); ++ break; ++ } ++ ++ mutex_unlock(×ync_svc->mutex); ++} ++ ++/* ++ * Schedule a new TimeSync INIT or PING operation serialized w/r to ++ * gb_timesync_worker(). ++ */ ++static int gb_timesync_schedule(struct gb_timesync_svc *timesync_svc, int state) ++{ ++ int ret = 0; ++ ++ if (state != GB_TIMESYNC_STATE_INIT && state != GB_TIMESYNC_STATE_PING) ++ return -EINVAL; ++ ++ mutex_lock(×ync_svc->mutex); ++ if (timesync_svc->state != GB_TIMESYNC_STATE_INVALID) { ++ gb_timesync_set_state_atomic(timesync_svc, state); ++ } else { ++ ret = -ENODEV; ++ } ++ mutex_unlock(×ync_svc->mutex); ++ return ret; ++} ++ ++static int __gb_timesync_schedule_synchronous( ++ struct gb_timesync_svc *timesync_svc, int state) ++{ ++ unsigned long flags; ++ int ret; ++ ++ ret = gb_timesync_schedule(timesync_svc, state); ++ if (ret) ++ return ret; ++ ++ ret = wait_event_interruptible(timesync_svc->wait_queue, ++ (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE || ++ timesync_svc->state == GB_TIMESYNC_STATE_INACTIVE || ++ timesync_svc->state == GB_TIMESYNC_STATE_INVALID)); ++ if (ret) ++ return ret; ++ ++ mutex_lock(×ync_svc->mutex); ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ ++ ret = __gb_timesync_get_status(timesync_svc); ++ ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++ mutex_unlock(×ync_svc->mutex); ++ ++ return ret; ++} ++ ++static struct gb_timesync_svc *gb_timesync_find_timesync_svc( ++ struct gb_host_device *hd) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ ++ list_for_each_entry(timesync_svc, &gb_timesync_svc_list, list) { ++ if (timesync_svc->svc == hd->svc) ++ return timesync_svc; ++ } ++ return NULL; ++} ++ ++static struct gb_timesync_interface *gb_timesync_find_timesync_interface( ++ struct gb_timesync_svc *timesync_svc, ++ struct gb_interface *interface) ++{ ++ struct gb_timesync_interface *timesync_interface; ++ ++ list_for_each_entry(timesync_interface, ×ync_svc->interface_list, list) { ++ if (timesync_interface->interface == interface) ++ return timesync_interface; ++ } ++ return NULL; ++} ++ ++int gb_timesync_schedule_synchronous(struct gb_interface *interface) ++{ ++ int ret; ++ struct gb_timesync_svc *timesync_svc; ++ int retries; ++ ++ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) ++ return 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ for (retries = 0; retries < GB_TIMESYNC_MAX_RETRIES; retries++) { ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ ret = __gb_timesync_schedule_synchronous(timesync_svc, ++ GB_TIMESYNC_STATE_INIT); ++ if (!ret) ++ break; ++ } ++ if (ret && retries == GB_TIMESYNC_MAX_RETRIES) ++ ret = -ETIMEDOUT; ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_schedule_synchronous); ++ ++void gb_timesync_schedule_asynchronous(struct gb_interface *interface) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ ++ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) ++ return; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) ++ goto done; ++ ++ gb_timesync_schedule(timesync_svc, GB_TIMESYNC_STATE_INIT); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_schedule_asynchronous); ++ ++static ssize_t gb_timesync_ping_read(struct file *file, char __user *ubuf, ++ size_t len, loff_t *offset, bool ktime) ++{ ++ struct gb_timesync_svc *timesync_svc = file->f_inode->i_private; ++ char *buf; ++ ssize_t ret = 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ mutex_lock(×ync_svc->mutex); ++ if (list_empty(×ync_svc->interface_list)) ++ ret = -ENODEV; ++ timesync_svc->print_ping = false; ++ mutex_unlock(×ync_svc->mutex); ++ if (ret) ++ goto done; ++ ++ ret = __gb_timesync_schedule_synchronous(timesync_svc, ++ GB_TIMESYNC_STATE_PING); ++ if (ret) ++ goto done; ++ ++ buf = kzalloc(PAGE_SIZE, GFP_KERNEL); ++ if (!buf) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ if (ktime) ++ ret = gb_timesync_log_frame_ktime(timesync_svc, buf, PAGE_SIZE); ++ else ++ ret = gb_timesync_log_frame_time(timesync_svc, buf, PAGE_SIZE); ++ if (ret > 0) ++ ret = simple_read_from_buffer(ubuf, len, offset, buf, ret); ++ kfree(buf); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++ ++static ssize_t gb_timesync_ping_read_frame_time(struct file *file, ++ char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ return gb_timesync_ping_read(file, buf, len, offset, false); ++} ++ ++static ssize_t gb_timesync_ping_read_frame_ktime(struct file *file, ++ char __user *buf, ++ size_t len, loff_t *offset) ++{ ++ return gb_timesync_ping_read(file, buf, len, offset, true); ++} ++ ++static const struct file_operations gb_timesync_debugfs_frame_time_ops = { ++ .read = gb_timesync_ping_read_frame_time, ++}; ++ ++static const struct file_operations gb_timesync_debugfs_frame_ktime_ops = { ++ .read = gb_timesync_ping_read_frame_ktime, ++}; ++ ++static int gb_timesync_hd_add(struct gb_timesync_svc *timesync_svc, ++ struct gb_host_device *hd) ++{ ++ struct gb_timesync_host_device *timesync_hd; ++ ++ timesync_hd = kzalloc(sizeof(*timesync_hd), GFP_KERNEL); ++ if (!timesync_hd) ++ return -ENOMEM; ++ ++ WARN_ON(timesync_svc->timesync_hd); ++ timesync_hd->hd = hd; ++ timesync_svc->timesync_hd = timesync_hd; ++ ++ return 0; ++} ++ ++static void gb_timesync_hd_remove(struct gb_timesync_svc *timesync_svc, ++ struct gb_host_device *hd) ++{ ++ if (timesync_svc->timesync_hd->hd == hd) { ++ kfree(timesync_svc->timesync_hd); ++ timesync_svc->timesync_hd = NULL; ++ return; ++ } ++ WARN_ON(1); ++} ++ ++int gb_timesync_svc_add(struct gb_svc *svc) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ int ret; ++ ++ timesync_svc = kzalloc(sizeof(*timesync_svc), GFP_KERNEL); ++ if (!timesync_svc) ++ return -ENOMEM; ++ ++ timesync_svc->work_queue = ++ create_singlethread_workqueue("gb-timesync-work_queue"); ++ ++ if (!timesync_svc->work_queue) { ++ kfree(timesync_svc); ++ return -ENOMEM; ++ } ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ INIT_LIST_HEAD(×ync_svc->interface_list); ++ INIT_DELAYED_WORK(×ync_svc->delayed_work, gb_timesync_worker); ++ mutex_init(×ync_svc->mutex); ++ spin_lock_init(×ync_svc->spinlock); ++ init_waitqueue_head(×ync_svc->wait_queue); ++ ++ timesync_svc->svc = svc; ++ timesync_svc->frame_time_offset = 0; ++ timesync_svc->capture_ping = false; ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INACTIVE); ++ ++ timesync_svc->frame_time_dentry = ++ debugfs_create_file("frame-time", S_IRUGO, svc->debugfs_dentry, ++ timesync_svc, ++ &gb_timesync_debugfs_frame_time_ops); ++ timesync_svc->frame_ktime_dentry = ++ debugfs_create_file("frame-ktime", S_IRUGO, svc->debugfs_dentry, ++ timesync_svc, ++ &gb_timesync_debugfs_frame_ktime_ops); ++ ++ list_add(×ync_svc->list, &gb_timesync_svc_list); ++ ret = gb_timesync_hd_add(timesync_svc, svc->hd); ++ if (ret) { ++ list_del(×ync_svc->list); ++ debugfs_remove(timesync_svc->frame_ktime_dentry); ++ debugfs_remove(timesync_svc->frame_time_dentry); ++ destroy_workqueue(timesync_svc->work_queue); ++ kfree(timesync_svc); ++ goto done; ++ } ++ ++ init_timer(×ync_svc->ktime_timer); ++ timesync_svc->ktime_timer.function = gb_timesync_ktime_timer_fn; ++ timesync_svc->ktime_timer.expires = jiffies + GB_TIMESYNC_KTIME_UPDATE; ++ timesync_svc->ktime_timer.data = (unsigned long)timesync_svc; ++ add_timer(×ync_svc->ktime_timer); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_svc_add); ++ ++void gb_timesync_svc_remove(struct gb_svc *svc) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ struct gb_timesync_interface *timesync_interface; ++ struct gb_timesync_interface *next; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(svc->hd); ++ if (!timesync_svc) ++ goto done; ++ ++ cancel_delayed_work_sync(×ync_svc->delayed_work); ++ ++ mutex_lock(×ync_svc->mutex); ++ ++ gb_timesync_set_state_atomic(timesync_svc, GB_TIMESYNC_STATE_INVALID); ++ del_timer_sync(×ync_svc->ktime_timer); ++ gb_timesync_teardown(timesync_svc); ++ ++ gb_timesync_hd_remove(timesync_svc, svc->hd); ++ list_for_each_entry_safe(timesync_interface, next, ++ ×ync_svc->interface_list, list) { ++ list_del(×ync_interface->list); ++ kfree(timesync_interface); ++ } ++ debugfs_remove(timesync_svc->frame_ktime_dentry); ++ debugfs_remove(timesync_svc->frame_time_dentry); ++ destroy_workqueue(timesync_svc->work_queue); ++ list_del(×ync_svc->list); ++ ++ mutex_unlock(×ync_svc->mutex); ++ ++ kfree(timesync_svc); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++} ++EXPORT_SYMBOL_GPL(gb_timesync_svc_remove); ++ ++/* ++ * Add a Greybus Interface to the set of TimeSync Interfaces. ++ */ ++int gb_timesync_interface_add(struct gb_interface *interface) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ struct gb_timesync_interface *timesync_interface; ++ int ret = 0; ++ ++ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) ++ return 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ timesync_interface = kzalloc(sizeof(*timesync_interface), GFP_KERNEL); ++ if (!timesync_interface) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ mutex_lock(×ync_svc->mutex); ++ timesync_interface->interface = interface; ++ list_add(×ync_interface->list, ×ync_svc->interface_list); ++ timesync_svc->strobe_mask |= 1 << interface->interface_id; ++ mutex_unlock(×ync_svc->mutex); ++ ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_interface_add); ++ ++/* ++ * Remove a Greybus Interface from the set of TimeSync Interfaces. ++ */ ++void gb_timesync_interface_remove(struct gb_interface *interface) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ struct gb_timesync_interface *timesync_interface; ++ ++ if (!(interface->features & GREYBUS_INTERFACE_FEATURE_TIMESYNC)) ++ return; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) ++ goto done; ++ ++ timesync_interface = gb_timesync_find_timesync_interface(timesync_svc, ++ interface); ++ if (!timesync_interface) ++ goto done; ++ ++ mutex_lock(×ync_svc->mutex); ++ timesync_svc->strobe_mask &= ~(1 << interface->interface_id); ++ list_del(×ync_interface->list); ++ kfree(timesync_interface); ++ mutex_unlock(×ync_svc->mutex); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++} ++EXPORT_SYMBOL_GPL(gb_timesync_interface_remove); ++ ++/* ++ * Give the authoritative FrameTime to the calling function. Returns zero if we ++ * are not in GB_TIMESYNC_STATE_ACTIVE. ++ */ ++static u64 gb_timesync_get_frame_time(struct gb_timesync_svc *timesync_svc) ++{ ++ unsigned long flags; ++ u64 ret; ++ ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ if (timesync_svc->state == GB_TIMESYNC_STATE_ACTIVE) ++ ret = __gb_timesync_get_frame_time(timesync_svc); ++ else ++ ret = 0; ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++ return ret; ++} ++ ++u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ u64 ret = 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) ++ goto done; ++ ++ ret = gb_timesync_get_frame_time(timesync_svc); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_interface); ++ ++u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ u64 ret = 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(svc->hd); ++ if (!timesync_svc) ++ goto done; ++ ++ ret = gb_timesync_get_frame_time(timesync_svc); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_get_frame_time_by_svc); ++ ++/* Incrementally updates the conversion base from FrameTime to ktime */ ++static void gb_timesync_ktime_timer_fn(unsigned long data) ++{ ++ struct gb_timesync_svc *timesync_svc = ++ (struct gb_timesync_svc *)data; ++ unsigned long flags; ++ u64 frame_time; ++ struct timespec ts; ++ ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ ++ if (timesync_svc->state != GB_TIMESYNC_STATE_ACTIVE) ++ goto done; ++ ++ ktime_get_ts(&ts); ++ frame_time = __gb_timesync_get_frame_time(timesync_svc); ++ gb_timesync_store_ktime(timesync_svc, ts, frame_time); ++ ++done: ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++ mod_timer(×ync_svc->ktime_timer, ++ jiffies + GB_TIMESYNC_KTIME_UPDATE); ++} ++ ++int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time, ++ struct timespec *ts) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ int ret = 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(svc->hd); ++ if (!timesync_svc) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_svc); ++ ++int gb_timesync_to_timespec_by_interface(struct gb_interface *interface, ++ u64 frame_time, struct timespec *ts) ++{ ++ struct gb_timesync_svc *timesync_svc; ++ int ret = 0; ++ ++ mutex_lock(&gb_timesync_svc_list_mutex); ++ timesync_svc = gb_timesync_find_timesync_svc(interface->hd); ++ if (!timesync_svc) { ++ ret = -ENODEV; ++ goto done; ++ } ++ ++ ret = gb_timesync_to_timespec(timesync_svc, frame_time, ts); ++done: ++ mutex_unlock(&gb_timesync_svc_list_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(gb_timesync_to_timespec_by_interface); ++ ++void gb_timesync_irq(struct gb_timesync_svc *timesync_svc) ++{ ++ unsigned long flags; ++ u64 strobe_time; ++ bool strobe_is_ping = true; ++ struct timespec ts; ++ ++ ktime_get_ts(&ts); ++ strobe_time = __gb_timesync_get_frame_time(timesync_svc); ++ ++ spin_lock_irqsave(×ync_svc->spinlock, flags); ++ ++ if (timesync_svc->state == GB_TIMESYNC_STATE_PING) { ++ if (!timesync_svc->capture_ping) ++ goto done_nolog; ++ timesync_svc->ap_ping_frame_time = strobe_time; ++ goto done_log; ++ } else if (timesync_svc->state != GB_TIMESYNC_STATE_WAIT_SVC) { ++ goto done_nolog; ++ } ++ ++ timesync_svc->strobe_data[timesync_svc->strobe].frame_time = strobe_time; ++ timesync_svc->strobe_data[timesync_svc->strobe].ts = ts; ++ ++ if (++timesync_svc->strobe == GB_TIMESYNC_MAX_STROBES) { ++ gb_timesync_set_state(timesync_svc, ++ GB_TIMESYNC_STATE_AUTHORITATIVE); ++ } ++ strobe_is_ping = false; ++done_log: ++ trace_gb_timesync_irq(strobe_is_ping, timesync_svc->strobe, ++ GB_TIMESYNC_MAX_STROBES, strobe_time); ++done_nolog: ++ spin_unlock_irqrestore(×ync_svc->spinlock, flags); ++} ++EXPORT_SYMBOL(gb_timesync_irq); ++ ++int __init gb_timesync_init(void) ++{ ++ int ret = 0; ++ ++ ret = gb_timesync_platform_init(); ++ if (ret) { ++ pr_err("timesync platform init fail!\n"); ++ return ret; ++ } ++ ++ gb_timesync_clock_rate = gb_timesync_platform_get_clock_rate(); ++ ++ /* Calculate nanoseconds and femtoseconds per clock */ ++ gb_timesync_fs_per_clock = FSEC_PER_SEC; ++ do_div(gb_timesync_fs_per_clock, gb_timesync_clock_rate); ++ gb_timesync_ns_per_clock = NSEC_PER_SEC; ++ do_div(gb_timesync_ns_per_clock, gb_timesync_clock_rate); ++ ++ /* Calculate the maximum number of clocks we will convert to ktime */ ++ gb_timesync_max_ktime_diff = ++ GB_TIMESYNC_MAX_KTIME_CONVERSION * gb_timesync_clock_rate; ++ ++ pr_info("Time-Sync @ %lu Hz max ktime conversion +/- %d seconds\n", ++ gb_timesync_clock_rate, GB_TIMESYNC_MAX_KTIME_CONVERSION); ++ return 0; ++} ++ ++void gb_timesync_exit(void) ++{ ++ gb_timesync_platform_exit(); ++} +--- /dev/null ++++ b/drivers/greybus/timesync.h +@@ -0,0 +1,45 @@ ++/* ++ * TimeSync API driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#ifndef __TIMESYNC_H ++#define __TIMESYNC_H ++ ++struct gb_svc; ++struct gb_interface; ++struct gb_timesync_svc; ++ ++/* Platform */ ++u64 gb_timesync_platform_get_counter(void); ++u32 gb_timesync_platform_get_clock_rate(void); ++int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata); ++void gb_timesync_platform_unlock_bus(void); ++ ++int gb_timesync_platform_init(void); ++void gb_timesync_platform_exit(void); ++ ++/* Core API */ ++int gb_timesync_interface_add(struct gb_interface *interface); ++void gb_timesync_interface_remove(struct gb_interface *interface); ++int gb_timesync_svc_add(struct gb_svc *svc); ++void gb_timesync_svc_remove(struct gb_svc *svc); ++ ++u64 gb_timesync_get_frame_time_by_interface(struct gb_interface *interface); ++u64 gb_timesync_get_frame_time_by_svc(struct gb_svc *svc); ++int gb_timesync_to_timespec_by_svc(struct gb_svc *svc, u64 frame_time, ++ struct timespec *ts); ++int gb_timesync_to_timespec_by_interface(struct gb_interface *interface, ++ u64 frame_time, struct timespec *ts); ++ ++int gb_timesync_schedule_synchronous(struct gb_interface *intf); ++void gb_timesync_schedule_asynchronous(struct gb_interface *intf); ++void gb_timesync_irq(struct gb_timesync_svc *timesync_svc); ++int gb_timesync_init(void); ++void gb_timesync_exit(void); ++ ++#endif /* __TIMESYNC_H */ +--- /dev/null ++++ b/drivers/greybus/timesync_platform.c +@@ -0,0 +1,77 @@ ++/* ++ * TimeSync API driver. ++ * ++ * Copyright 2016 Google Inc. ++ * Copyright 2016 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ * ++ * This code reads directly from an ARMv7 memory-mapped timer that lives in ++ * MMIO space. Since this counter lives inside of MMIO space its shared between ++ * cores and that means we don't have to worry about issues like TSC on x86 ++ * where each time-stamp-counter (TSC) is local to a particular core. ++ * ++ * Register-level access code is based on ++ * drivers/clocksource/arm_arch_timer.c ++ */ ++#include <linux/cpufreq.h> ++#include <linux/of_platform.h> ++ ++#include "greybus.h" ++#include "arche_platform.h" ++ ++static u32 gb_timesync_clock_frequency; ++int (*arche_platform_change_state_cb)(enum arche_platform_state state, ++ struct gb_timesync_svc *pdata); ++EXPORT_SYMBOL_GPL(arche_platform_change_state_cb); ++ ++u64 gb_timesync_platform_get_counter(void) ++{ ++ return (u64)get_cycles(); ++} ++ ++u32 gb_timesync_platform_get_clock_rate(void) ++{ ++ if (unlikely(!gb_timesync_clock_frequency)) ++ return cpufreq_get(0); ++ ++ return gb_timesync_clock_frequency; ++} ++ ++int gb_timesync_platform_lock_bus(struct gb_timesync_svc *pdata) ++{ ++ return arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_TIME_SYNC, ++ pdata); ++} ++ ++void gb_timesync_platform_unlock_bus(void) ++{ ++ arche_platform_change_state_cb(ARCHE_PLATFORM_STATE_ACTIVE, NULL); ++} ++ ++static const struct of_device_id arch_timer_of_match[] = { ++ { .compatible = "google,greybus-frame-time-counter", }, ++ {}, ++}; ++ ++int __init gb_timesync_platform_init(void) ++{ ++ struct device_node *np; ++ ++ np = of_find_matching_node(NULL, arch_timer_of_match); ++ if (!np) { ++ /* Tolerate not finding to allow BBB etc to continue */ ++ pr_warn("Unable to find a compatible ARMv7 timer\n"); ++ return 0; ++ } ++ ++ if (of_property_read_u32(np, "clock-frequency", ++ &gb_timesync_clock_frequency)) { ++ pr_err("Unable to find timer clock-frequency\n"); ++ return -ENODEV; ++ } ++ ++ return 0; ++} ++ ++void gb_timesync_platform_exit(void) {} diff --git a/greybus_tools.patch b/greybus_tools.patch new file mode 100644 index 00000000000000..e24e3689a99373 --- /dev/null +++ b/greybus_tools.patch @@ -0,0 +1,1435 @@ +--- + drivers/greybus/tools/.gitignore | 1 + drivers/greybus/tools/Android.mk | 10 + drivers/greybus/tools/Makefile | 31 + + drivers/greybus/tools/README.loopback | 198 ++++++ + drivers/greybus/tools/lbtest | 168 +++++ + drivers/greybus/tools/loopback_test.c | 1000 ++++++++++++++++++++++++++++++++++ + 6 files changed, 1408 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/tools/.gitignore +@@ -0,0 +1 @@ ++loopback_test +--- /dev/null ++++ b/drivers/greybus/tools/Android.mk +@@ -0,0 +1,10 @@ ++LOCAL_PATH:= $(call my-dir) ++ ++include $(CLEAR_VARS) ++ ++LOCAL_SRC_FILES:= loopback_test.c ++LOCAL_MODULE_TAGS := optional ++LOCAL_MODULE := gb_loopback_test ++ ++include $(BUILD_EXECUTABLE) ++ +--- /dev/null ++++ b/drivers/greybus/tools/Makefile +@@ -0,0 +1,31 @@ ++ifeq ($(strip $(V)), 1) ++ Q = ++else ++ Q = @ ++endif ++ ++CFLAGS += -std=gnu99 -Wall -Wextra -g \ ++ -D_GNU_SOURCE \ ++ -Wno-unused-parameter \ ++ -Wmaybe-uninitialized \ ++ -Wredundant-decls \ ++ -Wcast-align \ ++ -Wsign-compare \ ++ -Wno-missing-field-initializers ++ ++CC := $(CROSS_COMPILE)gcc ++ ++TOOLS = loopback_test ++ ++all: $(TOOLS) ++ ++%.o: %.c ../greybus_protocols.h ++ @echo ' TARGET_CC $@' ++ $(Q)$(CC) $(CFLAGS) -c $< -o $@ ++ ++loopback_%: loopback_%.o ++ @echo ' TARGET_LD $@' ++ $(Q)$(CC) $(CFLAGS) $(LDFLAGS) $^ -o $@ ++ ++clean:: ++ rm -f *.o $(TOOLS) +--- /dev/null ++++ b/drivers/greybus/tools/README.loopback +@@ -0,0 +1,198 @@ ++ ++ ++ 1 - LOOPBACK DRIVER ++ ++The driver implements the main logic of the loopback test and provides ++sysfs files to configure the test and retrieve the results. ++A user could run a test without the need of the test application given ++that he understands the sysfs interface of the loopback driver. ++ ++The loopback kernel driver needs to be loaded and at least one module ++with the loopback feature enabled must be present for the sysfs files to be ++created and for the loopback test application to be able to run. ++ ++To load the module: ++# modprobe gb-loopback ++ ++ ++When the module is probed, New files are available on the sysfs ++directory of the detected loopback device. ++(typically under "/sys/bus/graybus/devices"). ++ ++Here is a short summary of the sysfs interface files that should be visible: ++ ++* Loopback Configuration Files: ++ async - Use asynchronous operations. ++ iteration_max - Number of tests iterations to perform. ++ size - payload size of the transfer. ++ timeout - The number of microseconds to give an individual ++ asynchronous request before timing out. ++ us_wait - Time to wait between 2 messages ++ type - By writing the test type to this file, the test starts. ++ Valid tests are: ++ 0 stop the test ++ 2 - ping ++ 3 - transfer ++ 4 - sink ++ ++* Loopback feedback files: ++ error - number of errors that have occurred. ++ iteration_count - Number of iterations performed. ++ requests_completed - Number of requests successfully completed. ++ requests_timedout - Number of requests that have timed out. ++ timeout_max - Max allowed timeout ++ timeout_min - Min allowed timeout. ++ ++* Loopback result files: ++ apbridge_unipro_latency_avg ++ apbridge_unipro_latency_max ++ apbridge_unipro_latency_min ++ gpbridge_firmware_latency_avg ++ gpbridge_firmware_latency_max ++ gpbridge_firmware_latency_min ++ requests_per_second_avg ++ requests_per_second_max ++ requests_per_second_min ++ latency_avg ++ latency_max ++ latency_min ++ throughput_avg ++ throughput_max ++ throughput_min ++ ++ ++ ++ 2 - LOOPBACK TEST APPLICATION ++ ++The loopback test application manages and formats the results provided by ++the loopback kernel module. The purpose of this application ++is to: ++ - Start and manage multiple loopback device tests concurrently. ++ - Calculate the aggregate results for multiple devices. ++ - Gather and format test results (csv or human readable). ++ ++The best way to get up to date usage information for the application is ++usually to pass the "-h" parameter. ++Here is the summary of the available options: ++ ++ Mandatory arguments ++ -t must be one of the test names - sink, transfer or ping ++ -i iteration count - the number of iterations to run the test over ++ Optional arguments ++ -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/ ++ -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/ ++ -s size of data packet to send during test - defaults to zero ++ -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc ++ default is zero which means broadcast to all connections ++ -v verbose output ++ -d debug output ++ -r raw data output - when specified the full list of latency values are included in the output CSV ++ -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file ++ -a aggregate - show aggregation of all enabled devies ++ -l list found loopback devices and exit. ++ -x Async - Enable async transfers. ++ -o Timeout - Timeout in microseconds for async operations. ++ ++ ++ ++ 3 - REAL WORLD EXAMPLE USAGES ++ ++ 3.1 - Using the driver sysfs files to run a test on a single device: ++ ++* Run a 1000 transfers of a 100 byte packet. Each transfer is started only ++after the previous one finished successfully: ++ echo 0 > /sys/bus/greybus/devices/1-2.17/type ++ echo 0 > /sys/bus/greybus/devices/1-2.17/async ++ echo 2000 > /sys/bus/greybus/devices/1-2.17/us_wait ++ echo 100 > /sys/bus/greybus/devices/1-2.17/size ++ echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max ++ echo 0 > /sys/bus/greybus/devices/1-2.17/mask ++ echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout ++ echo 3 > /sys/bus/greybus/devices/1-2.17/type ++ ++* Run a 1000 transfers of a 100 byte packet. Transfers are started without ++waiting for the previous one to finish: ++ echo 0 > /sys/bus/greybus/devices/1-2.17/type ++ echo 3 > /sys/bus/greybus/devices/1-2.17/async ++ echo 0 > /sys/bus/greybus/devices/1-2.17/us_wait ++ echo 100 > /sys/bus/greybus/devices/1-2.17/size ++ echo 1000 > /sys/bus/greybus/devices/1-2.17/iteration_max ++ echo 0 > /sys/bus/greybus/devices/1-2.17/mask ++ echo 200000 > /sys/bus/greybus/devices/1-2.17/timeout ++ echo 3 > /sys/bus/greybus/devices/1-2.17/type ++ ++* Read the results from sysfs: ++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_min ++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_max ++ cat /sys/bus/greybus/devices/1-2.17/requests_per_second_avg ++ ++ cat /sys/bus/greybus/devices/1-2.17/latency_min ++ cat /sys/bus/greybus/devices/1-2.17/latency_max ++ cat /sys/bus/greybus/devices/1-2.17/latency_avg ++ ++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_min ++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_max ++ cat /sys/bus/greybus/devices/1-2.17/apbridge_unipro_latency_avg ++ ++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_min ++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_max ++ cat /sys/bus/greybus/devices/1-2.17/gpbridge_firmware_latency_avg ++ ++ cat /sys/bus/greybus/devices/1-2.17/error ++ cat /sys/bus/greybus/devices/1-2.17/requests_completed ++ cat /sys/bus/greybus/devices/1-2.17/requests_timedout ++ ++ ++3.2 - using the test application: ++ ++* Run a transfer test 10 iterations of size 100 bytes on all available devices ++ #/loopback_test -t transfer -i 10 -s 100 ++ 1970-1-1 0:10:7,transfer,1-4.17,100,10,0,443,509,471.700012,66,1963,2256,2124.600098,293,102776,118088,109318.898438,15312,1620,1998,1894.099976,378,56,57,56.799999,1 ++ 1970-1-1 0:10:7,transfer,1-5.17,100,10,0,399,542,463.399994,143,1845,2505,2175.800049,660,92568,125744,107393.296875,33176,1469,2305,1806.500000,836,56,57,56.799999,1 ++ ++ ++* Show the aggregate results of both devices. ("-a") ++ #/loopback_test -t transfer -i 10 -s 100 -a ++ 1970-1-1 0:10:35,transfer,1-4.17,100,10,0,448,580,494.100006,132,1722,2230,2039.400024,508,103936,134560,114515.703125,30624,1513,1980,1806.900024,467,56,57,57.299999,1 ++ 1970-1-1 0:10:35,transfer,1-5.17,100,10,0,383,558,478.600006,175,1791,2606,2115.199951,815,88856,129456,110919.703125,40600,1457,2246,1773.599976,789,56,57,57.099998,1 ++ 1970-1-1 0:10:35,transfer,aggregate,100,10,0,383,580,486.000000,197,1722,2606,2077.000000,884,88856,134560,112717.000000,45704,1457,2246,1789.000000,789,56,57,57.000000,1 ++ ++* Example usage of the mask option to select which devices will ++ run the test (1st, 2nd, or both devices): ++ # /loopback_test -t transfer -i 10 -s 100 -m 1 ++ 1970-1-1 0:11:56,transfer,1-4.17,100,10,0,514,558,544.900024,44,1791,1943,1836.599976,152,119248,129456,126301.296875,10208,1600,1001609,101613.601562,1000009,56,57,56.900002,1 ++ # /loopback_test -t transfer -i 10 -s 100 -m 2 ++ 1970-1-1 0:12:0,transfer,1-5.17,100,10,0,468,554,539.000000,86,1804,2134,1859.500000,330,108576,128528,124932.500000,19952,1606,1626,1619.300049,20,56,57,57.400002,1 ++ # /loopback_test -t transfer -i 10 -s 100 -m 3 ++ 1970-1-1 0:12:3,transfer,1-4.17,100,10,0,432,510,469.399994,78,1959,2313,2135.800049,354,100224,118320,108785.296875,18096,1610,2024,1893.500000,414,56,57,57.200001,1 ++ 1970-1-1 0:12:3,transfer,1-5.17,100,10,0,404,542,468.799988,138,1843,2472,2152.500000,629,93728,125744,108646.101562,32016,1504,2247,1853.099976,743,56,57,57.099998,1 ++ ++* Show output in human readable format ("-p") ++ # /loopback_test -t transfer -i 10 -s 100 -m 3 -p ++ ++ 1970-1-1 0:12:37 ++ test: transfer ++ path: 1-4.17 ++ size: 100 ++ iterations: 10 ++ errors: 0 ++ async: Disabled ++ requests per-sec: min=390, max=547, average=469.299988, jitter=157 ++ ap-throughput B/s: min=90480 max=126904 average=108762.101562 jitter=36424 ++ ap-latency usec: min=1826 max=2560 average=2146.000000 jitter=734 ++ apbridge-latency usec: min=1620 max=1982 average=1882.099976 jitter=362 ++ gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1 ++ ++ ++ 1970-1-1 0:12:37 ++ test: transfer ++ path: 1-5.17 ++ size: 100 ++ iterations: 10 ++ errors: 0 ++ async: Disabled ++ requests per-sec: min=397, max=538, average=461.700012, jitter=141 ++ ap-throughput B/s: min=92104 max=124816 average=106998.898438 jitter=32712 ++ ap-latency usec: min=1856 max=2514 average=2185.699951 jitter=658 ++ apbridge-latency usec: min=1460 max=2296 average=1828.599976 jitter=836 ++ gpbridge-latency usec: min=56 max=57 average=57.099998 jitter=1 +--- /dev/null ++++ b/drivers/greybus/tools/lbtest +@@ -0,0 +1,168 @@ ++#!/usr/bin/env python ++ ++# Copyright (c) 2015 Google, Inc. ++# Copyright (c) 2015 Linaro, Ltd. ++# All rights reserved. ++# ++# Redistribution and use in source and binary forms, with or without ++# modification, are permitted provided that the following conditions are met: ++# 1. Redistributions of source code must retain the above copyright notice, ++# this list of conditions and the following disclaimer. ++# 2. Redistributions in binary form must reproduce the above copyright notice, ++# this list of conditions and the following disclaimer in the documentation ++# and/or other materials provided with the distribution. ++# 3. Neither the name of the copyright holder nor the names of its ++# contributors may be used to endorse or promote products derived from this ++# software without specific prior written permission. ++# ++# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" ++# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR ++# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; ++# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, ++# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR ++# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ++# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++from __future__ import print_function ++import csv ++import datetime ++import sys ++import time ++ ++dict = {'ping': '2', 'transfer': '3', 'sink': '4'} ++verbose = 1 ++ ++def abort(): ++ sys.exit(1) ++ ++def usage(): ++ print('Usage: looptest TEST SIZE ITERATIONS PATH\n\n' ++ ' Run TEST for a number of ITERATIONS with operation data SIZE bytes\n' ++ ' TEST may be \'ping\' \'transfer\' or \'sink\'\n' ++ ' SIZE indicates the size of transfer <= greybus max payload bytes\n' ++ ' ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n' ++ ' Note if ITERATIONS is set to zero then this utility will\n' ++ ' initiate an infinite (non terminating) test and exit\n' ++ ' without logging any metrics data\n' ++ ' PATH indicates the sysfs path for the loopback greybus entries e.g.\n' ++ ' /sys/bus/greybus/devices/endo0:1:1:1:1/\n' ++ 'Examples:\n' ++ ' looptest transfer 128 10000\n' ++ ' looptest ping 0 128\n' ++ ' looptest sink 2030 32768\n' ++ .format(sys.argv[0]), file=sys.stderr) ++ ++ abort() ++ ++def read_sysfs_int(path): ++ try: ++ f = open(path, "r"); ++ val = f.read(); ++ f.close() ++ return int(val) ++ except IOError as e: ++ print("I/O error({0}): {1}".format(e.errno, e.strerror)) ++ print("Invalid path %s" % path) ++ ++def write_sysfs_val(path, val): ++ try: ++ f = open(path, "r+") ++ f.write(val) ++ f.close() ++ except IOError as e: ++ print("I/O error({0}): {1}".format(e.errno, e.strerror)) ++ print("Invalid path %s" % path) ++ ++def log_csv(test_name, size, iteration_max, sys_pfx): ++ # file name will test_name_size_iteration_max.csv ++ # every time the same test with the same parameters is run we will then ++ # append to the same CSV with datestamp - representing each test dataset ++ fname = test_name + '_' + size + '_' + str(iteration_max) + '.csv' ++ ++ try: ++ # gather data set ++ date = str(datetime.datetime.now()) ++ error = read_sysfs_int(sys_pfx + 'error') ++ request_min = read_sysfs_int(sys_pfx + 'requests_per_second_min') ++ request_max = read_sysfs_int(sys_pfx + 'requests_per_second_max') ++ request_avg = read_sysfs_int(sys_pfx + 'requests_per_second_avg') ++ latency_min = read_sysfs_int(sys_pfx + 'latency_min') ++ latency_max = read_sysfs_int(sys_pfx + 'latency_max') ++ latency_avg = read_sysfs_int(sys_pfx + 'latency_avg') ++ throughput_min = read_sysfs_int(sys_pfx + 'throughput_min') ++ throughput_max = read_sysfs_int(sys_pfx + 'throughput_max') ++ throughput_avg = read_sysfs_int(sys_pfx + 'throughput_avg') ++ ++ # derive jitter ++ request_jitter = request_max - request_min ++ latency_jitter = latency_max - latency_min ++ throughput_jitter = throughput_max - throughput_min ++ ++ # append data set to file ++ with open(fname, 'a') as csvf: ++ row = csv.writer(csvf, delimiter=",", quotechar="'", ++ quoting=csv.QUOTE_MINIMAL) ++ row.writerow([date, test_name, size, iteration_max, error, ++ request_min, request_max, request_avg, request_jitter, ++ latency_min, latency_max, latency_avg, latency_jitter, ++ throughput_min, throughput_max, throughput_avg, throughput_jitter]) ++ except IOError as e: ++ print("I/O error({0}): {1}".format(e.errno, e.strerror)) ++ ++def loopback_run(test_name, size, iteration_max, sys_pfx): ++ test_id = dict[test_name] ++ try: ++ # Terminate any currently running test ++ write_sysfs_val(sys_pfx + 'type', '0') ++ # Set parameter for no wait between messages ++ write_sysfs_val(sys_pfx + 'ms_wait', '0') ++ # Set operation size ++ write_sysfs_val(sys_pfx + 'size', size) ++ # Set iterations ++ write_sysfs_val(sys_pfx + 'iteration_max', str(iteration_max)) ++ # Initiate by setting loopback operation type ++ write_sysfs_val(sys_pfx + 'type', test_id) ++ time.sleep(1) ++ ++ if iteration_max == 0: ++ print ("Infinite test initiated CSV won't be logged\n") ++ return ++ ++ previous = 0 ++ err = 0 ++ while True: ++ # get current count bail out if it hasn't changed ++ iteration_count = read_sysfs_int(sys_pfx + 'iteration_count') ++ if previous == iteration_count: ++ err = 1 ++ break ++ elif iteration_count == iteration_max: ++ break ++ previous = iteration_count ++ if verbose: ++ print('%02d%% complete %d of %d ' % ++ (100 * iteration_count / iteration_max, ++ iteration_count, iteration_max)) ++ time.sleep(1) ++ if err: ++ print ('\nError executing test\n') ++ else: ++ log_csv(test_name, size, iteration_max, sys_pfx) ++ except ValueError as ve: ++ print("Error: %s " % format(e.strerror), file=sys.stderr) ++ abort() ++ ++def main(): ++ if len(sys.argv) < 5: ++ usage() ++ ++ if sys.argv[1] in dict.keys(): ++ loopback_run(sys.argv[1], sys.argv[2], int(sys.argv[3]), sys.argv[4]) ++ else: ++ usage() ++if __name__ == '__main__': ++ main() +--- /dev/null ++++ b/drivers/greybus/tools/loopback_test.c +@@ -0,0 +1,1000 @@ ++/* ++ * Loopback test application ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Provided under the three clause BSD license found in the LICENSE file. ++ */ ++#include <errno.h> ++#include <fcntl.h> ++#include <stdio.h> ++#include <string.h> ++#include <stdlib.h> ++#include <stdint.h> ++#include <poll.h> ++#include <sys/types.h> ++#include <time.h> ++#include <unistd.h> ++#include <dirent.h> ++#include <signal.h> ++ ++#define MAX_NUM_DEVICES 10 ++#define MAX_SYSFS_PATH 0x200 ++#define CSV_MAX_LINE 0x1000 ++#define SYSFS_MAX_INT 0x20 ++#define MAX_STR_LEN 255 ++#define DEFAULT_ASYNC_TIMEOUT 200000 ++ ++struct dict { ++ char *name; ++ int type; ++}; ++ ++static struct dict dict[] = { ++ {"ping", 2}, ++ {"transfer", 3}, ++ {"sink", 4}, ++ {NULL,} /* list termination */ ++}; ++ ++struct loopback_results { ++ float latency_avg; ++ uint32_t latency_max; ++ uint32_t latency_min; ++ uint32_t latency_jitter; ++ ++ float request_avg; ++ uint32_t request_max; ++ uint32_t request_min; ++ uint32_t request_jitter; ++ ++ float throughput_avg; ++ uint32_t throughput_max; ++ uint32_t throughput_min; ++ uint32_t throughput_jitter; ++ ++ float apbridge_unipro_latency_avg; ++ uint32_t apbridge_unipro_latency_max; ++ uint32_t apbridge_unipro_latency_min; ++ uint32_t apbridge_unipro_latency_jitter; ++ ++ float gbphy_firmware_latency_avg; ++ uint32_t gbphy_firmware_latency_max; ++ uint32_t gbphy_firmware_latency_min; ++ uint32_t gbphy_firmware_latency_jitter; ++ ++ uint32_t error; ++}; ++ ++struct loopback_device { ++ char name[MAX_SYSFS_PATH]; ++ char sysfs_entry[MAX_SYSFS_PATH]; ++ char debugfs_entry[MAX_SYSFS_PATH]; ++ struct loopback_results results; ++}; ++ ++struct loopback_test { ++ int verbose; ++ int debug; ++ int raw_data_dump; ++ int porcelain; ++ int mask; ++ int size; ++ int iteration_max; ++ int aggregate_output; ++ int test_id; ++ int device_count; ++ int list_devices; ++ int use_async; ++ int async_timeout; ++ int async_outstanding_operations; ++ int us_wait; ++ int file_output; ++ int stop_all; ++ int poll_count; ++ char test_name[MAX_STR_LEN]; ++ char sysfs_prefix[MAX_SYSFS_PATH]; ++ char debugfs_prefix[MAX_SYSFS_PATH]; ++ struct timespec poll_timeout; ++ struct loopback_device devices[MAX_NUM_DEVICES]; ++ struct loopback_results aggregate_results; ++ struct pollfd fds[MAX_NUM_DEVICES]; ++}; ++ ++struct loopback_test t; ++ ++/* Helper macros to calculate the aggregate results for all devices */ ++static inline int device_enabled(struct loopback_test *t, int dev_idx); ++ ++#define GET_MAX(field) \ ++static int get_##field##_aggregate(struct loopback_test *t) \ ++{ \ ++ uint32_t max = 0; \ ++ int i; \ ++ for (i = 0; i < t->device_count; i++) { \ ++ if (!device_enabled(t, i)) \ ++ continue; \ ++ if (t->devices[i].results.field > max) \ ++ max = t->devices[i].results.field; \ ++ } \ ++ return max; \ ++} \ ++ ++#define GET_MIN(field) \ ++static int get_##field##_aggregate(struct loopback_test *t) \ ++{ \ ++ uint32_t min = ~0; \ ++ int i; \ ++ for (i = 0; i < t->device_count; i++) { \ ++ if (!device_enabled(t, i)) \ ++ continue; \ ++ if (t->devices[i].results.field < min) \ ++ min = t->devices[i].results.field; \ ++ } \ ++ return min; \ ++} \ ++ ++#define GET_AVG(field) \ ++static int get_##field##_aggregate(struct loopback_test *t) \ ++{ \ ++ uint32_t val = 0; \ ++ uint32_t count = 0; \ ++ int i; \ ++ for (i = 0; i < t->device_count; i++) { \ ++ if (!device_enabled(t, i)) \ ++ continue; \ ++ count++; \ ++ val += t->devices[i].results.field; \ ++ } \ ++ if (count) \ ++ val /= count; \ ++ return val; \ ++} \ ++ ++GET_MAX(throughput_max); ++GET_MAX(request_max); ++GET_MAX(latency_max); ++GET_MAX(apbridge_unipro_latency_max); ++GET_MAX(gbphy_firmware_latency_max); ++GET_MIN(throughput_min); ++GET_MIN(request_min); ++GET_MIN(latency_min); ++GET_MIN(apbridge_unipro_latency_min); ++GET_MIN(gbphy_firmware_latency_min); ++GET_AVG(throughput_avg); ++GET_AVG(request_avg); ++GET_AVG(latency_avg); ++GET_AVG(apbridge_unipro_latency_avg); ++GET_AVG(gbphy_firmware_latency_avg); ++ ++void abort() ++{ ++ _exit(1); ++} ++ ++void usage(void) ++{ ++ fprintf(stderr, "Usage: loopback_test TEST [SIZE] ITERATIONS [SYSPATH] [DBGPATH]\n\n" ++ " Run TEST for a number of ITERATIONS with operation data SIZE bytes\n" ++ " TEST may be \'ping\' \'transfer\' or \'sink\'\n" ++ " SIZE indicates the size of transfer <= greybus max payload bytes\n" ++ " ITERATIONS indicates the number of times to execute TEST at SIZE bytes\n" ++ " Note if ITERATIONS is set to zero then this utility will\n" ++ " initiate an infinite (non terminating) test and exit\n" ++ " without logging any metrics data\n" ++ " SYSPATH indicates the sysfs path for the loopback greybus entries e.g.\n" ++ " /sys/bus/greybus/devices\n" ++ " DBGPATH indicates the debugfs path for the loopback greybus entries e.g.\n" ++ " /sys/kernel/debug/gb_loopback/\n" ++ " Mandatory arguments\n" ++ " -t must be one of the test names - sink, transfer or ping\n" ++ " -i iteration count - the number of iterations to run the test over\n" ++ " Optional arguments\n" ++ " -S sysfs location - location for greybus 'endo' entires default /sys/bus/greybus/devices/\n" ++ " -D debugfs location - location for loopback debugfs entries default /sys/kernel/debug/gb_loopback/\n" ++ " -s size of data packet to send during test - defaults to zero\n" ++ " -m mask - a bit mask of connections to include example: -m 8 = 4th connection -m 9 = 1st and 4th connection etc\n" ++ " default is zero which means broadcast to all connections\n" ++ " -v verbose output\n" ++ " -d debug output\n" ++ " -r raw data output - when specified the full list of latency values are included in the output CSV\n" ++ " -p porcelain - when specified printout is in a user-friendly non-CSV format. This option suppresses writing to CSV file\n" ++ " -a aggregate - show aggregation of all enabled devices\n" ++ " -l list found loopback devices and exit\n" ++ " -x Async - Enable async transfers\n" ++ " -o Async Timeout - Timeout in uSec for async operations\n" ++ " -O Poll loop time out in seconds(max time a test is expected to last, default: 30sec)\n" ++ " -c Max number of outstanding operations for async operations\n" ++ " -w Wait in uSec between operations\n" ++ " -z Enable output to a CSV file (incompatible with -p)\n" ++ " -f When starting new loopback test, stop currently running tests on all devices\n" ++ "Examples:\n" ++ " Send 10000 transfers with a packet size of 128 bytes to all active connections\n" ++ " loopback_test -t transfer -s 128 -i 10000 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" ++ " loopback_test -t transfer -s 128 -i 10000 -m 0\n" ++ " Send 10000 transfers with a packet size of 128 bytes to connection 1 and 4\n" ++ " loopback_test -t transfer -s 128 -i 10000 -m 9\n" ++ " loopback_test -t ping -s 0 128 -i -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n" ++ " loopback_test -t sink -s 2030 -i 32768 -S /sys/bus/greybus/devices/ -D /sys/kernel/debug/gb_loopback/\n"); ++ abort(); ++} ++ ++static inline int device_enabled(struct loopback_test *t, int dev_idx) ++{ ++ if (!t->mask || (t->mask & (1 << dev_idx))) ++ return 1; ++ ++ return 0; ++} ++ ++static void show_loopback_devices(struct loopback_test *t) ++{ ++ int i; ++ ++ if (t->device_count == 0) { ++ printf("No loopback devices.\n"); ++ return; ++ } ++ ++ for (i = 0; i < t->device_count; i++) ++ printf("device[%d] = %s\n", i, t->devices[i].name); ++ ++} ++ ++int open_sysfs(const char *sys_pfx, const char *node, int flags) ++{ ++ int fd; ++ char path[MAX_SYSFS_PATH]; ++ ++ snprintf(path, sizeof(path), "%s%s", sys_pfx, node); ++ fd = open(path, flags); ++ if (fd < 0) { ++ fprintf(stderr, "unable to open %s\n", path); ++ abort(); ++ } ++ return fd; ++} ++ ++int read_sysfs_int_fd(int fd, const char *sys_pfx, const char *node) ++{ ++ char buf[SYSFS_MAX_INT]; ++ ++ if (read(fd, buf, sizeof(buf)) < 0) { ++ fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node, ++ strerror(errno)); ++ close(fd); ++ abort(); ++ } ++ return atoi(buf); ++} ++ ++float read_sysfs_float_fd(int fd, const char *sys_pfx, const char *node) ++{ ++ char buf[SYSFS_MAX_INT]; ++ ++ if (read(fd, buf, sizeof(buf)) < 0) { ++ ++ fprintf(stderr, "unable to read from %s%s %s\n", sys_pfx, node, ++ strerror(errno)); ++ close(fd); ++ abort(); ++ } ++ return atof(buf); ++} ++ ++int read_sysfs_int(const char *sys_pfx, const char *node) ++{ ++ int fd, val; ++ ++ fd = open_sysfs(sys_pfx, node, O_RDONLY); ++ val = read_sysfs_int_fd(fd, sys_pfx, node); ++ close(fd); ++ return val; ++} ++ ++float read_sysfs_float(const char *sys_pfx, const char *node) ++{ ++ int fd; ++ float val; ++ ++ fd = open_sysfs(sys_pfx, node, O_RDONLY); ++ val = read_sysfs_float_fd(fd, sys_pfx, node); ++ close(fd); ++ return val; ++} ++ ++void write_sysfs_val(const char *sys_pfx, const char *node, int val) ++{ ++ int fd, len; ++ char buf[SYSFS_MAX_INT]; ++ ++ fd = open_sysfs(sys_pfx, node, O_RDWR); ++ len = snprintf(buf, sizeof(buf), "%d", val); ++ if (write(fd, buf, len) < 0) { ++ fprintf(stderr, "unable to write to %s%s %s\n", sys_pfx, node, ++ strerror(errno)); ++ close(fd); ++ abort(); ++ } ++ close(fd); ++} ++ ++static int get_results(struct loopback_test *t) ++{ ++ struct loopback_device *d; ++ struct loopback_results *r; ++ int i; ++ ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ d = &t->devices[i]; ++ r = &d->results; ++ ++ r->error = read_sysfs_int(d->sysfs_entry, "error"); ++ r->request_min = read_sysfs_int(d->sysfs_entry, "requests_per_second_min"); ++ r->request_max = read_sysfs_int(d->sysfs_entry, "requests_per_second_max"); ++ r->request_avg = read_sysfs_float(d->sysfs_entry, "requests_per_second_avg"); ++ ++ r->latency_min = read_sysfs_int(d->sysfs_entry, "latency_min"); ++ r->latency_max = read_sysfs_int(d->sysfs_entry, "latency_max"); ++ r->latency_avg = read_sysfs_float(d->sysfs_entry, "latency_avg"); ++ ++ r->throughput_min = read_sysfs_int(d->sysfs_entry, "throughput_min"); ++ r->throughput_max = read_sysfs_int(d->sysfs_entry, "throughput_max"); ++ r->throughput_avg = read_sysfs_float(d->sysfs_entry, "throughput_avg"); ++ ++ r->apbridge_unipro_latency_min = ++ read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_min"); ++ r->apbridge_unipro_latency_max = ++ read_sysfs_int(d->sysfs_entry, "apbridge_unipro_latency_max"); ++ r->apbridge_unipro_latency_avg = ++ read_sysfs_float(d->sysfs_entry, "apbridge_unipro_latency_avg"); ++ ++ r->gbphy_firmware_latency_min = ++ read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_min"); ++ r->gbphy_firmware_latency_max = ++ read_sysfs_int(d->sysfs_entry, "gbphy_firmware_latency_max"); ++ r->gbphy_firmware_latency_avg = ++ read_sysfs_float(d->sysfs_entry, "gbphy_firmware_latency_avg"); ++ ++ r->request_jitter = r->request_max - r->request_min; ++ r->latency_jitter = r->latency_max - r->latency_min; ++ r->throughput_jitter = r->throughput_max - r->throughput_min; ++ r->apbridge_unipro_latency_jitter = ++ r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; ++ r->gbphy_firmware_latency_jitter = ++ r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min; ++ ++ } ++ ++ /*calculate the aggregate results of all enabled devices */ ++ if (t->aggregate_output) { ++ r = &t->aggregate_results; ++ ++ r->request_min = get_request_min_aggregate(t); ++ r->request_max = get_request_max_aggregate(t); ++ r->request_avg = get_request_avg_aggregate(t); ++ ++ r->latency_min = get_latency_min_aggregate(t); ++ r->latency_max = get_latency_max_aggregate(t); ++ r->latency_avg = get_latency_avg_aggregate(t); ++ ++ r->throughput_min = get_throughput_min_aggregate(t); ++ r->throughput_max = get_throughput_max_aggregate(t); ++ r->throughput_avg = get_throughput_avg_aggregate(t); ++ ++ r->apbridge_unipro_latency_min = ++ get_apbridge_unipro_latency_min_aggregate(t); ++ r->apbridge_unipro_latency_max = ++ get_apbridge_unipro_latency_max_aggregate(t); ++ r->apbridge_unipro_latency_avg = ++ get_apbridge_unipro_latency_avg_aggregate(t); ++ ++ r->gbphy_firmware_latency_min = ++ get_gbphy_firmware_latency_min_aggregate(t); ++ r->gbphy_firmware_latency_max = ++ get_gbphy_firmware_latency_max_aggregate(t); ++ r->gbphy_firmware_latency_avg = ++ get_gbphy_firmware_latency_avg_aggregate(t); ++ ++ r->request_jitter = r->request_max - r->request_min; ++ r->latency_jitter = r->latency_max - r->latency_min; ++ r->throughput_jitter = r->throughput_max - r->throughput_min; ++ r->apbridge_unipro_latency_jitter = ++ r->apbridge_unipro_latency_max - r->apbridge_unipro_latency_min; ++ r->gbphy_firmware_latency_jitter = ++ r->gbphy_firmware_latency_max - r->gbphy_firmware_latency_min; ++ ++ } ++ ++ return 0; ++} ++ ++void log_csv_error(int len, int err) ++{ ++ fprintf(stderr, "unable to write %d bytes to csv %s\n", len, ++ strerror(err)); ++} ++ ++int format_output(struct loopback_test *t, ++ struct loopback_results *r, ++ const char *dev_name, ++ char *buf, int buf_len, ++ struct tm *tm) ++{ ++ int len = 0; ++ ++ memset(buf, 0x00, buf_len); ++ len = snprintf(buf, buf_len, "%u-%u-%u %u:%u:%u", ++ tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, ++ tm->tm_hour, tm->tm_min, tm->tm_sec); ++ ++ if (t->porcelain) { ++ len += snprintf(&buf[len], buf_len - len, ++ "\n test:\t\t\t%s\n path:\t\t\t%s\n size:\t\t\t%u\n iterations:\t\t%u\n errors:\t\t%u\n async:\t\t\t%s\n", ++ t->test_name, ++ dev_name, ++ t->size, ++ t->iteration_max, ++ r->error, ++ t->use_async ? "Enabled" : "Disabled"); ++ ++ len += snprintf(&buf[len], buf_len - len, ++ " requests per-sec:\tmin=%u, max=%u, average=%f, jitter=%u\n", ++ r->request_min, ++ r->request_max, ++ r->request_avg, ++ r->request_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ++ " ap-throughput B/s:\tmin=%u max=%u average=%f jitter=%u\n", ++ r->throughput_min, ++ r->throughput_max, ++ r->throughput_avg, ++ r->throughput_jitter); ++ len += snprintf(&buf[len], buf_len - len, ++ " ap-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", ++ r->latency_min, ++ r->latency_max, ++ r->latency_avg, ++ r->latency_jitter); ++ len += snprintf(&buf[len], buf_len - len, ++ " apbridge-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", ++ r->apbridge_unipro_latency_min, ++ r->apbridge_unipro_latency_max, ++ r->apbridge_unipro_latency_avg, ++ r->apbridge_unipro_latency_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ++ " gbphy-latency usec:\tmin=%u max=%u average=%f jitter=%u\n", ++ r->gbphy_firmware_latency_min, ++ r->gbphy_firmware_latency_max, ++ r->gbphy_firmware_latency_avg, ++ r->gbphy_firmware_latency_jitter); ++ ++ } else { ++ len += snprintf(&buf[len], buf_len- len, ",%s,%s,%u,%u,%u", ++ t->test_name, dev_name, t->size, t->iteration_max, ++ r->error); ++ ++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", ++ r->request_min, ++ r->request_max, ++ r->request_avg, ++ r->request_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", ++ r->latency_min, ++ r->latency_max, ++ r->latency_avg, ++ r->latency_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", ++ r->throughput_min, ++ r->throughput_max, ++ r->throughput_avg, ++ r->throughput_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", ++ r->apbridge_unipro_latency_min, ++ r->apbridge_unipro_latency_max, ++ r->apbridge_unipro_latency_avg, ++ r->apbridge_unipro_latency_jitter); ++ ++ len += snprintf(&buf[len], buf_len - len, ",%u,%u,%f,%u", ++ r->gbphy_firmware_latency_min, ++ r->gbphy_firmware_latency_max, ++ r->gbphy_firmware_latency_avg, ++ r->gbphy_firmware_latency_jitter); ++ } ++ ++ printf("\n%s\n", buf); ++ ++ return len; ++} ++ ++static int log_results(struct loopback_test *t) ++{ ++ int fd, i, len, ret; ++ struct tm tm; ++ time_t local_time; ++ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; ++ char file_name[MAX_SYSFS_PATH]; ++ char data[CSV_MAX_LINE]; ++ ++ local_time = time(NULL); ++ tm = *localtime(&local_time); ++ ++ /* ++ * file name will test_name_size_iteration_max.csv ++ * every time the same test with the same parameters is run we will then ++ * append to the same CSV with datestamp - representing each test ++ * dataset. ++ */ ++ if (t->file_output && !t->porcelain) { ++ snprintf(file_name, sizeof(file_name), "%s_%d_%d.csv", ++ t->test_name, t->size, t->iteration_max); ++ ++ fd = open(file_name, O_WRONLY | O_CREAT | O_APPEND, mode); ++ if (fd < 0) { ++ fprintf(stderr, "unable to open %s for appendation\n", file_name); ++ abort(); ++ } ++ ++ } ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ len = format_output(t, &t->devices[i].results, ++ t->devices[i].name, ++ data, sizeof(data), &tm); ++ if (t->file_output && !t->porcelain) { ++ ret = write(fd, data, len); ++ if (ret == -1) ++ fprintf(stderr, "unable to write %d bytes to csv.\n", len); ++ } ++ ++ } ++ ++ ++ if (t->aggregate_output) { ++ len = format_output(t, &t->aggregate_results, "aggregate", ++ data, sizeof(data), &tm); ++ if (t->file_output && !t->porcelain) { ++ ret = write(fd, data, len); ++ if (ret == -1) ++ fprintf(stderr, "unable to write %d bytes to csv.\n", len); ++ } ++ } ++ ++ if (t->file_output && !t->porcelain) ++ close(fd); ++ ++ return 0; ++} ++ ++int is_loopback_device(const char *path, const char *node) ++{ ++ char file[MAX_SYSFS_PATH]; ++ ++ snprintf(file, MAX_SYSFS_PATH, "%s%s/iteration_count", path, node); ++ if (access(file, F_OK) == 0) ++ return 1; ++ return 0; ++} ++ ++int find_loopback_devices(struct loopback_test *t) ++{ ++ struct dirent **namelist; ++ int i, n, ret; ++ unsigned int dev_id; ++ struct loopback_device *d; ++ ++ n = scandir(t->sysfs_prefix, &namelist, NULL, alphasort); ++ if (n < 0) { ++ perror("scandir"); ++ ret = -ENODEV; ++ goto baddir; ++ } ++ ++ /* Don't include '.' and '..' */ ++ if (n <= 2) { ++ ret = -ENOMEM; ++ goto done; ++ } ++ ++ for (i = 0; i < n; i++) { ++ ret = sscanf(namelist[i]->d_name, "gb_loopback%u", &dev_id); ++ if (ret != 1) ++ continue; ++ ++ if (!is_loopback_device(t->sysfs_prefix, namelist[i]->d_name)) ++ continue; ++ ++ if (t->device_count == MAX_NUM_DEVICES) { ++ fprintf(stderr, "max number of devices reached!\n"); ++ break; ++ } ++ ++ d = &t->devices[t->device_count++]; ++ snprintf(d->name, MAX_STR_LEN, "gb_loopback%u", dev_id); ++ ++ snprintf(d->sysfs_entry, MAX_SYSFS_PATH, "%s%s/", ++ t->sysfs_prefix, d->name); ++ ++ snprintf(d->debugfs_entry, MAX_SYSFS_PATH, "%sraw_latency_%s", ++ t->debugfs_prefix, d->name); ++ ++ if (t->debug) ++ printf("add %s %s\n", d->sysfs_entry, ++ d->debugfs_entry); ++ } ++ ++ ret = 0; ++done: ++ for (i = 0; i < n; i++) ++ free(namelist[n]); ++ free(namelist); ++baddir: ++ return ret; ++} ++ ++static int open_poll_files(struct loopback_test *t) ++{ ++ struct loopback_device *dev; ++ char buf[MAX_STR_LEN]; ++ char dummy; ++ int fds_idx = 0; ++ int i; ++ ++ for (i = 0; i < t->device_count; i++) { ++ dev = &t->devices[i]; ++ ++ if (!device_enabled(t, i)) ++ continue; ++ ++ snprintf(buf, sizeof(buf), "%s%s", dev->sysfs_entry, "iteration_count"); ++ t->fds[fds_idx].fd = open(buf, O_RDONLY); ++ if (t->fds[fds_idx].fd < 0) { ++ fprintf(stderr, "Error opening poll file!\n"); ++ goto err; ++ } ++ read(t->fds[fds_idx].fd, &dummy, 1); ++ t->fds[fds_idx].events = POLLERR|POLLPRI; ++ t->fds[fds_idx].revents = 0; ++ fds_idx++; ++ } ++ ++ t->poll_count = fds_idx; ++ ++ return 0; ++ ++err: ++ for (i = 0; i < fds_idx; i++) ++ close(t->fds[fds_idx].fd); ++ ++ return -1; ++} ++ ++static int close_poll_files(struct loopback_test *t) ++{ ++ int i; ++ for (i = 0; i < t->poll_count; i++) ++ close(t->fds[i].fd); ++ ++ return 0; ++} ++static int is_complete(struct loopback_test *t) ++{ ++ int iteration_count; ++ int i; ++ ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ iteration_count = read_sysfs_int(t->devices[i].sysfs_entry, ++ "iteration_count"); ++ ++ /* at least one device did not finish yet */ ++ if (iteration_count != t->iteration_max) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void stop_tests(struct loopback_test *t) ++{ ++ int i; ++ ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); ++ } ++} ++ ++static void handler(int sig) { /* do nothing */ } ++ ++static int wait_for_complete(struct loopback_test *t) ++{ ++ int number_of_events = 0; ++ char dummy; ++ int ret; ++ int i; ++ struct timespec *ts = NULL; ++ struct sigaction sa; ++ sigset_t mask_old, mask; ++ ++ sigemptyset(&mask); ++ sigemptyset(&mask_old); ++ sigaddset(&mask, SIGINT); ++ sigprocmask(SIG_BLOCK, &mask, &mask_old); ++ ++ sa.sa_handler = handler; ++ sa.sa_flags = 0; ++ sigemptyset(&sa.sa_mask); ++ if (sigaction(SIGINT, &sa, NULL) == -1) { ++ fprintf(stderr, "sigaction error\n"); ++ return -1; ++ } ++ ++ if (t->poll_timeout.tv_sec != 0) ++ ts = &t->poll_timeout; ++ ++ while (1) { ++ ++ ret = ppoll(t->fds, t->poll_count, ts, &mask_old); ++ if (ret <= 0) { ++ stop_tests(t); ++ fprintf(stderr, "Poll exit with errno %d\n", errno); ++ return -1; ++ } ++ ++ for (i = 0; i < t->poll_count; i++) { ++ if (t->fds[i].revents & POLLPRI) { ++ /* Dummy read to clear the event */ ++ read(t->fds[i].fd, &dummy, 1); ++ number_of_events++; ++ } ++ } ++ ++ if (number_of_events == t->poll_count) ++ break; ++ } ++ ++ if (!is_complete(t)) { ++ fprintf(stderr, "Iteration count did not finish!\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static void prepare_devices(struct loopback_test *t) ++{ ++ int i; ++ ++ /* Cancel any running tests on enabled devices. If ++ * stop_all option is given, stop test on all devices. ++ */ ++ for (i = 0; i < t->device_count; i++) ++ if (t->stop_all || device_enabled(t, i)) ++ write_sysfs_val(t->devices[i].sysfs_entry, "type", 0); ++ ++ ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ write_sysfs_val(t->devices[i].sysfs_entry, "us_wait", ++ t->us_wait); ++ ++ /* Set operation size */ ++ write_sysfs_val(t->devices[i].sysfs_entry, "size", t->size); ++ ++ /* Set iterations */ ++ write_sysfs_val(t->devices[i].sysfs_entry, "iteration_max", ++ t->iteration_max); ++ ++ if (t->use_async) { ++ write_sysfs_val(t->devices[i].sysfs_entry, ++ "async", 1); ++ write_sysfs_val(t->devices[i].sysfs_entry, ++ "timeout", t->async_timeout); ++ write_sysfs_val(t->devices[i].sysfs_entry, ++ "outstanding_operations_max", ++ t->async_outstanding_operations); ++ } else ++ write_sysfs_val(t->devices[i].sysfs_entry, ++ "async", 0); ++ } ++} ++ ++static int start(struct loopback_test *t) ++{ ++ int i; ++ ++ /* the test starts by writing test_id to the type file. */ ++ for (i = 0; i < t->device_count; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ write_sysfs_val(t->devices[i].sysfs_entry, "type", t->test_id); ++ } ++ ++ return 0; ++} ++ ++ ++void loopback_run(struct loopback_test *t) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; dict[i].name != NULL; i++) { ++ if (strstr(dict[i].name, t->test_name)) ++ t->test_id = dict[i].type; ++ } ++ if (!t->test_id) { ++ fprintf(stderr, "invalid test %s\n", t->test_name); ++ usage(); ++ return; ++ } ++ ++ prepare_devices(t); ++ ++ ret = open_poll_files(t); ++ if (ret) ++ goto err; ++ ++ start(t); ++ ++ ret = wait_for_complete(t); ++ close_poll_files(t); ++ if (ret) ++ goto err; ++ ++ ++ get_results(t); ++ ++ log_results(t); ++ ++ return; ++ ++err: ++ printf("Error running test\n"); ++ return; ++} ++ ++static int sanity_check(struct loopback_test *t) ++{ ++ int i; ++ ++ if (t->device_count == 0) { ++ fprintf(stderr, "No loopback devices found\n"); ++ return -1; ++ } ++ ++ for (i = 0; i < MAX_NUM_DEVICES; i++) { ++ if (!device_enabled(t, i)) ++ continue; ++ ++ if (t->mask && !strcmp(t->devices[i].name, "")) { ++ fprintf(stderr, "Bad device mask %x\n", (1 << i)); ++ return -1; ++ } ++ ++ } ++ ++ ++ return 0; ++} ++ ++int main(int argc, char *argv[]) ++{ ++ int o, ret; ++ char *sysfs_prefix = "/sys/class/gb_loopback/"; ++ char *debugfs_prefix = "/sys/kernel/debug/gb_loopback/"; ++ ++ memset(&t, 0, sizeof(t)); ++ ++ while ((o = getopt(argc, argv, ++ "t:s:i:S:D:m:v::d::r::p::a::l::x::o:O:c:w:z::f::")) != -1) { ++ switch (o) { ++ case 't': ++ snprintf(t.test_name, MAX_STR_LEN, "%s", optarg); ++ break; ++ case 's': ++ t.size = atoi(optarg); ++ break; ++ case 'i': ++ t.iteration_max = atoi(optarg); ++ break; ++ case 'S': ++ snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", optarg); ++ break; ++ case 'D': ++ snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", optarg); ++ break; ++ case 'm': ++ t.mask = atol(optarg); ++ break; ++ case 'v': ++ t.verbose = 1; ++ break; ++ case 'd': ++ t.debug = 1; ++ break; ++ case 'r': ++ t.raw_data_dump = 1; ++ break; ++ case 'p': ++ t.porcelain = 1; ++ break; ++ case 'a': ++ t.aggregate_output = 1; ++ break; ++ case 'l': ++ t.list_devices = 1; ++ break; ++ case 'x': ++ t.use_async = 1; ++ break; ++ case 'o': ++ t.async_timeout = atoi(optarg); ++ break; ++ case 'O': ++ t.poll_timeout.tv_sec = atoi(optarg); ++ break; ++ case 'c': ++ t.async_outstanding_operations = atoi(optarg); ++ break; ++ case 'w': ++ t.us_wait = atoi(optarg); ++ break; ++ case 'z': ++ t.file_output = 1; ++ break; ++ case 'f': ++ t.stop_all = 1; ++ break; ++ default: ++ usage(); ++ return -EINVAL; ++ } ++ } ++ ++ if (!strcmp(t.sysfs_prefix, "")) ++ snprintf(t.sysfs_prefix, MAX_SYSFS_PATH, "%s", sysfs_prefix); ++ ++ if (!strcmp(t.debugfs_prefix, "")) ++ snprintf(t.debugfs_prefix, MAX_SYSFS_PATH, "%s", debugfs_prefix); ++ ++ ret = find_loopback_devices(&t); ++ if (ret) ++ return ret; ++ ret = sanity_check(&t); ++ if (ret) ++ return ret; ++ ++ if (t.list_devices) { ++ show_loopback_devices(&t); ++ return 0; ++ } ++ ++ if (t.test_name[0] == '\0' || t.iteration_max == 0) ++ usage(); ++ ++ if (t.async_timeout == 0) ++ t.async_timeout = DEFAULT_ASYNC_TIMEOUT; ++ ++ loopback_run(&t); ++ ++ return 0; ++} diff --git a/greybus_trace.patch b/greybus_trace.patch new file mode 100644 index 00000000000000..c14c444932b89f --- /dev/null +++ b/greybus_trace.patch @@ -0,0 +1,538 @@ +--- + drivers/greybus/greybus_trace.h | 531 ++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 531 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/greybus_trace.h +@@ -0,0 +1,531 @@ ++/* ++ * Greybus driver and device API ++ * ++ * Copyright 2015 Google Inc. ++ * Copyright 2015 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++#undef TRACE_SYSTEM ++#define TRACE_SYSTEM greybus ++ ++#if !defined(_TRACE_GREYBUS_H) || defined(TRACE_HEADER_MULTI_READ) ++#define _TRACE_GREYBUS_H ++ ++#include <linux/tracepoint.h> ++ ++struct gb_message; ++struct gb_operation; ++struct gb_connection; ++struct gb_bundle; ++struct gb_host_device; ++ ++DECLARE_EVENT_CLASS(gb_message, ++ ++ TP_PROTO(struct gb_message *message), ++ ++ TP_ARGS(message), ++ ++ TP_STRUCT__entry( ++ __field(u16, size) ++ __field(u16, operation_id) ++ __field(u8, type) ++ __field(u8, result) ++ ), ++ ++ TP_fast_assign( ++ __entry->size = le16_to_cpu(message->header->size); ++ __entry->operation_id = ++ le16_to_cpu(message->header->operation_id); ++ __entry->type = message->header->type; ++ __entry->result = message->header->result; ++ ), ++ ++ TP_printk("size=%hu operation_id=0x%04x type=0x%02x result=0x%02x", ++ __entry->size, __entry->operation_id, ++ __entry->type, __entry->result) ++); ++ ++#define DEFINE_MESSAGE_EVENT(name) \ ++ DEFINE_EVENT(gb_message, name, \ ++ TP_PROTO(struct gb_message *message), \ ++ TP_ARGS(message)) ++ ++/* ++ * Occurs immediately before calling a host device's message_send() ++ * method. ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_send); ++ ++/* ++ * Occurs after an incoming request message has been received ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_recv_request); ++ ++/* ++ * Occurs after an incoming response message has been received, ++ * after its matching request has been found. ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_recv_response); ++ ++/* ++ * Occurs after an operation has been canceled, possibly before the ++ * cancellation is complete. ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_cancel_outgoing); ++ ++/* ++ * Occurs when an incoming request is cancelled; if the response has ++ * been queued for sending, this occurs after it is sent. ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_cancel_incoming); ++ ++/* ++ * Occurs in the host driver message_send() function just prior to ++ * handing off the data to be processed by hardware. ++ */ ++DEFINE_MESSAGE_EVENT(gb_message_submit); ++ ++#undef DEFINE_MESSAGE_EVENT ++ ++DECLARE_EVENT_CLASS(gb_operation, ++ ++ TP_PROTO(struct gb_operation *operation), ++ ++ TP_ARGS(operation), ++ ++ TP_STRUCT__entry( ++ __field(u16, cport_id) /* CPort of HD side of connection */ ++ __field(u16, id) /* Operation ID */ ++ __field(u8, type) ++ __field(unsigned long, flags) ++ __field(int, active) ++ __field(int, waiters) ++ __field(int, errno) ++ ), ++ ++ TP_fast_assign( ++ __entry->cport_id = operation->connection->hd_cport_id; ++ __entry->id = operation->id; ++ __entry->type = operation->type; ++ __entry->flags = operation->flags; ++ __entry->active = operation->active; ++ __entry->waiters = atomic_read(&operation->waiters); ++ __entry->errno = operation->errno; ++ ), ++ ++ TP_printk("id=%04x type=0x%02x cport_id=%04x flags=0x%lx active=%d waiters=%d errno=%d", ++ __entry->id, __entry->cport_id, __entry->type, __entry->flags, ++ __entry->active, __entry->waiters, __entry->errno) ++); ++ ++#define DEFINE_OPERATION_EVENT(name) \ ++ DEFINE_EVENT(gb_operation, name, \ ++ TP_PROTO(struct gb_operation *operation), \ ++ TP_ARGS(operation)) ++ ++/* ++ * Occurs after a new operation is created for an outgoing request ++ * has been successfully created. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_create); ++ ++/* ++ * Occurs after a new core operation has been created. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_create_core); ++ ++/* ++ * Occurs after a new operation has been created for an incoming ++ * request has been successfully created and initialized. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_create_incoming); ++ ++/* ++ * Occurs when the last reference to an operation has been dropped, ++ * prior to freeing resources. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_destroy); ++ ++/* ++ * Occurs when an operation has been marked active, after updating ++ * its active count. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_get_active); ++ ++/* ++ * Occurs when an operation has been marked active, before updating ++ * its active count. ++ */ ++DEFINE_OPERATION_EVENT(gb_operation_put_active); ++ ++#undef DEFINE_OPERATION_EVENT ++ ++DECLARE_EVENT_CLASS(gb_connection, ++ ++ TP_PROTO(struct gb_connection *connection), ++ ++ TP_ARGS(connection), ++ ++ TP_STRUCT__entry( ++ __field(int, hd_bus_id) ++ __field(u8, bundle_id) ++ /* name contains "hd_cport_id/intf_id:cport_id" */ ++ __dynamic_array(char, name, sizeof(connection->name)) ++ __field(enum gb_connection_state, state) ++ __field(unsigned long, flags) ++ ), ++ ++ TP_fast_assign( ++ __entry->hd_bus_id = connection->hd->bus_id; ++ __entry->bundle_id = connection->bundle ? ++ connection->bundle->id : BUNDLE_ID_NONE; ++ memcpy(__get_str(name), connection->name, ++ sizeof(connection->name)); ++ __entry->state = connection->state; ++ __entry->flags = connection->flags; ++ ), ++ ++ TP_printk("hd_bus_id=%d bundle_id=0x%02x name=\"%s\" state=%u flags=0x%lx", ++ __entry->hd_bus_id, __entry->bundle_id, __get_str(name), ++ (unsigned int)__entry->state, __entry->flags) ++); ++ ++#define DEFINE_CONNECTION_EVENT(name) \ ++ DEFINE_EVENT(gb_connection, name, \ ++ TP_PROTO(struct gb_connection *connection), \ ++ TP_ARGS(connection)) ++ ++/* ++ * Occurs after a new connection is successfully created. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_create); ++ ++/* ++ * Occurs when the last reference to a connection has been dropped, ++ * before its resources are freed. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_release); ++ ++/* ++ * Occurs when a new reference to connection is added, currently ++ * only when a message over the connection is received. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_get); ++ ++/* ++ * Occurs when a new reference to connection is dropped, after a ++ * a received message is handled, or when the connection is ++ * destroyed. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_put); ++ ++/* ++ * Occurs when a request to enable a connection is made, either for ++ * transmit only, or for both transmit and receive. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_enable); ++ ++/* ++ * Occurs when a request to disable a connection is made, either for ++ * receive only, or for both transmit and receive. Also occurs when ++ * a request to forcefully disable a connection is made. ++ */ ++DEFINE_CONNECTION_EVENT(gb_connection_disable); ++ ++#undef DEFINE_CONNECTION_EVENT ++ ++DECLARE_EVENT_CLASS(gb_bundle, ++ ++ TP_PROTO(struct gb_bundle *bundle), ++ ++ TP_ARGS(bundle), ++ ++ TP_STRUCT__entry( ++ __field(u8, intf_id) ++ __field(u8, id) ++ __field(u8, class) ++ __field(size_t, num_cports) ++ ), ++ ++ TP_fast_assign( ++ __entry->intf_id = bundle->intf->interface_id; ++ __entry->id = bundle->id; ++ __entry->class = bundle->class; ++ __entry->num_cports = bundle->num_cports; ++ ), ++ ++ TP_printk("intf_id=0x%02x id=%02x class=0x%02x num_cports=%zu", ++ __entry->intf_id, __entry->id, __entry->class, ++ __entry->num_cports) ++); ++ ++#define DEFINE_BUNDLE_EVENT(name) \ ++ DEFINE_EVENT(gb_bundle, name, \ ++ TP_PROTO(struct gb_bundle *bundle), \ ++ TP_ARGS(bundle)) ++ ++/* ++ * Occurs after a new bundle is successfully created. ++ */ ++DEFINE_BUNDLE_EVENT(gb_bundle_create); ++ ++/* ++ * Occurs when the last reference to a bundle has been dropped, ++ * before its resources are freed. ++ */ ++DEFINE_BUNDLE_EVENT(gb_bundle_release); ++ ++/* ++ * Occurs when a bundle is added to an interface when the interface ++ * is enabled. ++ */ ++DEFINE_BUNDLE_EVENT(gb_bundle_add); ++ ++/* ++ * Occurs when a registered bundle gets destroyed, normally at the ++ * time an interface is disabled. ++ */ ++DEFINE_BUNDLE_EVENT(gb_bundle_destroy); ++ ++#undef DEFINE_BUNDLE_EVENT ++ ++DECLARE_EVENT_CLASS(gb_interface, ++ ++ TP_PROTO(struct gb_interface *intf), ++ ++ TP_ARGS(intf), ++ ++ TP_STRUCT__entry( ++ __field(u8, module_id) ++ __field(u8, id) /* Interface id */ ++ __field(u8, device_id) ++ __field(int, disconnected) /* bool */ ++ __field(int, ejected) /* bool */ ++ __field(int, active) /* bool */ ++ __field(int, enabled) /* bool */ ++ __field(int, mode_switch) /* bool */ ++ ), ++ ++ TP_fast_assign( ++ __entry->module_id = intf->module->module_id; ++ __entry->id = intf->interface_id; ++ __entry->device_id = intf->device_id; ++ __entry->disconnected = intf->disconnected; ++ __entry->ejected = intf->ejected; ++ __entry->active = intf->active; ++ __entry->enabled = intf->enabled; ++ __entry->mode_switch = intf->mode_switch; ++ ), ++ ++ TP_printk("intf_id=%hhu device_id=%hhu module_id=%hhu D=%d J=%d A=%d E=%d M=%d", ++ __entry->id, __entry->device_id, __entry->module_id, ++ __entry->disconnected, __entry->ejected, __entry->active, ++ __entry->enabled, __entry->mode_switch) ++); ++ ++#define DEFINE_INTERFACE_EVENT(name) \ ++ DEFINE_EVENT(gb_interface, name, \ ++ TP_PROTO(struct gb_interface *intf), \ ++ TP_ARGS(intf)) ++ ++/* ++ * Occurs after a new interface is successfully created. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_create); ++ ++/* ++ * Occurs after the last reference to an interface has been dropped. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_release); ++ ++/* ++ * Occurs after an interface been registerd. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_add); ++ ++/* ++ * Occurs when a registered interface gets deregisterd. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_del); ++ ++/* ++ * Occurs when a registered interface has been successfully ++ * activated. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_activate); ++ ++/* ++ * Occurs when an activated interface is being deactivated. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_deactivate); ++ ++/* ++ * Occurs when an interface has been successfully enabled. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_enable); ++ ++/* ++ * Occurs when an enabled interface is being disabled. ++ */ ++DEFINE_INTERFACE_EVENT(gb_interface_disable); ++ ++#undef DEFINE_INTERFACE_EVENT ++ ++DECLARE_EVENT_CLASS(gb_module, ++ ++ TP_PROTO(struct gb_module *module), ++ ++ TP_ARGS(module), ++ ++ TP_STRUCT__entry( ++ __field(int, hd_bus_id) ++ __field(u8, module_id) ++ __field(size_t, num_interfaces) ++ __field(int, disconnected) /* bool */ ++ ), ++ ++ TP_fast_assign( ++ __entry->hd_bus_id = module->hd->bus_id; ++ __entry->module_id = module->module_id; ++ __entry->num_interfaces = module->num_interfaces; ++ __entry->disconnected = module->disconnected; ++ ), ++ ++ TP_printk("hd_bus_id=%d module_id=%hhu num_interfaces=%zu disconnected=%d", ++ __entry->hd_bus_id, __entry->module_id, ++ __entry->num_interfaces, __entry->disconnected) ++); ++ ++#define DEFINE_MODULE_EVENT(name) \ ++ DEFINE_EVENT(gb_module, name, \ ++ TP_PROTO(struct gb_module *module), \ ++ TP_ARGS(module)) ++ ++/* ++ * Occurs after a new module is successfully created, before ++ * creating any of its interfaces. ++ */ ++DEFINE_MODULE_EVENT(gb_module_create); ++ ++/* ++ * Occurs after the last reference to a module has been dropped. ++ */ ++DEFINE_MODULE_EVENT(gb_module_release); ++ ++/* ++ * Occurs after a module is successfully created, before registering ++ * any of its interfaces. ++ */ ++DEFINE_MODULE_EVENT(gb_module_add); ++ ++/* ++ * Occurs when a module is deleted, before deregistering its ++ * interfaces. ++ */ ++DEFINE_MODULE_EVENT(gb_module_del); ++ ++#undef DEFINE_MODULE_EVENT ++ ++DECLARE_EVENT_CLASS(gb_host_device, ++ ++ TP_PROTO(struct gb_host_device *hd), ++ ++ TP_ARGS(hd), ++ ++ TP_STRUCT__entry( ++ __field(int, bus_id) ++ __field(size_t, num_cports) ++ __field(size_t, buffer_size_max) ++ ), ++ ++ TP_fast_assign( ++ __entry->bus_id = hd->bus_id; ++ __entry->num_cports = hd->num_cports; ++ __entry->buffer_size_max = hd->buffer_size_max; ++ ), ++ ++ TP_printk("bus_id=%d num_cports=%zu mtu=%zu", ++ __entry->bus_id, __entry->num_cports, ++ __entry->buffer_size_max) ++); ++ ++#define DEFINE_HD_EVENT(name) \ ++ DEFINE_EVENT(gb_host_device, name, \ ++ TP_PROTO(struct gb_host_device *hd), \ ++ TP_ARGS(hd)) ++ ++/* ++ * Occurs after a new host device is successfully created, before ++ * its SVC has been set up. ++ */ ++DEFINE_HD_EVENT(gb_hd_create); ++ ++/* ++ * Occurs after the last reference to a host device has been ++ * dropped. ++ */ ++DEFINE_HD_EVENT(gb_hd_release); ++ ++/* ++ * Occurs after a new host device has been added, after the ++ * connection to its SVC has been enabled. ++ */ ++DEFINE_HD_EVENT(gb_hd_add); ++ ++/* ++ * Occurs when a host device is being disconnected from the AP USB ++ * host controller. ++ */ ++DEFINE_HD_EVENT(gb_hd_del); ++ ++/* ++ * Occurs when a host device has passed received data to the Greybus ++ * core, after it has been determined it is destined for a valid ++ * CPort. ++ */ ++DEFINE_HD_EVENT(gb_hd_in); ++ ++#undef DEFINE_HD_EVENT ++ ++/* ++ * Occurs on a TimeSync synchronization event or a TimeSync ping event. ++ */ ++TRACE_EVENT(gb_timesync_irq, ++ ++ TP_PROTO(u8 ping, u8 strobe, u8 count, u64 frame_time), ++ ++ TP_ARGS(ping, strobe, count, frame_time), ++ ++ TP_STRUCT__entry( ++ __field(u8, ping) ++ __field(u8, strobe) ++ __field(u8, count) ++ __field(u64, frame_time) ++ ), ++ ++ TP_fast_assign( ++ __entry->ping = ping; ++ __entry->strobe = strobe; ++ __entry->count = count; ++ __entry->frame_time = frame_time; ++ ), ++ ++ TP_printk("%s %d/%d frame-time %llu\n", ++ __entry->ping ? "ping" : "strobe", __entry->strobe, ++ __entry->count, __entry->frame_time) ++); ++ ++#endif /* _TRACE_GREYBUS_H */ ++ ++/* This part must be outside protection */ ++#undef TRACE_INCLUDE_PATH ++#define TRACE_INCLUDE_PATH . ++ ++/* ++ * TRACE_INCLUDE_FILE is not needed if the filename and TRACE_SYSTEM are equal ++ */ ++#undef TRACE_INCLUDE_FILE ++#define TRACE_INCLUDE_FILE greybus_trace ++#include <trace/define_trace.h> ++ diff --git a/greybus_uart.patch b/greybus_uart.patch new file mode 100644 index 00000000000000..6cbecc463c130e --- /dev/null +++ b/greybus_uart.patch @@ -0,0 +1,1082 @@ +--- + drivers/greybus/uart.c | 1075 +++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 1075 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/uart.c +@@ -0,0 +1,1075 @@ ++/* ++ * UART driver for the Greybus "generic" UART module. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ * ++ * Heavily based on drivers/usb/class/cdc-acm.c and ++ * drivers/usb/serial/usb-serial.c. ++ */ ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt ++ ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/wait.h> ++#include <linux/slab.h> ++#include <linux/uaccess.h> ++#include <linux/mutex.h> ++#include <linux/tty.h> ++#include <linux/serial.h> ++#include <linux/tty_driver.h> ++#include <linux/tty_flip.h> ++#include <linux/serial.h> ++#include <linux/idr.h> ++#include <linux/fs.h> ++#include <linux/kdev_t.h> ++#include <linux/kfifo.h> ++#include <linux/workqueue.h> ++#include <linux/completion.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++#define GB_NUM_MINORS 16 /* 16 is is more than enough */ ++#define GB_NAME "ttyGB" ++ ++#define GB_UART_WRITE_FIFO_SIZE PAGE_SIZE ++#define GB_UART_WRITE_ROOM_MARGIN 1 /* leave some space in fifo */ ++#define GB_UART_FIRMWARE_CREDITS 4096 ++#define GB_UART_CREDIT_WAIT_TIMEOUT_MSEC 10000 ++ ++struct gb_tty_line_coding { ++ __le32 rate; ++ __u8 format; ++ __u8 parity; ++ __u8 data_bits; ++ __u8 flow_control; ++}; ++ ++struct gb_tty { ++ struct gbphy_device *gbphy_dev; ++ struct tty_port port; ++ void *buffer; ++ size_t buffer_payload_max; ++ struct gb_connection *connection; ++ u16 cport_id; ++ unsigned int minor; ++ unsigned char clocal; ++ bool disconnected; ++ spinlock_t read_lock; ++ spinlock_t write_lock; ++ struct async_icount iocount; ++ struct async_icount oldcount; ++ wait_queue_head_t wioctl; ++ struct mutex mutex; ++ u8 ctrlin; /* input control lines */ ++ u8 ctrlout; /* output control lines */ ++ struct gb_tty_line_coding line_coding; ++ struct work_struct tx_work; ++ struct kfifo write_fifo; ++ bool close_pending; ++ unsigned int credits; ++ struct completion credits_complete; ++}; ++ ++static struct tty_driver *gb_tty_driver; ++static DEFINE_IDR(tty_minors); ++static DEFINE_MUTEX(table_lock); ++ ++static int gb_uart_receive_data_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_tty *gb_tty = gb_connection_get_data(connection); ++ struct tty_port *port = &gb_tty->port; ++ struct gb_message *request = op->request; ++ struct gb_uart_recv_data_request *receive_data; ++ u16 recv_data_size; ++ int count; ++ unsigned long tty_flags = TTY_NORMAL; ++ ++ if (request->payload_size < sizeof(*receive_data)) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "short receive-data request received (%zu < %zu)\n", ++ request->payload_size, sizeof(*receive_data)); ++ return -EINVAL; ++ } ++ ++ receive_data = op->request->payload; ++ recv_data_size = le16_to_cpu(receive_data->size); ++ ++ if (recv_data_size != request->payload_size - sizeof(*receive_data)) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "malformed receive-data request received (%u != %zu)\n", ++ recv_data_size, ++ request->payload_size - sizeof(*receive_data)); ++ return -EINVAL; ++ } ++ ++ if (!recv_data_size) ++ return -EINVAL; ++ ++ if (receive_data->flags) { ++ if (receive_data->flags & GB_UART_RECV_FLAG_BREAK) ++ tty_flags = TTY_BREAK; ++ else if (receive_data->flags & GB_UART_RECV_FLAG_PARITY) ++ tty_flags = TTY_PARITY; ++ else if (receive_data->flags & GB_UART_RECV_FLAG_FRAMING) ++ tty_flags = TTY_FRAME; ++ ++ /* overrun is special, not associated with a char */ ++ if (receive_data->flags & GB_UART_RECV_FLAG_OVERRUN) ++ tty_insert_flip_char(port, 0, TTY_OVERRUN); ++ } ++ count = tty_insert_flip_string_fixed_flag(port, receive_data->data, ++ tty_flags, recv_data_size); ++ if (count != recv_data_size) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "UART: RX 0x%08x bytes only wrote 0x%08x\n", ++ recv_data_size, count); ++ } ++ if (count) ++ tty_flip_buffer_push(port); ++ return 0; ++} ++ ++static int gb_uart_serial_state_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_tty *gb_tty = gb_connection_get_data(connection); ++ struct gb_message *request = op->request; ++ struct gb_uart_serial_state_request *serial_state; ++ ++ if (request->payload_size < sizeof(*serial_state)) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "short serial-state event received (%zu < %zu)\n", ++ request->payload_size, sizeof(*serial_state)); ++ return -EINVAL; ++ } ++ ++ serial_state = request->payload; ++ gb_tty->ctrlin = serial_state->control; ++ ++ return 0; ++} ++ ++static int gb_uart_receive_credits_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_tty *gb_tty = gb_connection_get_data(connection); ++ struct gb_message *request = op->request; ++ struct gb_uart_receive_credits_request *credit_request; ++ unsigned long flags; ++ unsigned int incoming_credits; ++ int ret = 0; ++ ++ if (request->payload_size < sizeof(*credit_request)) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "short receive_credits event received (%zu < %zu)\n", ++ request->payload_size, ++ sizeof(*credit_request)); ++ return -EINVAL; ++ } ++ ++ credit_request = request->payload; ++ incoming_credits = le16_to_cpu(credit_request->count); ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ gb_tty->credits += incoming_credits; ++ if (gb_tty->credits > GB_UART_FIRMWARE_CREDITS) { ++ gb_tty->credits -= incoming_credits; ++ ret = -EINVAL; ++ } ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ if (ret) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "invalid number of incoming credits: %d\n", ++ incoming_credits); ++ return ret; ++ } ++ ++ if (!gb_tty->close_pending) ++ schedule_work(&gb_tty->tx_work); ++ ++ /* ++ * the port the tty layer may be waiting for credits ++ */ ++ tty_port_tty_wakeup(&gb_tty->port); ++ ++ if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) ++ complete(&gb_tty->credits_complete); ++ ++ return ret; ++} ++ ++static int gb_uart_request_handler(struct gb_operation *op) ++{ ++ struct gb_connection *connection = op->connection; ++ struct gb_tty *gb_tty = gb_connection_get_data(connection); ++ int type = op->type; ++ int ret; ++ ++ switch (type) { ++ case GB_UART_TYPE_RECEIVE_DATA: ++ ret = gb_uart_receive_data_handler(op); ++ break; ++ case GB_UART_TYPE_SERIAL_STATE: ++ ret = gb_uart_serial_state_handler(op); ++ break; ++ case GB_UART_TYPE_RECEIVE_CREDITS: ++ ret = gb_uart_receive_credits_handler(op); ++ break; ++ default: ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "unsupported unsolicited request: 0x%02x\n", type); ++ ret = -EINVAL; ++ } ++ ++ return ret; ++} ++ ++static void gb_uart_tx_write_work(struct work_struct *work) ++{ ++ struct gb_uart_send_data_request *request; ++ struct gb_tty *gb_tty; ++ unsigned long flags; ++ unsigned int send_size; ++ int ret; ++ ++ gb_tty = container_of(work, struct gb_tty, tx_work); ++ request = gb_tty->buffer; ++ ++ while (1) { ++ if (gb_tty->close_pending) ++ break; ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ send_size = gb_tty->buffer_payload_max; ++ if (send_size > gb_tty->credits) ++ send_size = gb_tty->credits; ++ ++ send_size = kfifo_out_peek(&gb_tty->write_fifo, ++ &request->data[0], ++ send_size); ++ if (!send_size) { ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ break; ++ } ++ ++ gb_tty->credits -= send_size; ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ request->size = cpu_to_le16(send_size); ++ ret = gb_operation_sync(gb_tty->connection, ++ GB_UART_TYPE_SEND_DATA, ++ request, sizeof(*request) + send_size, ++ NULL, 0); ++ if (ret) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "send data error: %d\n", ret); ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ gb_tty->credits += send_size; ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ if (!gb_tty->close_pending) ++ schedule_work(work); ++ return; ++ } ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ ret = kfifo_out(&gb_tty->write_fifo, &request->data[0], ++ send_size); ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ tty_port_tty_wakeup(&gb_tty->port); ++ } ++} ++ ++static int send_line_coding(struct gb_tty *tty) ++{ ++ struct gb_uart_set_line_coding_request request; ++ ++ memcpy(&request, &tty->line_coding, ++ sizeof(tty->line_coding)); ++ return gb_operation_sync(tty->connection, GB_UART_TYPE_SET_LINE_CODING, ++ &request, sizeof(request), NULL, 0); ++} ++ ++static int send_control(struct gb_tty *gb_tty, u8 control) ++{ ++ struct gb_uart_set_control_line_state_request request; ++ ++ request.control = control; ++ return gb_operation_sync(gb_tty->connection, ++ GB_UART_TYPE_SET_CONTROL_LINE_STATE, ++ &request, sizeof(request), NULL, 0); ++} ++ ++static int send_break(struct gb_tty *gb_tty, u8 state) ++{ ++ struct gb_uart_set_break_request request; ++ ++ if ((state != 0) && (state != 1)) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "invalid break state of %d\n", state); ++ return -EINVAL; ++ } ++ ++ request.state = state; ++ return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_SEND_BREAK, ++ &request, sizeof(request), NULL, 0); ++} ++ ++static int gb_uart_wait_for_all_credits(struct gb_tty *gb_tty) ++{ ++ int ret; ++ ++ if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) ++ return 0; ++ ++ ret = wait_for_completion_timeout(&gb_tty->credits_complete, ++ msecs_to_jiffies(GB_UART_CREDIT_WAIT_TIMEOUT_MSEC)); ++ if (!ret) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "time out waiting for credits\n"); ++ return -ETIMEDOUT; ++ } ++ ++ return 0; ++} ++ ++static int gb_uart_flush(struct gb_tty *gb_tty, u8 flags) ++{ ++ struct gb_uart_serial_flush_request request; ++ ++ request.flags = flags; ++ return gb_operation_sync(gb_tty->connection, GB_UART_TYPE_FLUSH_FIFOS, ++ &request, sizeof(request), NULL, 0); ++} ++ ++static struct gb_tty *get_gb_by_minor(unsigned minor) ++{ ++ struct gb_tty *gb_tty; ++ ++ mutex_lock(&table_lock); ++ gb_tty = idr_find(&tty_minors, minor); ++ if (gb_tty) { ++ mutex_lock(&gb_tty->mutex); ++ if (gb_tty->disconnected) { ++ mutex_unlock(&gb_tty->mutex); ++ gb_tty = NULL; ++ } else { ++ tty_port_get(&gb_tty->port); ++ mutex_unlock(&gb_tty->mutex); ++ } ++ } ++ mutex_unlock(&table_lock); ++ return gb_tty; ++} ++ ++static int alloc_minor(struct gb_tty *gb_tty) ++{ ++ int minor; ++ ++ mutex_lock(&table_lock); ++ minor = idr_alloc(&tty_minors, gb_tty, 0, GB_NUM_MINORS, GFP_KERNEL); ++ mutex_unlock(&table_lock); ++ if (minor >= 0) ++ gb_tty->minor = minor; ++ return minor; ++} ++ ++static void release_minor(struct gb_tty *gb_tty) ++{ ++ int minor = gb_tty->minor; ++ ++ gb_tty->minor = 0; /* Maybe should use an invalid value instead */ ++ mutex_lock(&table_lock); ++ idr_remove(&tty_minors, minor); ++ mutex_unlock(&table_lock); ++} ++ ++static int gb_tty_install(struct tty_driver *driver, struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty; ++ int retval; ++ ++ gb_tty = get_gb_by_minor(tty->index); ++ if (!gb_tty) ++ return -ENODEV; ++ ++ retval = tty_standard_install(driver, tty); ++ if (retval) ++ goto error; ++ ++ tty->driver_data = gb_tty; ++ return 0; ++error: ++ tty_port_put(&gb_tty->port); ++ return retval; ++} ++ ++static int gb_tty_open(struct tty_struct *tty, struct file *file) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ return tty_port_open(&gb_tty->port, tty, file); ++} ++ ++static void gb_tty_close(struct tty_struct *tty, struct file *file) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ tty_port_close(&gb_tty->port, tty, file); ++} ++ ++static void gb_tty_cleanup(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ tty_port_put(&gb_tty->port); ++} ++ ++static void gb_tty_hangup(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ tty_port_hangup(&gb_tty->port); ++} ++ ++static int gb_tty_write(struct tty_struct *tty, const unsigned char *buf, ++ int count) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ count = kfifo_in_spinlocked(&gb_tty->write_fifo, buf, count, ++ &gb_tty->write_lock); ++ if (count && !gb_tty->close_pending) ++ schedule_work(&gb_tty->tx_work); ++ ++ return count; ++} ++ ++static int gb_tty_write_room(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ unsigned long flags; ++ int room; ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ room = kfifo_avail(&gb_tty->write_fifo); ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ room -= GB_UART_WRITE_ROOM_MARGIN; ++ if (room < 0) ++ return 0; ++ ++ return room; ++} ++ ++static int gb_tty_chars_in_buffer(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ unsigned long flags; ++ int chars; ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ chars = kfifo_len(&gb_tty->write_fifo); ++ if (gb_tty->credits < GB_UART_FIRMWARE_CREDITS) ++ chars += GB_UART_FIRMWARE_CREDITS - gb_tty->credits; ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ return chars; ++} ++ ++static int gb_tty_break_ctl(struct tty_struct *tty, int state) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ return send_break(gb_tty, state ? 1 : 0); ++} ++ ++static void gb_tty_set_termios(struct tty_struct *tty, ++ struct ktermios *termios_old) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ struct ktermios *termios = &tty->termios; ++ struct gb_tty_line_coding newline; ++ u8 newctrl = gb_tty->ctrlout; ++ ++ newline.rate = cpu_to_le32(tty_get_baud_rate(tty)); ++ newline.format = termios->c_cflag & CSTOPB ? ++ GB_SERIAL_2_STOP_BITS : GB_SERIAL_1_STOP_BITS; ++ newline.parity = termios->c_cflag & PARENB ? ++ (termios->c_cflag & PARODD ? 1 : 2) + ++ (termios->c_cflag & CMSPAR ? 2 : 0) : 0; ++ ++ switch (termios->c_cflag & CSIZE) { ++ case CS5: ++ newline.data_bits = 5; ++ break; ++ case CS6: ++ newline.data_bits = 6; ++ break; ++ case CS7: ++ newline.data_bits = 7; ++ break; ++ case CS8: ++ default: ++ newline.data_bits = 8; ++ break; ++ } ++ ++ /* FIXME: needs to clear unsupported bits in the termios */ ++ gb_tty->clocal = ((termios->c_cflag & CLOCAL) != 0); ++ ++ if (C_BAUD(tty) == B0) { ++ newline.rate = gb_tty->line_coding.rate; ++ newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); ++ } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { ++ newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); ++ } ++ ++ if (newctrl != gb_tty->ctrlout) { ++ gb_tty->ctrlout = newctrl; ++ send_control(gb_tty, newctrl); ++ } ++ ++ if (C_CRTSCTS(tty) && C_BAUD(tty) != B0) ++ newline.flow_control |= GB_SERIAL_AUTO_RTSCTS_EN; ++ else ++ newline.flow_control &= ~GB_SERIAL_AUTO_RTSCTS_EN; ++ ++ if (memcmp(&gb_tty->line_coding, &newline, sizeof(newline))) { ++ memcpy(&gb_tty->line_coding, &newline, sizeof(newline)); ++ send_line_coding(gb_tty); ++ } ++} ++ ++static int gb_tty_tiocmget(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ return (gb_tty->ctrlout & GB_UART_CTRL_DTR ? TIOCM_DTR : 0) | ++ (gb_tty->ctrlout & GB_UART_CTRL_RTS ? TIOCM_RTS : 0) | ++ (gb_tty->ctrlin & GB_UART_CTRL_DSR ? TIOCM_DSR : 0) | ++ (gb_tty->ctrlin & GB_UART_CTRL_RI ? TIOCM_RI : 0) | ++ (gb_tty->ctrlin & GB_UART_CTRL_DCD ? TIOCM_CD : 0) | ++ TIOCM_CTS; ++} ++ ++static int gb_tty_tiocmset(struct tty_struct *tty, unsigned int set, ++ unsigned int clear) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ u8 newctrl = gb_tty->ctrlout; ++ ++ set = (set & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | ++ (set & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); ++ clear = (clear & TIOCM_DTR ? GB_UART_CTRL_DTR : 0) | ++ (clear & TIOCM_RTS ? GB_UART_CTRL_RTS : 0); ++ ++ newctrl = (newctrl & ~clear) | set; ++ if (gb_tty->ctrlout == newctrl) ++ return 0; ++ ++ gb_tty->ctrlout = newctrl; ++ return send_control(gb_tty, newctrl); ++} ++ ++static void gb_tty_throttle(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ unsigned char stop_char; ++ int retval; ++ ++ if (I_IXOFF(tty)) { ++ stop_char = STOP_CHAR(tty); ++ retval = gb_tty_write(tty, &stop_char, 1); ++ if (retval <= 0) ++ return; ++ } ++ ++ if (tty->termios.c_cflag & CRTSCTS) { ++ gb_tty->ctrlout &= ~GB_UART_CTRL_RTS; ++ retval = send_control(gb_tty, gb_tty->ctrlout); ++ } ++} ++ ++static void gb_tty_unthrottle(struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ unsigned char start_char; ++ int retval; ++ ++ if (I_IXOFF(tty)) { ++ start_char = START_CHAR(tty); ++ retval = gb_tty_write(tty, &start_char, 1); ++ if (retval <= 0) ++ return; ++ } ++ ++ if (tty->termios.c_cflag & CRTSCTS) { ++ gb_tty->ctrlout |= GB_UART_CTRL_RTS; ++ retval = send_control(gb_tty, gb_tty->ctrlout); ++ } ++} ++ ++static int get_serial_info(struct gb_tty *gb_tty, ++ struct serial_struct __user *info) ++{ ++ struct serial_struct tmp; ++ ++ if (!info) ++ return -EINVAL; ++ ++ memset(&tmp, 0, sizeof(tmp)); ++ tmp.flags = ASYNC_LOW_LATENCY | ASYNC_SKIP_TEST; ++ tmp.type = PORT_16550A; ++ tmp.line = gb_tty->minor; ++ tmp.xmit_fifo_size = 16; ++ tmp.baud_base = 9600; ++ tmp.close_delay = gb_tty->port.close_delay / 10; ++ tmp.closing_wait = gb_tty->port.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ++ ASYNC_CLOSING_WAIT_NONE : gb_tty->port.closing_wait / 10; ++ ++ if (copy_to_user(info, &tmp, sizeof(tmp))) ++ return -EFAULT; ++ return 0; ++} ++ ++static int set_serial_info(struct gb_tty *gb_tty, ++ struct serial_struct __user *newinfo) ++{ ++ struct serial_struct new_serial; ++ unsigned int closing_wait; ++ unsigned int close_delay; ++ int retval = 0; ++ ++ if (copy_from_user(&new_serial, newinfo, sizeof(new_serial))) ++ return -EFAULT; ++ ++ close_delay = new_serial.close_delay * 10; ++ closing_wait = new_serial.closing_wait == ASYNC_CLOSING_WAIT_NONE ? ++ ASYNC_CLOSING_WAIT_NONE : new_serial.closing_wait * 10; ++ ++ mutex_lock(&gb_tty->port.mutex); ++ if (!capable(CAP_SYS_ADMIN)) { ++ if ((close_delay != gb_tty->port.close_delay) || ++ (closing_wait != gb_tty->port.closing_wait)) ++ retval = -EPERM; ++ else ++ retval = -EOPNOTSUPP; ++ } else { ++ gb_tty->port.close_delay = close_delay; ++ gb_tty->port.closing_wait = closing_wait; ++ } ++ mutex_unlock(&gb_tty->port.mutex); ++ return retval; ++} ++ ++static int wait_serial_change(struct gb_tty *gb_tty, unsigned long arg) ++{ ++ int retval = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ struct async_icount old; ++ struct async_icount new; ++ ++ if (!(arg & (TIOCM_DSR | TIOCM_RI | TIOCM_CD))) ++ return -EINVAL; ++ ++ do { ++ spin_lock_irq(&gb_tty->read_lock); ++ old = gb_tty->oldcount; ++ new = gb_tty->iocount; ++ gb_tty->oldcount = new; ++ spin_unlock_irq(&gb_tty->read_lock); ++ ++ if ((arg & TIOCM_DSR) && (old.dsr != new.dsr)) ++ break; ++ if ((arg & TIOCM_CD) && (old.dcd != new.dcd)) ++ break; ++ if ((arg & TIOCM_RI) && (old.rng != new.rng)) ++ break; ++ ++ add_wait_queue(&gb_tty->wioctl, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule(); ++ remove_wait_queue(&gb_tty->wioctl, &wait); ++ if (gb_tty->disconnected) { ++ if (arg & TIOCM_CD) ++ break; ++ retval = -ENODEV; ++ } else if (signal_pending(current)) { ++ retval = -ERESTARTSYS; ++ } ++ } while (!retval); ++ ++ return retval; ++} ++ ++static int get_serial_usage(struct gb_tty *gb_tty, ++ struct serial_icounter_struct __user *count) ++{ ++ struct serial_icounter_struct icount; ++ int retval = 0; ++ ++ memset(&icount, 0, sizeof(icount)); ++ icount.dsr = gb_tty->iocount.dsr; ++ icount.rng = gb_tty->iocount.rng; ++ icount.dcd = gb_tty->iocount.dcd; ++ icount.frame = gb_tty->iocount.frame; ++ icount.overrun = gb_tty->iocount.overrun; ++ icount.parity = gb_tty->iocount.parity; ++ icount.brk = gb_tty->iocount.brk; ++ ++ if (copy_to_user(count, &icount, sizeof(icount)) > 0) ++ retval = -EFAULT; ++ ++ return retval; ++} ++ ++static int gb_tty_ioctl(struct tty_struct *tty, unsigned int cmd, ++ unsigned long arg) ++{ ++ struct gb_tty *gb_tty = tty->driver_data; ++ ++ switch (cmd) { ++ case TIOCGSERIAL: ++ return get_serial_info(gb_tty, ++ (struct serial_struct __user *)arg); ++ case TIOCSSERIAL: ++ return set_serial_info(gb_tty, ++ (struct serial_struct __user *)arg); ++ case TIOCMIWAIT: ++ return wait_serial_change(gb_tty, arg); ++ case TIOCGICOUNT: ++ return get_serial_usage(gb_tty, ++ (struct serial_icounter_struct __user *)arg); ++ } ++ ++ return -ENOIOCTLCMD; ++} ++ ++static void gb_tty_dtr_rts(struct tty_port *port, int on) ++{ ++ struct gb_tty *gb_tty; ++ u8 newctrl; ++ ++ gb_tty = container_of(port, struct gb_tty, port); ++ newctrl = gb_tty->ctrlout; ++ ++ if (on) ++ newctrl |= (GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); ++ else ++ newctrl &= ~(GB_UART_CTRL_DTR | GB_UART_CTRL_RTS); ++ ++ gb_tty->ctrlout = newctrl; ++ send_control(gb_tty, newctrl); ++} ++ ++static int gb_tty_port_activate(struct tty_port *port, ++ struct tty_struct *tty) ++{ ++ struct gb_tty *gb_tty; ++ ++ gb_tty = container_of(port, struct gb_tty, port); ++ ++ return gbphy_runtime_get_sync(gb_tty->gbphy_dev); ++} ++ ++static void gb_tty_port_shutdown(struct tty_port *port) ++{ ++ struct gb_tty *gb_tty; ++ unsigned long flags; ++ int ret; ++ ++ gb_tty = container_of(port, struct gb_tty, port); ++ ++ gb_tty->close_pending = true; ++ ++ cancel_work_sync(&gb_tty->tx_work); ++ ++ spin_lock_irqsave(&gb_tty->write_lock, flags); ++ kfifo_reset_out(&gb_tty->write_fifo); ++ spin_unlock_irqrestore(&gb_tty->write_lock, flags); ++ ++ if (gb_tty->credits == GB_UART_FIRMWARE_CREDITS) ++ goto out; ++ ++ ret = gb_uart_flush(gb_tty, GB_SERIAL_FLAG_FLUSH_TRANSMITTER); ++ if (ret) { ++ dev_err(&gb_tty->gbphy_dev->dev, ++ "error flushing transmitter: %d\n", ret); ++ } ++ ++ gb_uart_wait_for_all_credits(gb_tty); ++ ++out: ++ gb_tty->close_pending = false; ++ ++ gbphy_runtime_put_autosuspend(gb_tty->gbphy_dev); ++} ++ ++static const struct tty_operations gb_ops = { ++ .install = gb_tty_install, ++ .open = gb_tty_open, ++ .close = gb_tty_close, ++ .cleanup = gb_tty_cleanup, ++ .hangup = gb_tty_hangup, ++ .write = gb_tty_write, ++ .write_room = gb_tty_write_room, ++ .ioctl = gb_tty_ioctl, ++ .throttle = gb_tty_throttle, ++ .unthrottle = gb_tty_unthrottle, ++ .chars_in_buffer = gb_tty_chars_in_buffer, ++ .break_ctl = gb_tty_break_ctl, ++ .set_termios = gb_tty_set_termios, ++ .tiocmget = gb_tty_tiocmget, ++ .tiocmset = gb_tty_tiocmset, ++}; ++ ++static struct tty_port_operations gb_port_ops = { ++ .dtr_rts = gb_tty_dtr_rts, ++ .activate = gb_tty_port_activate, ++ .shutdown = gb_tty_port_shutdown, ++}; ++ ++static int gb_uart_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ size_t max_payload; ++ struct gb_tty *gb_tty; ++ struct device *tty_dev; ++ int retval; ++ int minor; ++ ++ gb_tty = kzalloc(sizeof(*gb_tty), GFP_KERNEL); ++ if (!gb_tty) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ gb_uart_request_handler); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto exit_tty_free; ++ } ++ ++ max_payload = gb_operation_get_payload_size_max(connection); ++ if (max_payload < sizeof(struct gb_uart_send_data_request)) { ++ retval = -EINVAL; ++ goto exit_connection_destroy; ++ } ++ ++ gb_tty->buffer_payload_max = max_payload - ++ sizeof(struct gb_uart_send_data_request); ++ ++ gb_tty->buffer = kzalloc(gb_tty->buffer_payload_max, GFP_KERNEL); ++ if (!gb_tty->buffer) { ++ retval = -ENOMEM; ++ goto exit_connection_destroy; ++ } ++ ++ INIT_WORK(&gb_tty->tx_work, gb_uart_tx_write_work); ++ ++ retval = kfifo_alloc(&gb_tty->write_fifo, GB_UART_WRITE_FIFO_SIZE, ++ GFP_KERNEL); ++ if (retval) ++ goto exit_buf_free; ++ ++ gb_tty->credits = GB_UART_FIRMWARE_CREDITS; ++ init_completion(&gb_tty->credits_complete); ++ ++ minor = alloc_minor(gb_tty); ++ if (minor < 0) { ++ if (minor == -ENOSPC) { ++ dev_err(&connection->bundle->dev, ++ "no more free minor numbers\n"); ++ retval = -ENODEV; ++ } else { ++ retval = minor; ++ } ++ goto exit_kfifo_free; ++ } ++ ++ gb_tty->minor = minor; ++ spin_lock_init(&gb_tty->write_lock); ++ spin_lock_init(&gb_tty->read_lock); ++ init_waitqueue_head(&gb_tty->wioctl); ++ mutex_init(&gb_tty->mutex); ++ ++ tty_port_init(&gb_tty->port); ++ gb_tty->port.ops = &gb_port_ops; ++ ++ gb_tty->connection = connection; ++ gb_tty->gbphy_dev = gbphy_dev; ++ gb_connection_set_data(connection, gb_tty); ++ gb_gbphy_set_data(gbphy_dev, gb_tty); ++ ++ retval = gb_connection_enable_tx(connection); ++ if (retval) ++ goto exit_release_minor; ++ ++ send_control(gb_tty, gb_tty->ctrlout); ++ ++ /* initialize the uart to be 9600n81 */ ++ gb_tty->line_coding.rate = cpu_to_le32(9600); ++ gb_tty->line_coding.format = GB_SERIAL_1_STOP_BITS; ++ gb_tty->line_coding.parity = GB_SERIAL_NO_PARITY; ++ gb_tty->line_coding.data_bits = 8; ++ send_line_coding(gb_tty); ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto exit_connection_disable; ++ ++ tty_dev = tty_port_register_device(&gb_tty->port, gb_tty_driver, minor, ++ &gbphy_dev->dev); ++ if (IS_ERR(tty_dev)) { ++ retval = PTR_ERR(tty_dev); ++ goto exit_connection_disable; ++ } ++ ++ gbphy_runtime_put_autosuspend(gbphy_dev); ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_release_minor: ++ release_minor(gb_tty); ++exit_kfifo_free: ++ kfifo_free(&gb_tty->write_fifo); ++exit_buf_free: ++ kfree(gb_tty->buffer); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_tty_free: ++ kfree(gb_tty); ++ ++ return retval; ++} ++ ++static void gb_uart_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_tty *gb_tty = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = gb_tty->connection; ++ struct tty_struct *tty; ++ int ret; ++ ++ ret = gbphy_runtime_get_sync(gbphy_dev); ++ if (ret) ++ gbphy_runtime_get_noresume(gbphy_dev); ++ ++ mutex_lock(&gb_tty->mutex); ++ gb_tty->disconnected = true; ++ ++ wake_up_all(&gb_tty->wioctl); ++ mutex_unlock(&gb_tty->mutex); ++ ++ tty = tty_port_tty_get(&gb_tty->port); ++ if (tty) { ++ tty_vhangup(tty); ++ tty_kref_put(tty); ++ } ++ ++ gb_connection_disable_rx(connection); ++ tty_unregister_device(gb_tty_driver, gb_tty->minor); ++ ++ /* FIXME - free transmit / receive buffers */ ++ ++ gb_connection_disable(connection); ++ tty_port_destroy(&gb_tty->port); ++ gb_connection_destroy(connection); ++ release_minor(gb_tty); ++ kfifo_free(&gb_tty->write_fifo); ++ kfree(gb_tty->buffer); ++ kfree(gb_tty); ++} ++ ++static int gb_tty_init(void) ++{ ++ int retval = 0; ++ ++ gb_tty_driver = tty_alloc_driver(GB_NUM_MINORS, 0); ++ if (IS_ERR(gb_tty_driver)) { ++ pr_err("Can not allocate tty driver\n"); ++ retval = -ENOMEM; ++ goto fail_unregister_dev; ++ } ++ ++ gb_tty_driver->driver_name = "gb"; ++ gb_tty_driver->name = GB_NAME; ++ gb_tty_driver->major = 0; ++ gb_tty_driver->minor_start = 0; ++ gb_tty_driver->type = TTY_DRIVER_TYPE_SERIAL; ++ gb_tty_driver->subtype = SERIAL_TYPE_NORMAL; ++ gb_tty_driver->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV; ++ gb_tty_driver->init_termios = tty_std_termios; ++ gb_tty_driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; ++ tty_set_operations(gb_tty_driver, &gb_ops); ++ ++ retval = tty_register_driver(gb_tty_driver); ++ if (retval) { ++ pr_err("Can not register tty driver: %d\n", retval); ++ goto fail_put_gb_tty; ++ } ++ ++ return 0; ++ ++fail_put_gb_tty: ++ put_tty_driver(gb_tty_driver); ++fail_unregister_dev: ++ return retval; ++} ++ ++static void gb_tty_exit(void) ++{ ++ tty_unregister_driver(gb_tty_driver); ++ put_tty_driver(gb_tty_driver); ++ idr_destroy(&tty_minors); ++} ++ ++static const struct gbphy_device_id gb_uart_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_UART) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_uart_id_table); ++ ++static struct gbphy_driver uart_driver = { ++ .name = "uart", ++ .probe = gb_uart_probe, ++ .remove = gb_uart_remove, ++ .id_table = gb_uart_id_table, ++}; ++ ++static int gb_uart_driver_init(void) ++{ ++ int ret; ++ ++ ret = gb_tty_init(); ++ if (ret) ++ return ret; ++ ++ ret = gb_gbphy_register(&uart_driver); ++ if (ret) { ++ gb_tty_exit(); ++ return ret; ++ } ++ ++ return 0; ++} ++module_init(gb_uart_driver_init); ++ ++static void gb_uart_driver_exit(void) ++{ ++ gb_gbphy_deregister(&uart_driver); ++ gb_tty_exit(); ++} ++ ++module_exit(gb_uart_driver_exit); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_usb.patch b/greybus_usb.patch new file mode 100644 index 00000000000000..c2719d11714126 --- /dev/null +++ b/greybus_usb.patch @@ -0,0 +1,254 @@ +--- + drivers/greybus/usb.c | 247 ++++++++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 247 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/usb.c +@@ -0,0 +1,247 @@ ++/* ++ * USB host driver for the Greybus "generic" USB module. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/usb.h> ++#include <linux/usb/hcd.h> ++ ++#include "greybus.h" ++#include "gbphy.h" ++ ++/* Greybus USB request types */ ++#define GB_USB_TYPE_HCD_START 0x02 ++#define GB_USB_TYPE_HCD_STOP 0x03 ++#define GB_USB_TYPE_HUB_CONTROL 0x04 ++ ++struct gb_usb_hub_control_request { ++ __le16 typeReq; ++ __le16 wValue; ++ __le16 wIndex; ++ __le16 wLength; ++}; ++ ++struct gb_usb_hub_control_response { ++ u8 buf[0]; ++}; ++ ++struct gb_usb_device { ++ struct gb_connection *connection; ++ struct gbphy_device *gbphy_dev; ++}; ++ ++static inline struct gb_usb_device *to_gb_usb_device(struct usb_hcd *hcd) ++{ ++ return (struct gb_usb_device *)hcd->hcd_priv; ++} ++ ++static inline struct usb_hcd *gb_usb_device_to_hcd(struct gb_usb_device *dev) ++{ ++ return container_of((void *)dev, struct usb_hcd, hcd_priv); ++} ++ ++static void hcd_stop(struct usb_hcd *hcd) ++{ ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ int ret; ++ ++ ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_STOP, ++ NULL, 0, NULL, 0); ++ if (ret) ++ dev_err(&dev->gbphy_dev->dev, "HCD stop failed '%d'\n", ret); ++} ++ ++static int hcd_start(struct usb_hcd *hcd) ++{ ++ struct usb_bus *bus = hcd_to_bus(hcd); ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ int ret; ++ ++ ret = gb_operation_sync(dev->connection, GB_USB_TYPE_HCD_START, ++ NULL, 0, NULL, 0); ++ if (ret) { ++ dev_err(&dev->gbphy_dev->dev, "HCD start failed '%d'\n", ret); ++ return ret; ++ } ++ ++ hcd->state = HC_STATE_RUNNING; ++ if (bus->root_hub) ++ usb_hcd_resume_root_hub(hcd); ++ return 0; ++} ++ ++static int urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) ++{ ++ return -ENXIO; ++} ++ ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++{ ++ return -ENXIO; ++} ++ ++static int get_frame_number(struct usb_hcd *hcd) ++{ ++ return 0; ++} ++ ++static int hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ return 0; ++} ++ ++static int hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, u16 wIndex, ++ char *buf, u16 wLength) ++{ ++ struct gb_usb_device *dev = to_gb_usb_device(hcd); ++ struct gb_operation *operation; ++ struct gb_usb_hub_control_request *request; ++ struct gb_usb_hub_control_response *response; ++ size_t response_size; ++ int ret; ++ ++ /* FIXME: handle unspecified lengths */ ++ response_size = sizeof(*response) + wLength; ++ ++ operation = gb_operation_create(dev->connection, ++ GB_USB_TYPE_HUB_CONTROL, ++ sizeof(*request), ++ response_size, ++ GFP_KERNEL); ++ if (!operation) ++ return -ENOMEM; ++ ++ request = operation->request->payload; ++ request->typeReq = cpu_to_le16(typeReq); ++ request->wValue = cpu_to_le16(wValue); ++ request->wIndex = cpu_to_le16(wIndex); ++ request->wLength = cpu_to_le16(wLength); ++ ++ ret = gb_operation_request_send_sync(operation); ++ if (ret) ++ goto out; ++ ++ if (wLength) { ++ /* Greybus core has verified response size */ ++ response = operation->response->payload; ++ memcpy(buf, response->buf, wLength); ++ } ++out: ++ gb_operation_put(operation); ++ ++ return ret; ++} ++ ++static struct hc_driver usb_gb_hc_driver = { ++ .description = "greybus-hcd", ++ .product_desc = "Greybus USB Host Controller", ++ .hcd_priv_size = sizeof(struct gb_usb_device), ++ ++ .flags = HCD_USB2, ++ ++ .start = hcd_start, ++ .stop = hcd_stop, ++ ++ .urb_enqueue = urb_enqueue, ++ .urb_dequeue = urb_dequeue, ++ ++ .get_frame_number = get_frame_number, ++ .hub_status_data = hub_status_data, ++ .hub_control = hub_control, ++}; ++ ++static int gb_usb_probe(struct gbphy_device *gbphy_dev, ++ const struct gbphy_device_id *id) ++{ ++ struct gb_connection *connection; ++ struct device *dev = &gbphy_dev->dev; ++ struct gb_usb_device *gb_usb_dev; ++ struct usb_hcd *hcd; ++ int retval; ++ ++ hcd = usb_create_hcd(&usb_gb_hc_driver, dev, dev_name(dev)); ++ if (!hcd) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(gbphy_dev->bundle, ++ le16_to_cpu(gbphy_dev->cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto exit_usb_put; ++ } ++ ++ gb_usb_dev = to_gb_usb_device(hcd); ++ gb_usb_dev->connection = connection; ++ gb_connection_set_data(connection, gb_usb_dev); ++ gb_usb_dev->gbphy_dev = gbphy_dev; ++ gb_gbphy_set_data(gbphy_dev, gb_usb_dev); ++ ++ hcd->has_tt = 1; ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto exit_connection_destroy; ++ ++ /* ++ * FIXME: The USB bridged-PHY protocol driver depends on changes to ++ * USB core which are not yet upstream. ++ * ++ * Disable for now. ++ */ ++ if (1) { ++ dev_warn(dev, "USB protocol disabled\n"); ++ retval = -EPROTONOSUPPORT; ++ goto exit_connection_disable; ++ } ++ ++ retval = usb_add_hcd(hcd, 0, 0); ++ if (retval) ++ goto exit_connection_disable; ++ ++ return 0; ++ ++exit_connection_disable: ++ gb_connection_disable(connection); ++exit_connection_destroy: ++ gb_connection_destroy(connection); ++exit_usb_put: ++ usb_put_hcd(hcd); ++ ++ return retval; ++} ++ ++static void gb_usb_remove(struct gbphy_device *gbphy_dev) ++{ ++ struct gb_usb_device *gb_usb_dev = gb_gbphy_get_data(gbphy_dev); ++ struct gb_connection *connection = gb_usb_dev->connection; ++ struct usb_hcd *hcd = gb_usb_device_to_hcd(gb_usb_dev); ++ ++ usb_remove_hcd(hcd); ++ gb_connection_disable(connection); ++ gb_connection_destroy(connection); ++ usb_put_hcd(hcd); ++} ++ ++static const struct gbphy_device_id gb_usb_id_table[] = { ++ { GBPHY_PROTOCOL(GREYBUS_PROTOCOL_USB) }, ++ { }, ++}; ++MODULE_DEVICE_TABLE(gbphy, gb_usb_id_table); ++ ++static struct gbphy_driver usb_driver = { ++ .name = "usb", ++ .probe = gb_usb_probe, ++ .remove = gb_usb_remove, ++ .id_table = gb_usb_id_table, ++}; ++ ++module_gbphy_driver(usb_driver); ++MODULE_LICENSE("GPL v2"); diff --git a/greybus_vibrator.patch b/greybus_vibrator.patch new file mode 100644 index 00000000000000..083b9fdf368efa --- /dev/null +++ b/greybus_vibrator.patch @@ -0,0 +1,256 @@ +--- + drivers/greybus/vibrator.c | 249 +++++++++++++++++++++++++++++++++++++++++++++ + 1 file changed, 249 insertions(+) + +--- /dev/null ++++ b/drivers/greybus/vibrator.c +@@ -0,0 +1,249 @@ ++/* ++ * Greybus Vibrator protocol driver. ++ * ++ * Copyright 2014 Google Inc. ++ * Copyright 2014 Linaro Ltd. ++ * ++ * Released under the GPLv2 only. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++#include <linux/device.h> ++#include <linux/kdev_t.h> ++#include <linux/idr.h> ++#include <linux/pm_runtime.h> ++ ++#include "greybus.h" ++ ++struct gb_vibrator_device { ++ struct gb_connection *connection; ++ struct device *dev; ++ int minor; /* vibrator minor number */ ++ struct delayed_work delayed_work; ++}; ++ ++/* Greybus Vibrator operation types */ ++#define GB_VIBRATOR_TYPE_ON 0x02 ++#define GB_VIBRATOR_TYPE_OFF 0x03 ++ ++static int turn_off(struct gb_vibrator_device *vib) ++{ ++ struct gb_bundle *bundle = vib->connection->bundle; ++ int ret; ++ ++ ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_OFF, ++ NULL, 0, NULL, 0); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return ret; ++} ++ ++static int turn_on(struct gb_vibrator_device *vib, u16 timeout_ms) ++{ ++ struct gb_bundle *bundle = vib->connection->bundle; ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ return ret; ++ ++ /* Vibrator was switched ON earlier */ ++ if (cancel_delayed_work_sync(&vib->delayed_work)) ++ turn_off(vib); ++ ++ ret = gb_operation_sync(vib->connection, GB_VIBRATOR_TYPE_ON, ++ NULL, 0, NULL, 0); ++ if (ret) { ++ gb_pm_runtime_put_autosuspend(bundle); ++ return ret; ++ } ++ ++ schedule_delayed_work(&vib->delayed_work, msecs_to_jiffies(timeout_ms)); ++ ++ return 0; ++} ++ ++static void gb_vibrator_worker(struct work_struct *work) ++{ ++ struct delayed_work *delayed_work = to_delayed_work(work); ++ struct gb_vibrator_device *vib = ++ container_of(delayed_work, struct gb_vibrator_device, delayed_work); ++ ++ turn_off(vib); ++} ++ ++static ssize_t timeout_store(struct device *dev, struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct gb_vibrator_device *vib = dev_get_drvdata(dev); ++ unsigned long val; ++ int retval; ++ ++ retval = kstrtoul(buf, 10, &val); ++ if (retval < 0) { ++ dev_err(dev, "could not parse timeout value %d\n", retval); ++ return retval; ++ } ++ ++ if (val) ++ retval = turn_on(vib, (u16)val); ++ else ++ retval = turn_off(vib); ++ if (retval) ++ return retval; ++ ++ return count; ++} ++static DEVICE_ATTR_WO(timeout); ++ ++static struct attribute *vibrator_attrs[] = { ++ &dev_attr_timeout.attr, ++ NULL, ++}; ++ATTRIBUTE_GROUPS(vibrator); ++ ++static struct class vibrator_class = { ++ .name = "vibrator", ++ .owner = THIS_MODULE, ++ .dev_groups = vibrator_groups, ++}; ++ ++static DEFINE_IDA(minors); ++ ++static int gb_vibrator_probe(struct gb_bundle *bundle, ++ const struct greybus_bundle_id *id) ++{ ++ struct greybus_descriptor_cport *cport_desc; ++ struct gb_connection *connection; ++ struct gb_vibrator_device *vib; ++ struct device *dev; ++ int retval; ++ ++ if (bundle->num_cports != 1) ++ return -ENODEV; ++ ++ cport_desc = &bundle->cport_desc[0]; ++ if (cport_desc->protocol_id != GREYBUS_PROTOCOL_VIBRATOR) ++ return -ENODEV; ++ ++ vib = kzalloc(sizeof(*vib), GFP_KERNEL); ++ if (!vib) ++ return -ENOMEM; ++ ++ connection = gb_connection_create(bundle, le16_to_cpu(cport_desc->id), ++ NULL); ++ if (IS_ERR(connection)) { ++ retval = PTR_ERR(connection); ++ goto err_free_vib; ++ } ++ gb_connection_set_data(connection, vib); ++ ++ vib->connection = connection; ++ ++ greybus_set_drvdata(bundle, vib); ++ ++ retval = gb_connection_enable(connection); ++ if (retval) ++ goto err_connection_destroy; ++ ++ /* ++ * For now we create a device in sysfs for the vibrator, but odds are ++ * there is a "real" device somewhere in the kernel for this, but I ++ * can't find it at the moment... ++ */ ++ vib->minor = ida_simple_get(&minors, 0, 0, GFP_KERNEL); ++ if (vib->minor < 0) { ++ retval = vib->minor; ++ goto err_connection_disable; ++ } ++ dev = device_create(&vibrator_class, &bundle->dev, ++ MKDEV(0, 0), vib, "vibrator%d", vib->minor); ++ if (IS_ERR(dev)) { ++ retval = -EINVAL; ++ goto err_ida_remove; ++ } ++ vib->dev = dev; ++ ++ INIT_DELAYED_WORK(&vib->delayed_work, gb_vibrator_worker); ++ ++ gb_pm_runtime_put_autosuspend(bundle); ++ ++ return 0; ++ ++err_ida_remove: ++ ida_simple_remove(&minors, vib->minor); ++err_connection_disable: ++ gb_connection_disable(connection); ++err_connection_destroy: ++ gb_connection_destroy(connection); ++err_free_vib: ++ kfree(vib); ++ ++ return retval; ++} ++ ++static void gb_vibrator_disconnect(struct gb_bundle *bundle) ++{ ++ struct gb_vibrator_device *vib = greybus_get_drvdata(bundle); ++ int ret; ++ ++ ret = gb_pm_runtime_get_sync(bundle); ++ if (ret) ++ gb_pm_runtime_get_noresume(bundle); ++ ++ if (cancel_delayed_work_sync(&vib->delayed_work)) ++ turn_off(vib); ++ ++ device_unregister(vib->dev); ++ ida_simple_remove(&minors, vib->minor); ++ gb_connection_disable(vib->connection); ++ gb_connection_destroy(vib->connection); ++ kfree(vib); ++} ++ ++static const struct greybus_bundle_id gb_vibrator_id_table[] = { ++ { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_VIBRATOR) }, ++ { } ++}; ++MODULE_DEVICE_TABLE(greybus, gb_vibrator_id_table); ++ ++static struct greybus_driver gb_vibrator_driver = { ++ .name = "vibrator", ++ .probe = gb_vibrator_probe, ++ .disconnect = gb_vibrator_disconnect, ++ .id_table = gb_vibrator_id_table, ++}; ++ ++static __init int gb_vibrator_init(void) ++{ ++ int retval; ++ ++ retval = class_register(&vibrator_class); ++ if (retval) ++ return retval; ++ ++ retval = greybus_register(&gb_vibrator_driver); ++ if (retval) ++ goto err_class_unregister; ++ ++ return 0; ++ ++err_class_unregister: ++ class_unregister(&vibrator_class); ++ ++ return retval; ++} ++module_init(gb_vibrator_init); ++ ++static __exit void gb_vibrator_exit(void) ++{ ++ greybus_deregister(&gb_vibrator_driver); ++ class_unregister(&vibrator_class); ++ ida_destroy(&minors); ++} ++module_exit(gb_vibrator_exit); ++ ++MODULE_LICENSE("GPL v2"); @@ -1,57 +1,109 @@ -usb-core-add-missing-license-information.patch - -# broken patch! - -csdio2.patch -#input-xpad-set-the-leds-properly-on-xbox-wireless-controllers.patch -#defer-input-nodes-and-led-support -#input-xpad-move-the-input-device-creation-to-a-new-function.patch -#input-xpad-set-the-correct-led-number.patch -#input-xpad-disconnect-all-wireless-controllers-at-init.patch -#input-xpad-handle-present-and-gone-correctly.patch -#input-xpad-properly-name-the-led-class-devices.patch - - -staging-exfat-add-filesystem-to-drivers-staging-exfat.patch -staging-exfat-add-filesystem-to-the-build.patch -staging-exfat-include-aio.h.patch -staging-exfat-hlist_for_each-api-change.patch -staging-exfat-truncage_pagecache-api-change.patch -staging-exfat-readdir-to-iterate-change.patch -staging-exfat-kuid-fixes.patch - - -# patches already in my git trees, but still here so I don't loose them. - -# usb DEBUG cleanups - - -# My specific stuff, at the top to make it easier to work stuff below. - -# driver core attribute cleanup work - -# dev_groups to struct class work - - -simulate-fake-fn-key-on-ps-2-keyboards-w-o-one-eg.-chromebook-pixel.patch - -#gregkh/gkh-version.patch - -driver-core-remove-struct-bus_type.dev_attrs.patch - -p18.patch -p19.patch -p20.patch -p21.patch -p22.patch -p23.patch -p24.patch -p25.patch -p26.patch -p27.patch -p28.patch -p29.patch - -qlcnic_sysfs.patch - -d01.patch +greybus_protocol.patch +greybus_interface.patch +greybus_operations.patch +greybus_hd.patch +greybus_trace.patch +greybus_svc.patch +greybus_core.patch + +greybus_bootrom.patch +greybus_firmware.patch + +greybus_audio.patch +greybus_camera.patch +greybus_es2.patch +greybus_hid.patch +greybus_light.patch +greybus_log.patch +greybus_loopback.patch +greybus_power.patch +greybus_raw.patch +greybus_timesync.patch +greybus_vibrator.patch + +greybus_arche.patch + +greybus_gbphy.patch +greybus_gpio.patch +greybus_i2c.patch +greybus_pwm.patch +greybus_sdio.patch +greybus_spi.patch +greybus_uart.patch +greybus_usb.patch + +greybus_tools.patch +greybus_docs.patch +greybus_build.patch + + +# +#usb-core-add-missing-license-information.patch +# +## broken patch! +# +#csdio2.patch +##input-xpad-set-the-leds-properly-on-xbox-wireless-controllers.patch +##defer-input-nodes-and-led-support +##input-xpad-move-the-input-device-creation-to-a-new-function.patch +##input-xpad-set-the-correct-led-number.patch +##input-xpad-disconnect-all-wireless-controllers-at-init.patch +##input-xpad-handle-present-and-gone-correctly.patch +##input-xpad-properly-name-the-led-class-devices.patch +# +# +#staging-exfat-add-filesystem-to-drivers-staging-exfat.patch +#staging-exfat-add-filesystem-to-the-build.patch +#staging-exfat-include-aio.h.patch +#staging-exfat-hlist_for_each-api-change.patch +#staging-exfat-truncage_pagecache-api-change.patch +#staging-exfat-readdir-to-iterate-change.patch +#staging-exfat-kuid-fixes.patch +# +# +## patches already in my git trees, but still here so I don't loose them. +# +## usb DEBUG cleanups +# +# +## My specific stuff, at the top to make it easier to work stuff below. +# +## driver core attribute cleanup work +# +## dev_groups to struct class work +# +# +#simulate-fake-fn-key-on-ps-2-keyboards-w-o-one-eg.-chromebook-pixel.patch +# +##gregkh/gkh-version.patch +# +#driver-core-remove-struct-bus_type.dev_attrs.patch +# +#p18.patch +#p19.patch +#p20.patch +#p21.patch +#p22.patch +#p23.patch +#p24.patch +#p25.patch +#p26.patch +#p27.patch +#p28.patch +#p29.patch +# +#qlcnic_sysfs.patch +# +#d01.patch +# +# +# +# +# +# +# +# +# +# +# +# |