diff options
Diffstat (limited to 'drivers')
57 files changed, 2073 insertions, 901 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig index 59992788966c..602cbb04bee8 100644 --- a/drivers/base/Kconfig +++ b/drivers/base/Kconfig @@ -165,6 +165,11 @@ config FW_LOADER_USER_HELPER_FALLBACK If you are unsure about this, say N here. +config FW_CACHE + bool "Enable firmware caching during suspend" + depends on PM_SLEEP + default n + config WANT_DEV_COREDUMP bool help diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index a1696e1d199f..c1093c0d4dea 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -1148,7 +1148,7 @@ static int fw_load_from_user_helper(struct firmware *firmware, return _request_firmware_load(fw_priv, desc->opt_flags, timeout); } -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_FW_CACHE /* kill pending requests without uevent to avoid blocking suspend */ static void kill_requests_without_uevent(void) { @@ -1626,7 +1626,7 @@ request_firmware_nowait_into_buf( } EXPORT_SYMBOL_GPL(request_firmware_nowait_into_buf); -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_FW_CACHE static ASYNC_DOMAIN_EXCLUSIVE(fw_cache_domain); /** @@ -1972,7 +1972,7 @@ static void __init fw_cache_init(void) INIT_LIST_HEAD(&fw_cache.head); fw_cache.state = FW_LOADER_NO_CACHE; -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_FW_CACHE spin_lock_init(&fw_cache.name_lock); INIT_LIST_HEAD(&fw_cache.fw_names); @@ -1999,7 +1999,7 @@ static int __init firmware_class_init(void) static void __exit firmware_class_exit(void) { -#ifdef CONFIG_PM_SLEEP +#ifdef CONFIG_FW_CACHE unregister_syscore_ops(&fw_syscore_ops); unregister_pm_notifier(&fw_cache.pm_notify); #endif diff --git a/drivers/char/adsprpc.c b/drivers/char/adsprpc.c index c0787608af56..165c5707a9f7 100644 --- a/drivers/char/adsprpc.c +++ b/drivers/char/adsprpc.c @@ -2318,8 +2318,7 @@ static int fastrpc_file_free(struct fastrpc_file *fl) spin_unlock(&fl->apps->hlock); if (!fl->sctx) { - kfree(fl); - return 0; + goto bail; } spin_lock(&fl->hlock); @@ -2337,6 +2336,8 @@ static int fastrpc_file_free(struct fastrpc_file *fl) fastrpc_session_free(&fl->apps->channel[cid], fl->sctx); if (fl->secsctx) fastrpc_session_free(&fl->apps->channel[cid], fl->secsctx); +bail: + mutex_destroy(&fl->map_mutex); kfree(fl); return 0; } @@ -2348,7 +2349,7 @@ static int fastrpc_device_release(struct inode *inode, struct file *file) if (fl) { if (fl->debugfs_file != NULL) debugfs_remove(fl->debugfs_file); - mutex_destroy(&fl->map_mutex); + fastrpc_file_free(fl); file->private_data = NULL; } diff --git a/drivers/char/diag/Makefile b/drivers/char/diag/Makefile index c5ec4f081c55..d57ebd8d671e 100644 --- a/drivers/char/diag/Makefile +++ b/drivers/char/diag/Makefile @@ -1,6 +1,5 @@ obj-$(CONFIG_DIAG_CHAR) := diagchar.o obj-$(CONFIG_DIAGFWD_BRIDGE_CODE) += diagfwd_bridge.o obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE) += diagfwd_hsic.o -obj-$(CONFIG_USB_QCOM_DIAG_BRIDGE) += diagfwd_smux.o obj-$(CONFIG_MSM_MHI) += diagfwd_mhi.o diagchar-objs := diagchar_core.o diagchar_hdlc.o diagfwd.o diagfwd_glink.o diagfwd_peripheral.o diagfwd_smd.o diagfwd_socket.o diag_mux.o diag_memorydevice.o diag_usb.o diagmem.o diagfwd_cntl.o diag_dci.o diag_masks.o diag_debugfs.o diff --git a/drivers/char/diag/diag_debugfs.c b/drivers/char/diag/diag_debugfs.c index 5e455878ac3e..3d916e790814 100644 --- a/drivers/char/diag/diag_debugfs.c +++ b/drivers/char/diag/diag_debugfs.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -23,7 +23,6 @@ #endif #ifdef CONFIG_USB_QCOM_DIAG_BRIDGE #include "diagfwd_hsic.h" -#include "diagfwd_smux.h" #endif #ifdef CONFIG_MSM_MHI #include "diagfwd_mhi.h" @@ -801,6 +800,7 @@ static ssize_t diag_dbgfs_read_glinkinfo(struct file *file, char __user *ubuf, return ret; } +#ifdef CONFIG_IPC_LOGGING static ssize_t diag_dbgfs_write_debug(struct file *fp, const char __user *buf, size_t count, loff_t *ppos) { @@ -831,6 +831,7 @@ static ssize_t diag_dbgfs_write_debug(struct file *fp, const char __user *buf, diag_debug_mask = (uint16_t)value; return count; } +#endif #ifdef CONFIG_DIAGFWD_BRIDGE_CODE #ifdef CONFIG_USB_QCOM_DIAG_BRIDGE @@ -1088,9 +1089,11 @@ const struct file_operations diag_dbgfs_power_ops = { .read = diag_dbgfs_read_power, }; +#ifdef CONFIG_IPC_LOGGING const struct file_operations diag_dbgfs_debug_ops = { .write = diag_dbgfs_write_debug }; +#endif int diag_debugfs_init(void) { @@ -1145,11 +1148,12 @@ int diag_debugfs_init(void) if (!entry) goto err; +#ifdef CONFIG_IPC_LOGGING entry = debugfs_create_file("debug", 0444, diag_dbgfs_dent, 0, &diag_dbgfs_debug_ops); if (!entry) goto err; - +#endif #ifdef CONFIG_DIAGFWD_BRIDGE_CODE entry = debugfs_create_file("bridge", 0444, diag_dbgfs_dent, 0, &diag_dbgfs_bridge_ops); diff --git a/drivers/char/diag/diag_ipc_logging.h b/drivers/char/diag/diag_ipc_logging.h index 4b8dd1b12c1c..839c8ca02e7c 100644 --- a/drivers/char/diag/diag_ipc_logging.h +++ b/drivers/char/diag/diag_ipc_logging.h @@ -26,9 +26,7 @@ #define DIAG_DEBUG_BRIDGE 0x0040 #define DIAG_DEBUG_CONTROL 0x0080 -#define DIAG_DEBUG - -#ifdef DIAG_DEBUG +#ifdef CONFIG_IPC_LOGGING extern uint16_t diag_debug_mask; extern void *diag_ipc_log; diff --git a/drivers/char/diag/diagchar_core.c b/drivers/char/diag/diagchar_core.c index 6df597dfa750..2bac98117c03 100644 --- a/drivers/char/diag/diagchar_core.c +++ b/drivers/char/diag/diagchar_core.c @@ -165,7 +165,7 @@ static struct mutex apps_data_mutex; #define DIAGPKT_MAX_DELAYED_RSP 0xFFFF -#ifdef DIAG_DEBUG +#ifdef CONFIG_IPC_LOGGING uint16_t diag_debug_mask; void *diag_ipc_log; #endif @@ -1160,7 +1160,7 @@ static void diag_remote_exit(void) return; } -int diagfwd_bridge_init(void) +int diagfwd_bridge_init(bool use_mhi) { return 0; } @@ -3626,7 +3626,7 @@ void diag_ws_release() pm_relax(driver->diag_dev); } -#ifdef DIAG_DEBUG +#ifdef CONFIG_IPC_LOGGING static void diag_debug_init(void) { diag_ipc_log = ipc_log_context_create(DIAG_IPC_LOG_PAGES, "diag", 0); @@ -3743,7 +3743,7 @@ static int diag_mhi_probe(struct platform_device *pdev) diag_remote_exit(); return ret; } - ret = diagfwd_bridge_init(); + ret = diagfwd_bridge_init(true); if (ret) { diagfwd_bridge_exit(); return ret; @@ -3766,6 +3766,39 @@ static struct platform_driver diag_mhi_driver = { }, }; +static int diagfwd_usb_probe(struct platform_device *pdev) +{ + int ret; + + driver->pdev = pdev; + ret = diag_remote_init(); + if (ret) { + diag_remote_exit(); + return ret; + } + ret = diagfwd_bridge_init(false); + if (ret) { + diagfwd_bridge_exit(); + return ret; + } + pr_debug("diag: usb device is ready\n"); + return 0; +} + +static const struct of_device_id diagfwd_usb_table[] = { + {.compatible = "qcom,diagfwd-usb"}, + {}, +}; + +static struct platform_driver diagfwd_usb_driver = { + .probe = diagfwd_usb_probe, + .driver = { + .name = "DIAGFWD USB Platform", + .owner = THIS_MODULE, + .of_match_table = diagfwd_usb_table, + }, +}; + static int __init diagchar_init(void) { dev_t dev; @@ -3892,6 +3925,7 @@ static int __init diagchar_init(void) pr_debug("diagchar initialized now"); platform_driver_register(&diag_mhi_driver); + platform_driver_register(&diagfwd_usb_driver); return 0; fail: diff --git a/drivers/char/diag/diagfwd_bridge.c b/drivers/char/diag/diagfwd_bridge.c index 3342984eb795..ad6203fe5684 100644 --- a/drivers/char/diag/diagfwd_bridge.c +++ b/drivers/char/diag/diagfwd_bridge.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -18,22 +18,26 @@ #include <linux/workqueue.h> #include <linux/ratelimit.h> #include <linux/platform_device.h> -#ifdef USB_QCOM_DIAG_BRIDGE -#include <linux/smux.h> -#endif #include "diag_mux.h" #include "diagfwd_bridge.h" -#ifdef USB_QCOM_DIAG_BRIDGE +#ifdef CONFIG_USB_QCOM_DIAG_BRIDGE #include "diagfwd_hsic.h" -#include "diagfwd_smux.h" #endif #include "diagfwd_mhi.h" #include "diag_dci.h" -#ifdef CONFIG_MSM_MHI -#define diag_mdm_init diag_mhi_init -#else -#define diag_mdm_init diag_hsic_init +#ifndef CONFIG_USB_QCOM_DIAG_BRIDGE +static int diag_hsic_init(void) +{ + return -EINVAL; +} +#endif + +#ifndef CONFIG_MSM_MHI +static int diag_mhi_init(void) +{ + return -EINVAL; +} #endif #define BRIDGE_TO_MUX(x) (x + DIAG_MUX_BRIDGE_BASE) @@ -265,18 +269,16 @@ int diag_remote_dev_write_done(int id, unsigned char *buf, int len, int ctxt) return err; } -int diagfwd_bridge_init() +int diagfwd_bridge_init(bool use_mhi) { int err = 0; - err = diag_mdm_init(); + if (use_mhi) + err = diag_mhi_init(); + else + err = diag_hsic_init(); if (err) goto fail; - #ifdef USB_QCOM_DIAG_BRIDGE - err = diag_smux_init(); - if (err) - goto fail; - #endif return 0; fail: @@ -288,7 +290,6 @@ void diagfwd_bridge_exit() { #ifdef USB_QCOM_DIAG_BRIDGE diag_hsic_exit(); - diag_smux_exit(); #endif } diff --git a/drivers/char/diag/diagfwd_bridge.h b/drivers/char/diag/diagfwd_bridge.h index 62d6b08b5b89..250ef07b0b04 100644 --- a/drivers/char/diag/diagfwd_bridge.h +++ b/drivers/char/diag/diagfwd_bridge.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2014, 2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -51,7 +51,7 @@ struct diagfwd_bridge_info { }; extern struct diagfwd_bridge_info bridge_info[NUM_REMOTE_DEV]; -int diagfwd_bridge_init(void); +int diagfwd_bridge_init(bool use_mhi); void diagfwd_bridge_exit(void); int diagfwd_bridge_close(int id); int diagfwd_bridge_write(int id, unsigned char *buf, int len); diff --git a/drivers/clk/msm/clock-mmss-8996.c b/drivers/clk/msm/clock-mmss-8996.c index 91c871cae225..30169d3f3a98 100644 --- a/drivers/clk/msm/clock-mmss-8996.c +++ b/drivers/clk/msm/clock-mmss-8996.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2014-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2014-2016, 2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -1517,6 +1517,7 @@ static struct rcg_clk extpclk_clk_src = { .dbg_name = "extpclk_clk_src", .parent = &ext_extpclk_clk_src.c, .ops = &clk_ops_byte, + .flags = CLKFLAG_NO_RATE_CACHE, VDD_DIG_FMAX_MAP3(LOWER, 150000000, LOW, 300000000, NOMINAL, 600000000), CLK_INIT(extpclk_clk_src.c), @@ -2532,6 +2533,7 @@ static struct branch_clk mdss_extpclk_clk = { .dbg_name = "mdss_extpclk_clk", .parent = &extpclk_clk_src.c, .ops = &clk_ops_branch, + .flags = CLKFLAG_NO_RATE_CACHE, CLK_INIT(mdss_extpclk_clk.c), }, }; diff --git a/drivers/gpu/drm/msm/Makefile b/drivers/gpu/drm/msm/Makefile index 678b2178cb69..f8758e063146 100644 --- a/drivers/gpu/drm/msm/Makefile +++ b/drivers/gpu/drm/msm/Makefile @@ -49,6 +49,7 @@ msm_drm-y := \ sde/sde_color_processing.o \ sde/sde_vbif.o \ sde/sde_splash.o \ + sde/sde_recovery_manager.o \ sde_dbg.o \ sde_dbg_evtlog.o \ sde_io_util.o \ diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c index b1cd666f8be4..252a6289881f 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_ctrl.c @@ -484,7 +484,7 @@ static int dsi_ctrl_init_regmap(struct platform_device *pdev, } ctrl->hw.base = ptr; - pr_debug("[%s] map dsi_ctrl registers to %p\n", ctrl->name, + pr_debug("[%s] map dsi_ctrl registers to %pK\n", ctrl->name, ctrl->hw.base); ptr = msm_ioremap(pdev, "mmss_misc", ctrl->name); diff --git a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c index da3b3b548e5f..5bcd0d0634b6 100644 --- a/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c +++ b/drivers/gpu/drm/msm/dsi-staging/dsi_phy.c @@ -93,7 +93,8 @@ static int dsi_phy_regmap_init(struct platform_device *pdev, phy->hw.base = ptr; - pr_debug("[%s] map dsi_phy registers to %p\n", phy->name, phy->hw.base); + pr_debug("[%s] map dsi_phy registers to %pK\n", + phy->name, phy->hw.base); return rc; } diff --git a/drivers/gpu/drm/msm/edp/edp.c b/drivers/gpu/drm/msm/edp/edp.c index 0940e84b2821..2c9d11638f29 100644 --- a/drivers/gpu/drm/msm/edp/edp.c +++ b/drivers/gpu/drm/msm/edp/edp.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014-2015, The Linux Foundation. All rights reserved. + * Copyright (c) 2014-2015,2018 The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -54,7 +54,7 @@ static struct msm_edp *edp_init(struct platform_device *pdev) ret = -ENOMEM; goto fail; } - DBG("eDP probed=%p", edp); + DBG("eDP probed=%pK", edp); edp->pdev = pdev; platform_set_drvdata(pdev, edp); diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c index 46cc521a09f3..302ed39d9218 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.c @@ -3069,6 +3069,8 @@ static int _sde_hdmi_parse_dt(struct device_node *node, { int rc = 0; + struct hdmi *hdmi = display->ctrl.ctrl; + display->name = of_get_property(node, "label", NULL); display->display_type = of_get_property(node, @@ -3079,6 +3081,11 @@ static int _sde_hdmi_parse_dt(struct device_node *node, display->non_pluggable = of_property_read_bool(node, "qcom,non-pluggable"); + display->skip_ddc = of_property_read_bool(node, + "qcom,skip_ddc"); + if (!display->non_pluggable) + hdmi_i2c_destroy(hdmi->i2c); + rc = _sde_hdmi_parse_dt_modes(node, &display->mode_list, &display->num_of_modes); if (rc) diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h index 9cf807e829c7..2aa8d9496c5b 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi.h @@ -154,6 +154,7 @@ struct sde_hdmi { struct sde_edid_ctrl *edid_ctrl; bool non_pluggable; + bool skip_ddc; u32 num_of_modes; struct list_head mode_list; struct drm_display_mode mode; diff --git a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c index bae6b1c84420..2d65fc924f07 100644 --- a/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c +++ b/drivers/gpu/drm/msm/hdmi-staging/sde_hdmi_bridge.c @@ -512,8 +512,8 @@ static void _sde_hdmi_bridge_pre_enable(struct drm_bridge *bridge) } hdmi->power_on = true; } - - _sde_hdmi_bridge_setup_scrambler(hdmi, &display->mode); + if (!display->skip_ddc) + _sde_hdmi_bridge_setup_scrambler(hdmi, &display->mode); if (phy) phy->funcs->powerup(phy, hdmi->pixclock); @@ -822,6 +822,8 @@ static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi, */ int dc_format; struct drm_connector *connector = hdmi->connector; + struct sde_connector *c_conn = to_sde_connector(connector); + struct sde_hdmi *display = (struct sde_hdmi *)c_conn->display; dc_format = sde_hdmi_sink_dc_support(connector, mode); if (dc_format & MSM_MODE_FLAG_RGB444_DC_ENABLE) @@ -835,7 +837,8 @@ static u32 _sde_hdmi_choose_best_format(struct hdmi *hdmi, else if (mode->flags & DRM_MODE_FLAG_SUPPORTS_YUV) return MSM_MODE_FLAG_COLOR_FORMAT_YCBCR420; - SDE_ERROR("Can't get available best display format\n"); + if (display && !display->non_pluggable) + SDE_ERROR("Can't get available best display format\n"); return MSM_MODE_FLAG_COLOR_FORMAT_RGB444; } diff --git a/drivers/gpu/drm/msm/msm_drv.c b/drivers/gpu/drm/msm/msm_drv.c index b57663013dcb..f1bd9967ba81 100644 --- a/drivers/gpu/drm/msm/msm_drv.c +++ b/drivers/gpu/drm/msm/msm_drv.c @@ -145,7 +145,8 @@ void __iomem *msm_ioremap(struct platform_device *pdev, const char *name, } if (reglog) - printk(KERN_DEBUG "IO:region %s %p %08lx\n", dbgname, ptr, size); + dev_dbg(&pdev->dev, "IO:region %s %pK %08lx\n", + dbgname, ptr, size); return ptr; } @@ -158,7 +159,7 @@ void msm_iounmap(struct platform_device *pdev, void __iomem *addr) void msm_writel(u32 data, void __iomem *addr) { if (reglog) - printk(KERN_DEBUG "IO:W %p %08x\n", addr, data); + pr_debug("IO:W %pK %08x\n", addr, data); writel(data, addr); } @@ -166,7 +167,7 @@ u32 msm_readl(const void __iomem *addr) { u32 val = readl(addr); if (reglog) - printk(KERN_ERR "IO:R %p %08x\n", addr, val); + pr_err("IO:R %pK %08x\n", addr, val); return val; } @@ -895,7 +896,7 @@ static int msm_enable_vblank(struct drm_device *dev, unsigned int pipe) struct msm_kms *kms = priv->kms; if (!kms) return -ENXIO; - DBG("dev=%p, crtc=%u", dev, pipe); + DBG("dev=%pK, crtc=%u", dev, pipe); return vblank_ctrl_queue_work(priv, pipe, true); } @@ -905,7 +906,7 @@ static void msm_disable_vblank(struct drm_device *dev, unsigned int pipe) struct msm_kms *kms = priv->kms; if (!kms) return; - DBG("dev=%p, crtc=%u", dev, pipe); + DBG("dev=%pK, crtc=%u", dev, pipe); vblank_ctrl_queue_work(priv, pipe, false); } diff --git a/drivers/gpu/drm/msm/msm_fb.c b/drivers/gpu/drm/msm/msm_fb.c index d222fdd69a57..8d6d83bf6540 100644 --- a/drivers/gpu/drm/msm/msm_fb.c +++ b/drivers/gpu/drm/msm/msm_fb.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. * Copyright (C) 2013 Red Hat * Author: Rob Clark <robdclark@gmail.com> * @@ -59,7 +60,7 @@ static void msm_framebuffer_destroy(struct drm_framebuffer *fb) msm_fb = to_msm_framebuffer(fb); n = drm_format_num_planes(fb->pixel_format); - DBG("destroy: FB ID: %d (%p)", fb->base.id, fb); + DBG("destroy: FB ID: %d (%pK)", fb->base.id, fb); drm_framebuffer_cleanup(fb); @@ -239,7 +240,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, unsigned int hsub, vsub; bool is_modified = false; - DBG("create framebuffer: dev=%p, mode_cmd=%p (%dx%d@%4.4s)", + DBG("create framebuffer: dev=%pK, mode_cmd=%pK (%dx%d@%4.4s)", dev, mode_cmd, mode_cmd->width, mode_cmd->height, (char *)&mode_cmd->pixel_format); @@ -322,7 +323,7 @@ struct drm_framebuffer *msm_framebuffer_init(struct drm_device *dev, goto fail; } - DBG("create: FB ID: %d (%p)", fb->base.id, fb); + DBG("create: FB ID: %d (%pK)", fb->base.id, fb); return fb; diff --git a/drivers/gpu/drm/msm/msm_fbdev.c b/drivers/gpu/drm/msm/msm_fbdev.c index 28b98cc1433c..c71e662d0da1 100644 --- a/drivers/gpu/drm/msm/msm_fbdev.c +++ b/drivers/gpu/drm/msm/msm_fbdev.c @@ -144,7 +144,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, goto fail_unlock; } - DBG("fbi=%p, dev=%p", fbi, dev); + DBG("fbi=%pK, dev=%pK", fbi, dev); fbdev->fb = fb; helper->fb = fb; @@ -166,7 +166,7 @@ static int msm_fbdev_create(struct drm_fb_helper *helper, fbi->fix.smem_start = lower_32_bits(paddr); fbi->fix.smem_len = fbdev->bo->size; - DBG("par=%p, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); + DBG("par=%pK, %dx%d", fbi->par, fbi->var.xres, fbi->var.yres); DBG("allocated %dx%d fb", fbdev->fb->width, fbdev->fb->height); mutex_unlock(&dev->struct_mutex); diff --git a/drivers/gpu/drm/msm/msm_gem.c b/drivers/gpu/drm/msm/msm_gem.c index 3610c8fca5f3..ce5adef21a00 100644 --- a/drivers/gpu/drm/msm/msm_gem.c +++ b/drivers/gpu/drm/msm/msm_gem.c @@ -410,7 +410,7 @@ int msm_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf) pfn = page_to_pfn(pages[pgoff]); - VERB("Inserting %p pfn %lx, pa %lx", vmf->virtual_address, + VERB("Inserting %pK pfn %lx, pa %lx", vmf->virtual_address, pfn, pfn << PAGE_SHIFT); ret = vm_insert_mixed(vma, (unsigned long)vmf->virtual_address, pfn); @@ -770,7 +770,7 @@ void msm_gem_describe(struct drm_gem_object *obj, struct seq_file *m) uint64_t off = drm_vma_node_start(&obj->vma_node); WARN_ON(!mutex_is_locked(&dev->struct_mutex)); - seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %p\t", + seq_printf(m, "%08x: %c(r=%u,w=%u) %2d (%2d) %08llx %pK\t", msm_obj->flags, is_active(msm_obj) ? 'A' : 'I', msm_obj->read_fence, msm_obj->write_fence, obj->name, obj->refcount.refcount.counter, diff --git a/drivers/gpu/drm/msm/sde/sde_crtc.c b/drivers/gpu/drm/msm/sde/sde_crtc.c index 6ad1ce16c20a..01da32ae5958 100644 --- a/drivers/gpu/drm/msm/sde/sde_crtc.c +++ b/drivers/gpu/drm/msm/sde/sde_crtc.c @@ -1563,7 +1563,7 @@ void sde_crtc_cancel_pending_flip(struct drm_crtc *crtc, { struct sde_crtc *sde_crtc = to_sde_crtc(crtc); - SDE_DEBUG("%s: cancel: %p\n", sde_crtc->name, file); + SDE_DEBUG("%s: cancel: %pK\n", sde_crtc->name, file); _sde_crtc_complete_flip(crtc, file); } diff --git a/drivers/gpu/drm/msm/sde/sde_encoder.c b/drivers/gpu/drm/msm/sde/sde_encoder.c index fa17768d9939..77fdcd86c920 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder.c @@ -21,6 +21,7 @@ #include <linux/seq_file.h> #include "msm_drv.h" +#include "sde_recovery_manager.h" #include "sde_kms.h" #include "drm_crtc.h" #include "drm_crtc_helper.h" @@ -603,6 +604,7 @@ static void sde_encoder_underrun_callback(struct drm_encoder *drm_enc, atomic_inc(&phy_enc->underrun_cnt); SDE_EVT32(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); + sde_recovery_set_events(SDE_UNDERRUN); trace_sde_encoder_underrun(DRMID(drm_enc), atomic_read(&phy_enc->underrun_cnt)); SDE_DBG_CTRL("stop_ftrace"); diff --git a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c index 2f89c571fcfc..8db77f2c60e8 100644 --- a/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c +++ b/drivers/gpu/drm/msm/sde/sde_encoder_phys_vid.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -11,6 +11,7 @@ */ #define pr_fmt(fmt) "[drm:%s:%d] " fmt, __func__, __LINE__ +#include "sde_recovery_manager.h" #include "sde_encoder_phys.h" #include "sde_hw_interrupts.h" #include "sde_core_irq.h" @@ -704,6 +705,7 @@ static int sde_encoder_phys_vid_wait_for_vblank( SDE_EVT32(DRMID(phys_enc->parent), vid_enc->hw_intf->idx - INTF_0); SDE_ERROR_VIDENC(vid_enc, "kickoff timed out\n"); + sde_recovery_set_events(SDE_VSYNC_MISS); if (notify && phys_enc->parent_ops.handle_frame_done) phys_enc->parent_ops.handle_frame_done( phys_enc->parent, phys_enc, diff --git a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c index 6a8d9e0cf2e3..76d99c1e8e65 100644 --- a/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c +++ b/drivers/gpu/drm/msm/sde/sde_hw_color_processing_v1_7.c @@ -367,7 +367,7 @@ void sde_setup_dspp_pcc_v1_7(struct sde_hw_dspp *ctx, void *cfg) void __iomem *base; if (!hw_cfg || (hw_cfg->len != sizeof(*pcc) && hw_cfg->payload)) { - DRM_ERROR("invalid params hw %p payload %p payloadsize %d \"\ + DRM_ERROR("invalid params hw %pK payload %pK payloadsize %d \"\ exp size %zd\n", hw_cfg, ((hw_cfg) ? hw_cfg->payload : NULL), ((hw_cfg) ? hw_cfg->len : 0), sizeof(*pcc)); diff --git a/drivers/gpu/drm/msm/sde/sde_kms.c b/drivers/gpu/drm/msm/sde/sde_kms.c index 95ab14ffc3ac..676d480ca802 100644 --- a/drivers/gpu/drm/msm/sde/sde_kms.c +++ b/drivers/gpu/drm/msm/sde/sde_kms.c @@ -37,6 +37,7 @@ #include "sde_encoder.h" #include "sde_plane.h" #include "sde_crtc.h" +#include "sde_recovery_manager.h" #define CREATE_TRACE_POINTS #include "sde_trace.h" @@ -58,6 +59,19 @@ #define SDE_DEBUGFS_DIR "msm_sde" #define SDE_DEBUGFS_HWMASKNAME "hw_log_mask" +static int sde_kms_recovery_callback(int err_code, + struct recovery_client_info *client_info); + +static struct recovery_client_info info = { + .name = "sde_kms", + .recovery_cb = sde_kms_recovery_callback, + .err_supported[0] = {SDE_UNDERRUN, 0, 0}, + .err_supported[1] = {SDE_VSYNC_MISS, 0, 0}, + .no_of_err = 2, + .handle = NULL, + .pdata = NULL, +}; + /** * sdecustom - enable certain driver customizations for sde clients * Enabling this modifies the standard DRM behavior slightly and assumes @@ -1062,6 +1076,8 @@ static void sde_kms_destroy(struct msm_kms *kms) return; } + sde_recovery_client_unregister(info.handle); + info.handle = NULL; _sde_kms_hw_destroy(sde_kms, dev->platformdev); kfree(sde_kms); } @@ -1264,6 +1280,11 @@ static int sde_kms_hw_init(struct msm_kms *kms) goto end; } + rc = sde_recovery_client_register(&info); + if (rc) + pr_err("%s recovery mgr register failed %d\n", + __func__, rc); + sde_kms = to_sde_kms(kms); dev = sde_kms->dev; if (!dev || !dev->platformdev) { @@ -1283,7 +1304,7 @@ static int sde_kms_hw_init(struct msm_kms *kms) SDE_ERROR("mdp register memory map failed\n"); goto error; } - DRM_INFO("mapped mdp address space @%p\n", sde_kms->mmio); + DRM_INFO("mapped mdp address space @%pK\n", sde_kms->mmio); rc = sde_dbg_reg_register_base(SDE_DBG_NAME, sde_kms->mmio, sde_kms->mmio_len); @@ -1487,10 +1508,34 @@ end: return rc; } +static int sde_kms_recovery_callback(int err_code, + struct recovery_client_info *client_info) +{ + int rc = 0; + + switch (err_code) { + case SDE_UNDERRUN: + pr_debug("%s [SDE_UNDERRUN] error is auto HW receovered\n", + __func__); + break; + + case SDE_VSYNC_MISS: + pr_debug("%s [SDE_VSYNC_MISS] trigger soft reset\n", __func__); + break; + + default: + pr_err("%s error %d undefined\n", __func__, err_code); + + } + + return rc; +} + struct msm_kms *sde_kms_init(struct drm_device *dev) { struct msm_drm_private *priv; struct sde_kms *sde_kms; + int rc = 0; if (!dev || !dev->dev_private) { SDE_ERROR("drm device node invalid\n"); @@ -1505,6 +1550,13 @@ struct msm_kms *sde_kms_init(struct drm_device *dev) return ERR_PTR(-ENOMEM); } + rc = sde_init_recovery_mgr(dev); + if (rc) { + SDE_ERROR("Failed SDE recovery mgr Init, err = %d\n", rc); + kfree(sde_kms); + return ERR_PTR(-EFAULT); + } + msm_kms_init(&sde_kms->base, &kms_funcs); sde_kms->dev = dev; diff --git a/drivers/gpu/drm/msm/sde/sde_recovery_manager.c b/drivers/gpu/drm/msm/sde/sde_recovery_manager.c new file mode 100644 index 000000000000..ae42fd309293 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_recovery_manager.c @@ -0,0 +1,399 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 for more details. + * + */ + +#include "sde_recovery_manager.h" +#include "sde_kms.h" + + +static struct recovery_mgr_info *rec_mgr; + +static ssize_t sde_recovery_mgr_rda_clients_attr(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t len = 0; + struct list_head *pos; + struct recovery_client_db *temp = NULL; + + mutex_lock(&rec_mgr->rec_lock); + + len = snprintf(buf, PAGE_SIZE, "Clients:\n"); + + list_for_each(pos, &rec_mgr->client_list) { + temp = list_entry(pos, struct recovery_client_db, list); + + len += snprintf(buf + len, PAGE_SIZE - len, "%s\n", + temp->client_info.name); + } + + mutex_unlock(&rec_mgr->rec_lock); + + return len; +} + +static DEVICE_ATTR(clients, S_IRUGO, sde_recovery_mgr_rda_clients_attr, NULL); + +static struct attribute *recovery_attrs[] = { + &dev_attr_clients.attr, + NULL, +}; + +static struct attribute_group recovery_mgr_attr_group = { + .attrs = recovery_attrs, +}; + +static void sde_recovery_mgr_notify(bool err_state) +{ + char *envp[2]; + char *uevent_str = kzalloc(SZ_4K, GFP_KERNEL); + + if (uevent_str == NULL) { + DRM_ERROR("failed to allocate event string\n"); + return; + } + if (err_state == true) + snprintf(uevent_str, MAX_REC_UEVENT_LEN, + "DISPLAY_ERROR_RECOVERED\n"); + else + snprintf(uevent_str, MAX_REC_UEVENT_LEN, + "DISPLAY_CRITICAL_ERROR\n"); + + DRM_DEBUG("generating uevent [%s]\n", uevent_str); + + envp[0] = uevent_str; + envp[1] = NULL; + + mutex_lock(&rec_mgr->dev->mode_config.mutex); + kobject_uevent_env(&rec_mgr->dev->primary->kdev->kobj, + KOBJ_CHANGE, envp); + mutex_unlock(&rec_mgr->dev->mode_config.mutex); + kfree(uevent_str); +} + +static void sde_recovery_mgr_recover(int err_code) +{ + struct list_head *pos; + struct recovery_client_db *c = NULL; + int tmp_err, rc, pre, post, i; + bool found = false; + static bool rec_flag = true; + + mutex_lock(&rec_mgr->rec_lock); + list_for_each(pos, &rec_mgr->client_list) { + c = list_entry(pos, struct recovery_client_db, list); + + mutex_unlock(&rec_mgr->rec_lock); + + for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { + tmp_err = c->client_info.err_supported[i]. + reported_err_code; + if (tmp_err == err_code) { + found = true; + break; + } + } + + if (found == true) { + + pre = c->client_info.err_supported[i].pre_err_code; + if (pre && pre != '0') + sde_recovery_mgr_recover(pre); + + if (c->client_info.recovery_cb) { + rc = c->client_info.recovery_cb(err_code, + &c->client_info); + if (rc) { + pr_err("%s failed to recover error %d\n", + __func__, err_code); + rec_flag = false; + } else { + pr_debug("%s Recovery successful[%d]\n", + __func__, err_code); + } + } + + post = c->client_info.err_supported[i].post_err_code; + if (post && post != '0') + sde_recovery_mgr_recover(post); + + } + mutex_lock(&rec_mgr->rec_lock); + + if (found) + break; + } + + if (rec_flag) { + pr_debug("%s successful full recovery\n", __func__); + sde_recovery_mgr_notify(true); + } + + mutex_unlock(&rec_mgr->rec_lock); +} + +static void sde_recovery_mgr_event_work(struct work_struct *work) +{ + struct list_head *pos, *q; + struct recovery_event_db *temp_event; + int err_code; + + if (!rec_mgr) { + pr_err("%s recovery manager is NULL\n", __func__); + return; + } + + mutex_lock(&rec_mgr->rec_lock); + + list_for_each_safe(pos, q, &rec_mgr->event_list) { + temp_event = list_entry(pos, struct recovery_event_db, list); + + err_code = temp_event->err; + + rec_mgr->recovery_ongoing = true; + + mutex_unlock(&rec_mgr->rec_lock); + + /* notify error */ + sde_recovery_mgr_notify(false); + /* recover error */ + sde_recovery_mgr_recover(err_code); + + mutex_lock(&rec_mgr->rec_lock); + + list_del(pos); + kfree(temp_event); + } + + rec_mgr->recovery_ongoing = false; + mutex_unlock(&rec_mgr->rec_lock); + +} + +int sde_recovery_set_events(int err) +{ + int rc = 0; + struct list_head *pos; + struct recovery_event_db *temp; + bool found = false; + + mutex_lock(&rec_mgr->rec_lock); + + /* check if there is same event in the list */ + list_for_each(pos, &rec_mgr->event_list) { + temp = list_entry(pos, struct recovery_event_db, list); + if (err == temp->err) { + found = true; + pr_info("%s error %d is already present in list\n", + __func__, err); + break; + } + } + + if (!found) { + temp = kzalloc(sizeof(struct recovery_event_db), GFP_KERNEL); + if (!temp) { + pr_err("%s out of memory\n", __func__); + rc = -ENOMEM; + goto out; + } + temp->err = err; + + list_add_tail(&temp->list, &rec_mgr->event_list); + queue_work(rec_mgr->event_queue, &rec_mgr->event_work); + } + +out: + mutex_unlock(&rec_mgr->rec_lock); + return rc; +} + +int sde_recovery_client_register(struct recovery_client_info *client) +{ + int rc = 0; + struct list_head *pos; + struct recovery_client_db *c = NULL; + bool found = false; + + if (!rec_mgr) { + pr_err("%s recovery manager is not initialized\n", __func__); + return -EPERM; + } + + if (!strlen(client->name)) { + pr_err("%s client name is empty\n", __func__); + return -EINVAL; + } + + mutex_lock(&rec_mgr->rec_lock); + + /* check if there is same client */ + list_for_each(pos, &rec_mgr->client_list) { + c = list_entry(pos, struct recovery_client_db, list); + if (!strcmp(c->client_info.name, + client->name)) { + found = true; + break; + } + } + + if (!found) { + c = kzalloc(sizeof(*c), GFP_KERNEL); + if (!c) { + pr_err("%s out of memory for client", __func__); + rc = -ENOMEM; + goto out; + } + } else { + pr_err("%s client = %s is already registered\n", + __func__, client->name); + client->handle = c; + goto out; + } + + memcpy(&(c->client_info), client, sizeof(struct recovery_client_info)); + + list_add_tail(&c->list, &rec_mgr->client_list); + rec_mgr->num_of_clients++; + + client->handle = c; + +out: + mutex_unlock(&rec_mgr->rec_lock); + return rc; +} + +int sde_recovery_client_unregister(void *handle) +{ + struct list_head *pos, *q, *pos1; + struct recovery_client_db *temp_client; + struct recovery_event_db *temp; + int client_err = 0; + bool found = false; + bool found_pending = false; + int i, rc = 0; + struct recovery_client_info *client = + &((struct recovery_client_db *)handle)->client_info; + + if (!handle) { + pr_err("%s handle is NULL\n", __func__); + return -EINVAL; + } + + if (!strlen(client->name)) { + pr_err("%s client name is empty\n", __func__); + return -EINVAL; + } + + mutex_lock(&rec_mgr->rec_lock); + + if (rec_mgr->recovery_ongoing) { + pr_err("%s SDE Executing Recovery, Failed! Unregister client %s\n", + __func__, client->name); + goto out; + } + + /* check if client is present in the list */ + list_for_each_safe(pos, q, &rec_mgr->client_list) { + temp_client = list_entry(pos, struct recovery_client_db, list); + if (!strcmp(temp_client->client_info.name, client->name)) { + found = true; + + /* free any pending event for this client */ + list_for_each(pos1, &rec_mgr->event_list) { + temp = list_entry(pos1, + struct recovery_event_db, list); + + found_pending = false; + for (i = 0; i < MAX_REC_ERR_SUPPORT; i++) { + client_err = temp_client-> + client_info.err_supported[i]. + reported_err_code; + if (temp->err == client_err) + found_pending = true; + } + + if (found_pending) { + list_del(pos1); + kfree(temp); + } + } + + list_del(pos); + kfree(temp_client); + rec_mgr->num_of_clients--; + break; + } + } + + if (!found) { + pr_err("%s can't find the client[%s] from db\n", + __func__, client->name); + rc = -EFAULT; + } + +out: + mutex_unlock(&rec_mgr->rec_lock); + return rc; +} + +int sde_init_recovery_mgr(struct drm_device *dev) +{ + struct recovery_mgr_info *rec = NULL; + int rc = 0; + + if (!dev || !dev->dev_private) { + SDE_ERROR("drm device node invalid\n"); + return -EINVAL; + } + + rec = kzalloc(sizeof(struct recovery_mgr_info), GFP_KERNEL); + if (!rec) + return -ENOMEM; + + mutex_init(&rec->rec_lock); + + rec->dev = dev; + rc = sysfs_create_group(&dev->primary->kdev->kobj, + &recovery_mgr_attr_group); + if (rc) { + pr_err("%s sysfs_create_group fails=%d", __func__, rc); + rec->sysfs_created = false; + } else { + rec->sysfs_created = true; + } + + INIT_LIST_HEAD(&rec->event_list); + INIT_LIST_HEAD(&rec->client_list); + INIT_WORK(&rec->event_work, sde_recovery_mgr_event_work); + rec->event_queue = create_workqueue("recovery_event"); + + if (IS_ERR_OR_NULL(rec->event_queue)) { + pr_err("%s unable to create queue; errno = %ld", + __func__, PTR_ERR(rec->event_queue)); + rec->event_queue = NULL; + rc = -EFAULT; + goto err; + } + + rec_mgr = rec; + + return rc; + +err: + mutex_destroy(&rec->rec_lock); + if (rec->sysfs_created) + sysfs_remove_group(&rec_mgr->dev->primary->kdev->kobj, + &recovery_mgr_attr_group); + kfree(rec); + return rc; +} diff --git a/drivers/gpu/drm/msm/sde/sde_recovery_manager.h b/drivers/gpu/drm/msm/sde/sde_recovery_manager.h new file mode 100644 index 000000000000..32fe17a187a0 --- /dev/null +++ b/drivers/gpu/drm/msm/sde/sde_recovery_manager.h @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 for more details. + * + */ + +#ifndef __SDE_RECOVERY_MANAGER_H__ +#define __SDE_RECOVERY_MANAGER_H__ + +#include <linux/list.h> +#include <linux/kthread.h> +#include <linux/platform_device.h> +#include <linux/kobject.h> +#include <drm/msm_drm.h> +#include <linux/slab.h> +#include <drm/drmP.h> + + + +/* MSM Recovery Manager related definitions */ + +#define MAX_REC_NAME_LEN (16) +#define MAX_REC_UEVENT_LEN (64) +#define MAX_REC_ERR_SUPPORT (2) + +/* MSM Recovery Manager Error Code */ +#define SDE_UNDERRUN 222 +#define SDE_VSYNC_MISS 333 + +/** + * struct recovery_mgr_info - Recovery manager information + * @dev: drm device. + * @rec_lock: mutex lock for synchronized access to recovery mgr data. + * @event_list: list of reported events. + * @client_list: list of registered clients. + * @event_work: work for event handling. + * @event_queue: Queue for scheduling the event work. + * @num_of_clients: no. of clients registered. + * @recovery_ongoing: status indicating execution of recovery thread. + */ +struct recovery_mgr_info { + struct drm_device *dev; + struct mutex rec_lock; + struct list_head event_list; + struct list_head client_list; + struct work_struct event_work; + struct workqueue_struct *event_queue; + int num_of_clients; + int sysfs_created; + int recovery_ongoing; +}; + +/** + * struct recovery_error_info - Error information + * @reported_err_code: error reported for recovery. + * @pre_err_code: list of errors to be recovered before reported_err_code. + * @post_err_code: list of errors to be recovered after reported_err_code. + */ +struct recovery_error_info { + int reported_err_code; + int pre_err_code; + int post_err_code; +}; + +/** + * struct recovery_client_info - Client information + * @name: name of the client. + * @recovery_cb: recovery callback to recover the errors reported. + * @err_supported: list of errors that can be detected by client. + * @no_of_err: no. of errors supported by the client. + * @handle: Opaque handle passed to client + */ +struct recovery_client_info { + char name[MAX_REC_NAME_LEN]; + int (*recovery_cb)(int err_code, + struct recovery_client_info *client_info); + struct recovery_error_info + err_supported[MAX_REC_ERR_SUPPORT]; + int no_of_err; + void *pdata; + void *handle; +}; + +/** + * struct recovery_event_db - event database. + * @err: error code that client reports. + * @list: list pointer. + */ +struct recovery_event_db { + int err; + struct list_head list; +}; + +/** + * struct recovery_client_db - client database. + * @client_info: information that client registers. + * @list: list pointer. + */ +struct recovery_client_db { + struct recovery_client_info client_info; + struct list_head list; +}; + +int sde_recovery_set_events(int err); +int sde_recovery_client_register(struct recovery_client_info *client); +int sde_recovery_client_unregister(void *handle); +int sde_init_recovery_mgr(struct drm_device *dev); + + +#endif /* __SDE_RECOVERY_MANAGER_H__ */ diff --git a/drivers/iommu/iommu-debug.c b/drivers/iommu/iommu-debug.c index c21846c1f8d5..566572ae051e 100644 --- a/drivers/iommu/iommu-debug.c +++ b/drivers/iommu/iommu-debug.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015-2017, The Linux Foundation. All rights reserved. + * Copyright (c) 2015-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -168,6 +168,7 @@ struct iommu_debug_device { size_t len; struct list_head list; struct mutex clk_lock; + struct mutex dev_lock; unsigned int clk_count; }; @@ -1239,6 +1240,7 @@ static ssize_t __iommu_debug_dma_attach_write(struct file *file, ssize_t retval = -EINVAL; int val; + mutex_lock(&ddev->dev_lock); if (kstrtoint_from_user(ubuf, count, 0, &val)) { pr_err("Invalid format. Expected a hex or decimal integer"); retval = -EFAULT; @@ -1282,12 +1284,14 @@ static ssize_t __iommu_debug_dma_attach_write(struct file *file, arm_iommu_release_mapping(dev->archdata.mapping); pr_err("Detached\n"); } + mutex_unlock(&ddev->dev_lock); retval = count; return retval; out_release_mapping: arm_iommu_release_mapping(dma_mapping); out: + mutex_unlock(&ddev->dev_lock); return retval; } @@ -1300,6 +1304,7 @@ static ssize_t __iommu_debug_attach_write(struct file *file, ssize_t retval; int val; + mutex_lock(&ddev->dev_lock); if (kstrtoint_from_user(ubuf, count, 0, &val)) { pr_err("Invalid format. Expected a hex or decimal integer"); retval = -EFAULT; @@ -1336,6 +1341,7 @@ static ssize_t __iommu_debug_attach_write(struct file *file, retval = count; out: + mutex_unlock(&ddev->dev_lock); return retval; } @@ -1714,6 +1720,7 @@ static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, if (kstrtoint(comma3 + 1, 0, &prot)) goto invalid_format; + mutex_lock(&ddev->dev_lock); ret = iommu_map(ddev->domain, iova, phys, size, prot); if (ret) { pr_err("iommu_map failed with %d\n", ret); @@ -1725,6 +1732,7 @@ static ssize_t iommu_debug_map_write(struct file *file, const char __user *ubuf, pr_err("Mapped %pa to %pa (len=0x%zx, prot=0x%x)\n", &iova, &phys, size, prot); out: + mutex_unlock(&ddev->dev_lock); return retval; invalid_format: @@ -1812,14 +1820,17 @@ static ssize_t iommu_debug_dma_map_write(struct file *file, else goto invalid_format; + mutex_lock(&ddev->dev_lock); iova = dma_map_single_attrs(dev, v_addr, size, DMA_TO_DEVICE, dma_attrs); if (dma_mapping_error(dev, iova)) { pr_err("Failed to perform dma_map_single\n"); ret = -EINVAL; + mutex_unlock(&ddev->dev_lock); goto out; } + mutex_unlock(&ddev->dev_lock); retval = count; pr_err("Mapped 0x%p to %pa (len=0x%zx)\n", @@ -1926,12 +1937,15 @@ static ssize_t iommu_debug_unmap_write(struct file *file, if (kstrtosize_t(comma1 + 1, 0, &size)) goto invalid_format; + mutex_lock(&ddev->dev_lock); unmapped = iommu_unmap(ddev->domain, iova, size); if (unmapped != size) { pr_err("iommu_unmap failed. Expected to unmap: 0x%zx, unmapped: 0x%zx", size, unmapped); + mutex_unlock(&ddev->dev_lock); return -EIO; } + mutex_unlock(&ddev->dev_lock); retval = count; pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); @@ -2017,7 +2031,9 @@ static ssize_t iommu_debug_dma_unmap_write(struct file *file, else goto invalid_format; + mutex_lock(&ddev->dev_lock); dma_unmap_single_attrs(dev, iova, size, DMA_TO_DEVICE, dma_attrs); + mutex_unlock(&ddev->dev_lock); retval = count; pr_err("Unmapped %pa (len=0x%zx)\n", &iova, size); @@ -2112,6 +2128,7 @@ static int snarf_iommu_devices(struct device *dev, const char *name) if (!ddev) return -ENODEV; mutex_init(&ddev->clk_lock); + mutex_init(&ddev->dev_lock); ddev->dev = dev; dir = debugfs_create_dir(name, debugfs_tests_dir); if (!dir) { diff --git a/drivers/media/i2c/adv7481.c b/drivers/media/i2c/adv7481.c index 713a47cfea7f..eb2b37ce1095 100644 --- a/drivers/media/i2c/adv7481.c +++ b/drivers/media/i2c/adv7481.c @@ -1134,11 +1134,12 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) case VIDIOC_G_AVI_INFOFRAME: { int int_raw = adv7481_rd_byte(&state->i2c_client, state->i2c_io_addr, - IO_HDMI_EDG_RAW_STATUS_1_ADDR); + IO_HDMI_LVL_RAW_STATUS_1_ADDR); adv7481_wr_byte(&state->i2c_client, state->i2c_io_addr, - IO_HDMI_EDG_INT_CLEAR_1_ADDR, int_raw); - if (ADV_REG_GETFIELD(int_raw, IO_NEW_AVI_INFO_RAW)) { + IO_HDMI_LVL_INT_CLEAR_1_ADDR, int_raw); + pr_debug("%s: VIDIOC_G_AVI_INFOFRAME\n", __func__); + if (ADV_REG_GETFIELD(int_raw, IO_AVI_INFO_RAW)) { inf_buffer[0] = adv7481_rd_byte(&state->i2c_client, state->i2c_hdmi_inf_addr, HDMI_REG_AVI_PACKET_ID_ADDR); @@ -1154,13 +1155,13 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) &inf_buffer[3], INFOFRAME_DATA_SIZE); if (ret) { - pr_err("%s:Error in VIDIOC_G_AVI_INFOFRAME\n", + pr_err("%s: Error in reading AVI Infoframe\n", __func__); return -EINVAL; } if (hdmi_infoframe_unpack(&hdmi_info_frame, (void *)inf_buffer) < 0) { - pr_err("%s: infoframe unpack fail\n", __func__); + pr_err("%s: Infoframe unpack fail\n", __func__); return -EINVAL; } hdmi_infoframe_log(KERN_ERR, dev, &hdmi_info_frame); @@ -1173,12 +1174,13 @@ static long adv7481_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg) state->hdmi_avi_infoframe.video_code = hdmi_info_frame.avi.video_code; } else { - pr_err("%s: No new AVI Infoframe\n", __func__); + pr_err("%s: No AVI Infoframe\n", __func__); + return -EINVAL; } if (copy_to_user((void __user *)adv_arg.ptr, (void *)&state->hdmi_avi_infoframe, sizeof(struct avi_infoframe_params))) { - pr_err("%s: Failed to copy Infoframe\n", __func__); + pr_err("%s: Failed to copy AVI Infoframe\n", __func__); return -EINVAL; } break; diff --git a/drivers/media/i2c/adv7481_reg.h b/drivers/media/i2c/adv7481_reg.h index 403e538b6127..e1984f17d125 100644 --- a/drivers/media/i2c/adv7481_reg.h +++ b/drivers/media/i2c/adv7481_reg.h @@ -73,6 +73,10 @@ #define IO_CTRL_MASTER_PWDN_REG_VALUE 0x01 /* Interrupts */ +#define IO_HDMI_LVL_RAW_STATUS_1_ADDR 0x67 +#define IO_AVI_INFO_RAW_BMSK 0x0001 +#define IO_AVI_INFO_RAW_SHFT 0 + #define IO_HDMI_LVL_INT_CLEAR_1_ADDR 0x69 #define IO_HDMI_LVL_INT_MASKB_1_ADDR 0x6B diff --git a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c index 925a89601636..da4db983f367 100644 --- a/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c +++ b/drivers/media/platform/msm/camera_v2/isp/msm_isp_axi_util.c @@ -528,6 +528,12 @@ static void msm_isp_cfg_framedrop_reg( enum msm_vfe_input_src frame_src = SRC_TO_INTF(stream_info->stream_src); int i; + if (vfe_dev == NULL) { + pr_err("%s %d returning vfe_dev is NULL\n", + __func__, __LINE__); + return; + } + if (vfe_dev->axi_data.src_info[frame_src].frame_id >= stream_info->init_frame_drop) runtime_init_frame_drop = 0; diff --git a/drivers/mmc/card/block.c b/drivers/mmc/card/block.c index 0747f22ce56c..38ed503c637b 100644 --- a/drivers/mmc/card/block.c +++ b/drivers/mmc/card/block.c @@ -4725,9 +4725,7 @@ static int mmc_blk_probe(struct mmc_card *card) dev_set_drvdata(&card->dev, md); -#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME mmc_set_bus_resume_policy(card->host, 1); -#endif if (mmc_add_disk(md)) goto out; @@ -4772,9 +4770,7 @@ static void mmc_blk_remove(struct mmc_card *card) pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); dev_set_drvdata(&card->dev, NULL); -#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME mmc_set_bus_resume_policy(card->host, 0); -#endif } static int _mmc_blk_suspend(struct mmc_card *card, bool wait) diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 151643caac84..e4e4e04e1d0c 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -1883,10 +1883,9 @@ EXPORT_SYMBOL(mmc_start_req); */ void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq) { -#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME if (mmc_bus_needs_resume(host)) mmc_resume_bus(host); -#endif + __mmc_start_req(host, mrq); mmc_wait_for_req_done(host, mrq); } @@ -2322,10 +2321,9 @@ void mmc_get_card(struct mmc_card *card) { pm_runtime_get_sync(&card->dev); mmc_claim_host(card->host); -#ifdef CONFIG_MMC_BLOCK_DEFERRED_RESUME + if (mmc_bus_needs_resume(card->host)) mmc_resume_bus(card->host); -#endif } EXPORT_SYMBOL(mmc_get_card); @@ -3277,6 +3275,7 @@ int mmc_resume_bus(struct mmc_host *host) { unsigned long flags; int err = 0; + int card_present = true; if (!mmc_bus_needs_resume(host)) return -EINVAL; @@ -3287,7 +3286,10 @@ int mmc_resume_bus(struct mmc_host *host) spin_unlock_irqrestore(&host->lock, flags); mmc_bus_get(host); - if (host->bus_ops && !host->bus_dead && host->card) { + if (host->ops->get_cd) + card_present = host->ops->get_cd(host); + + if (host->bus_ops && !host->bus_dead && host->card && card_present) { mmc_power_up(host, host->card->ocr); BUG_ON(!host->bus_ops->resume); err = host->bus_ops->resume(host); @@ -4523,7 +4525,7 @@ int mmc_pm_notify(struct notifier_block *notify_block, struct mmc_host *host = container_of( notify_block, struct mmc_host, pm_notify); unsigned long flags; - int err = 0; + int err = 0, present = 0; switch (mode) { case PM_RESTORE_PREPARE: @@ -4570,8 +4572,12 @@ int mmc_pm_notify(struct notifier_block *notify_block, spin_lock_irqsave(&host->lock, flags); host->rescan_disable = 0; + if (host->ops->get_cd) + present = host->ops->get_cd(host); + if (mmc_bus_manual_resume(host) && - !host->ignore_bus_resume_flags) { + !host->ignore_bus_resume_flags && + present) { spin_unlock_irqrestore(&host->lock, flags); break; } diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index 2aa04b6bdfb3..bf896b605487 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -1143,6 +1143,9 @@ static void mmc_sd_remove(struct mmc_host *host) */ static int mmc_sd_alive(struct mmc_host *host) { + if (host->ops->get_cd && !host->ops->get_cd(host)) + return -ENOMEDIUM; + return mmc_send_status(host->card, NULL); } @@ -1171,7 +1174,15 @@ static void mmc_sd_detect(struct mmc_host *host) return; } - mmc_power_up(host, host->ocr_avail); + if (mmc_bus_needs_resume(host)) + mmc_resume_bus(host); + + if (host->ops->get_cd && !host->ops->get_cd(host)) { + err = -ENOMEDIUM; + mmc_card_set_removed(host->card); + mmc_card_clr_suspended(host->card); + goto out; + } /* * Just check if our card has been removed. @@ -1195,6 +1206,7 @@ static void mmc_sd_detect(struct mmc_host *host) err = _mmc_detect_card_removed(host); #endif +out: mmc_put_card(host->card); if (err) { @@ -1279,6 +1291,11 @@ static int _mmc_sd_resume(struct mmc_host *host) if (!mmc_card_suspended(host->card)) goto out; + if (host->ops->get_cd && !host->ops->get_cd(host)) { + mmc_card_clr_suspended(host->card); + goto out; + } + mmc_power_up(host, host->card->ocr); #ifdef CONFIG_MMC_PARANOID_SD_INIT retries = 5; @@ -1379,6 +1396,9 @@ static int mmc_sd_runtime_resume(struct mmc_host *host) static int mmc_sd_reset(struct mmc_host *host) { + if (host->ops->get_cd && !host->ops->get_cd(host)) + return -ENOMEDIUM; + mmc_power_cycle(host, host->card->ocr); return mmc_sd_init_card(host, host->card->ocr, host->card); } diff --git a/drivers/mmc/core/slot-gpio.c b/drivers/mmc/core/slot-gpio.c index 27117ba47073..baa60fbccbae 100644 --- a/drivers/mmc/core/slot-gpio.c +++ b/drivers/mmc/core/slot-gpio.c @@ -34,6 +34,10 @@ static irqreturn_t mmc_gpio_cd_irqt(int irq, void *dev_id) { /* Schedule a card detection after a debounce timeout */ struct mmc_host *host = dev_id; + int present = host->ops->get_cd(host); + + pr_debug("%s: cd gpio irq, gpio state %d (CARD_%s)\n", + mmc_hostname(host), present, present?"INSERT":"REMOVAL"); host->trigger_card_event = true; mmc_detect_change(host, msecs_to_jiffies(200)); diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 9710cf71054a..c0bcfa979253 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -42,7 +42,6 @@ #include <linux/mii.h> #include <linux/usb.h> #include <linux/usb/usbnet.h> -#include <linux/usb/cdc.h> #include <linux/slab.h> #include <linux/kernel.h> #include <linux/pm_runtime.h> @@ -1964,147 +1963,6 @@ out: return err; } -int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, - struct usb_interface *intf, - u8 *buffer, - int buflen) -{ - /* duplicates are ignored */ - struct usb_cdc_union_desc *union_header = NULL; - - /* duplicates are not tolerated */ - struct usb_cdc_header_desc *header = NULL; - struct usb_cdc_ether_desc *ether = NULL; - struct usb_cdc_mdlm_detail_desc *detail = NULL; - struct usb_cdc_mdlm_desc *desc = NULL; - - unsigned int elength; - int cnt = 0; - - memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); - hdr->phonet_magic_present = false; - while (buflen > 0) { - elength = buffer[0]; - if (!elength) { - dev_err(&intf->dev, "skipping garbage byte\n"); - elength = 1; - goto next_desc; - } - if ((buflen < elength) || (elength < 3)) { - dev_err(&intf->dev, "invalid descriptor buffer length\n"); - break; - } - if (buffer[1] != USB_DT_CS_INTERFACE) { - dev_err(&intf->dev, "skipping garbage\n"); - goto next_desc; - } - - switch (buffer[2]) { - case USB_CDC_UNION_TYPE: /* we've found it */ - if (elength < sizeof(struct usb_cdc_union_desc)) - goto next_desc; - if (union_header) { - dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); - goto next_desc; - } - union_header = (struct usb_cdc_union_desc *)buffer; - break; - case USB_CDC_COUNTRY_TYPE: - if (elength < sizeof(struct usb_cdc_country_functional_desc)) - goto next_desc; - hdr->usb_cdc_country_functional_desc = - (struct usb_cdc_country_functional_desc *)buffer; - break; - case USB_CDC_HEADER_TYPE: - if (elength != sizeof(struct usb_cdc_header_desc)) - goto next_desc; - if (header) - return -EINVAL; - header = (struct usb_cdc_header_desc *)buffer; - break; - case USB_CDC_ACM_TYPE: - if (elength < sizeof(struct usb_cdc_acm_descriptor)) - goto next_desc; - hdr->usb_cdc_acm_descriptor = - (struct usb_cdc_acm_descriptor *)buffer; - break; - case USB_CDC_ETHERNET_TYPE: - if (elength != sizeof(struct usb_cdc_ether_desc)) - goto next_desc; - if (ether) - return -EINVAL; - ether = (struct usb_cdc_ether_desc *)buffer; - break; - case USB_CDC_CALL_MANAGEMENT_TYPE: - if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) - goto next_desc; - hdr->usb_cdc_call_mgmt_descriptor = - (struct usb_cdc_call_mgmt_descriptor *)buffer; - break; - case USB_CDC_DMM_TYPE: - if (elength < sizeof(struct usb_cdc_dmm_desc)) - goto next_desc; - hdr->usb_cdc_dmm_desc = - (struct usb_cdc_dmm_desc *)buffer; - break; - case USB_CDC_MDLM_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_desc *)) - goto next_desc; - if (desc) - return -EINVAL; - desc = (struct usb_cdc_mdlm_desc *)buffer; - break; - case USB_CDC_MDLM_DETAIL_TYPE: - if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) - goto next_desc; - if (detail) - return -EINVAL; - detail = (struct usb_cdc_mdlm_detail_desc *)buffer; - break; - case USB_CDC_NCM_TYPE: - if (elength < sizeof(struct usb_cdc_ncm_desc)) - goto next_desc; - hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; - break; - case USB_CDC_MBIM_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_desc)) - goto next_desc; - - hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; - break; - case USB_CDC_MBIM_EXTENDED_TYPE: - if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) - break; - hdr->usb_cdc_mbim_extended_desc = - (struct usb_cdc_mbim_extended_desc *)buffer; - break; - case CDC_PHONET_MAGIC_NUMBER: - hdr->phonet_magic_present = true; - break; - default: - /* - * there are LOTS more CDC descriptors that - * could legitimately be found here. - */ - dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", - buffer[2], elength); - goto next_desc; - } - cnt++; -next_desc: - buflen -= elength; - buffer += elength; - } - hdr->usb_cdc_union_desc = union_header; - hdr->usb_cdc_header_desc = header; - hdr->usb_cdc_mdlm_detail_desc = detail; - hdr->usb_cdc_mdlm_desc = desc; - hdr->usb_cdc_ether_desc = ether; - return cnt; -} - -EXPORT_SYMBOL(cdc_parse_cdc_header); - /* * The function can't be called inside suspend/resume callback, * otherwise deadlock will be caused. diff --git a/drivers/net/wireless/cnss2/Makefile b/drivers/net/wireless/cnss2/Makefile index b49d0898178b..318076f23213 100644 --- a/drivers/net/wireless/cnss2/Makefile +++ b/drivers/net/wireless/cnss2/Makefile @@ -1,6 +1,7 @@ obj-$(CONFIG_CNSS2) += cnss2.o cnss2-y := main.o +cnss2-y += bus.o cnss2-y += debug.o cnss2-y += pci.o cnss2-y += power.o diff --git a/drivers/net/wireless/cnss2/bus.c b/drivers/net/wireless/cnss2/bus.c new file mode 100644 index 000000000000..0d46b4f6b6a4 --- /dev/null +++ b/drivers/net/wireless/cnss2/bus.c @@ -0,0 +1,356 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 for more details. + */ + +#include "bus.h" +#include "debug.h" +#include "pci.h" + +enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) +{ + if (!dev) + return CNSS_BUS_NONE; + + if (!dev->bus) + return CNSS_BUS_NONE; + + if (memcmp(dev->bus->name, "pci", 3) == 0) + return CNSS_BUS_PCI; + else + return CNSS_BUS_NONE; +} + +enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id) +{ + switch (device_id) { + case QCA6174_DEVICE_ID: + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + return CNSS_BUS_PCI; + default: + cnss_pr_err("Unknown device_id: 0x%lx\n", device_id); + return CNSS_BUS_NONE; + } +} + +void *cnss_bus_dev_to_bus_priv(struct device *dev) +{ + if (!dev) + return NULL; + + switch (cnss_get_dev_bus_type(dev)) { + case CNSS_BUS_PCI: + return cnss_get_pci_priv(to_pci_dev(dev)); + default: + return NULL; + } +} + +struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev) +{ + void *bus_priv; + + if (!dev) + return cnss_get_plat_priv(NULL); + + bus_priv = cnss_bus_dev_to_bus_priv(dev); + if (!bus_priv) + return NULL; + + switch (cnss_get_dev_bus_type(dev)) { + case CNSS_BUS_PCI: + return cnss_pci_priv_to_plat_priv(bus_priv); + default: + return NULL; + } +} + +int cnss_bus_init(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_init(plat_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_deinit(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + cnss_pci_deinit(plat_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + +int cnss_bus_load_m3(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_load_m3(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_alloc_fw_mem(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_get_wake_msi(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_force_fw_assert_hdlr(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +void cnss_bus_fw_boot_timeout_hdlr(unsigned long data) +{ + struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data; + + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_fw_boot_timeout_hdlr(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + +void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv) +{ + int ret; + + if (!plat_priv) + return; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + ret = cnss_pci_set_mhi_state(plat_priv->bus_priv, + CNSS_MHI_RDDM); + if (ret) { + cnss_pr_err("Failed to complete RDDM, err = %d\n", ret); + break; + } + return cnss_pci_collect_dump_info(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return; + } +} + +int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_call_driver_probe(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_call_driver_remove(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_dev_powerup(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_dev_shutdown(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_dev_crash_shutdown(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_dev_ramdump(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_register_driver_hdlr(plat_priv->bus_priv, data); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_unregister_driver_hdlr(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv, + int modem_current_status) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_call_driver_modem_status(plat_priv->bus_priv, + modem_current_status); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} + +int cnss_bus_recovery_update_status(struct cnss_plat_data *plat_priv) +{ + if (!plat_priv) + return -ENODEV; + + switch (plat_priv->bus_type) { + case CNSS_BUS_PCI: + return cnss_pci_recovery_update_status(plat_priv->bus_priv); + default: + cnss_pr_err("Unsupported bus type: %d\n", + plat_priv->bus_type); + return -EINVAL; + } +} diff --git a/drivers/net/wireless/cnss2/bus.h b/drivers/net/wireless/cnss2/bus.h new file mode 100644 index 000000000000..4e3d1500bd76 --- /dev/null +++ b/drivers/net/wireless/cnss2/bus.h @@ -0,0 +1,51 @@ +/* Copyright (c) 2018, The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 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 for more details. + */ + +#ifndef _CNSS_BUS_H +#define _CNSS_BUS_H + +#include "main.h" + +#define QCA6174_VENDOR_ID 0x168C +#define QCA6174_DEVICE_ID 0x003E +#define QCA6174_REV_ID_OFFSET 0x08 +#define QCA6174_REV3_VERSION 0x5020000 +#define QCA6174_REV3_2_VERSION 0x5030000 +#define QCA6290_VENDOR_ID 0x17CB +#define QCA6290_DEVICE_ID 0x1100 +#define QCA6290_EMULATION_VENDOR_ID 0x168C +#define QCA6290_EMULATION_DEVICE_ID 0xABCD + +enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev); +enum cnss_dev_bus_type cnss_get_bus_type(unsigned long device_id); +void *cnss_bus_dev_to_bus_priv(struct device *dev); +struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); +int cnss_bus_init(struct cnss_plat_data *plat_priv); +void cnss_bus_deinit(struct cnss_plat_data *plat_priv); +int cnss_bus_load_m3(struct cnss_plat_data *plat_priv); +int cnss_bus_alloc_fw_mem(struct cnss_plat_data *plat_priv); +u32 cnss_bus_get_wake_irq(struct cnss_plat_data *plat_priv); +int cnss_bus_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv); +void cnss_bus_fw_boot_timeout_hdlr(unsigned long data); +void cnss_bus_collect_dump_info(struct cnss_plat_data *plat_priv); +int cnss_bus_call_driver_probe(struct cnss_plat_data *plat_priv); +int cnss_bus_call_driver_remove(struct cnss_plat_data *plat_priv); +int cnss_bus_dev_powerup(struct cnss_plat_data *plat_priv); +int cnss_bus_dev_shutdown(struct cnss_plat_data *plat_priv); +int cnss_bus_dev_crash_shutdown(struct cnss_plat_data *plat_priv); +int cnss_bus_dev_ramdump(struct cnss_plat_data *plat_priv); +int cnss_bus_register_driver_hdlr(struct cnss_plat_data *plat_priv, void *data); +int cnss_bus_unregister_driver_hdlr(struct cnss_plat_data *plat_priv); +int cnss_bus_call_driver_modem_status(struct cnss_plat_data *plat_priv, + int modem_current_status); +int cnss_bus_recovery_update_status(struct cnss_plat_data *plat_priv); +#endif /* _CNSS_BUS_H */ diff --git a/drivers/net/wireless/cnss2/main.c b/drivers/net/wireless/cnss2/main.c index 5183f3de7c9b..10bcad10bb20 100644 --- a/drivers/net/wireless/cnss2/main.c +++ b/drivers/net/wireless/cnss2/main.c @@ -23,8 +23,8 @@ #include <soc/qcom/subsystem_notif.h> #include "main.h" +#include "bus.h" #include "debug.h" -#include "pci.h" #define CNSS_DUMP_FORMAT_VER 0x11 #define CNSS_DUMP_FORMAT_VER_V2 0x22 @@ -37,7 +37,6 @@ #define FW_READY_TIMEOUT 20000 #define FW_ASSERT_TIMEOUT 5000 #define CNSS_EVENT_PENDING 2989 -#define WAKE_MSI_NAME "WAKE" static struct cnss_plat_data *plat_env; @@ -55,13 +54,6 @@ module_param(enable_waltest, bool, 0600); MODULE_PARM_DESC(enable_waltest, "Enable to handle firmware waltest"); #endif -enum cnss_debug_quirks { - LINK_DOWN_SELF_RECOVERY, - SKIP_DEVICE_BOOT, - USE_CORE_ONLY_FW, - SKIP_RECOVERY, -}; - unsigned long quirks; #ifdef CONFIG_CNSS2_DEBUG module_param(quirks, ulong, 0600); @@ -87,64 +79,17 @@ struct cnss_driver_event { void *data; }; -static enum cnss_dev_bus_type cnss_get_dev_bus_type(struct device *dev) -{ - if (!dev) - return CNSS_BUS_NONE; - - if (!dev->bus) - return CNSS_BUS_NONE; - - if (memcmp(dev->bus->name, "pci", 3) == 0) - return CNSS_BUS_PCI; - else - return CNSS_BUS_NONE; -} - static void cnss_set_plat_priv(struct platform_device *plat_dev, struct cnss_plat_data *plat_priv) { plat_env = plat_priv; } -static struct cnss_plat_data *cnss_get_plat_priv(struct platform_device - *plat_dev) +struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev) { return plat_env; } -void *cnss_bus_dev_to_bus_priv(struct device *dev) -{ - if (!dev) - return NULL; - - switch (cnss_get_dev_bus_type(dev)) { - case CNSS_BUS_PCI: - return cnss_get_pci_priv(to_pci_dev(dev)); - default: - return NULL; - } -} - -struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev) -{ - void *bus_priv; - - if (!dev) - return cnss_get_plat_priv(NULL); - - bus_priv = cnss_bus_dev_to_bus_priv(dev); - if (!bus_priv) - return NULL; - - switch (cnss_get_dev_bus_type(dev)) { - case CNSS_BUS_PCI: - return cnss_pci_priv_to_plat_priv(bus_priv); - default: - return NULL; - } -} - static int cnss_pm_notify(struct notifier_block *b, unsigned long event, void *p) { @@ -274,23 +219,6 @@ int cnss_get_platform_cap(struct device *dev, struct cnss_platform_cap *cap) } EXPORT_SYMBOL(cnss_get_platform_cap); -int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) -{ - int ret = 0; - struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); - void *bus_priv = cnss_bus_dev_to_bus_priv(dev); - - if (!plat_priv) - return -ENODEV; - - ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa); - if (ret) - return ret; - - return 0; -} -EXPORT_SYMBOL(cnss_get_soc_info); - void cnss_request_pm_qos(struct device *dev, u32 qos_val) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); @@ -506,22 +434,14 @@ int cnss_set_fw_log_mode(struct device *dev, u8 fw_log_mode) } EXPORT_SYMBOL(cnss_set_fw_log_mode); -u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv) +unsigned long *cnss_get_debug_quirks(void) { - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - int ret, num_vectors; - u32 user_base_data, base_vector; - - ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, - WAKE_MSI_NAME, &num_vectors, - &user_base_data, &base_vector); - - if (ret) { - cnss_pr_err("WAKE MSI is not valid\n"); - return 0; - } + return &quirks; +} - return user_base_data; +bool *cnss_get_qmi_bypass(void) +{ + return &qmi_bypass; } static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) @@ -541,7 +461,7 @@ static int cnss_fw_mem_ready_hdlr(struct cnss_plat_data *plat_priv) if (ret) goto out; - ret = cnss_pci_load_m3(plat_priv->bus_priv); + ret = cnss_bus_load_m3(plat_priv); if (ret) goto out; @@ -554,79 +474,6 @@ out: return ret; } -static int cnss_driver_call_probe(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { - clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); - cnss_pr_dbg("Skip driver probe\n"); - goto out; - } - - if (!plat_priv->driver_ops) { - cnss_pr_err("driver_ops is NULL\n"); - ret = -EINVAL; - goto out; - } - - if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && - test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { - ret = plat_priv->driver_ops->reinit(pci_priv->pci_dev, - pci_priv->pci_device_id); - if (ret) { - cnss_pr_err("Failed to reinit host driver, err = %d\n", - ret); - goto out; - } - clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); - } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { - ret = plat_priv->driver_ops->probe(pci_priv->pci_dev, - pci_priv->pci_device_id); - if (ret) { - cnss_pr_err("Failed to probe host driver, err = %d\n", - ret); - goto out; - } - clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); - clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); - set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); - } - - return 0; - -out: - return ret; -} - -static int cnss_driver_call_remove(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || - test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { - cnss_pr_dbg("Skip driver remove\n"); - return 0; - } - - if (!plat_priv->driver_ops) { - cnss_pr_err("driver_ops is NULL\n"); - return -EINVAL; - } - - if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && - test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { - plat_priv->driver_ops->shutdown(pci_priv->pci_dev); - } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - plat_priv->driver_ops->remove(pci_priv->pci_dev); - clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); - } - - return 0; -} - static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -650,7 +497,7 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) QMI_WLFW_CALIBRATION_V01); } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { - ret = cnss_driver_call_probe(plat_priv); + ret = cnss_bus_call_driver_probe(plat_priv); } else { complete(&plat_priv->power_up_complete); } @@ -663,9 +510,7 @@ static int cnss_fw_ready_hdlr(struct cnss_plat_data *plat_priv) return 0; shutdown: - cnss_pci_stop_mhi(plat_priv->bus_priv); - cnss_suspend_pci_link(plat_priv->bus_priv); - cnss_power_off_device(plat_priv); + cnss_bus_dev_shutdown(plat_priv); clear_bit(CNSS_FW_READY, &plat_priv->driver_state); clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); @@ -837,44 +682,6 @@ int cnss_power_down(struct device *dev) } EXPORT_SYMBOL(cnss_power_down); -int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) -{ - int ret = 0; - struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL); - - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL!\n"); - return -ENODEV; - } - - if (plat_priv->driver_ops) { - cnss_pr_err("Driver has already registered!\n"); - return -EEXIST; - } - - ret = cnss_driver_event_post(plat_priv, - CNSS_DRIVER_EVENT_REGISTER_DRIVER, - CNSS_EVENT_SYNC_UNINTERRUPTIBLE, - driver_ops); - return ret; -} -EXPORT_SYMBOL(cnss_wlan_register_driver); - -void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops) -{ - struct cnss_plat_data *plat_priv = cnss_get_plat_priv(NULL); - - if (!plat_priv) { - cnss_pr_err("plat_priv is NULL!\n"); - return; - } - - cnss_driver_event_post(plat_priv, - CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, - CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); -} -EXPORT_SYMBOL(cnss_wlan_unregister_driver); - static int cnss_get_resources(struct cnss_plat_data *plat_priv) { int ret = 0; @@ -906,13 +713,11 @@ static int cnss_modem_notifier_nb(struct notifier_block *nb, { struct cnss_plat_data *plat_priv = container_of(nb, struct cnss_plat_data, modem_nb); - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; struct cnss_esoc_info *esoc_info; - struct cnss_wlan_driver *driver_ops; cnss_pr_dbg("Modem notifier: event %lu\n", code); - if (!pci_priv) + if (!plat_priv) return NOTIFY_DONE; esoc_info = &plat_priv->esoc_info; @@ -924,13 +729,10 @@ static int cnss_modem_notifier_nb(struct notifier_block *nb, else return NOTIFY_DONE; - driver_ops = plat_priv->driver_ops; - if (!driver_ops || !driver_ops->modem_status) + if (!cnss_bus_call_driver_modem_status(plat_priv, + esoc_info->modem_current_status)) return NOTIFY_DONE; - driver_ops->modem_status(pci_priv->pci_dev, - esoc_info->modem_current_status); - return NOTIFY_OK; } @@ -1004,246 +806,6 @@ static void cnss_unregister_esoc(struct cnss_plat_data *plat_priv) devm_unregister_esoc_client(dev, esoc_info->esoc_desc); } -static int cnss_qca6174_powerup(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL!\n"); - return -ENODEV; - } - - ret = cnss_power_on_device(plat_priv); - if (ret) { - cnss_pr_err("Failed to power on device, err = %d\n", ret); - goto out; - } - - ret = cnss_resume_pci_link(pci_priv); - if (ret) { - cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); - goto power_off; - } - - ret = cnss_driver_call_probe(plat_priv); - if (ret) - goto suspend_link; - - return 0; -suspend_link: - cnss_suspend_pci_link(pci_priv); -power_off: - cnss_power_off_device(plat_priv); -out: - return ret; -} - -static int cnss_qca6174_shutdown(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (!pci_priv) - return -ENODEV; - - cnss_pm_request_resume(pci_priv); - - cnss_driver_call_remove(plat_priv); - - cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, - CNSS_BUS_WIDTH_NONE); - cnss_pci_set_monitor_wake_intr(pci_priv, false); - cnss_pci_set_auto_suspended(pci_priv, 0); - - ret = cnss_suspend_pci_link(pci_priv); - if (ret) - cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); - - cnss_power_off_device(plat_priv); - - clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - - return ret; -} - -static void cnss_qca6174_crash_shutdown(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (!plat_priv->driver_ops) - return; - - plat_priv->driver_ops->crash_shutdown(pci_priv->pci_dev); -} - -static int cnss_qca6290_powerup(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - unsigned int timeout; - - if (!pci_priv) { - cnss_pr_err("pci_priv is NULL!\n"); - return -ENODEV; - } - - if (plat_priv->ramdump_info_v2.dump_data_valid || - test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { - cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); - cnss_pci_clear_dump_info(pci_priv); - } - - ret = cnss_power_on_device(plat_priv); - if (ret) { - cnss_pr_err("Failed to power on device, err = %d\n", ret); - goto out; - } - - ret = cnss_resume_pci_link(pci_priv); - if (ret) { - cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); - goto power_off; - } - - timeout = cnss_get_qmi_timeout(); - - ret = cnss_pci_start_mhi(pci_priv); - if (ret) { - cnss_pr_err("Failed to start MHI, err = %d\n", ret); - if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) && - !pci_priv->pci_link_down_ind && timeout) - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(timeout >> 1)); - return 0; - } - - if (test_bit(USE_CORE_ONLY_FW, &quirks)) { - clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state); - clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); - return 0; - } - - cnss_set_pin_connect_status(plat_priv); - - if (qmi_bypass) { - ret = cnss_driver_call_probe(plat_priv); - if (ret) - goto stop_mhi; - } else if (timeout) { - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(timeout << 1)); - } - - return 0; - -stop_mhi: - cnss_pci_stop_mhi(pci_priv); - cnss_suspend_pci_link(pci_priv); -power_off: - cnss_power_off_device(plat_priv); -out: - return ret; -} - -static int cnss_qca6290_shutdown(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - if (!pci_priv) - return -ENODEV; - - cnss_pm_request_resume(pci_priv); - - cnss_driver_call_remove(plat_priv); - - cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, - CNSS_BUS_WIDTH_NONE); - cnss_pci_set_monitor_wake_intr(pci_priv, false); - cnss_pci_set_auto_suspended(pci_priv, 0); - - cnss_pci_stop_mhi(pci_priv); - - ret = cnss_suspend_pci_link(pci_priv); - if (ret) - cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); - - cnss_power_off_device(plat_priv); - - clear_bit(CNSS_FW_READY, &plat_priv->driver_state); - clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); - clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - - return ret; -} - -static void cnss_qca6290_crash_shutdown(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - int ret = 0; - - cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n", - plat_priv->driver_state); - - if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || - test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { - cnss_pr_dbg("Ignore crash shutdown\n"); - return; - } - - ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM_KERNEL_PANIC); - if (ret) { - cnss_pr_err("Fail to complete RDDM, err = %d\n", ret); - return; - } - - cnss_pci_collect_dump_info(pci_priv); -} - -static int cnss_powerup(struct cnss_plat_data *plat_priv) -{ - int ret; - - switch (plat_priv->device_id) { - case QCA6174_DEVICE_ID: - ret = cnss_qca6174_powerup(plat_priv); - break; - case QCA6290_EMULATION_DEVICE_ID: - case QCA6290_DEVICE_ID: - ret = cnss_qca6290_powerup(plat_priv); - break; - default: - cnss_pr_err("Unknown device_id found: 0x%lx\n", - plat_priv->device_id); - ret = -ENODEV; - } - - return ret; -} - -static int cnss_shutdown(struct cnss_plat_data *plat_priv) -{ - int ret; - - switch (plat_priv->device_id) { - case QCA6174_DEVICE_ID: - ret = cnss_qca6174_shutdown(plat_priv); - break; - case QCA6290_EMULATION_DEVICE_ID: - case QCA6290_DEVICE_ID: - ret = cnss_qca6290_shutdown(plat_priv); - break; - default: - cnss_pr_err("Unknown device_id found: 0x%lx\n", - plat_priv->device_id); - ret = -ENODEV; - } - - return ret; -} - static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) { struct cnss_plat_data *plat_priv; @@ -1264,7 +826,7 @@ static int cnss_subsys_powerup(const struct subsys_desc *subsys_desc) return 0; } - return cnss_powerup(plat_priv); + return cnss_bus_dev_powerup(plat_priv); } static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc, @@ -1288,68 +850,12 @@ static int cnss_subsys_shutdown(const struct subsys_desc *subsys_desc, return 0; } - return cnss_shutdown(plat_priv); -} - -static int cnss_qca6290_ramdump(struct cnss_plat_data *plat_priv) -{ - struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2; - struct cnss_dump_data *dump_data = &info_v2->dump_data; - struct cnss_dump_seg *dump_seg = info_v2->dump_data_vaddr; - struct ramdump_segment *ramdump_segs, *s; - int i, ret = 0; - - if (!info_v2->dump_data_valid || - dump_data->nentries == 0) - return 0; - - ramdump_segs = kcalloc(dump_data->nentries, - sizeof(*ramdump_segs), - GFP_KERNEL); - if (!ramdump_segs) - return -ENOMEM; - - s = ramdump_segs; - for (i = 0; i < dump_data->nentries; i++) { - s->address = dump_seg->address; - s->v_address = dump_seg->v_address; - s->size = dump_seg->size; - s++; - dump_seg++; - } - - ret = do_elf_ramdump(info_v2->ramdump_dev, ramdump_segs, - dump_data->nentries); - kfree(ramdump_segs); - - cnss_pci_set_mhi_state(plat_priv->bus_priv, CNSS_MHI_DEINIT); - cnss_pci_clear_dump_info(plat_priv->bus_priv); - - return ret; -} - -static int cnss_qca6174_ramdump(struct cnss_plat_data *plat_priv) -{ - int ret = 0; - struct cnss_ramdump_info *ramdump_info; - struct ramdump_segment segment; - - ramdump_info = &plat_priv->ramdump_info; - if (!ramdump_info->ramdump_size) - return -EINVAL; - - memset(&segment, 0, sizeof(segment)); - segment.v_address = ramdump_info->ramdump_va; - segment.size = ramdump_info->ramdump_size; - ret = do_ramdump(ramdump_info->ramdump_dev, &segment, 1); - - return ret; + return cnss_bus_dev_shutdown(plat_priv); } static int cnss_subsys_ramdump(int enable, const struct subsys_desc *subsys_desc) { - int ret = 0; struct cnss_plat_data *plat_priv = dev_get_drvdata(subsys_desc->dev); if (!plat_priv) { @@ -1360,21 +866,7 @@ static int cnss_subsys_ramdump(int enable, if (!enable) return 0; - switch (plat_priv->device_id) { - case QCA6174_DEVICE_ID: - ret = cnss_qca6174_ramdump(plat_priv); - break; - case QCA6290_EMULATION_DEVICE_ID: - case QCA6290_DEVICE_ID: - ret = cnss_qca6290_ramdump(plat_priv); - break; - default: - cnss_pr_err("Unknown device_id found: 0x%lx\n", - plat_priv->device_id); - ret = -ENODEV; - } - - return ret; + return cnss_bus_dev_ramdump(plat_priv); } void *cnss_get_virt_ramdump_mem(struct device *dev, unsigned long *size) @@ -1417,19 +909,7 @@ static void cnss_subsys_crash_shutdown(const struct subsys_desc *subsys_desc) cnss_pr_err("plat_priv is NULL!\n"); return; } - - switch (plat_priv->device_id) { - case QCA6174_DEVICE_ID: - cnss_qca6174_crash_shutdown(plat_priv); - break; - case QCA6290_EMULATION_DEVICE_ID: - case QCA6290_DEVICE_ID: - cnss_qca6290_crash_shutdown(plat_priv); - break; - default: - cnss_pr_err("Unknown device_id found: 0x%lx\n", - plat_priv->device_id); - } + cnss_bus_dev_crash_shutdown(plat_priv); } static const char *cnss_recovery_reason_to_str(enum cnss_recovery_reason reason) @@ -1451,20 +931,15 @@ static const char *cnss_recovery_reason_to_str(enum cnss_recovery_reason reason) static int cnss_do_recovery(struct cnss_plat_data *plat_priv, enum cnss_recovery_reason reason) { - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; struct cnss_subsys_info *subsys_info = &plat_priv->subsys_info; - int ret = 0; plat_priv->recovery_count++; if (plat_priv->device_id == QCA6174_DEVICE_ID) goto self_recovery; - if (plat_priv->driver_ops && - test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) - plat_priv->driver_ops->update_status(pci_priv->pci_dev, - CNSS_RECOVERY); + cnss_bus_recovery_update_status(plat_priv); if (test_bit(SKIP_RECOVERY, &quirks)) { cnss_pr_dbg("Skip device recovery\n"); @@ -1478,12 +953,7 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, break; case CNSS_REASON_RDDM: clear_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); - ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM); - if (ret) { - cnss_pr_err("Failed to complete RDDM, err = %d\n", ret); - break; - } - cnss_pci_collect_dump_info(pci_priv); + cnss_bus_collect_dump_info(plat_priv); break; case CNSS_REASON_DEFAULT: case CNSS_REASON_TIMEOUT: @@ -1503,8 +973,8 @@ static int cnss_do_recovery(struct cnss_plat_data *plat_priv, return 0; self_recovery: - cnss_shutdown(plat_priv); - cnss_powerup(plat_priv); + cnss_bus_dev_shutdown(plat_priv); + cnss_bus_dev_powerup(plat_priv); return 0; } @@ -1590,28 +1060,6 @@ void cnss_schedule_recovery(struct device *dev, } EXPORT_SYMBOL(cnss_schedule_recovery); -static int cnss_force_fw_assert_hdlr(struct cnss_plat_data *plat_priv) -{ - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - int ret; - - ret = cnss_pci_set_mhi_state(plat_priv->bus_priv, - CNSS_MHI_TRIGGER_RDDM); - if (ret) { - cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_DEFAULT); - return 0; - } - - if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { - mod_timer(&plat_priv->fw_boot_timer, - jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); - } - - return 0; -} - int cnss_force_fw_assert(struct device *dev) { struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); @@ -1639,49 +1087,12 @@ int cnss_force_fw_assert(struct device *dev) } EXPORT_SYMBOL(cnss_force_fw_assert); -void fw_boot_timeout(unsigned long data) -{ - struct cnss_plat_data *plat_priv = (struct cnss_plat_data *)data; - struct cnss_pci_data *pci_priv = plat_priv->bus_priv; - - cnss_pr_err("Timeout waiting for FW ready indication!\n"); - - cnss_schedule_recovery(&pci_priv->pci_dev->dev, - CNSS_REASON_TIMEOUT); -} - -static int cnss_register_driver_hdlr(struct cnss_plat_data *plat_priv, - void *data) -{ - int ret = 0; - - set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); - plat_priv->driver_ops = data; - - ret = cnss_powerup(plat_priv); - if (ret) { - clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); - plat_priv->driver_ops = NULL; - } - - return ret; -} - -static int cnss_unregister_driver_hdlr(struct cnss_plat_data *plat_priv) -{ - set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); - cnss_shutdown(plat_priv); - plat_priv->driver_ops = NULL; - - return 0; -} - static int cnss_cold_boot_cal_start_hdlr(struct cnss_plat_data *plat_priv) { int ret = 0; set_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); - ret = cnss_powerup(plat_priv); + ret = cnss_bus_dev_powerup(plat_priv); if (ret) clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); @@ -1692,7 +1103,7 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) { plat_priv->cal_done = true; cnss_wlfw_wlan_mode_send_sync(plat_priv, QMI_WLFW_OFF_V01); - cnss_shutdown(plat_priv); + cnss_bus_dev_shutdown(plat_priv); clear_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state); return 0; @@ -1700,12 +1111,12 @@ static int cnss_cold_boot_cal_done_hdlr(struct cnss_plat_data *plat_priv) static int cnss_power_up_hdlr(struct cnss_plat_data *plat_priv) { - return cnss_powerup(plat_priv); + return cnss_bus_dev_powerup(plat_priv); } static int cnss_power_down_hdlr(struct cnss_plat_data *plat_priv) { - cnss_shutdown(plat_priv); + cnss_bus_dev_shutdown(plat_priv); return 0; } @@ -1746,7 +1157,7 @@ static void cnss_driver_event_work(struct work_struct *work) ret = cnss_wlfw_server_exit(plat_priv); break; case CNSS_DRIVER_EVENT_REQUEST_MEM: - ret = cnss_pci_alloc_fw_mem(plat_priv->bus_priv); + ret = cnss_bus_alloc_fw_mem(plat_priv); if (ret) break; ret = cnss_wlfw_respond_mem_send_sync(plat_priv); @@ -1764,18 +1175,18 @@ static void cnss_driver_event_work(struct work_struct *work) ret = cnss_cold_boot_cal_done_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_REGISTER_DRIVER: - ret = cnss_register_driver_hdlr(plat_priv, - event->data); + ret = cnss_bus_register_driver_hdlr(plat_priv, + event->data); break; case CNSS_DRIVER_EVENT_UNREGISTER_DRIVER: - ret = cnss_unregister_driver_hdlr(plat_priv); + ret = cnss_bus_unregister_driver_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_RECOVERY: ret = cnss_driver_recovery_hdlr(plat_priv, event->data); break; case CNSS_DRIVER_EVENT_FORCE_FW_ASSERT: - ret = cnss_force_fw_assert_hdlr(plat_priv); + ret = cnss_bus_force_fw_assert_hdlr(plat_priv); break; case CNSS_DRIVER_EVENT_POWER_UP: ret = cnss_power_up_hdlr(plat_priv); @@ -2232,6 +1643,7 @@ static int cnss_probe(struct platform_device *plat_dev) plat_priv->plat_dev = plat_dev; plat_priv->device_id = device_id->driver_data; + plat_priv->bus_type = cnss_get_bus_type(plat_priv->device_id); cnss_set_plat_priv(plat_dev, plat_priv); platform_set_drvdata(plat_dev, plat_priv); @@ -2244,14 +1656,14 @@ static int cnss_probe(struct platform_device *plat_dev) if (ret) goto free_res; - ret = cnss_pci_init(plat_priv); + ret = cnss_bus_init(plat_priv); if (ret) goto power_off; } ret = cnss_register_esoc(plat_priv); if (ret) - goto deinit_pci; + goto deinit_bus; ret = cnss_register_bus_scale(plat_priv); if (ret) @@ -2274,7 +1686,7 @@ static int cnss_probe(struct platform_device *plat_dev) goto deinit_qmi; setup_timer(&plat_priv->fw_boot_timer, - fw_boot_timeout, (unsigned long)plat_priv); + cnss_bus_fw_boot_timeout_hdlr, (unsigned long)plat_priv); register_pm_notifier(&cnss_pm_notifier); @@ -2300,9 +1712,9 @@ unreg_bus_scale: cnss_unregister_bus_scale(plat_priv); unreg_esoc: cnss_unregister_esoc(plat_priv); -deinit_pci: +deinit_bus: if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) - cnss_pci_deinit(plat_priv); + cnss_bus_deinit(plat_priv); power_off: if (!test_bit(SKIP_DEVICE_BOOT, &quirks)) cnss_power_off_device(plat_priv); @@ -2329,7 +1741,7 @@ static int cnss_remove(struct platform_device *plat_dev) cnss_remove_sysfs(plat_priv); cnss_unregister_bus_scale(plat_priv); cnss_unregister_esoc(plat_priv); - cnss_pci_deinit(plat_priv); + cnss_bus_deinit(plat_priv); cnss_put_resources(plat_priv); platform_set_drvdata(plat_dev, NULL); plat_env = NULL; diff --git a/drivers/net/wireless/cnss2/main.h b/drivers/net/wireless/cnss2/main.h index a36281cb560f..9dc64e016d82 100644 --- a/drivers/net/wireless/cnss2/main.h +++ b/drivers/net/wireless/cnss2/main.h @@ -164,9 +164,17 @@ struct cnss_pin_connect_result { u32 host_pin_result; }; +enum cnss_debug_quirks { + LINK_DOWN_SELF_RECOVERY, + SKIP_DEVICE_BOOT, + USE_CORE_ONLY_FW, + SKIP_RECOVERY, +}; + struct cnss_plat_data { struct platform_device *plat_dev; void *bus_priv; + enum cnss_dev_bus_type bus_type; struct cnss_vreg_info *vreg_info; struct cnss_pinctrl_info pinctrl_info; struct cnss_subsys_info subsys_info; @@ -178,7 +186,6 @@ struct cnss_plat_data { struct cnss_platform_cap cap; struct pm_qos_request qos_request; unsigned long device_id; - struct cnss_wlan_driver *driver_ops; enum cnss_driver_status driver_status; u32 recovery_count; unsigned long driver_state; @@ -209,8 +216,8 @@ struct cnss_plat_data { bool cal_done; }; -void *cnss_bus_dev_to_bus_priv(struct device *dev); -struct cnss_plat_data *cnss_bus_dev_to_plat_priv(struct device *dev); +struct cnss_plat_data *cnss_get_plat_priv(struct platform_device *plat_dev); +unsigned long *cnss_get_debug_quirks(void); int cnss_driver_event_post(struct cnss_plat_data *plat_priv, enum cnss_driver_event_type type, u32 flags, void *data); @@ -237,5 +244,5 @@ int cnss_register_ramdump(struct cnss_plat_data *plat_priv); void cnss_unregister_ramdump(struct cnss_plat_data *plat_priv); void cnss_set_pin_connect_status(struct cnss_plat_data *plat_priv); u32 cnss_get_wake_msi(struct cnss_plat_data *plat_priv); - +bool *cnss_get_qmi_bypass(void); #endif /* _CNSS_MAIN_H */ diff --git a/drivers/net/wireless/cnss2/pci.c b/drivers/net/wireless/cnss2/pci.c index 2356caa3af78..ff053b098c22 100644 --- a/drivers/net/wireless/cnss2/pci.c +++ b/drivers/net/wireless/cnss2/pci.c @@ -16,8 +16,11 @@ #include <linux/msi.h> #include <linux/of.h> #include <linux/pm_runtime.h> +#include <linux/memblock.h> +#include <soc/qcom/ramdump.h> #include "main.h" +#include "bus.h" #include "debug.h" #include "pci.h" @@ -40,10 +43,15 @@ #endif #define MHI_NODE_NAME "qcom,mhi" +#define MHI_MSI_NAME "MHI" #define MAX_M3_FILE_NAME_LENGTH 13 #define DEFAULT_M3_FILE_NAME "m3.bin" +#define WAKE_MSI_NAME "WAKE" + +#define FW_ASSERT_TIMEOUT 5000 + #ifdef CONFIG_PCI_MSM static DEFINE_SPINLOCK(pci_link_down_lock); #endif @@ -178,7 +186,6 @@ int cnss_resume_pci_link(struct cnss_pci_data *pci_priv) goto out; } - pci_set_master(pci_priv->pci_dev); if (pci_priv->pci_link_down_ind) @@ -228,6 +235,532 @@ static int cnss_set_pci_link(struct cnss_pci_data *pci_priv, bool link_up) } #endif /* CONFIG_PCI_MSM */ +int cnss_pci_recovery_update_status(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + + plat_priv = pci_priv->plat_priv; + + if (pci_priv->driver_ops && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) + pci_priv->driver_ops->update_status(pci_priv->pci_dev, + CNSS_RECOVERY); + return 0; +} + +int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + + if (test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + cnss_pr_dbg("Skip driver probe\n"); + goto out; + } + + if (!pci_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + ret = -EINVAL; + goto out; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { + ret = pci_priv->driver_ops->reinit(pci_priv->pci_dev, + pci_priv->pci_device_id); + if (ret) { + cnss_pr_err("Failed to reinit host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + } else if (test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state)) { + ret = pci_priv->driver_ops->probe(pci_priv->pci_dev, + pci_priv->pci_device_id); + if (ret) { + cnss_pr_err("Failed to probe host driver, err = %d\n", + ret); + goto out; + } + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + set_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; + +out: + return ret; +} + +int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + + if (test_bit(CNSS_COLD_BOOT_CAL, &plat_priv->driver_state) || + test_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_DEBUG, &plat_priv->driver_state)) { + cnss_pr_dbg("Skip driver remove\n"); + return 0; + } + + if (!pci_priv->driver_ops) { + cnss_pr_err("driver_ops is NULL\n"); + return -EINVAL; + } + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) && + test_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state)) { + pci_priv->driver_ops->shutdown(pci_priv->pci_dev); + } else if (test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + pci_priv->driver_ops->remove(pci_priv->pci_dev); + clear_bit(CNSS_DRIVER_PROBED, &plat_priv->driver_state); + } + + return 0; +} + +int cnss_pci_call_driver_modem_status(struct cnss_pci_data *pci_priv, + int modem_current_status) +{ + struct cnss_wlan_driver *driver_ops; + + if (!pci_priv) + return -ENODEV; + + driver_ops = pci_priv->driver_ops; + if (!driver_ops || !driver_ops->modem_status) + return -EINVAL; + + driver_ops->modem_status(pci_priv->pci_dev, modem_current_status); + + return 0; +} + +static int cnss_qca6174_powerup(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", ret); + goto out; + } + + ret = cnss_resume_pci_link(pci_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + goto power_off; + } + + ret = cnss_pci_call_driver_probe(pci_priv); + if (ret) + goto suspend_link; + + return 0; +suspend_link: + cnss_suspend_pci_link(pci_priv); +power_off: + cnss_power_off_device(plat_priv); +out: + return ret; +} + +static int cnss_qca6174_shutdown(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + cnss_pm_request_resume(pci_priv); + + cnss_pci_call_driver_remove(pci_priv); + + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); + + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + cnss_power_off_device(plat_priv); + + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + + return ret; +} + +static void cnss_qca6174_crash_shutdown(struct cnss_pci_data *pci_priv) +{ + if (pci_priv->driver_ops && pci_priv->driver_ops->crash_shutdown) + pci_priv->driver_ops->crash_shutdown(pci_priv->pci_dev); +} + +static int cnss_qca6174_ramdump(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_ramdump_info *ramdump_info; + struct ramdump_segment segment; + + ramdump_info = &plat_priv->ramdump_info; + if (!ramdump_info->ramdump_size) + return -EINVAL; + + memset(&segment, 0, sizeof(segment)); + segment.v_address = ramdump_info->ramdump_va; + segment.size = ramdump_info->ramdump_size; + ret = do_ramdump(ramdump_info->ramdump_dev, &segment, 1); + + return ret; +} + +static int cnss_qca6290_powerup(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + unsigned int timeout; + + if (plat_priv->ramdump_info_v2.dump_data_valid || + test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state)) { + cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_DEINIT); + cnss_pci_clear_dump_info(pci_priv); + } + + ret = cnss_power_on_device(plat_priv); + if (ret) { + cnss_pr_err("Failed to power on device, err = %d\n", ret); + goto out; + } + + ret = cnss_resume_pci_link(pci_priv); + if (ret) { + cnss_pr_err("Failed to resume PCI link, err = %d\n", ret); + goto power_off; + } + + timeout = cnss_get_qmi_timeout(); + + ret = cnss_pci_start_mhi(pci_priv); + if (ret) { + cnss_pr_err("Failed to start MHI, err = %d\n", ret); + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state) && + !pci_priv->pci_link_down_ind && timeout) + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout >> 1)); + return 0; + } + + if (test_bit(USE_CORE_ONLY_FW, cnss_get_debug_quirks())) { + clear_bit(CNSS_FW_BOOT_RECOVERY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state); + return 0; + } + + cnss_set_pin_connect_status(plat_priv); + + if (*cnss_get_qmi_bypass()) { + ret = cnss_pci_call_driver_probe(pci_priv); + if (ret) + goto stop_mhi; + } else if (timeout) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(timeout << 1)); + } + + return 0; + +stop_mhi: + cnss_pci_stop_mhi(pci_priv); + cnss_suspend_pci_link(pci_priv); +power_off: + cnss_power_off_device(plat_priv); +out: + return ret; +} + +static int cnss_qca6290_shutdown(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + cnss_pm_request_resume(pci_priv); + + cnss_pci_call_driver_remove(pci_priv); + + cnss_request_bus_bandwidth(&plat_priv->plat_dev->dev, + CNSS_BUS_WIDTH_NONE); + cnss_pci_set_monitor_wake_intr(pci_priv, false); + cnss_pci_set_auto_suspended(pci_priv, 0); + + cnss_pci_stop_mhi(pci_priv); + + ret = cnss_suspend_pci_link(pci_priv); + if (ret) + cnss_pr_err("Failed to suspend PCI link, err = %d\n", ret); + + cnss_power_off_device(plat_priv); + + clear_bit(CNSS_FW_READY, &plat_priv->driver_state); + clear_bit(CNSS_FW_MEM_READY, &plat_priv->driver_state); + clear_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + + return ret; +} + +static void cnss_qca6290_crash_shutdown(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + int ret = 0; + + cnss_pr_dbg("Crash shutdown with driver_state 0x%lx\n", + plat_priv->driver_state); + + if (test_bit(CNSS_DRIVER_RECOVERY, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state) || + test_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state)) { + cnss_pr_dbg("Ignore crash shutdown\n"); + return; + } + + ret = cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RDDM_KERNEL_PANIC); + if (ret) { + cnss_pr_err("Fail to complete RDDM, err = %d\n", ret); + return; + } + + cnss_pci_collect_dump_info(pci_priv); +} + +static int cnss_qca6290_ramdump(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + struct cnss_ramdump_info_v2 *info_v2 = &plat_priv->ramdump_info_v2; + struct cnss_dump_data *dump_data = &info_v2->dump_data; + struct cnss_dump_seg *dump_seg = info_v2->dump_data_vaddr; + struct ramdump_segment *ramdump_segs, *s; + int i, ret = 0; + + if (!info_v2->dump_data_valid || + dump_data->nentries == 0) + return 0; + + ramdump_segs = kcalloc(dump_data->nentries, + sizeof(*ramdump_segs), + GFP_KERNEL); + if (!ramdump_segs) + return -ENOMEM; + + s = ramdump_segs; + for (i = 0; i < dump_data->nentries; i++) { + s->address = dump_seg->address; + s->v_address = dump_seg->v_address; + s->size = dump_seg->size; + s++; + dump_seg++; + } + + ret = do_elf_ramdump(info_v2->ramdump_dev, ramdump_segs, + dump_data->nentries); + kfree(ramdump_segs); + + cnss_pci_set_mhi_state(plat_priv->bus_priv, CNSS_MHI_DEINIT); + cnss_pci_clear_dump_info(plat_priv->bus_priv); + + return ret; +} + +int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + switch (pci_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_powerup(pci_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_powerup(pci_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%x\n", + pci_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + switch (pci_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_shutdown(pci_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_shutdown(pci_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%x\n", + pci_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + switch (pci_priv->device_id) { + case QCA6174_DEVICE_ID: + cnss_qca6174_crash_shutdown(pci_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + cnss_qca6290_crash_shutdown(pci_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%x\n", + pci_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv) +{ + int ret = 0; + + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + switch (pci_priv->device_id) { + case QCA6174_DEVICE_ID: + ret = cnss_qca6174_ramdump(pci_priv); + break; + case QCA6290_EMULATION_DEVICE_ID: + case QCA6290_DEVICE_ID: + ret = cnss_qca6290_ramdump(pci_priv); + break; + default: + cnss_pr_err("Unknown device_id found: 0x%x\n", + pci_priv->device_id); + ret = -ENODEV; + } + + return ret; +} + +int cnss_wlan_register_driver(struct cnss_wlan_driver *driver_ops) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + struct cnss_pci_data *pci_priv; + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return -ENODEV; + } + + pci_priv = plat_priv->bus_priv; + if (!pci_priv) { + cnss_pr_err("pci_priv is NULL\n"); + return -ENODEV; + } + + if (pci_priv->driver_ops) { + cnss_pr_err("Driver has already registered\n"); + return -EEXIST; + } + + ret = cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_REGISTER_DRIVER, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, + driver_ops); + return ret; +} +EXPORT_SYMBOL(cnss_wlan_register_driver); + +void cnss_wlan_unregister_driver(struct cnss_wlan_driver *driver_ops) +{ + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(NULL); + + if (!plat_priv) { + cnss_pr_err("plat_priv is NULL\n"); + return; + } + + cnss_driver_event_post(plat_priv, + CNSS_DRIVER_EVENT_UNREGISTER_DRIVER, + CNSS_EVENT_SYNC_UNINTERRUPTIBLE, NULL); +} +EXPORT_SYMBOL(cnss_wlan_unregister_driver); + +int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv, + void *data) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + set_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + pci_priv->driver_ops = data; + + ret = cnss_pci_dev_powerup(pci_priv); + if (ret) { + clear_bit(CNSS_DRIVER_LOADING, &plat_priv->driver_state); + pci_priv->driver_ops = NULL; + } + + return ret; +} + +int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv = pci_priv->plat_priv; + + set_bit(CNSS_DRIVER_UNLOADING, &plat_priv->driver_state); + cnss_pci_dev_shutdown(pci_priv); + pci_priv->driver_ops = NULL; + + return 0; +} + static int cnss_pci_init_smmu(struct cnss_pci_data *pci_priv) { int ret = 0; @@ -381,7 +914,7 @@ static int cnss_pci_suspend(struct device *dev) if (!plat_priv) goto out; - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->suspend) { ret = driver_ops->suspend(pci_dev, state); if (ret) { @@ -453,7 +986,7 @@ static int cnss_pci_resume(struct device *dev) cnss_pci_set_mhi_state(pci_priv, CNSS_MHI_RESUME); } - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->resume) { ret = driver_ops->resume(pci_dev); if (ret) @@ -482,7 +1015,7 @@ static int cnss_pci_suspend_noirq(struct device *dev) if (!plat_priv) goto out; - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->suspend_noirq) ret = driver_ops->suspend_noirq(pci_dev); @@ -505,7 +1038,7 @@ static int cnss_pci_resume_noirq(struct device *dev) if (!plat_priv) goto out; - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->resume_noirq && !pci_priv->pci_link_down_ind) ret = driver_ops->resume_noirq(pci_dev); @@ -536,7 +1069,7 @@ static int cnss_pci_runtime_suspend(struct device *dev) cnss_pr_dbg("Runtime suspend start\n"); - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->runtime_ops && driver_ops->runtime_ops->runtime_suspend) ret = driver_ops->runtime_ops->runtime_suspend(pci_dev); @@ -568,7 +1101,7 @@ static int cnss_pci_runtime_resume(struct device *dev) cnss_pr_dbg("Runtime resume start\n"); - driver_ops = plat_priv->driver_ops; + driver_ops = pci_priv->driver_ops; if (driver_ops && driver_ops->runtime_ops && driver_ops->runtime_ops->runtime_resume) ret = driver_ops->runtime_ops->runtime_resume(pci_dev); @@ -841,6 +1374,63 @@ static void cnss_pci_free_m3_mem(struct cnss_pci_data *pci_priv) m3_mem->size = 0; } +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv) +{ + struct cnss_plat_data *plat_priv; + int ret; + + if (!pci_priv) + return -ENODEV; + + plat_priv = pci_priv->plat_priv; + if (!plat_priv) + return -ENODEV; + + ret = cnss_pci_set_mhi_state(pci_priv, + CNSS_MHI_TRIGGER_RDDM); + if (ret) { + cnss_pr_err("Failed to trigger RDDM, err = %d\n", ret); + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_DEFAULT); + return 0; + } + + if (!test_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state)) { + mod_timer(&plat_priv->fw_boot_timer, + jiffies + msecs_to_jiffies(FW_ASSERT_TIMEOUT)); + } + + return 0; +} + +void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv) +{ + if (!pci_priv) + return; + + cnss_pr_err("Timeout waiting for FW ready indication\n"); + + cnss_schedule_recovery(&pci_priv->pci_dev->dev, + CNSS_REASON_TIMEOUT); +} + +int cnss_get_soc_info(struct device *dev, struct cnss_soc_info *info) +{ + int ret = 0; + struct cnss_plat_data *plat_priv = cnss_bus_dev_to_plat_priv(dev); + void *bus_priv = cnss_bus_dev_to_bus_priv(dev); + + if (!plat_priv) + return -ENODEV; + + ret = cnss_pci_get_bar_info(bus_priv, &info->va, &info->pa); + if (ret) + return ret; + + return 0; +} +EXPORT_SYMBOL(cnss_get_soc_info); + int cnss_pci_get_bar_info(struct cnss_pci_data *pci_priv, void __iomem **va, phys_addr_t *pa) { @@ -991,6 +1581,29 @@ void cnss_get_msi_address(struct device *dev, u32 *msi_addr_low, } EXPORT_SYMBOL(cnss_get_msi_address); +static char *get_wake_msi_name(void) +{ + return (char *)WAKE_MSI_NAME; +} + +u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv) +{ + int ret, num_vectors; + u32 user_base_data, base_vector; + char *wake_msi_name = get_wake_msi_name(); + + ret = cnss_get_user_msi_assignment(&pci_priv->pci_dev->dev, + wake_msi_name, &num_vectors, + &user_base_data, &base_vector); + + if (ret) { + cnss_pr_err("WAKE MSI is not valid\n"); + return 0; + } + + return user_base_data; +} + #ifdef CONFIG_PCI_MSM static inline int cnss_pci_set_dma_mask(struct pci_dev *pci_dev) { @@ -1206,8 +1819,8 @@ static void cnss_mhi_notify_status(enum MHI_CB_REASON reason, void *priv) cnss_pr_dbg("MHI status cb is called with reason %d\n", reason); - if (plat_priv->driver_ops && plat_priv->driver_ops->update_status) - plat_priv->driver_ops->update_status(pci_priv->pci_dev, + if (pci_priv->driver_ops && pci_priv->driver_ops->update_status) + pci_priv->driver_ops->update_status(pci_priv->pci_dev, CNSS_FW_DOWN); set_bit(CNSS_DEV_ERR_NOTIFY, &plat_priv->driver_state); diff --git a/drivers/net/wireless/cnss2/pci.h b/drivers/net/wireless/cnss2/pci.h index a00ca61972f0..182355ae7577 100644 --- a/drivers/net/wireless/cnss2/pci.h +++ b/drivers/net/wireless/cnss2/pci.h @@ -62,6 +62,7 @@ struct cnss_pci_data { const struct pci_device_id *pci_device_id; u32 device_id; u16 revision_id; + struct cnss_wlan_driver *driver_ops; bool pci_link_state; bool pci_link_down_ind; struct pci_saved_state *saved_state; @@ -152,5 +153,18 @@ void cnss_pci_stop_mhi(struct cnss_pci_data *pci_priv); void cnss_pci_collect_dump_info(struct cnss_pci_data *pci_priv); void cnss_pci_clear_dump_info(struct cnss_pci_data *pci_priv); int cnss_pm_request_resume(struct cnss_pci_data *pci_priv); - +u32 cnss_pci_get_wake_msi(struct cnss_pci_data *pci_priv); +int cnss_pci_force_fw_assert_hdlr(struct cnss_pci_data *pci_priv); +void cnss_pci_fw_boot_timeout_hdlr(struct cnss_pci_data *pci_priv); +int cnss_pci_call_driver_probe(struct cnss_pci_data *pci_priv); +int cnss_pci_call_driver_remove(struct cnss_pci_data *pci_priv); +int cnss_pci_dev_powerup(struct cnss_pci_data *pci_priv); +int cnss_pci_dev_shutdown(struct cnss_pci_data *pci_priv); +int cnss_pci_dev_crash_shutdown(struct cnss_pci_data *pci_priv); +int cnss_pci_dev_ramdump(struct cnss_pci_data *pci_priv); +int cnss_pci_register_driver_hdlr(struct cnss_pci_data *pci_priv, void *data); +int cnss_pci_unregister_driver_hdlr(struct cnss_pci_data *pci_priv); +int cnss_pci_call_driver_modem_status(struct cnss_pci_data *pci_priv, + int modem_current_status); +int cnss_pci_recovery_update_status(struct cnss_pci_data *pci_priv); #endif /* _CNSS_PCI_H */ diff --git a/drivers/net/wireless/cnss2/qmi.c b/drivers/net/wireless/cnss2/qmi.c index b8777c18d252..669816c84e37 100644 --- a/drivers/net/wireless/cnss2/qmi.c +++ b/drivers/net/wireless/cnss2/qmi.c @@ -15,8 +15,9 @@ #include <linux/qmi_encdec.h> #include <soc/qcom/msm_qmi_interface.h> -#include "main.h" +#include "bus.h" #include "debug.h" +#include "main.h" #include "qmi.h" #define WLFW_SERVICE_INS_ID_V01 1 @@ -163,7 +164,7 @@ static int cnss_wlfw_host_cap_send_sync(struct cnss_plat_data *plat_priv) req.num_clients = daemon_support ? 2 : 1; cnss_pr_dbg("Number of clients is %d\n", req.num_clients); - req.wake_msi = cnss_get_wake_msi(plat_priv); + req.wake_msi = cnss_bus_get_wake_irq(plat_priv); if (req.wake_msi) { cnss_pr_dbg("WAKE MSI base data is %d\n", req.wake_msi); req.wake_msi_valid = 1; @@ -514,17 +515,17 @@ int cnss_wlfw_bdf_dnld_send_sync(struct cnss_plat_data *plat_priv) BDF_FILE_NAME_PREFIX "%02x", plat_priv->board_info.board_id); + if (bdf_bypass) { + cnss_pr_info("bdf_bypass is enabled, sending dummy BDF\n"); + temp = filename; + remaining = MAX_BDF_FILE_NAME; + goto bypass_bdf; + } + ret = request_firmware(&fw_entry, filename, &plat_priv->plat_dev->dev); if (ret) { cnss_pr_err("Failed to load BDF: %s\n", filename); - if (bdf_bypass) { - cnss_pr_info("bdf_bypass is enabled, sending dummy BDF\n"); - temp = filename; - remaining = MAX_BDF_FILE_NAME; - goto bypass_bdf; - } else { - goto err_req_fw; - } + goto err_req_fw; } temp = fw_entry->data; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa.c b/drivers/platform/msm/ipa/ipa_v2/ipa.c index b510bbd7d6c7..ad7d2d6175bd 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -3883,7 +3883,7 @@ static int ipa_init(const struct ipa_plat_drv_res *resource_p, ipa_ctx->logbuf = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa", 0); if (ipa_ctx->logbuf == NULL) - IPAERR("failed to create IPC log, continue...\n"); + IPADBG("failed to create IPC log, continue...\n"); ipa_ctx->pdev = ipa_dev; ipa_ctx->uc_pdev = ipa_dev; diff --git a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c index 1a704ffab07a..e17526e46323 100644 --- a/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v2/ipa_debugfs.c @@ -1901,7 +1901,7 @@ static ssize_t ipa_enable_ipc_low(struct file *file, ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa_low", 0); if (ipa_ipc_low_buff == NULL) - IPAERR("failed to get logbuf_low\n"); + IPADBG("failed to get logbuf_low\n"); } ipa_ctx->logbuf_low = ipa_ipc_low_buff; } else { diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa.c b/drivers/platform/msm/ipa/ipa_v3/ipa.c index a869b6419e5e..681b2d945945 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2012-2017, The Linux Foundation. All rights reserved. +/* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 and @@ -4549,7 +4549,7 @@ static int ipa3_pre_init(const struct ipa3_plat_drv_res *resource_p, ipa3_ctx->logbuf = ipc_log_context_create(IPA_IPC_LOG_PAGES, "ipa", 0); if (ipa3_ctx->logbuf == NULL) - IPAERR("failed to create IPC log, continue...\n"); + IPADBG("failed to create IPC log, continue...\n"); ipa3_ctx->pdev = ipa_dev; ipa3_ctx->uc_pdev = ipa_dev; diff --git a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c index eb9a6877c39f..0dd5b8165ac1 100644 --- a/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c +++ b/drivers/platform/msm/ipa/ipa_v3/ipa_debugfs.c @@ -1872,7 +1872,7 @@ static ssize_t ipa3_enable_ipc_low(struct file *file, "ipa_low", 0); } if (ipa_ipc_low_buff == NULL) - IPAERR("failed to get logbuf_low\n"); + IPADBG("failed to get logbuf_low\n"); ipa3_ctx->logbuf_low = ipa_ipc_low_buff; } else { ipa3_ctx->logbuf_low = NULL; diff --git a/drivers/scsi/ufs/ufs-qcom.c b/drivers/scsi/ufs/ufs-qcom.c index 8b501f9d08c6..6e307b5dce81 100644 --- a/drivers/scsi/ufs/ufs-qcom.c +++ b/drivers/scsi/ufs/ufs-qcom.c @@ -2722,7 +2722,7 @@ static void ufs_qcom_print_utp_hci_testbus(struct ufs_hba *hba) return; host->testbus.select_major = TSTBUS_UTP_HCI; - for (i = 0; i <= nminor; i++) { + for (i = 0; i < nminor; i++) { host->testbus.select_minor = i; ufs_qcom_testbus_config(host); testbus[i] = ufshcd_readl(hba, UFS_TEST_BUS); diff --git a/drivers/soc/qcom/hab/hab_mem_linux.c b/drivers/soc/qcom/hab/hab_mem_linux.c index 74ee88a037af..e9e42554cbe2 100644 --- a/drivers/soc/qcom/hab/hab_mem_linux.c +++ b/drivers/soc/qcom/hab/hab_mem_linux.c @@ -361,18 +361,19 @@ static int hab_map_fault(struct vm_area_struct *vma, struct vm_fault *vmf) { struct page *page; struct pages_list *pglist; - - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - - /* PHY address */ - unsigned long fault_offset = - (unsigned long)vmf->virtual_address - vma->vm_start + offset; - unsigned long fault_index = fault_offset>>PAGE_SHIFT; + unsigned long offset, fault_offset, fault_index; int page_idx; if (vma == NULL) return VM_FAULT_SIGBUS; + offset = vma->vm_pgoff << PAGE_SHIFT; + + /* PHY address */ + fault_offset = + (unsigned long)vmf->virtual_address - vma->vm_start + offset; + fault_index = fault_offset>>PAGE_SHIFT; + pglist = vma->vm_private_data; page_idx = fault_index - pglist->index; @@ -463,6 +464,7 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx, int i, j, k = 0; pgprot_t prot = PAGE_KERNEL; int32_t fd, size; + int ret; DEFINE_DMA_BUF_EXPORT_INFO(exp_info); if (!pfn_table || !priv) @@ -505,9 +507,10 @@ static int habmem_imp_hyp_map_fd(void *imp_ctx, exp_info.priv = pglist; pglist->dmabuf = dma_buf_export(&exp_info); if (IS_ERR(pglist->dmabuf)) { + ret = PTR_ERR(pglist->dmabuf); kfree(pages); kfree(pglist); - return PTR_ERR(pglist->dmabuf); + return ret; } fd = dma_buf_fd(pglist->dmabuf, O_CLOEXEC); @@ -579,8 +582,8 @@ static int habmem_imp_hyp_map_kva(void *imp_ctx, pglist->kva = vmap(pglist->pages, pglist->npages, VM_MAP, prot); if (pglist->kva == NULL) { kfree(pages); - kfree(pglist); pr_err("%ld pages vmap failed\n", pglist->npages); + kfree(pglist); return -ENOMEM; } diff --git a/drivers/soc/qcom/hab/hab_mimex.c b/drivers/soc/qcom/hab/hab_mimex.c index 86d763f65657..3e9046381b12 100644 --- a/drivers/soc/qcom/hab/hab_mimex.c +++ b/drivers/soc/qcom/hab/hab_mimex.c @@ -124,8 +124,7 @@ void habmem_remove_export(struct export_desc *exp) struct uhab_context *ctx; if (!exp || !exp->ctx || !exp->pchan) { - pr_err("failed to find valid info in exp %pK ctx %pK pchan %pK\n", - exp, exp->ctx, exp->pchan); + pr_err("failed to find valid info in exp %pK\n", exp); return; } @@ -240,7 +239,7 @@ int hab_mem_export(struct uhab_context *ctx, int page_count; int compressed = 0; - if (!ctx || !param) + if (!ctx || !param || !param->buffer) return -EINVAL; vchan = hab_get_vchan_fromvcid(param->vcid, ctx); diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 29adabdb305f..7855b3e9a97f 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -12,6 +12,7 @@ #include <linux/nls.h> #include <linux/device.h> #include <linux/scatterlist.h> +#include <linux/usb/cdc.h> #include <linux/usb/quirks.h> #include <linux/usb/hcd.h> /* for usbcore internals */ #include <asm/byteorder.h> @@ -2031,3 +2032,159 @@ int usb_driver_set_configuration(struct usb_device *udev, int config) return 0; } EXPORT_SYMBOL_GPL(usb_driver_set_configuration); + +/** + * cdc_parse_cdc_header - parse the extra headers present in CDC devices + * @hdr: the place to put the results of the parsing + * @intf: the interface for which parsing is requested + * @buffer: pointer to the extra headers to be parsed + * @buflen: length of the extra headers + * + * This evaluates the extra headers present in CDC devices which + * bind the interfaces for data and control and provide details + * about the capabilities of the device. + * + * Return: number of descriptors parsed or -EINVAL + * if the header is contradictory beyond salvage + */ + +int cdc_parse_cdc_header(struct usb_cdc_parsed_header *hdr, + struct usb_interface *intf, + u8 *buffer, + int buflen) +{ + /* duplicates are ignored */ + struct usb_cdc_union_desc *union_header = NULL; + + /* duplicates are not tolerated */ + struct usb_cdc_header_desc *header = NULL; + struct usb_cdc_ether_desc *ether = NULL; + struct usb_cdc_mdlm_detail_desc *detail = NULL; + struct usb_cdc_mdlm_desc *desc = NULL; + + unsigned int elength; + int cnt = 0; + + memset(hdr, 0x00, sizeof(struct usb_cdc_parsed_header)); + hdr->phonet_magic_present = false; + while (buflen > 0) { + elength = buffer[0]; + if (!elength) { + dev_err(&intf->dev, "skipping garbage byte\n"); + elength = 1; + goto next_desc; + } + if ((buflen < elength) || (elength < 3)) { + dev_err(&intf->dev, "invalid descriptor buffer length\n"); + break; + } + if (buffer[1] != USB_DT_CS_INTERFACE) { + dev_err(&intf->dev, "skipping garbage\n"); + goto next_desc; + } + + switch (buffer[2]) { + case USB_CDC_UNION_TYPE: /* we've found it */ + if (elength < sizeof(struct usb_cdc_union_desc)) + goto next_desc; + if (union_header) { + dev_err(&intf->dev, "More than one union descriptor, skipping ...\n"); + goto next_desc; + } + union_header = (struct usb_cdc_union_desc *)buffer; + break; + case USB_CDC_COUNTRY_TYPE: + if (elength < sizeof(struct usb_cdc_country_functional_desc)) + goto next_desc; + hdr->usb_cdc_country_functional_desc = + (struct usb_cdc_country_functional_desc *)buffer; + break; + case USB_CDC_HEADER_TYPE: + if (elength != sizeof(struct usb_cdc_header_desc)) + goto next_desc; + if (header) + return -EINVAL; + header = (struct usb_cdc_header_desc *)buffer; + break; + case USB_CDC_ACM_TYPE: + if (elength < sizeof(struct usb_cdc_acm_descriptor)) + goto next_desc; + hdr->usb_cdc_acm_descriptor = + (struct usb_cdc_acm_descriptor *)buffer; + break; + case USB_CDC_ETHERNET_TYPE: + if (elength != sizeof(struct usb_cdc_ether_desc)) + goto next_desc; + if (ether) + return -EINVAL; + ether = (struct usb_cdc_ether_desc *)buffer; + break; + case USB_CDC_CALL_MANAGEMENT_TYPE: + if (elength < sizeof(struct usb_cdc_call_mgmt_descriptor)) + goto next_desc; + hdr->usb_cdc_call_mgmt_descriptor = + (struct usb_cdc_call_mgmt_descriptor *)buffer; + break; + case USB_CDC_DMM_TYPE: + if (elength < sizeof(struct usb_cdc_dmm_desc)) + goto next_desc; + hdr->usb_cdc_dmm_desc = + (struct usb_cdc_dmm_desc *)buffer; + break; + case USB_CDC_MDLM_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_desc *)) + goto next_desc; + if (desc) + return -EINVAL; + desc = (struct usb_cdc_mdlm_desc *)buffer; + break; + case USB_CDC_MDLM_DETAIL_TYPE: + if (elength < sizeof(struct usb_cdc_mdlm_detail_desc *)) + goto next_desc; + if (detail) + return -EINVAL; + detail = (struct usb_cdc_mdlm_detail_desc *)buffer; + break; + case USB_CDC_NCM_TYPE: + if (elength < sizeof(struct usb_cdc_ncm_desc)) + goto next_desc; + hdr->usb_cdc_ncm_desc = (struct usb_cdc_ncm_desc *)buffer; + break; + case USB_CDC_MBIM_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_desc)) + goto next_desc; + + hdr->usb_cdc_mbim_desc = (struct usb_cdc_mbim_desc *)buffer; + break; + case USB_CDC_MBIM_EXTENDED_TYPE: + if (elength < sizeof(struct usb_cdc_mbim_extended_desc)) + break; + hdr->usb_cdc_mbim_extended_desc = + (struct usb_cdc_mbim_extended_desc *)buffer; + break; + case CDC_PHONET_MAGIC_NUMBER: + hdr->phonet_magic_present = true; + break; + default: + /* + * there are LOTS more CDC descriptors that + * could legitimately be found here. + */ + dev_dbg(&intf->dev, "Ignoring descriptor: type %02x, length %ud\n", + buffer[2], elength); + goto next_desc; + } + cnt++; +next_desc: + buflen -= elength; + buffer += elength; + } + hdr->usb_cdc_union_desc = union_header; + hdr->usb_cdc_header_desc = header; + hdr->usb_cdc_mdlm_detail_desc = detail; + hdr->usb_cdc_mdlm_desc = desc; + hdr->usb_cdc_ether_desc = ether; + return cnt; +} + +EXPORT_SYMBOL(cdc_parse_cdc_header); diff --git a/drivers/usb/dwc3/dwc3-msm.c b/drivers/usb/dwc3/dwc3-msm.c index f94d0ba2f966..de6bb295bf67 100644 --- a/drivers/usb/dwc3/dwc3-msm.c +++ b/drivers/usb/dwc3/dwc3-msm.c @@ -46,6 +46,7 @@ #include <linux/irq.h> #include <linux/extcon.h> #include <linux/reset.h> +#include <soc/qcom/boot_stats.h> #include "power.h" #include "core.h" @@ -2162,10 +2163,12 @@ static int dwc3_msm_suspend(struct dwc3_msm *mdwc, bool hibernation) * case of host bus suspend and device bus suspend. */ if (mdwc->vbus_active || mdwc->in_host_mode) { - enable_irq_wake(mdwc->hs_phy_irq); + if (!mdwc->no_wakeup_src_in_hostmode) + enable_irq_wake(mdwc->hs_phy_irq); enable_irq(mdwc->hs_phy_irq); if (mdwc->ss_phy_irq) { - enable_irq_wake(mdwc->ss_phy_irq); + if (!mdwc->no_wakeup_src_in_hostmode) + enable_irq_wake(mdwc->ss_phy_irq); enable_irq(mdwc->ss_phy_irq); } mdwc->lpm_flags |= MDWC3_ASYNC_IRQ_WAKE_CAPABILITY; @@ -2287,10 +2290,12 @@ static int dwc3_msm_resume(struct dwc3_msm *mdwc) /* Disable wakeup capable for HS_PHY IRQ & SS_PHY_IRQ if enabled */ if (mdwc->lpm_flags & MDWC3_ASYNC_IRQ_WAKE_CAPABILITY) { - disable_irq_wake(mdwc->hs_phy_irq); + if (!mdwc->no_wakeup_src_in_hostmode) + disable_irq_wake(mdwc->hs_phy_irq); disable_irq_nosync(mdwc->hs_phy_irq); if (mdwc->ss_phy_irq) { - disable_irq_wake(mdwc->ss_phy_irq); + if (!mdwc->no_wakeup_src_in_hostmode) + disable_irq_wake(mdwc->ss_phy_irq); disable_irq_nosync(mdwc->ss_phy_irq); } mdwc->lpm_flags &= ~MDWC3_ASYNC_IRQ_WAKE_CAPABILITY; @@ -2913,6 +2918,7 @@ static int dwc3_msm_probe(struct platform_device *pdev) int ret = 0; int ext_hub_reset_gpio; u32 val; + char boot_marker[40]; mdwc = devm_kzalloc(&pdev->dev, sizeof(*mdwc), GFP_KERNEL); if (!mdwc) @@ -3260,8 +3266,15 @@ static int dwc3_msm_probe(struct platform_device *pdev) mdwc->host_only_mode = true; mdwc->id_state = DWC3_ID_GROUND; dwc3_ext_event_notify(mdwc); + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER %s Host Ready", dev_name(&pdev->dev)); + } else { + snprintf(boot_marker, sizeof(boot_marker), + "M - DRIVER %s Device Ready", dev_name(&pdev->dev)); } + place_marker(boot_marker); + return 0; put_dwc3: diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 4ec0dd4f0a8c..5ccc09888345 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -4299,11 +4299,8 @@ DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc); static int ffs_init(void) { ffs_ipc_log = ipc_log_context_create(NUM_PAGES, "f_fs", 0); - if (IS_ERR_OR_NULL(ffs_ipc_log)) { + if (IS_ERR_OR_NULL(ffs_ipc_log)) ffs_ipc_log = NULL; - pr_err("%s: Create IPC log context failure\n", - __func__); - } return 0; } diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 4954e22a421b..50a1b0a34617 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -3659,6 +3659,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) del_timer_sync(&virt_dev->eps[i].stop_cmd_timer); } + virt_dev->udev = NULL; spin_lock_irqsave(&xhci->lock, flags); /* Don't disable the slot if the host controller is dead. */ state = readl(&xhci->op_regs->status); diff --git a/drivers/usb/misc/diag_ipc_bridge.c b/drivers/usb/misc/diag_ipc_bridge.c index b9ced8d0062d..a652c8f9bab7 100644 --- a/drivers/usb/misc/diag_ipc_bridge.c +++ b/drivers/usb/misc/diag_ipc_bridge.c @@ -730,6 +730,8 @@ static int diag_bridge_resume(struct usb_interface *ifc) static const struct usb_device_id diag_bridge_ids[] = { { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9001, 0), .driver_info = DEV_ID(0), }, + { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x901D, 0), + .driver_info = DEV_ID(0), }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9034, 0), .driver_info = DEV_ID(0), }, { USB_DEVICE_INTERFACE_NUMBER(0x5c6, 0x9048, 0), |
