From 98a667af9e9eece8af41a916e6ed925fcfbd5671 Mon Sep 17 00:00:00 2001 From: Ziqi Chen Date: Fri, 4 Aug 2017 17:37:33 +0800 Subject: [PATCH] msm: usb_bam: Fix failure of remove by of_platform_depopulate If device is created by of_platform_populate and removed by of_platform_depopulate from parent device driver, platform_data will be released twice and lead to kernel panic. of_platform_depopulate will free platform_data of each device driver. Usually we use devm_kzalloc to malloc for platform_data so it can be released during driver unregister. So if device is removed by of_platform_depopulate and platform_data is malloced by devm_kzalloc, platform_data will be freed twice. To fix it, move this data into struct usb_bam_ctx_type and then store usb_bam_ctx_type into device driver data. In this way, it can be get in remove function. The member regs of usb_bam_ctx_type is not necessary. It is only used for ioremap which already has been done by virt_addr of struct sps_bam_props. Without sps_deregister_bam_device, bam child device will not be created again during defer probe. So deregister in remove function. Change-Id: Ibae80745be5810d400a1c05566a99efcc2190020 Signed-off-by: Ziqi Chen --- drivers/platform/msm/usb_bam.c | 134 +++++++++++++++------------------ include/linux/usb_bam.h | 6 +- 2 files changed, 65 insertions(+), 75 deletions(-) diff --git a/drivers/platform/msm/usb_bam.c b/drivers/platform/msm/usb_bam.c index 48d31140834a..49e0666d0c23 100644 --- a/drivers/platform/msm/usb_bam.c +++ b/drivers/platform/msm/usb_bam.c @@ -103,7 +103,6 @@ struct usb_bam_sps_type { struct usb_bam_ctx_type { struct usb_bam_sps_type usb_bam_sps; struct resource *io_res; - void __iomem *regs; int irq; struct platform_device *usb_bam_pdev; struct workqueue_struct *usb_bam_wq; @@ -113,6 +112,7 @@ struct usb_bam_ctx_type { u32 inactivity_timer_ms; bool is_bam_inactivity; struct usb_bam_pipe_connect *usb_bam_connections; + struct msm_usb_bam_data *usb_bam_data; spinlock_t usb_bam_lock; }; @@ -320,8 +320,6 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) { int ret = 0; struct usb_bam_ctx_type *ctx = &msm_usb_bam[pipe_connect->bam_type]; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; struct sps_mem_buffer *data_buf = &(pipe_connect->data_mem_buf); struct sps_mem_buffer *desc_buf = &(pipe_connect->desc_mem_buf); @@ -360,7 +358,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) } data_buf->phys_base = pipe_connect->data_fifo_base_offset + - pdata->usb_bam_fifo_baseaddr; + ctx->usb_bam_data->usb_bam_fifo_baseaddr; data_buf->size = pipe_connect->data_fifo_size; data_buf->base = ioremap(data_buf->phys_base, data_buf->size); if (!data_buf->base) { @@ -372,7 +370,7 @@ static int usb_bam_alloc_buffer(struct usb_bam_pipe_connect *pipe_connect) memset_io(data_buf->base, 0, data_buf->size); desc_buf->phys_base = pipe_connect->desc_fifo_base_offset + - pdata->usb_bam_fifo_baseaddr; + ctx->usb_bam_data->usb_bam_fifo_baseaddr; desc_buf->size = pipe_connect->desc_fifo_size; desc_buf->base = ioremap(desc_buf->phys_base, desc_buf->size); if (!desc_buf->base) { @@ -1070,7 +1068,6 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx) struct usb_bam_pipe_connect *pipe_connect = &ctx->usb_bam_connections[idx]; struct device *bam_dev = &ctx->usb_bam_pdev->dev; - struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data; enum usb_bam_mode cur_mode; if (pipe_connect->enabled) { @@ -1096,7 +1093,7 @@ int usb_bam_connect(enum usb_ctrl cur_bam, int idx, u32 *bam_pipe_idx) spin_lock(&ctx->usb_bam_lock); /* Check if BAM requires RESET before connect and reset of first pipe */ - if ((pdata->reset_on_connect == true) && + if ((ctx->usb_bam_data->reset_on_connect == true) && (ctx->pipes_enabled_per_bam == 0)) { spin_unlock(&ctx->usb_bam_lock); @@ -1596,8 +1593,6 @@ static int ss_usb_cons_release_resource(void) static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam) { struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; struct ipa_rm_create_params usb_prod_create_params; struct ipa_rm_create_params usb_cons_create_params; int ret; @@ -1606,7 +1601,8 @@ static void usb_bam_ipa_create_resources(enum usb_ctrl cur_bam) memset(&usb_prod_create_params, 0, sizeof(usb_prod_create_params)); usb_prod_create_params.name = ipa_rm_resource_prod[cur_bam]; usb_prod_create_params.reg_params.notify_cb = usb_prod_notify_cb; - usb_prod_create_params.reg_params.user_data = &pdata->bam_type; + usb_prod_create_params.reg_params.user_data + = &ctx->usb_bam_data->bam_type; usb_prod_create_params.floor_voltage = IPA_VOLTAGE_SVS; ret = ipa_rm_create_resource(&usb_prod_create_params); if (ret) { @@ -2136,16 +2132,14 @@ static int usb_bam_set_ipa_perf(enum usb_ctrl cur_bam, int ret; struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; struct ipa_rm_perf_profile ipa_rm_perf_prof; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; if (usb_connection_speed == USB_SPEED_SUPER) ipa_rm_perf_prof.max_supported_bandwidth_mbps = - pdata->max_mbps_superspeed; + ctx->usb_bam_data->max_mbps_superspeed; else /* Bam2Bam is supported only for SS and HS (HW limitation) */ ipa_rm_perf_prof.max_supported_bandwidth_mbps = - pdata->max_mbps_highspeed; + ctx->usb_bam_data->max_mbps_highspeed; /* * Having a max mbps property in dtsi file is a must @@ -2182,7 +2176,6 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam, struct usb_bam_ctx_type *ctx = &msm_usb_bam[cur_bam]; struct usb_bam_pipe_connect *pipe_connect; struct device *bam_dev = &ctx->usb_bam_pdev->dev; - struct msm_usb_bam_platform_data *pdata = bam_dev->platform_data; int ret; bool bam2bam, is_dpl; @@ -2260,7 +2253,8 @@ int usb_bam_connect_ipa(enum usb_ctrl cur_bam, /* Check if BAM requires RESET before connect and reset first pipe */ spin_lock(&ctx->usb_bam_lock); - if (pdata->reset_on_connect && !ctx->pipes_enabled_per_bam) { + if (ctx->usb_bam_data->reset_on_connect && + !ctx->pipes_enabled_per_bam) { spin_unlock(&ctx->usb_bam_lock); if (cur_bam == CI_CTRL) @@ -2610,8 +2604,6 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx) struct usb_bam_pipe_connect *pipe_connect; struct device *bam_dev = &ctx->usb_bam_pdev->dev; int ret; - struct msm_usb_bam_platform_data *pdata = - ctx->usb_bam_pdev->dev.platform_data; pipe_connect = &ctx->usb_bam_connections[idx]; @@ -2639,7 +2631,8 @@ int usb_bam_disconnect_pipe(enum usb_ctrl bam_type, u8 idx) log_event_dbg("%s: success disconnecting pipe %d\n", __func__, idx); - if (pdata->reset_on_disconnect && !ctx->pipes_enabled_per_bam) { + if (ctx->usb_bam_data->reset_on_disconnect + && !ctx->pipes_enabled_per_bam) { if (bam_type == CI_CTRL) msm_hw_bam_disable(1); @@ -2807,10 +2800,10 @@ static void usb_bam_sps_events(enum sps_callback_case sps_cb_case, void *user) } } -static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( +static struct msm_usb_bam_data *usb_bam_dt_to_data( struct platform_device *pdev, u32 usb_addr) { - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; struct device_node *node = pdev->dev.of_node; int rc = 0; u8 i = 0; @@ -2819,10 +2812,10 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( u32 threshold, max_connections = 0; static struct usb_bam_pipe_connect *usb_bam_connections; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); - if (!pdata) { + usb_bam_data = devm_kzalloc(&pdev->dev, sizeof(*usb_bam_data), + GFP_KERNEL); + if (!usb_bam_data) return NULL; - } rc = of_property_read_u32(node, "qcom,bam-type", &bam); if (rc) { @@ -2835,7 +2828,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( __func__, bam); return NULL; } - pdata->bam_type = bam; + usb_bam_data->bam_type = bam; rc = of_property_read_u32(node, "qcom,bam-mode", &bam_mode); if (rc) { @@ -2845,49 +2838,49 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( bam_mode = USB_BAM_DEVICE; } - pdata->reset_on_connect = of_property_read_bool(node, + usb_bam_data->reset_on_connect = of_property_read_bool(node, "qcom,reset-bam-on-connect"); - pdata->reset_on_disconnect = of_property_read_bool(node, + usb_bam_data->reset_on_disconnect = of_property_read_bool(node, "qcom,reset-bam-on-disconnect"); rc = of_property_read_u32(node, "qcom,usb-bam-num-pipes", - &pdata->usb_bam_num_pipes); + &usb_bam_data->usb_bam_num_pipes); if (rc) { log_event_err("Invalid usb bam num pipes property\n"); return NULL; } rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-highspeed", - &pdata->max_mbps_highspeed); + &usb_bam_data->max_mbps_highspeed); if (rc) - pdata->max_mbps_highspeed = 0; + usb_bam_data->max_mbps_highspeed = 0; rc = of_property_read_u32(node, "qcom,usb-bam-max-mbps-superspeed", - &pdata->max_mbps_superspeed); + &usb_bam_data->max_mbps_superspeed); if (rc) - pdata->max_mbps_superspeed = 0; + usb_bam_data->max_mbps_superspeed = 0; rc = of_property_read_u32(node, "qcom,usb-bam-fifo-baseaddr", &addr); if (rc) pr_debug("%s: Invalid usb base address property\n", __func__); else - pdata->usb_bam_fifo_baseaddr = addr; + usb_bam_data->usb_bam_fifo_baseaddr = addr; - pdata->ignore_core_reset_ack = of_property_read_bool(node, + usb_bam_data->ignore_core_reset_ack = of_property_read_bool(node, "qcom,ignore-core-reset-ack"); - pdata->disable_clk_gating = of_property_read_bool(node, + usb_bam_data->disable_clk_gating = of_property_read_bool(node, "qcom,disable-clk-gating"); rc = of_property_read_u32(node, "qcom,usb-bam-override-threshold", &threshold); if (rc) - pdata->override_threshold = USB_THRESHOLD; + usb_bam_data->override_threshold = USB_THRESHOLD; else - pdata->override_threshold = threshold; + usb_bam_data->override_threshold = threshold; - pdata->enable_hsusb_bam_on_boot = of_property_read_bool(node, + usb_bam_data->enable_hsusb_bam_on_boot = of_property_read_bool(node, "qcom,enable-hsusb-bam-on-boot"); for_each_child_of_node(pdev->dev.of_node, node) @@ -2923,7 +2916,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( goto err; if (usb_bam_connections[i].mem_type == OCI_MEM) { - if (!pdata->usb_bam_fifo_baseaddr) { + if (!usb_bam_data->usb_bam_fifo_baseaddr) { log_event_err("%s: base address is missing\n", __func__); goto err; @@ -3007,7 +3000,7 @@ static struct msm_usb_bam_platform_data *usb_bam_dt_to_pdata( msm_usb_bam[bam].usb_bam_connections = usb_bam_connections; msm_usb_bam[bam].max_connections = max_connections; - return pdata; + return usb_bam_data; err: log_event_err("%s: failed\n", __func__); return NULL; @@ -3016,9 +3009,8 @@ err: static int usb_bam_init(struct platform_device *pdev) { int ret; - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type; struct sps_bam_props props; memset(&props, 0, sizeof(props)); @@ -3027,12 +3019,11 @@ static int usb_bam_init(struct platform_device *pdev) bam_enable_strings[bam_type]); props.phys_addr = ctx->io_res->start; - props.virt_addr = ctx->regs; props.virt_size = resource_size(ctx->io_res); props.irq = ctx->irq; - props.summing_threshold = pdata->override_threshold; - props.event_threshold = pdata->override_threshold; - props.num_pipes = pdata->usb_bam_num_pipes; + props.summing_threshold = ctx->usb_bam_data->override_threshold; + props.event_threshold = ctx->usb_bam_data->override_threshold; + props.num_pipes = ctx->usb_bam_data->usb_bam_num_pipes; props.callback = usb_bam_sps_events; props.user = bam_enable_strings[bam_type]; @@ -3040,10 +3031,10 @@ static int usb_bam_init(struct platform_device *pdev) * HSUSB and HSIC Cores don't support RESET ACK signal to BAMs * Hence, let BAM to ignore acknowledge from USB while resetting PIPE */ - if (pdata->ignore_core_reset_ack && bam_type != DWC3_CTRL) + if (ctx->usb_bam_data->ignore_core_reset_ack && bam_type != DWC3_CTRL) props.options = SPS_BAM_NO_EXT_P_RST; - if (pdata->disable_clk_gating) + if (ctx->usb_bam_data->disable_clk_gating) props.options |= SPS_BAM_NO_LOCAL_CLK_GATING; /* @@ -3051,7 +3042,8 @@ static int usb_bam_init(struct platform_device *pdev) * starting peripheral controller to avoid switching USB core mode * from legacy to BAM with ongoing data transfers. */ - if (pdata->enable_hsusb_bam_on_boot && bam_type == CI_CTRL) { + if (ctx->usb_bam_data->enable_hsusb_bam_on_boot + && bam_type == CI_CTRL) { pr_debug("Register and enable HSUSB BAM\n"); props.options |= SPS_BAM_OPT_ENABLE_AT_BOOT; } @@ -3068,9 +3060,8 @@ static int usb_bam_init(struct platform_device *pdev) static int enable_usb_bam(struct platform_device *pdev) { int ret; - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + enum usb_ctrl bam_type = ctx->usb_bam_data->bam_type; ret = usb_bam_init(pdev); if (ret) { @@ -3137,14 +3128,19 @@ void usb_bam_register_panic_hdlr(void) &usb_bam_panic_blk); } +static void usb_bam_unregister_panic_hdlr(void) +{ + atomic_notifier_chain_unregister(&panic_notifier_list, + &usb_bam_panic_blk); +} + static int usb_bam_probe(struct platform_device *pdev) { int ret, i, irq; struct resource *io_res; - void __iomem *regs; enum usb_ctrl bam_type; struct usb_bam_ctx_type *ctx; - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; dev_dbg(&pdev->dev, "usb_bam_probe\n"); @@ -3160,25 +3156,19 @@ static int usb_bam_probe(struct platform_device *pdev) return irq; } - regs = devm_ioremap(&pdev->dev, io_res->start, resource_size(io_res)); - if (!regs) { - log_event_err("%s: ioremap failed\n", __func__); - return -ENOMEM; - } - /* specify BAM physical address to be filled in BAM connections */ - pdata = usb_bam_dt_to_pdata(pdev, io_res->start); - if (!pdata) + usb_bam_data = usb_bam_dt_to_data(pdev, io_res->start); + if (!usb_bam_data) return -EINVAL; - pdev->dev.platform_data = pdata; - bam_type = pdata->bam_type; + bam_type = usb_bam_data->bam_type; ctx = &msm_usb_bam[bam_type]; + dev_set_drvdata(&pdev->dev, ctx); ctx->usb_bam_pdev = pdev; ctx->irq = irq; - ctx->regs = regs; ctx->io_res = io_res; + ctx->usb_bam_data = usb_bam_data; for (i = 0; i < ctx->max_connections; i++) { ctx->usb_bam_connections[i].enabled = 0; @@ -3319,15 +3309,15 @@ EXPORT_SYMBOL(usb_bam_get_bam_type); bool msm_usb_bam_enable(enum usb_ctrl bam, bool bam_enable) { - struct msm_usb_bam_platform_data *pdata; + struct msm_usb_bam_data *usb_bam_data; struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam]; if (!ctx->usb_bam_pdev) return 0; - pdata = ctx->usb_bam_pdev->dev.platform_data; + usb_bam_data = ctx->usb_bam_data; if ((bam != CI_CTRL) || !(bam_enable || - pdata->enable_hsusb_bam_on_boot)) + usb_bam_data->enable_hsusb_bam_on_boot)) return 0; msm_hw_bam_disable(1); @@ -3409,10 +3399,10 @@ EXPORT_SYMBOL(msm_bam_hsic_lpm_ok); static int usb_bam_remove(struct platform_device *pdev) { - struct msm_usb_bam_platform_data *pdata = pdev->dev.platform_data; - enum usb_ctrl bam_type = pdata->bam_type; - struct usb_bam_ctx_type *ctx = &msm_usb_bam[bam_type]; + struct usb_bam_ctx_type *ctx = dev_get_drvdata(&pdev->dev); + usb_bam_unregister_panic_hdlr(); + sps_deregister_bam_device(ctx->h_bam); destroy_workqueue(ctx->usb_bam_wq); return 0; diff --git a/include/linux/usb_bam.h b/include/linux/usb_bam.h index e65fb12c1410..b5b9edaab783 100644 --- a/include/linux/usb_bam.h +++ b/include/linux/usb_bam.h @@ -1,4 +1,4 @@ -/* Copyright (c) 2011-2016, The Linux Foundation. All rights reserved. +/* Copyright (c) 2011-2017, 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 @@ -191,7 +191,7 @@ struct usb_bam_pipe_connect { }; /** - * struct msm_usb_bam_platform_data: pipe connection information + * struct msm_usb_bam_data: pipe connection information * between USB/HSIC BAM and another BAM. USB/HSIC BAM can be * either src BAM or dst BAM * @usb_bam_num_pipes: max number of pipes to use. @@ -211,7 +211,7 @@ struct usb_bam_pipe_connect { * can work at in bam2bam mode when connected to SS host. * @enable_hsusb_bam_on_boot: Enable HSUSB BAM (non-NDP) on bootup itself */ -struct msm_usb_bam_platform_data { +struct msm_usb_bam_data { u8 max_connections; int usb_bam_num_pipes; phys_addr_t usb_bam_fifo_baseaddr;