This version is much much better. On Sun, Jul 15, 2018 at 12:34:28PM -0400, Jacob Feder wrote: > +static ssize_t sysfs_write(struct device *dev, const char *buf, > + size_t count, unsigned int addr_offset) > +{ > + struct axis_fifo *fifo = dev_get_drvdata(dev); > + unsigned long tmp; > + > + if (kstrtoul(buf, 0, &tmp)) > + return -EINVAL; It's better to preserve the error code from kstrtoul(). I wouldn't comment on this except there are a bunch of places which need to preserve the error code. > + > + iowrite32(tmp, fifo->base_addr + addr_offset); > + > + return count; > +} > + > +static ssize_t sysfs_read(struct device *dev, char *buf, > + unsigned int addr_offset) > +{ > + struct axis_fifo *fifo = dev_get_drvdata(dev); > + unsigned int read_val; > + unsigned int strlen; > + char tmp[32]; > + > + read_val = ioread32(fifo->base_addr + addr_offset); > + > + strlen = snprintf(tmp, 32, "0x%08x\n", read_val); > + if (strlen < 0) > + return -EINVAL; Kernel snprintf() won't ever return error codes, so this check is not required... Just delete it. Or at least preserve the error code. > + > + memcpy(buf, tmp, strlen); > + > + return strlen; > +} > + > +static void reset_ip_core(struct axis_fifo *fifo) > +{ > + iowrite32(XLLF_SRR_RESET_MASK, > + fifo->base_addr + XLLF_SRR_OFFSET); > + iowrite32(XLLF_TDFR_RESET_MASK, > + fifo->base_addr + XLLF_TDFR_OFFSET); > + iowrite32(XLLF_RDFR_RESET_MASK, > + fifo->base_addr + XLLF_RDFR_OFFSET); These can fit in 80 characters now. iowrite32(XLLF_RDFR_RESET_MASK, fifo->base_addr + XLLF_RDFR_OFFSET); > + iowrite32(XLLF_INT_TC_MASK | XLLF_INT_RC_MASK | XLLF_INT_RPURE_MASK | > + XLLF_INT_RPORE_MASK | XLLF_INT_RPUE_MASK | > + XLLF_INT_TPOE_MASK | XLLF_INT_TSE_MASK, > + fifo->base_addr + XLLF_IER_OFFSET); > + iowrite32(XLLF_INT_ALL_MASK, > + fifo->base_addr + XLLF_ISR_OFFSET); > +} > + > +/* reads a single packet from the fifo as dictated by the tlast signal */ > +static ssize_t axis_fifo_read(struct file *f, char __user *buf, > + size_t len, loff_t *off) > +{ > + struct axis_fifo *fifo = (struct axis_fifo *)f->private_data; > + unsigned int bytes_available; > + unsigned int words_available; > + unsigned int copied; > + unsigned int copy; > + unsigned int i; > + int ret; > + u32 tmp_buf[READ_BUF_SIZE]; > + > + if (fifo->read_flags & O_NONBLOCK) { > + /* opened in non-blocking mode > + * return if there are no packets available > + */ > + if (!ioread32(fifo->base_addr + XLLF_RDFO_OFFSET)) > + return -EAGAIN; > + } else { > + /* opened in blocking mode > + * wait for a packet available interrupt (or timeout) > + * if nothing is currently available > + */ > + spin_lock_irq(&fifo->read_queue_lock); > + if (read_timeout < 0) { > + ret = wait_event_interruptible_lock_irq_timeout( > + fifo->read_queue, > + ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), > + fifo->read_queue_lock, MAX_SCHEDULE_TIMEOUT); > + } else { > + ret = wait_event_interruptible_lock_irq_timeout( > + fifo->read_queue, > + ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), > + fifo->read_queue_lock, > + msecs_to_jiffies(read_timeout)); > + } I'm not a huge fan of ternaries but they would help here: ret = wait_event_interruptible_lock_irq_timeout( fifo->read_queue, ioread32(fifo->base_addr + XLLF_RDFO_OFFSET), fifo->read_queue_lock, (read_timeout >= 0) ? msecs_to_jiffies(read_timeout) : MAX_SCHEDULE_TIMEOUT); > + spin_unlock_irq(&fifo->read_queue_lock); > + > + if (ret == 0) { > + /* timeout occurred */ > + dev_dbg(fifo->dt_device, "read timeout"); > + return -EAGAIN; > + } else if (ret == -ERESTARTSYS) { > + /* signal received */ > + return -ERESTARTSYS; > + } else if (ret < 0) { > + dev_err(fifo->dt_device, "wait_event_interruptible_timeout() error in read (ret=%i)\n", > + ret); > + return ret; > + } > + } > + > + bytes_available = ioread32(fifo->base_addr + XLLF_RLR_OFFSET); > + if (!bytes_available) { > + dev_err(fifo->dt_device, "received a packet of length 0 - fifo core will be reset\n"); > + reset_ip_core(fifo); > + return -EIO; > + } > + > + if (bytes_available > len) { > + dev_err(fifo->dt_device, "user read buffer too small (available bytes=%u user buffer bytes=%u) - fifo core will be reset\n", > + bytes_available, len); > + reset_ip_core(fifo); > + return -EINVAL; > + } > + > + if (bytes_available % sizeof(u32)) { > + /* this probably can't happen unless IP > + * registers were previously mishandled > + */ > + dev_err(fifo->dt_device, "received a packet that isn't word-aligned - fifo core will be reset\n"); > + reset_ip_core(fifo); > + return -EIO; > + } > + > + words_available = bytes_available / sizeof(u32); > + > + /* read data into an intermediate buffer, copying the contents > + * to userspace when the buffer is full > + */ > + copied = 0; > + while (words_available > 0) { > + copy = min(words_available, READ_BUF_SIZE); > + > + for (i = 0; i < copy; i++) { > + tmp_buf[i] = ioread32(fifo->base_addr + > + XLLF_RDFD_OFFSET); > + } > + > + if (copy_to_user(buf + copied * sizeof(u32), tmp_buf, > + copy * sizeof(u32))) { > + reset_ip_core(fifo); > + return -EFAULT; > + } > + > + copied += copy; > + words_available -= copy; > + } > + > + return bytes_available; > +} > + > +static ssize_t axis_fifo_write(struct file *f, const char __user *buf, > + size_t len, loff_t *off) > +{ > + struct axis_fifo *fifo = (struct axis_fifo *)f->private_data; > + unsigned int words_to_write; > + unsigned int copied; > + unsigned int copy; > + unsigned int i; > + int ret; > + u32 tmp_buf[WRITE_BUF_SIZE]; > + > + if (len % sizeof(u32)) { > + dev_err(fifo->dt_device, > + "tried to send a packet that isn't word-aligned\n"); > + return -EINVAL; > + } > + > + words_to_write = len / sizeof(u32); > + > + if (!words_to_write) { > + dev_err(fifo->dt_device, > + "tried to send a packet of length 0\n"); > + return -EINVAL; > + } > + > + if (words_to_write > fifo->tx_fifo_depth) { > + dev_err(fifo->dt_device, "tried to write more words [%u] than slots in the fifo buffer [%u]\n", > + words_to_write, fifo->tx_fifo_depth); > + return -EINVAL; > + } > + > + if (fifo->write_flags & O_NONBLOCK) { > + /* opened in non-blocking mode > + * return if there is not enough room available in the fifo > + */ > + if (words_to_write > ioread32(fifo->base_addr + > + XLLF_TDFV_OFFSET)) { > + return -EAGAIN; > + } > + } else { > + /* opened in blocking mode */ > + > + /* wait for an interrupt (or timeout) if there isn't > + * currently enough room in the fifo > + */ > + spin_lock_irq(&fifo->write_queue_lock); > + if (write_timeout < 0) { > + ret = wait_event_interruptible_lock_irq_timeout( > + fifo->write_queue, > + ioread32(fifo->base_addr + > + XLLF_TDFV_OFFSET) >= > + words_to_write, > + fifo->write_queue_lock, > + MAX_SCHEDULE_TIMEOUT); > + } else { > + ret = wait_event_interruptible_lock_irq_timeout( > + fifo->write_queue, > + ioread32(fifo->base_addr + > + XLLF_TDFV_OFFSET) >= > + words_to_write, > + fifo->write_queue_lock, > + msecs_to_jiffies(write_timeout)); > + } ret = wait_event_interruptible_lock_irq_timeout( fifo->write_queue, ioread32(fifo->base_addr + XLLF_TDFV_OFFSET) >= words_to_write, fifo->write_queue_lock, (write_timeout < 0) ? MAX_SCHEDULE_TIMEOUT : msecs_to_jiffies(write_timeout)); > + spin_unlock_irq(&fifo->write_queue_lock); > + > + if (ret == 0) { > + /* timeout occurred */ > + dev_dbg(fifo->dt_device, "write timeout\n"); > + return -EAGAIN; > + } else if (ret == -ERESTARTSYS) { > + /* signal received */ > + return -ERESTARTSYS; > + } else if (ret < 0) { > + /* unknown error */ > + dev_err(fifo->dt_device, > + "wait_event_interruptible_timeout() error in write (ret=%i)\n", > + ret); > + return ret; > + } > + } > + > + /* write data from an intermediate buffer into the fifo IP, refilling > + * the buffer with userspace data as needed > + */ > + copied = 0; > + while (words_to_write > 0) { > + copy = min(words_to_write, WRITE_BUF_SIZE); > + > + if (copy_from_user(tmp_buf, buf + copied * sizeof(u32), > + copy * sizeof(u32))) { > + reset_ip_core(fifo); > + return -EFAULT; > + } > + > + for (i = 0; i < copy; i++) > + iowrite32(tmp_buf[i], fifo->base_addr + > + XLLF_TDFD_OFFSET); > + > + copied += copy; > + words_to_write -= copy; > + } > + > + /* write packet size to fifo */ > + iowrite32(copied * sizeof(u32), fifo->base_addr + XLLF_TLR_OFFSET); > + > + return (ssize_t)copied * sizeof(u32); > +} > + > +/* read named property from the device tree */ > +static int get_dts_property(struct axis_fifo *fifo, > + char *name, unsigned int *var) > +{ > + if (of_property_read_u32(fifo->dt_device->of_node, > + name, var) < 0) { > + dev_err(fifo->dt_device, > + "couldn't read IP dts property '%s'", name); > + return -1; Preserve the error code. Plus it looks nicer: rc = of_property_read_u32(fifo->dt_device->of_node, name, var); if (rc) { dev_err(fifo->dt_device, "couldn't read IP dts property '%s'", name); return rc; } > + } > + dev_dbg(fifo->dt_device, "dts property '%s' = %u\n", > + name, *var); > + > + return 0; > +} > + > +static int axis_fifo_probe(struct platform_device *pdev) > +{ > + struct resource *r_irq; /* interrupt resources */ > + struct resource *r_mem; /* IO mem resources */ > + struct device *dev = &pdev->dev; /* OS device (from device tree) */ > + struct axis_fifo *fifo = NULL; > + > + char device_name[32]; > + > + int rc = 0; /* error return value */ > + > + /* IP properties from device tree */ > + unsigned int rxd_tdata_width; > + unsigned int txc_tdata_width; > + unsigned int txd_tdata_width; > + unsigned int tdest_width; > + unsigned int tid_width; > + unsigned int tuser_width; > + unsigned int data_interface_type; > + unsigned int has_tdest; > + unsigned int has_tid; > + unsigned int has_tkeep; > + unsigned int has_tstrb; > + unsigned int has_tuser; > + unsigned int rx_fifo_depth; > + unsigned int rx_programmable_empty_threshold; > + unsigned int rx_programmable_full_threshold; > + unsigned int axi_id_width; > + unsigned int axi4_data_width; > + unsigned int select_xpm; > + unsigned int tx_fifo_depth; > + unsigned int tx_programmable_empty_threshold; > + unsigned int tx_programmable_full_threshold; > + unsigned int use_rx_cut_through; > + unsigned int use_rx_data; > + unsigned int use_tx_control; > + unsigned int use_tx_cut_through; > + unsigned int use_tx_data; > + > + /* ---------------------------- > + * init wrapper device > + * ---------------------------- > + */ > + > + /* allocate device wrapper memory */ > + fifo = devm_kmalloc(dev, sizeof(*fifo), GFP_KERNEL); > + if (!fifo) > + return -ENOMEM; > + > + dev_set_drvdata(dev, fifo); > + fifo->dt_device = dev; > + > + init_waitqueue_head(&fifo->read_queue); > + init_waitqueue_head(&fifo->write_queue); > + > + dev_dbg(fifo->dt_device, "initialized queues\n"); This print doesn't tell you anything except that the function was called. Just remove it and use ftrace. > + > + spin_lock_init(&fifo->read_queue_lock); > + spin_lock_init(&fifo->write_queue_lock); > + > + dev_dbg(fifo->dt_device, "initialized spinlocks\n"); Delete. > + > + /* ---------------------------- > + * init device memory space > + * ---------------------------- > + */ > + > + /* get iospace for the device */ > + r_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); > + if (!r_mem) { > + dev_err(fifo->dt_device, "invalid address\n"); > + rc = -ENODEV; > + goto err_initial; > + } > + > + fifo->mem_start = r_mem->start; > + fifo->mem_end = r_mem->end; Why not just save r_mem? > + > + /* request physical memory */ > + if (!request_mem_region(fifo->mem_start, > + fifo->mem_end - > + fifo->mem_start + 1, resource_size(r_mem), > + DRIVER_NAME)) { > + dev_err(fifo->dt_device, > + "couldn't lock memory region at %p\n", > + (void *)fifo->mem_start); > + rc = -EBUSY; > + goto err_initial; > + } > + dev_dbg(fifo->dt_device, > + "got memory location [0x%x - 0x%x]\n", > + fifo->mem_start, fifo->mem_end); > + > + /* map physical memory to kernel virtual address space */ > + fifo->base_addr = ioremap(fifo->mem_start, fifo->mem_end - > + fifo->mem_start + 1); fifo->base_addr = ioremap(fifo->mem_start, resource_size(r_mem)); Fits on one line. > + > + if (!fifo->base_addr) { > + dev_err(fifo->dt_device, > + "couldn't map physical memory\n"); > + rc = -EIO; For ioremap() failure the "rc = -ENOMEM;". > + goto err_mem; > + } > + dev_dbg(fifo->dt_device, "remapped memory to 0x%x\n", > + (unsigned int)fifo->base_addr); Use %p. Casting a pointer to (unsigned int) will generate a GCC warning. Plus casting is ugly. > + > + /* create unique device name */ > + snprintf(device_name, 32, DRIVER_NAME "_%x", > + (unsigned int)fifo->mem_start); Get rid of the magic 32 and the cast. snprintf(device_name, sizeof(device_name) "%s_%x", DRIVER_NAME, fifo->mem_start); > + > + dev_dbg(fifo->dt_device, "device name [%s]\n", device_name); > + > + /* ---------------------------- > + * init IP > + * ---------------------------- > + */ > + > + /* retrieve device tree properties */ > + if (get_dts_property(fifo, "xlnx,axi-str-rxd-tdata-width", > + &rxd_tdata_width)) { > + rc = -EIO; > + goto err_mem; After the ioremap() succeeds all the gotos should be goto unmap; until we get to the next allocation. Also preserve the error code: rc = get_dts_property(fifo, "xlnx,axi-str-rxd-tdata-width", &rxd_tdata_width); if (rc) goto unmap; > + } > + if (get_dts_property(fifo, "xlnx,axi-str-txc-tdata-width", > + &txc_tdata_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,axi-str-txd-tdata-width", > + &txd_tdata_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,axis-tdest-width", > + &tdest_width)) { This will fit on one line: rc = get_dts_property(fifo, "xlnx,axis-tdest-width", &tdest_width); if (rc) goto unmap; > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,axis-tid-width", > + &tid_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,axis-tuser-width", > + &tuser_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,data-interface-type", > + &data_interface_type)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,has-axis-tdest", > + &has_tdest)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,has-axis-tid", > + &has_tid)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,has-axis-tkeep", > + &has_tkeep)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,has-axis-tstrb", > + &has_tstrb)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,has-axis-tuser", > + &has_tuser)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,rx-fifo-depth", > + &rx_fifo_depth)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,rx-fifo-pe-threshold", > + &rx_programmable_empty_threshold)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,rx-fifo-pf-threshold", > + &rx_programmable_full_threshold)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,s-axi-id-width", > + &axi_id_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,s-axi4-data-width", > + &axi4_data_width)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,select-xpm", > + &select_xpm)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,tx-fifo-depth", > + &tx_fifo_depth)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,tx-fifo-pe-threshold", > + &tx_programmable_empty_threshold)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,tx-fifo-pf-threshold", > + &tx_programmable_full_threshold)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,use-rx-cut-through", > + &use_rx_cut_through)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,use-rx-data", > + &use_rx_data)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,use-tx-ctrl", > + &use_tx_control)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,use-tx-cut-through", > + &use_tx_cut_through)) { > + rc = -EIO; > + goto err_mem; > + } > + if (get_dts_property(fifo, "xlnx,use-tx-data", > + &use_tx_data)) { > + rc = -EIO; > + goto err_mem; > + } > + > + /* check validity of device tree properties */ > + if (rxd_tdata_width != 32) { > + dev_err(fifo->dt_device, > + "rxd_tdata_width width [%u] unsupported\n", > + rxd_tdata_width); > + rc = -EIO; > + goto err_mem; > + } > + if (txd_tdata_width != 32) { > + dev_err(fifo->dt_device, > + "txd_tdata_width width [%u] unsupported\n", > + txd_tdata_width); > + rc = -EIO; > + goto err_mem; > + } > + if (has_tdest) { > + dev_err(fifo->dt_device, "tdest not supported\n"); > + rc = -EIO; > + goto err_mem; > + } Just out of curiosity, what would happen if we remove this check? > + if (has_tid) { > + dev_err(fifo->dt_device, "tid not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (has_tkeep) { > + dev_err(fifo->dt_device, "tkeep not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (has_tstrb) { > + dev_err(fifo->dt_device, "tstrb not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (has_tuser) { > + dev_err(fifo->dt_device, "tuser not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (use_rx_cut_through) { > + dev_err(fifo->dt_device, > + "rx cut-through not supported\n"); Fits on one line: dev_err(fifo->dt_device, "rx cut-through not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (use_tx_cut_through) { > + dev_err(fifo->dt_device, > + "tx cut-through not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + if (use_tx_control) { > + dev_err(fifo->dt_device, > + "tx control not supported\n"); > + rc = -EIO; > + goto err_mem; > + } > + > + /* TODO > + * these exist in the device tree but it's unclear what they do > + * - select-xpm > + * - data-interface-type > + */ > + > + /* set device wrapper properties based on IP config */ > + fifo->rx_fifo_depth = rx_fifo_depth; > + /* IP sets TDFV to fifo depth - 4 so we will do the same */ > + fifo->tx_fifo_depth = tx_fifo_depth - 4; > + fifo->has_rx_fifo = use_rx_data; > + fifo->has_tx_fifo = use_tx_data; > + > + reset_ip_core(fifo); > + > + /* ---------------------------- > + * init device interrupts > + * ---------------------------- > + */ > + > + /* get IRQ resource */ > + r_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); > + if (!r_irq) { > + dev_err(fifo->dt_device, > + "no IRQ found at 0x%08x mapped to 0x%08x\n", > + (unsigned int __force)fifo->mem_start, > + (unsigned int __force)fifo->base_addr); No need for __force. Just get rid to the casts actually. > + rc = -EIO; > + goto err_mem; > + } > + dev_dbg(fifo->dt_device, "found IRQ\n"); > + > + /* request IRQ */ > + fifo->irq = r_irq->start; > + rc = request_irq(fifo->irq, &axis_fifo_irq, 0, > + DRIVER_NAME, fifo); > + if (rc) { > + dev_err(fifo->dt_device, > + "couldn't allocate interrupt %i\n", > + fifo->irq); > + rc = -EIO; Just leave rc as is. > + goto err_mem; > + } > + dev_dbg(fifo->dt_device, > + "initialized IRQ %i\n", > + fifo->irq); Fits on one line. > + > + /* ---------------------------- > + * init char device > + * ---------------------------- > + */ > + > + /* allocate device number */ > + if (alloc_chrdev_region(&fifo->devt, 0, 1, DRIVER_NAME) < 0) { > + dev_err(fifo->dt_device, "couldn't allocate dev_t\n"); > + rc = -EIO; > + goto err_irq; > + } rc = alloc_chrdev_region(&fifo->devt, 0, 1, DRIVER_NAME); if (rc) goto err_irq; Most of these functions have their own printks built in so there isn't really a need to have a printk here. > + dev_dbg(fifo->dt_device, > + "allocated device number major %i minor %i\n", > + MAJOR(fifo->devt), MINOR(fifo->devt)); > + > + /* create driver file */ > + fifo->device = NULL; Delete this line. > + fifo->device = device_create(axis_fifo_driver_class, NULL, fifo->devt, > + NULL, device_name); > + if (!fifo->device) { > + dev_err(fifo->dt_device, > + "couldn't create driver file\n"); > + rc = -EIO; rc = -ENOMEM; > + goto err_chrdev_region; > + } > + dev_set_drvdata(fifo->device, fifo); > + dev_dbg(fifo->dt_device, "created device file\n"); Just delete these debugging lines. It's obviously to the point where it's working and you can test it now. > + > + /* create character device */ > + cdev_init(&fifo->char_device, &fops); > + if (cdev_add(&fifo->char_device, > + fifo->devt, 1) < 0) { > + dev_err(fifo->dt_device, > + "couldn't create character device\n"); > + rc = -EIO; > + goto err_dev; > + } Preserve the code. > + dev_dbg(fifo->dt_device, "created character device\n"); > + > + /* create sysfs entries */ > + if (sysfs_create_group(&fifo->device->kobj, > + &axis_fifo_attrs_group) < 0) { > + dev_err(fifo->dt_device, > + "couldn't register sysfs group\n"); > + rc = -EIO; > + goto err_cdev; > + } > + > + dev_info(fifo->dt_device, > + "axis-fifo created at 0x%08x mapped to 0x%08x, irq=%i, major=%i, minor=%i\n", > + (unsigned int __force)fifo->mem_start, > + (unsigned int __force)fifo->base_addr, > + fifo->irq, MAJOR(fifo->devt), > + MINOR(fifo->devt)); > + > + return 0; > + > +err_cdev: > + cdev_del(&fifo->char_device); > +err_dev: > + dev_set_drvdata(fifo->device, NULL); This hopefully isn't required since we delete fifo->device on the next line. > + device_destroy(axis_fifo_driver_class, fifo->devt); > +err_chrdev_region: > + unregister_chrdev_region(fifo->devt, 1); > +err_irq: > + free_irq(fifo->irq, fifo); > +err_mem: > + release_mem_region(fifo->mem_start, > + fifo->mem_end - > + fifo->mem_start + 1); > +err_initial: > + dev_set_drvdata(dev, NULL); > + return rc; > +} > + > +static int axis_fifo_remove(struct platform_device *pdev) > +{ > + struct device *dev = &pdev->dev; > + struct axis_fifo *fifo = dev_get_drvdata(dev); > + > + dev_info(dev, "removing\n"); Delete. ftrace. > + > + sysfs_remove_group(&fifo->device->kobj, > + &axis_fifo_attrs_group); One line: sysfs_remove_group(&fifo->device->kobj, &axis_fifo_attrs_group); > + cdev_del(&fifo->char_device); > + dev_set_drvdata(fifo->device, NULL); > + device_destroy(axis_fifo_driver_class, fifo->devt); > + unregister_chrdev_region(fifo->devt, 1); > + free_irq(fifo->irq, fifo); Don't forget to do an iounmap() here as well. > + release_mem_region(fifo->mem_start, > + fifo->mem_end - > + fifo->mem_start + 1); > + dev_set_drvdata(dev, NULL); > + return 0; > +} > + > +#ifdef CONFIG_OF > +static const struct of_device_id axis_fifo_of_match[] = { > + { .compatible = "xlnx,axi-fifo-mm-s-4.1", }, > + { }, > +}; > +MODULE_DEVICE_TABLE(of, axis_fifo_of_match); > +#else > +# define axis_fifo_of_match > +#endif > + > +static struct platform_driver axis_fifo_driver = { > + .driver = { > + .name = DRIVER_NAME, > + .owner = THIS_MODULE, > + .of_match_table = axis_fifo_of_match, > + }, > + .probe = axis_fifo_probe, > + .remove = axis_fifo_remove, > +}; > + > +static int __init axis_fifo_init(void) > +{ > + printk(KERN_INFO "axis-fifo driver loaded with parameters read_timeout = %i, write_timeout = %i\n", > + read_timeout, write_timeout); Delete. Or update to use pr_info() at least. > + axis_fifo_driver_class = class_create(THIS_MODULE, DRIVER_NAME); > + return platform_driver_register(&axis_fifo_driver); > +} > + > +module_init(axis_fifo_init); > + > +static void __exit axis_fifo_exit(void) > +{ > + platform_driver_unregister(&axis_fifo_driver); > + class_destroy(axis_fifo_driver_class); > + printk(KERN_INFO "axis-fifo driver exit\n"); Delete. > +} > + regards, dan carpenter _______________________________________________ devel mailing list devel@xxxxxxxxxxxxxxxxxxxxxx http://driverdev.linuxdriverproject.org/mailman/listinfo/driverdev-devel