// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. * * Description: CoreSight TMC USB driver */ #include #include #include #include #include #include #include "coresight-tmc-usb.h" #include "coresight-priv.h" #include "coresight-common.h" #include "coresight-tmc.h" #define USB_BLK_SIZE 65536 #define USB_TOTAL_IRQ (TMC_ETR_SW_USB_BUF_SIZE/USB_BLK_SIZE) #define USB_SG_NUM (USB_BLK_SIZE / PAGE_SIZE) #define USB_BUF_NUM 255 #define USB_TIME_OUT (5 * HZ) #define TMC_AXICTL_VALUE (0xf02) #define TMC_FFCR_VALUE (0x133) static int usb_bypass_start(struct byte_cntr *byte_cntr_data) { long offset; struct tmc_drvdata *tmcdrvdata; if (!byte_cntr_data) return -ENOMEM; tmcdrvdata = byte_cntr_data->tmcdrvdata; mutex_lock(&byte_cntr_data->usb_bypass_lock); dev_info(&tmcdrvdata->csdev->dev, "%s: Start usb bypass\n", __func__); if (tmcdrvdata->mode != CS_MODE_SYSFS) { mutex_unlock(&byte_cntr_data->usb_bypass_lock); return -EINVAL; } offset = tmc_get_rwp_offset(tmcdrvdata); if (offset < 0) { dev_err(&tmcdrvdata->csdev->dev, "%s: invalid rwp offset value\n", __func__); mutex_unlock(&byte_cntr_data->usb_bypass_lock); return offset; } byte_cntr_data->offset = offset; tmcdrvdata->usb_data->drop_data_size = 0; tmcdrvdata->usb_data->data_overwritten = false; /*Ensure usbch is ready*/ if (!tmcdrvdata->usb_data->usbch) { int i; for (i = TIMEOUT_US; i > 0; i--) { if (tmcdrvdata->usb_data->usbch) break; if (i - 1) udelay(1); else { dev_err(&tmcdrvdata->csdev->dev, "timeout while waiting usbch to be ready\n"); mutex_unlock(&byte_cntr_data->usb_bypass_lock); return -EAGAIN; } } } atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM); byte_cntr_data->read_active = true; /* * IRQ is a '8- byte' counter and to observe interrupt at * 'block_size' bytes of data */ coresight_csr_set_byte_cntr(byte_cntr_data->csr, byte_cntr_data->irqctrl_offset, USB_BLK_SIZE / 8); atomic_set(&byte_cntr_data->irq_cnt, 0); byte_cntr_data->total_size = 0; mutex_unlock(&byte_cntr_data->usb_bypass_lock); return 0; } static void usb_bypass_stop(struct byte_cntr *byte_cntr_data) { if (!byte_cntr_data) return; mutex_lock(&byte_cntr_data->usb_bypass_lock); if (byte_cntr_data->read_active) byte_cntr_data->read_active = false; else { mutex_unlock(&byte_cntr_data->usb_bypass_lock); return; } wake_up(&byte_cntr_data->usb_wait_wq); pr_info("coresight: stop usb bypass\n"); coresight_csr_set_byte_cntr(byte_cntr_data->csr, byte_cntr_data->irqctrl_offset, 0); dev_dbg(&byte_cntr_data->tmcdrvdata->csdev->dev, "write to usb data total size: %lld bytes, total irq_cnt: %lld, current irq cnt: %d, offset: %ld, drop_data: %lld\n", byte_cntr_data->total_size, byte_cntr_data->total_irq, atomic_read(&byte_cntr_data->irq_cnt), byte_cntr_data->offset, byte_cntr_data->tmcdrvdata->usb_data->drop_data_size); byte_cntr_data->total_irq = 0; mutex_unlock(&byte_cntr_data->usb_bypass_lock); } static int usb_transfer_small_packet(struct byte_cntr *drvdata, size_t *small_size) { int ret = 0; struct tmc_drvdata *tmcdrvdata = drvdata->tmcdrvdata; struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf; struct qdss_request *usb_req = NULL; size_t req_size; long actual; long w_offset; w_offset = tmc_get_rwp_offset(tmcdrvdata); if (w_offset < 0) { ret = w_offset; dev_err_ratelimited(&tmcdrvdata->csdev->dev, "%s: RWP offset is invalid\n", __func__); goto out; } if (unlikely(atomic_read(&drvdata->irq_cnt) > USB_TOTAL_IRQ)) { tmcdrvdata->usb_data->data_overwritten = true; dev_err_ratelimited(&tmcdrvdata->csdev->dev, "ETR data is overwritten.\n"); } req_size = ((w_offset < drvdata->offset) ? etr_buf->size : 0) + w_offset - drvdata->offset; req_size = ((req_size + *small_size) < USB_BLK_SIZE) ? req_size : (USB_BLK_SIZE - *small_size); while (req_size > 0) { usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL); if (!usb_req) { ret = -EFAULT; goto out; } actual = tmc_etr_buf_get_data(etr_buf, drvdata->offset, req_size, &usb_req->buf); if (actual <= 0 || actual > req_size) { kfree(usb_req); usb_req = NULL; dev_err_ratelimited(&tmcdrvdata->csdev->dev, "%s: Invalid data in ETR\n", __func__); ret = -EINVAL; goto out; } usb_req->length = actual; drvdata->usb_req = usb_req; req_size -= actual; if ((drvdata->offset + actual) >= tmcdrvdata->sysfs_buf->size) drvdata->offset = 0; else drvdata->offset += actual; *small_size += actual; if (atomic_read(&drvdata->usb_free_buf) > 0) { ret = usb_qdss_write(tmcdrvdata->usb_data->usbch, usb_req); if (ret) { kfree(usb_req); usb_req = NULL; drvdata->usb_req = NULL; dev_err_ratelimited(&tmcdrvdata->csdev->dev, "Write data failed:%d\n", ret); goto out; } drvdata->total_size += actual; atomic_dec(&drvdata->usb_free_buf); } else { dev_err_ratelimited(&tmcdrvdata->csdev->dev, "Drop data, offset = %d, len = %d\n", drvdata->offset, req_size); tmcdrvdata->usb_data->drop_data_size += actual; kfree(usb_req); drvdata->usb_req = NULL; } } out: return ret; } static void usb_read_work_fn(struct work_struct *work) { int ret, i, seq = 0; struct qdss_request *usb_req = NULL; size_t req_size, req_sg_num, small_size = 0; long actual; ssize_t actual_total = 0; char *buf; struct byte_cntr *drvdata = container_of(work, struct byte_cntr, read_work); struct tmc_drvdata *tmcdrvdata = drvdata->tmcdrvdata; struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf; while (tmcdrvdata->mode == CS_MODE_SYSFS && tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_USB) { if (!atomic_read(&drvdata->irq_cnt)) { ret = wait_event_interruptible_timeout( drvdata->usb_wait_wq, atomic_read(&drvdata->irq_cnt) > 0 || tmcdrvdata->mode != CS_MODE_SYSFS || tmcdrvdata->out_mode != TMC_ETR_OUT_MODE_USB || !drvdata->read_active, USB_TIME_OUT); if (ret == -ERESTARTSYS || tmcdrvdata->mode != CS_MODE_SYSFS || tmcdrvdata->out_mode != TMC_ETR_OUT_MODE_USB || !drvdata->read_active) break; if (ret == 0) { ret = usb_transfer_small_packet(drvdata, &small_size); if (ret && ret != -EAGAIN) return; continue; } } if (unlikely(atomic_read(&drvdata->irq_cnt) > USB_TOTAL_IRQ)) { tmcdrvdata->usb_data->data_overwritten = true; dev_err_ratelimited(&tmcdrvdata->csdev->dev, "ETR data is overwritten.\n"); } req_size = USB_BLK_SIZE - small_size; small_size = 0; actual_total = 0; if (req_size > 0) { seq++; req_sg_num = (req_size - 1) / PAGE_SIZE + 1; usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL); if (!usb_req) return; usb_req->sg = kcalloc(req_sg_num, sizeof(*(usb_req->sg)), GFP_KERNEL); if (!usb_req->sg) { kfree(usb_req); usb_req = NULL; return; } for (i = 0; i < req_sg_num; i++) { actual = tmc_etr_buf_get_data(etr_buf, drvdata->offset, PAGE_SIZE, &buf); if (actual <= 0 || actual > PAGE_SIZE) { kfree(usb_req->sg); kfree(usb_req); usb_req = NULL; dev_err_ratelimited( &tmcdrvdata->csdev->dev, "Invalid data in ETR\n"); return; } sg_set_buf(&usb_req->sg[i], buf, actual); if (i == 0) usb_req->buf = buf; if (i == req_sg_num - 1) sg_mark_end(&usb_req->sg[i]); if ((drvdata->offset + actual) >= tmcdrvdata->sysfs_buf->size) drvdata->offset = 0; else drvdata->offset += actual; actual_total += actual; } usb_req->length = actual_total; drvdata->usb_req = usb_req; usb_req->num_sgs = i; if (atomic_read(&drvdata->usb_free_buf) > 0) { ret = usb_qdss_write(tmcdrvdata->usb_data->usbch, drvdata->usb_req); if (ret) { kfree(usb_req->sg); kfree(usb_req); usb_req = NULL; drvdata->usb_req = NULL; dev_err_ratelimited( &tmcdrvdata->csdev->dev, "Write data failed:%d\n", ret); if (ret == -EAGAIN) continue; return; } drvdata->total_size += actual_total; atomic_dec(&drvdata->usb_free_buf); } else { dev_err_ratelimited(&tmcdrvdata->csdev->dev, "Drop data, offset = %d, seq = %d, irq = %d\n", drvdata->offset, seq, atomic_read(&drvdata->irq_cnt)); tmcdrvdata->usb_data->drop_data_size += actual_total; kfree(usb_req->sg); kfree(usb_req); drvdata->usb_req = NULL; } } if (atomic_read(&drvdata->irq_cnt) > 0) atomic_dec(&drvdata->irq_cnt); } dev_err(&tmcdrvdata->csdev->dev, "TMC has been stopped.\n"); } static void usb_write_done(struct byte_cntr *drvdata, struct qdss_request *d_req) { atomic_inc(&drvdata->usb_free_buf); if (d_req->status && d_req->status != -ECONNRESET && d_req->status != -ESHUTDOWN) pr_err_ratelimited("USB write failed err:%d\n", d_req->status); kfree(d_req->sg); kfree(d_req); } static int usb_bypass_init(struct byte_cntr *byte_cntr_data) { byte_cntr_data->usb_wq = create_singlethread_workqueue("byte-cntr"); if (!byte_cntr_data->usb_wq) return -ENOMEM; byte_cntr_data->offset = 0; mutex_init(&byte_cntr_data->usb_bypass_lock); init_waitqueue_head(&byte_cntr_data->usb_wait_wq); atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM); INIT_WORK(&(byte_cntr_data->read_work), usb_read_work_fn); return 0; } static int tmc_etr_fill_usb_bam_data(struct tmc_usb_data *usb_data) { struct tmc_usb_bam_data *bamdata = usb_data->bamdata; struct tmc_drvdata *tmcdrvdata = usb_data->tmcdrvdata; dma_addr_t data_fifo_iova, desc_fifo_iova; get_qdss_bam_connection_info(&bamdata->dest, &bamdata->dest_pipe_idx, &bamdata->src_pipe_idx, &bamdata->desc_fifo, &bamdata->data_fifo, NULL); if (bamdata->props.options & SPS_BAM_SMMU_EN) { data_fifo_iova = dma_map_resource(tmcdrvdata->csdev->dev.parent, bamdata->data_fifo.phys_base, bamdata->data_fifo.size, DMA_BIDIRECTIONAL, 0); if (!data_fifo_iova) return -ENOMEM; dev_dbg(&tmcdrvdata->csdev->dev, "%s:data p_addr:%pa,iova:%pad,size:%x\n", __func__, &(bamdata->data_fifo.phys_base), &data_fifo_iova, bamdata->data_fifo.size); bamdata->data_fifo.iova = data_fifo_iova; desc_fifo_iova = dma_map_resource(tmcdrvdata->csdev->dev.parent, bamdata->desc_fifo.phys_base, bamdata->desc_fifo.size, DMA_BIDIRECTIONAL, 0); if (!desc_fifo_iova) return -ENOMEM; dev_dbg(&tmcdrvdata->csdev->dev, "%s:desc p_addr:%pa,iova:%pad,size:%x\n", __func__, &(bamdata->desc_fifo.phys_base), &desc_fifo_iova, bamdata->desc_fifo.size); bamdata->desc_fifo.iova = desc_fifo_iova; } return 0; } static void __tmc_etr_enable_to_bam(struct tmc_usb_data *usb_data) { struct tmc_usb_bam_data *bamdata = usb_data->bamdata; struct tmc_drvdata *tmcdrvdata = usb_data->tmcdrvdata; if (usb_data->enable_to_bam) return; /* Configure and enable required CSR registers */ msm_qdss_csr_enable_bam_to_usb(tmcdrvdata->csr); /* Configure and enable ETR for usb bam output */ CS_UNLOCK(tmcdrvdata->base); writel_relaxed(bamdata->data_fifo.size / 4, tmcdrvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, tmcdrvdata->base + TMC_MODE); writel_relaxed(TMC_AXICTL_VALUE, tmcdrvdata->base + TMC_AXICTL); if (bamdata->props.options & SPS_BAM_SMMU_EN) { writel_relaxed((uint32_t)bamdata->data_fifo.iova, tmcdrvdata->base + TMC_DBALO); writel_relaxed((((uint64_t)bamdata->data_fifo.iova) >> 32) & 0xFF, tmcdrvdata->base + TMC_DBAHI); } else { writel_relaxed((uint32_t)bamdata->data_fifo.phys_base, tmcdrvdata->base + TMC_DBALO); writel_relaxed((((uint64_t)bamdata->data_fifo.phys_base) >> 32) & 0xFF, tmcdrvdata->base + TMC_DBAHI); } /* Set FOnFlIn for periodic flush */ writel_relaxed(TMC_FFCR_VALUE, tmcdrvdata->base + TMC_FFCR); writel_relaxed(tmcdrvdata->trigger_cntr, tmcdrvdata->base + TMC_TRG); tmc_enable_hw(tmcdrvdata); CS_LOCK(tmcdrvdata->base); msm_qdss_csr_enable_flush(tmcdrvdata->csr); usb_data->enable_to_bam = true; } static int get_usb_bam_iova(struct device *dev, unsigned long usb_bam_handle, unsigned long *iova) { int ret = 0; phys_addr_t p_addr; u32 bam_size; ret = sps_get_bam_addr(usb_bam_handle, &p_addr, &bam_size); if (ret) { dev_err(dev, "sps_get_bam_addr failed at handle:%lx, err:%d\n", usb_bam_handle, ret); return ret; } *iova = dma_map_resource(dev, p_addr, bam_size, DMA_BIDIRECTIONAL, 0); if (!(*iova)) return -ENOMEM; return 0; } static int tmc_etr_bam_enable(struct tmc_usb_data *usb_data) { struct tmc_usb_bam_data *bamdata; struct tmc_drvdata *tmcdrvdata; unsigned long iova; int ret; if (usb_data == NULL) return -EINVAL; bamdata = usb_data->bamdata; tmcdrvdata = usb_data->tmcdrvdata; if (bamdata->enable) return 0; /* Reset bam to start with */ ret = sps_device_reset(bamdata->handle); if (ret) goto err0; /* Now configure and enable bam */ bamdata->pipe = sps_alloc_endpoint(); if (!bamdata->pipe) return -ENOMEM; ret = sps_get_config(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->connect.mode = SPS_MODE_SRC; bamdata->connect.source = bamdata->handle; bamdata->connect.event_thresh = 0x4; bamdata->connect.src_pipe_index = TMC_USB_BAM_PIPE_INDEX; bamdata->connect.options = SPS_O_AUTO_ENABLE; bamdata->connect.destination = bamdata->dest; bamdata->connect.dest_pipe_index = bamdata->dest_pipe_idx; bamdata->connect.desc = bamdata->desc_fifo; bamdata->connect.data = bamdata->data_fifo; if (bamdata->props.options & SPS_BAM_SMMU_EN) { ret = get_usb_bam_iova(tmcdrvdata->csdev->dev.parent, bamdata->dest, &iova); if (ret) goto err1; bamdata->connect.dest_iova = iova; } ret = sps_connect(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->enable = true; return 0; err1: sps_free_endpoint(bamdata->pipe); err0: return ret; } static void tmc_wait_for_flush(struct tmc_drvdata *drvdata) { int count; /* Ensure no flush is in progress */ for (count = TIMEOUT_US; BVAL(readl_relaxed(drvdata->base + TMC_FFSR), 0) != 0 && count > 0; count--) udelay(1); WARN(count == 0, "timeout while waiting for TMC flush, TMC_FFSR: %#x\n", readl_relaxed(drvdata->base + TMC_FFSR)); } void __tmc_etr_disable_to_bam(struct tmc_usb_data *usb_data) { struct tmc_drvdata *tmcdrvdata = usb_data->tmcdrvdata; if (!usb_data->enable_to_bam) return; if (tmcdrvdata->csr == NULL) return; /* Ensure periodic flush is disabled in CSR block */ msm_qdss_csr_disable_flush(tmcdrvdata->csr); CS_UNLOCK(tmcdrvdata->base); tmc_wait_for_flush(tmcdrvdata); tmc_disable_hw(tmcdrvdata); CS_LOCK(tmcdrvdata->base); /* Disable CSR configuration */ msm_qdss_csr_disable_bam_to_usb(tmcdrvdata->csr); usb_data->enable_to_bam = false; } void tmc_etr_bam_disable(struct tmc_usb_data *usb_data) { struct tmc_usb_bam_data *bamdata; if (usb_data == NULL) return; bamdata = usb_data->bamdata; if (!bamdata->enable) return; sps_disconnect(bamdata->pipe); sps_free_endpoint(bamdata->pipe); bamdata->enable = false; } void usb_notifier(void *priv, unsigned int event, struct qdss_request *d_req, struct usb_qdss_ch *ch) { struct tmc_drvdata *drvdata = priv; unsigned long flags; int ret = 0; if (!drvdata) return; if (drvdata->out_mode != TMC_ETR_OUT_MODE_USB) { dev_err(&drvdata->csdev->dev, "%s: ETR is not USB mode.\n", __func__); return; } switch (event) { case USB_QDSS_CONNECT: if (drvdata->mode == CS_MODE_DISABLED) { dev_err_ratelimited(&drvdata->csdev->dev, "%s: ETR is disabled.\n", __func__); return; } if (drvdata->usb_data->usb_mode == TMC_ETR_USB_BAM_TO_BAM) { ret = tmc_etr_fill_usb_bam_data(drvdata->usb_data); if (ret) dev_err(&drvdata->csdev->dev, "ETR get usb bam data failed\n"); ret = tmc_etr_bam_enable(drvdata->usb_data); if (ret) dev_err(&drvdata->csdev->dev, "ETR BAM enable failed\n"); spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_enable_to_bam(drvdata->usb_data); spin_unlock_irqrestore(&drvdata->spinlock, flags); } else if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW) { ret = usb_bypass_start(drvdata->byte_cntr); if (ret < 0) return; usb_qdss_alloc_req(ch, USB_BUF_NUM); queue_work(drvdata->byte_cntr->usb_wq, &(drvdata->byte_cntr->read_work)); } break; case USB_QDSS_DISCONNECT: if (drvdata->mode == CS_MODE_DISABLED) { dev_err_ratelimited(&drvdata->csdev->dev, "%s: ETR is disabled.\n", __func__); return; } if (drvdata->usb_data->usb_mode == TMC_ETR_USB_BAM_TO_BAM) { spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_disable_to_bam(drvdata->usb_data); spin_unlock_irqrestore(&drvdata->spinlock, flags); tmc_etr_bam_disable(drvdata->usb_data); } else if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW) { usb_bypass_stop(drvdata->byte_cntr); flush_work(&((drvdata->byte_cntr->read_work))); usb_qdss_free_req(drvdata->usb_data->usbch); } break; case USB_QDSS_DATA_WRITE_DONE: if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW) usb_write_done(drvdata->byte_cntr, d_req); break; default: break; } } static bool tmc_etr_support_usb_bam(struct device *dev) { return fwnode_property_present(dev->fwnode, "usb_bam_support"); } static bool tmc_etr_support_usb_bypass(struct device *dev) { return fwnode_property_present(dev->fwnode, "qcom,sw-usb"); } int tmc_usb_enable(struct tmc_usb_data *usb_data) { struct tmc_drvdata *tmcdrvdata; if (!usb_data) return -EINVAL; tmcdrvdata = usb_data->tmcdrvdata; if (usb_data->usb_mode == TMC_ETR_USB_BAM_TO_BAM) usb_data->usbch = usb_qdss_open(USB_QDSS_CH_MSM, tmcdrvdata, usb_notifier); else if (usb_data->usb_mode == TMC_ETR_USB_SW) usb_data->usbch = usb_qdss_open(USB_QDSS_CH_SW, tmcdrvdata, usb_notifier); if (IS_ERR_OR_NULL(usb_data->usbch)) { dev_err(&tmcdrvdata->csdev->dev, "usb_qdss_open failed for qdss.\n"); return -ENODEV; } return 0; } void tmc_usb_disable(struct tmc_usb_data *usb_data) { unsigned long flags; struct tmc_drvdata *tmcdrvdata = usb_data->tmcdrvdata; if (usb_data->usb_mode == TMC_ETR_USB_BAM_TO_BAM) { spin_lock_irqsave(&tmcdrvdata->spinlock, flags); __tmc_etr_disable_to_bam(usb_data); spin_unlock_irqrestore(&tmcdrvdata->spinlock, flags); tmc_etr_bam_disable(usb_data); } else if (usb_data->usb_mode == TMC_ETR_USB_SW) { usb_bypass_stop(tmcdrvdata->byte_cntr); flush_work(&tmcdrvdata->byte_cntr->read_work); } if (usb_data->usbch) usb_qdss_close(usb_data->usbch); else dev_err(&tmcdrvdata->csdev->dev, "usb channel is null.\n"); } int tmc_etr_usb_init(struct amba_device *adev, struct tmc_drvdata *drvdata) { int ret; struct device *dev = &adev->dev; struct resource res; struct tmc_usb_bam_data *bamdata; int mapping_config = 0; struct iommu_domain *domain; struct tmc_usb_data *usb_data = NULL; struct byte_cntr *byte_cntr_data; if (tmc_etr_support_usb_bypass(dev) || tmc_etr_support_usb_bam(dev)) { usb_data = devm_kzalloc(dev, sizeof(*usb_data), GFP_KERNEL); if (!usb_data) return -ENOMEM; drvdata->usb_data = usb_data; drvdata->usb_data->tmcdrvdata = drvdata; drvdata->mode_support |= BIT(TMC_ETR_OUT_MODE_USB); if (tmc_etr_support_usb_bypass(dev)) { byte_cntr_data = drvdata->byte_cntr; usb_data->usb_mode = TMC_ETR_USB_SW; usb_data->drop_data_size = 0; usb_data->data_overwritten = false; ret = of_property_read_u32(dev->of_node, "sw_usb_buf_size", &usb_data->buf_size); if (ret) usb_data->buf_size = TMC_ETR_SW_USB_BUF_SIZE; dev_info(dev, "setting tmc etr sw usb buf size 0x%x\n", usb_data->buf_size); if (!byte_cntr_data) return -EINVAL; ret = usb_bypass_init(byte_cntr_data); if (ret) return -EINVAL; return 0; } else if (tmc_etr_support_usb_bam(dev)) { usb_data->usb_mode = TMC_ETR_USB_BAM_TO_BAM; bamdata = devm_kzalloc(dev, sizeof(*bamdata), GFP_KERNEL); if (!bamdata) return -ENOMEM; drvdata->usb_data->bamdata = bamdata; ret = of_address_to_resource(adev->dev.of_node, 1, &res); if (ret) return -ENODEV; bamdata->props.phys_addr = res.start; bamdata->props.virt_addr = devm_ioremap(dev, res.start, resource_size(&res)); if (!bamdata->props.virt_addr) return -ENOMEM; bamdata->props.virt_size = resource_size(&res); bamdata->props.event_threshold = 0x4; /* Pipe event threshold */ bamdata->props.summing_threshold = 0x10; /* BAM event threshold */ bamdata->props.irq = 0; bamdata->props.num_pipes = TMC_USB_BAM_NR_PIPES; domain = iommu_get_domain_for_dev(dev); if (domain) { mapping_config = qcom_iommu_get_mappings_configuration(domain); if (mapping_config < 0) return -ENOMEM; if (!(mapping_config & QCOM_IOMMU_MAPPING_CONF_S1_BYPASS)) { pr_debug("%s: setting SPS_BAM_SMMU_EN flag with (%s)\n", __func__, dev_name(dev)); bamdata->props.options |= SPS_BAM_SMMU_EN; } } return sps_register_bam_device(&bamdata->props, &bamdata->handle); } } drvdata->usb_data = NULL; pr_err("%s: ETR usb property is not configured!\n", __func__, dev_name(dev)); return 0; }