123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (c) 2020 Synopsys, Inc. and/or its affiliates.
- * Synopsys DesignWare xData driver
- *
- * Author: Gustavo Pimentel <[email protected]>
- */
- #include <linux/miscdevice.h>
- #include <linux/bitfield.h>
- #include <linux/pci-epf.h>
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/device.h>
- #include <linux/bitops.h>
- #include <linux/mutex.h>
- #include <linux/delay.h>
- #include <linux/pci.h>
- #define DW_XDATA_DRIVER_NAME "dw-xdata-pcie"
- #define DW_XDATA_EP_MEM_OFFSET 0x8000000
- static DEFINE_IDA(xdata_ida);
- #define STATUS_DONE BIT(0)
- #define CONTROL_DOORBELL BIT(0)
- #define CONTROL_IS_WRITE BIT(1)
- #define CONTROL_LENGTH(a) FIELD_PREP(GENMASK(13, 2), a)
- #define CONTROL_PATTERN_INC BIT(16)
- #define CONTROL_NO_ADDR_INC BIT(18)
- #define XPERF_CONTROL_ENABLE BIT(5)
- #define BURST_REPEAT BIT(31)
- #define BURST_VALUE 0x1001
- #define PATTERN_VALUE 0x0
- struct dw_xdata_regs {
- u32 addr_lsb; /* 0x000 */
- u32 addr_msb; /* 0x004 */
- u32 burst_cnt; /* 0x008 */
- u32 control; /* 0x00c */
- u32 pattern; /* 0x010 */
- u32 status; /* 0x014 */
- u32 RAM_addr; /* 0x018 */
- u32 RAM_port; /* 0x01c */
- u32 _reserved0[14]; /* 0x020..0x054 */
- u32 perf_control; /* 0x058 */
- u32 _reserved1[41]; /* 0x05c..0x0fc */
- u32 wr_cnt_lsb; /* 0x100 */
- u32 wr_cnt_msb; /* 0x104 */
- u32 rd_cnt_lsb; /* 0x108 */
- u32 rd_cnt_msb; /* 0x10c */
- } __packed;
- struct dw_xdata_region {
- phys_addr_t paddr; /* physical address */
- void __iomem *vaddr; /* virtual address */
- };
- struct dw_xdata {
- struct dw_xdata_region rg_region; /* registers */
- size_t max_wr_len; /* max wr xfer len */
- size_t max_rd_len; /* max rd xfer len */
- struct mutex mutex;
- struct pci_dev *pdev;
- struct miscdevice misc_dev;
- };
- static inline struct dw_xdata_regs __iomem *__dw_regs(struct dw_xdata *dw)
- {
- return dw->rg_region.vaddr;
- }
- static void dw_xdata_stop(struct dw_xdata *dw)
- {
- u32 burst;
- mutex_lock(&dw->mutex);
- burst = readl(&(__dw_regs(dw)->burst_cnt));
- if (burst & BURST_REPEAT) {
- burst &= ~(u32)BURST_REPEAT;
- writel(burst, &(__dw_regs(dw)->burst_cnt));
- }
- mutex_unlock(&dw->mutex);
- }
- static void dw_xdata_start(struct dw_xdata *dw, bool write)
- {
- struct device *dev = &dw->pdev->dev;
- u32 control, status;
- /* Stop first if xfer in progress */
- dw_xdata_stop(dw);
- mutex_lock(&dw->mutex);
- /* Clear status register */
- writel(0x0, &(__dw_regs(dw)->status));
- /* Burst count register set for continuous until stopped */
- writel(BURST_REPEAT | BURST_VALUE, &(__dw_regs(dw)->burst_cnt));
- /* Pattern register */
- writel(PATTERN_VALUE, &(__dw_regs(dw)->pattern));
- /* Control register */
- control = CONTROL_DOORBELL | CONTROL_PATTERN_INC | CONTROL_NO_ADDR_INC;
- if (write) {
- control |= CONTROL_IS_WRITE;
- control |= CONTROL_LENGTH(dw->max_wr_len);
- } else {
- control |= CONTROL_LENGTH(dw->max_rd_len);
- }
- writel(control, &(__dw_regs(dw)->control));
- /*
- * The xData HW block needs about 100 ms to initiate the traffic
- * generation according this HW block datasheet.
- */
- usleep_range(100, 150);
- status = readl(&(__dw_regs(dw)->status));
- mutex_unlock(&dw->mutex);
- if (!(status & STATUS_DONE))
- dev_dbg(dev, "xData: started %s direction\n",
- write ? "write" : "read");
- }
- static void dw_xdata_perf_meas(struct dw_xdata *dw, u64 *data, bool write)
- {
- if (write) {
- *data = readl(&(__dw_regs(dw)->wr_cnt_msb));
- *data <<= 32;
- *data |= readl(&(__dw_regs(dw)->wr_cnt_lsb));
- } else {
- *data = readl(&(__dw_regs(dw)->rd_cnt_msb));
- *data <<= 32;
- *data |= readl(&(__dw_regs(dw)->rd_cnt_lsb));
- }
- }
- static u64 dw_xdata_perf_diff(u64 *m1, u64 *m2, u64 time)
- {
- u64 rate = (*m1 - *m2);
- rate *= (1000 * 1000 * 1000);
- rate >>= 20;
- rate = DIV_ROUND_CLOSEST_ULL(rate, time);
- return rate;
- }
- static void dw_xdata_perf(struct dw_xdata *dw, u64 *rate, bool write)
- {
- struct device *dev = &dw->pdev->dev;
- u64 data[2], time[2], diff;
- mutex_lock(&dw->mutex);
- /* First acquisition of current count frames */
- writel(0x0, &(__dw_regs(dw)->perf_control));
- dw_xdata_perf_meas(dw, &data[0], write);
- time[0] = jiffies;
- writel((u32)XPERF_CONTROL_ENABLE, &(__dw_regs(dw)->perf_control));
- /*
- * Wait 100ms between the 1st count frame acquisition and the 2nd
- * count frame acquisition, in order to calculate the speed later
- */
- mdelay(100);
- /* Second acquisition of current count frames */
- writel(0x0, &(__dw_regs(dw)->perf_control));
- dw_xdata_perf_meas(dw, &data[1], write);
- time[1] = jiffies;
- writel((u32)XPERF_CONTROL_ENABLE, &(__dw_regs(dw)->perf_control));
- /*
- * Speed calculation
- *
- * rate = (2nd count frames - 1st count frames) / (time elapsed)
- */
- diff = jiffies_to_nsecs(time[1] - time[0]);
- *rate = dw_xdata_perf_diff(&data[1], &data[0], diff);
- mutex_unlock(&dw->mutex);
- dev_dbg(dev, "xData: time=%llu us, %s=%llu MB/s\n",
- diff, write ? "write" : "read", *rate);
- }
- static struct dw_xdata *misc_dev_to_dw(struct miscdevice *misc_dev)
- {
- return container_of(misc_dev, struct dw_xdata, misc_dev);
- }
- static ssize_t write_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct miscdevice *misc_dev = dev_get_drvdata(dev);
- struct dw_xdata *dw = misc_dev_to_dw(misc_dev);
- u64 rate;
- dw_xdata_perf(dw, &rate, true);
- return sysfs_emit(buf, "%llu\n", rate);
- }
- static ssize_t write_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct miscdevice *misc_dev = dev_get_drvdata(dev);
- struct dw_xdata *dw = misc_dev_to_dw(misc_dev);
- bool enabled;
- int ret;
- ret = kstrtobool(buf, &enabled);
- if (ret < 0)
- return ret;
- if (enabled) {
- dev_dbg(dev, "xData: requested write transfer\n");
- dw_xdata_start(dw, true);
- } else {
- dev_dbg(dev, "xData: requested stop transfer\n");
- dw_xdata_stop(dw);
- }
- return size;
- }
- static DEVICE_ATTR_RW(write);
- static ssize_t read_show(struct device *dev, struct device_attribute *attr,
- char *buf)
- {
- struct miscdevice *misc_dev = dev_get_drvdata(dev);
- struct dw_xdata *dw = misc_dev_to_dw(misc_dev);
- u64 rate;
- dw_xdata_perf(dw, &rate, false);
- return sysfs_emit(buf, "%llu\n", rate);
- }
- static ssize_t read_store(struct device *dev, struct device_attribute *attr,
- const char *buf, size_t size)
- {
- struct miscdevice *misc_dev = dev_get_drvdata(dev);
- struct dw_xdata *dw = misc_dev_to_dw(misc_dev);
- bool enabled;
- int ret;
- ret = kstrtobool(buf, &enabled);
- if (ret < 0)
- return ret;
- if (enabled) {
- dev_dbg(dev, "xData: requested read transfer\n");
- dw_xdata_start(dw, false);
- } else {
- dev_dbg(dev, "xData: requested stop transfer\n");
- dw_xdata_stop(dw);
- }
- return size;
- }
- static DEVICE_ATTR_RW(read);
- static struct attribute *xdata_attrs[] = {
- &dev_attr_write.attr,
- &dev_attr_read.attr,
- NULL,
- };
- ATTRIBUTE_GROUPS(xdata);
- static int dw_xdata_pcie_probe(struct pci_dev *pdev,
- const struct pci_device_id *pid)
- {
- struct device *dev = &pdev->dev;
- struct dw_xdata *dw;
- char name[24];
- u64 addr;
- int err;
- int id;
- /* Enable PCI device */
- err = pcim_enable_device(pdev);
- if (err) {
- dev_err(dev, "enabling device failed\n");
- return err;
- }
- /* Mapping PCI BAR regions */
- err = pcim_iomap_regions(pdev, BIT(BAR_0), pci_name(pdev));
- if (err) {
- dev_err(dev, "xData BAR I/O remapping failed\n");
- return err;
- }
- pci_set_master(pdev);
- /* Allocate memory */
- dw = devm_kzalloc(dev, sizeof(*dw), GFP_KERNEL);
- if (!dw)
- return -ENOMEM;
- /* Data structure initialization */
- mutex_init(&dw->mutex);
- dw->rg_region.vaddr = pcim_iomap_table(pdev)[BAR_0];
- if (!dw->rg_region.vaddr)
- return -ENOMEM;
- dw->rg_region.paddr = pdev->resource[BAR_0].start;
- dw->max_wr_len = pcie_get_mps(pdev);
- dw->max_wr_len >>= 2;
- dw->max_rd_len = pcie_get_readrq(pdev);
- dw->max_rd_len >>= 2;
- dw->pdev = pdev;
- id = ida_simple_get(&xdata_ida, 0, 0, GFP_KERNEL);
- if (id < 0) {
- dev_err(dev, "xData: unable to get id\n");
- return id;
- }
- snprintf(name, sizeof(name), DW_XDATA_DRIVER_NAME ".%d", id);
- dw->misc_dev.name = kstrdup(name, GFP_KERNEL);
- if (!dw->misc_dev.name) {
- err = -ENOMEM;
- goto err_ida_remove;
- }
- dw->misc_dev.minor = MISC_DYNAMIC_MINOR;
- dw->misc_dev.parent = dev;
- dw->misc_dev.groups = xdata_groups;
- writel(0x0, &(__dw_regs(dw)->RAM_addr));
- writel(0x0, &(__dw_regs(dw)->RAM_port));
- addr = dw->rg_region.paddr + DW_XDATA_EP_MEM_OFFSET;
- writel(lower_32_bits(addr), &(__dw_regs(dw)->addr_lsb));
- writel(upper_32_bits(addr), &(__dw_regs(dw)->addr_msb));
- dev_dbg(dev, "xData: target address = 0x%.16llx\n", addr);
- dev_dbg(dev, "xData: wr_len = %zu, rd_len = %zu\n",
- dw->max_wr_len * 4, dw->max_rd_len * 4);
- /* Saving data structure reference */
- pci_set_drvdata(pdev, dw);
- /* Register misc device */
- err = misc_register(&dw->misc_dev);
- if (err) {
- dev_err(dev, "xData: failed to register device\n");
- goto err_kfree_name;
- }
- return 0;
- err_kfree_name:
- kfree(dw->misc_dev.name);
- err_ida_remove:
- ida_simple_remove(&xdata_ida, id);
- return err;
- }
- static void dw_xdata_pcie_remove(struct pci_dev *pdev)
- {
- struct dw_xdata *dw = pci_get_drvdata(pdev);
- int id;
- if (sscanf(dw->misc_dev.name, DW_XDATA_DRIVER_NAME ".%d", &id) != 1)
- return;
- if (id < 0)
- return;
- dw_xdata_stop(dw);
- misc_deregister(&dw->misc_dev);
- kfree(dw->misc_dev.name);
- ida_simple_remove(&xdata_ida, id);
- }
- static const struct pci_device_id dw_xdata_pcie_id_table[] = {
- { PCI_DEVICE_DATA(SYNOPSYS, EDDA, NULL) },
- { }
- };
- MODULE_DEVICE_TABLE(pci, dw_xdata_pcie_id_table);
- static struct pci_driver dw_xdata_pcie_driver = {
- .name = DW_XDATA_DRIVER_NAME,
- .id_table = dw_xdata_pcie_id_table,
- .probe = dw_xdata_pcie_probe,
- .remove = dw_xdata_pcie_remove,
- };
- module_pci_driver(dw_xdata_pcie_driver);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("Synopsys DesignWare xData PCIe driver");
- MODULE_AUTHOR("Gustavo Pimentel <[email protected]>");
|