前一段时间购买了AIO-RK3288J主板,调试SPI2的驱动程序遇到了如下问题:

[ 本帖最后由 八龄先生 于 2020-12-3 11:58 编辑 ]\n\n[ 本帖最后由 八龄先生 于 2020-12-3 11:58 编辑 ]\n\n[ 本帖最后由 八龄先生 于 2020-12-3 11:52 编辑 ]\n\n设备树如下:
&spi2 {
status = “okay”;
max-freq = <24000000>;
dma-names = “tx”, “rx”; //enable dma
pinctrl-names = “default”, “high_speed”;
pinctrl-0 = <&spi2_clk &spi2_tx &spi2_rx &spi2_cs0 &spi2_cs1>;

    fm175xx@00{
            status = "okay";
            compatible = "fdw,nfc_fm175xx";
            reg = <0x00>;
            spi-max-frequency = <24000000>;
            spi-cpha = <0>;
            spi-cpol = <0>;
    };

};

驱动中的compatible属性如下:
static const struct of_device_id fm175xx_dt_ids = {
{ .compatible = “fdw,nfc_fm175xx” },
{},
};

目前程序进不了到probe函数是怎么回事呢?
设备树配置如下图:
C:\Users\EDZ\Desktop\1.jpg
也可以看到设备节点:
C:\Users\EDZ\Desktop\2.jpg


驱动代码如下,理论上设备和驱动匹配后会打印probe函数里面的第一个printk信息,目前没反应。

/*

  • Simple synchronous userspace interface to SPI devices
  • Copyright (C) 2006 SWAPP
  • Andrea Paterniani <a.paterniani@swapp-eng.it>
  • Copyright (C) 2007 David Brownell (simplification, cleanup)
  • This program is free software; you can redistribute it and/or modify
  • it under the terms of the GNU General Public License as published by
  • the Free Software Foundation; either version 2 of the License, or
  • (at your option) any later version.
  • 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 <linux/init.h>
#include <linux/module.h>
#include <linux/ioctl.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/list.h>
#include <linux/errno.h>
#include <linux/mutex.h>
#include <linux/slab.h>
#include <linux/compat.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/spi/spidev.h>
#include <linux/uaccess.h>

/*

  • This supports access to SPI devices using normal userspace I/O calls.
  • Note that while traditional UNIX/POSIX I/O semantics are half duplex,
  • and often mask message boundaries, full SPI support requires full duplex
  • transfers. There are several kinds of internal message boundaries to
  • handle chipselect management and other protocol options.
  • SPI has a character major number assigned. We allocate minor numbers
  • dynamically using a bitmask. You must use hotplug tools, such as udev
  • (or mdev with busybox) to create and destroy the /dev/spidevB.C device
  • nodes, since there is no fixed association of minor numbers with any
  • particular SPI bus or device.
    /
    #define SPIDEV_MAJOR 153 /
    assigned /
    #define N_SPI_MINORS 32 /
    … up to 256 */

static DECLARE_BITMAP(minors, N_SPI_MINORS);
int fm175xx_id;
static struct class *fm175xx_class;

/* Bit masks for spi_device.mode management. Note that incorrect

  • settings for some settings can cause lots of trouble for other
  • devices on a shared bus:
    • CS_HIGH … this device will be active when it shouldn’t be
    • 3WIRE … when active, it won’t behave as it should
    • NO_CS … there will be no explicit message boundaries; this
  • is completely incompatible with the shared bus model
    • READY … transfers may proceed when they shouldn’t.
      device_create *
  • REVISIT should changing those flags be privileged?
    */
    #define SPI_MODE_MASK (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH
    | SPI_LSB_FIRST | SPI_3WIRE | SPI_LOOP
    | SPI_NO_CS | SPI_READY | SPI_TX_DUAL
    | SPI_TX_QUAD | SPI_RX_DUAL | SPI_RX_QUAD)

struct spidev_data {
dev_t devt;
spinlock_t spi_lock;
struct spi_device *spi;
struct list_head device_entry;

/* TX/RX buffers are NULL unless this device is open (users > 0) */
struct mutex		buf_lock;
unsigned		users;
u8			*tx_buffer;
u8			*rx_buffer;
u32			speed_hz;

};

static LIST_HEAD(device_list);
static DEFINE_MUTEX(device_list_lock);

static unsigned bufsiz = 4096;
module_param(bufsiz, uint, S_IRUGO);
MODULE_PARM_DESC(bufsiz, “data bytes in biggest supported SPI message”);

/-------------------------------------------------------------------------/

static ssize_t
spidev_sync(struct spidev_data *spidev, struct spi_message *message)
{
DECLARE_COMPLETION_ONSTACK(done);
int status;
struct spi_device *spi;

spin_lock_irq(&spidev->spi_lock);
spi = spidev->spi;
spin_unlock_irq(&spidev->spi_lock);

if (spi == NULL)
	status = -ESHUTDOWN;
else
	status = spi_sync(spi, message);

if (status == 0)
	status = message->actual_length;

return status;

}

static inline ssize_t
spidev_sync_write(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.tx_buf = spidev->tx_buffer,
.len = len,
.speed_hz = spidev->speed_hz,
};
struct spi_message m;

spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spidev_sync(spidev, &m);

}

static inline ssize_t
spidev_sync_read(struct spidev_data *spidev, size_t len)
{
struct spi_transfer t = {
.rx_buf = spidev->rx_buffer,
.len = len,
.speed_hz = spidev->speed_hz,
};
struct spi_message m;

spi_message_init(&m);
spi_message_add_tail(&t, &m);
return spidev_sync(spidev, &m);

}

/-------------------------------------------------------------------------/

/* Read-only message with current device setup */
static ssize_t
spidev_read(struct file *filp, char __user *buf, size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;

/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
	return -EMSGSIZE;

spidev = filp->private_data;

mutex_lock(&spidev->buf_lock);
status = spidev_sync_read(spidev, count);
if (status > 0) {
	unsigned long	missing;

	missing = copy_to_user(buf, spidev->rx_buffer, status);
	if (missing == status)
		status = -EFAULT;
	else
		status = status - missing;
}
mutex_unlock(&spidev->buf_lock);

return status;

}

/* Write-only message with current device setup */
static ssize_t
spidev_write(struct file *filp, const char __user *buf,
size_t count, loff_t *f_pos)
{
struct spidev_data *spidev;
ssize_t status = 0;
unsigned long missing;

/* chipselect only toggles at start or end of operation */
if (count > bufsiz)
	return -EMSGSIZE;

spidev = filp->private_data;

mutex_lock(&spidev->buf_lock);
missing = copy_from_user(spidev->tx_buffer, buf, count);
if (missing == 0)
	status = spidev_sync_write(spidev, count);
else
	status = -EFAULT;
mutex_unlock(&spidev->buf_lock);

return status;

}

static int spidev_message(struct spidev_data *spidev,
struct spi_ioc_transfer *u_xfers, unsigned n_xfers)
{
struct spi_message msg;
struct spi_transfer *k_xfers;
struct spi_transfer *k_tmp;
struct spi_ioc_transfer *u_tmp;
unsigned n, total, tx_total, rx_total;
u8 *tx_buf, *rx_buf;
int status = -EFAULT;

spi_message_init(&msg);
k_xfers = kcalloc(n_xfers, sizeof(*k_tmp), GFP_KERNEL);
if (k_xfers == NULL)
	return -ENOMEM;

/* Construct spi_message, copying any tx data to bounce buffer.
 * We walk the array of user-provided transfers, using each one
 * to initialize a kernel version of the same transfer.
 */
tx_buf = spidev->tx_buffer;
rx_buf = spidev->rx_buffer;
total = 0;
tx_total = 0;
rx_total = 0;
for (n = n_xfers, k_tmp = k_xfers, u_tmp = u_xfers;
		n;
		n--, k_tmp++, u_tmp++) {
	k_tmp->len = u_tmp->len;

	total += k_tmp->len;
	/* Since the function returns the total length of transfers
	 * on success, restrict the total to positive int values to
	 * avoid the return value looking like an error.  Also check
	 * each transfer length to avoid arithmetic overflow.
	 */
	if (total > INT_MAX || k_tmp->len > INT_MAX) {
		status = -EMSGSIZE;
		goto done;
	}

	if (u_tmp->rx_buf) {
		/* this transfer needs space in RX bounce buffer */
		rx_total += k_tmp->len;
		if (rx_total > bufsiz) {
			status = -EMSGSIZE;
			goto done;
		}
		k_tmp->rx_buf = rx_buf;
		if (!access_ok(VERIFY_WRITE, (u8 __user *)
					(uintptr_t) u_tmp->rx_buf,
					u_tmp->len))
			goto done;
		rx_buf += k_tmp->len;
	}
	if (u_tmp->tx_buf) {
		/* this transfer needs space in TX bounce buffer */
		tx_total += k_tmp->len;
		if (tx_total > bufsiz) {
			status = -EMSGSIZE;
			goto done;
		}
		k_tmp->tx_buf = tx_buf;
		if (copy_from_user(tx_buf, (const u8 __user *)
					(uintptr_t) u_tmp->tx_buf,
				u_tmp->len))
			goto done;
		tx_buf += k_tmp->len;
	}

	k_tmp->cs_change = !!u_tmp->cs_change;
	k_tmp->tx_nbits = u_tmp->tx_nbits;
	k_tmp->rx_nbits = u_tmp->rx_nbits;
	k_tmp->bits_per_word = u_tmp->bits_per_word;
	k_tmp->delay_usecs = u_tmp->delay_usecs;
	k_tmp->speed_hz = u_tmp->speed_hz;
	if (!k_tmp->speed_hz)
		k_tmp->speed_hz = spidev->speed_hz;

#ifdef VERBOSE
dev_dbg(&spidev->spi->dev,
" xfer len %zd %s%s%s%dbits %u usec %uHz\n",
u_tmp->len,
u_tmp->rx_buf ? "rx " : “”,
u_tmp->tx_buf ? "tx " : “”,
u_tmp->cs_change ? "cs " : “”,
u_tmp->bits_per_word ? : spidev->spi->bits_per_word,
u_tmp->delay_usecs,
u_tmp->speed_hz ? : spidev->spi->max_speed_hz);
#endif
spi_message_add_tail(k_tmp, &msg);
}

status = spidev_sync(spidev, &msg);
if (status < 0)
	goto done;

/* copy any rx data out of bounce buffer */
rx_buf = spidev->rx_buffer;
for (n = n_xfers, u_tmp = u_xfers; n; n--, u_tmp++) {
	if (u_tmp->rx_buf) {
		if (__copy_to_user((u8 __user *)
				(uintptr_t) u_tmp->rx_buf, rx_buf,
				u_tmp->len)) {
			status = -EFAULT;
			goto done;
		}
		rx_buf += u_tmp->len;
	}
}
status = total;

done:
kfree(k_xfers);
return status;
}

static struct spi_ioc_transfer *spidev_get_ioc_message(unsigned int cmd, struct spi_ioc_transfer __user *u_ioc,
unsigned *n_ioc)
{
struct spi_ioc_transfer *ioc;
u32 tmp;

/* Check type, command number and direction */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC
		|| _IOC_NR(cmd) != _IOC_NR(SPI_IOC_MESSAGE(0))
		|| _IOC_DIR(cmd) != _IOC_WRITE)
	return ERR_PTR(-ENOTTY);

tmp = _IOC_SIZE(cmd);
if ((tmp % sizeof(struct spi_ioc_transfer)) != 0)
	return ERR_PTR(-EINVAL);
*n_ioc = tmp / sizeof(struct spi_ioc_transfer);
if (*n_ioc == 0)
	return NULL;

/* copy into scratch area */
ioc = kmalloc(tmp, GFP_KERNEL);
if (!ioc)
	return ERR_PTR(-ENOMEM);
if (__copy_from_user(ioc, u_ioc, tmp)) {
	kfree(ioc);
	return ERR_PTR(-EFAULT);
}
return ioc;

}

static long spidev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int err = 0;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
u32 tmp;
unsigned n_ioc;
struct spi_ioc_transfer *ioc;

/* Check type and command number */
if (_IOC_TYPE(cmd) != SPI_IOC_MAGIC)
	return -ENOTTY;

/* Check access direction once here; don't repeat below.
 * IOC_DIR is from the user perspective, while access_ok is
 * from the kernel perspective; so they look reversed.
 */
if (_IOC_DIR(cmd) & _IOC_READ)
	err = !access_ok(VERIFY_WRITE,
			(void __user *)arg, _IOC_SIZE(cmd));
if (err == 0 && _IOC_DIR(cmd) & _IOC_WRITE)
	err = !access_ok(VERIFY_READ,
			(void __user *)arg, _IOC_SIZE(cmd));
if (err)
	return -EFAULT;

/* guard against device removal before, or while,
 * we issue this ioctl.
 */
spidev = filp->private_data;
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
spin_unlock_irq(&spidev->spi_lock);

if (spi == NULL)
	return -ESHUTDOWN;

/* use the buffer lock here for triple duty:
 *  - prevent I/O (from us) so calling spi_setup() is safe;
 *  - prevent concurrent SPI_IOC_WR_* from morphing
 *    data fields while SPI_IOC_RD_* reads them;
 *  - SPI_IOC_MESSAGE needs the buffer locked "normally".
 */
mutex_lock(&spidev->buf_lock);

switch (cmd) {
/* read requests */
case SPI_IOC_RD_MODE:
	retval = __put_user(spi->mode & SPI_MODE_MASK,
				(__u8 __user *)arg);
	break;
case SPI_IOC_RD_MODE32:
	retval = __put_user(spi->mode & SPI_MODE_MASK,
				(__u32 __user *)arg);
	break;
case SPI_IOC_RD_LSB_FIRST:
	retval = __put_user((spi->mode & SPI_LSB_FIRST) ?  1 : 0,
				(__u8 __user *)arg);
	break;
case SPI_IOC_RD_BITS_PER_WORD:
	retval = __put_user(spi->bits_per_word, (__u8 __user *)arg);
	break;
case SPI_IOC_RD_MAX_SPEED_HZ:
	retval = __put_user(spidev->speed_hz, (__u32 __user *)arg);
	break;

/* write requests */
case SPI_IOC_WR_MODE:
case SPI_IOC_WR_MODE32:
	if (cmd == SPI_IOC_WR_MODE)
		retval = __get_user(tmp, (u8 __user *)arg);
	else
		retval = __get_user(tmp, (u32 __user *)arg);
	if (retval == 0) {
		u32	save = spi->mode;

		if (tmp & ~SPI_MODE_MASK) {
			retval = -EINVAL;
			break;
		}

		tmp |= spi->mode & ~SPI_MODE_MASK;
		spi->mode = (u16)tmp;
		retval = spi_setup(spi);
		if (retval < 0)
			spi->mode = save;
		else
			dev_dbg(&spi->dev, "spi mode %x\n", tmp);
	}
	break;
case SPI_IOC_WR_LSB_FIRST:
	retval = __get_user(tmp, (__u8 __user *)arg);
	if (retval == 0) {
		u32	save = spi->mode;

		if (tmp)
			spi->mode |= SPI_LSB_FIRST;
		else
			spi->mode &= ~SPI_LSB_FIRST;
		retval = spi_setup(spi);
		if (retval < 0)
			spi->mode = save;
		else
			dev_dbg(&spi->dev, "%csb first\n",
					tmp ? 'l' : 'm');
	}
	break;
case SPI_IOC_WR_BITS_PER_WORD:
	retval = __get_user(tmp, (__u8 __user *)arg);
	if (retval == 0) {
		u8	save = spi->bits_per_word;

		spi->bits_per_word = tmp;
		retval = spi_setup(spi);
		if (retval < 0)
			spi->bits_per_word = save;
		else
			dev_dbg(&spi->dev, "%d bits per word\n", tmp);
	}
	break;
case SPI_IOC_WR_MAX_SPEED_HZ:
	retval = __get_user(tmp, (__u32 __user *)arg);
	if (retval == 0) {
		u32	save = spi->max_speed_hz;

		spi->max_speed_hz = tmp;
		retval = spi_setup(spi);
		if (retval >= 0)
			spidev->speed_hz = tmp;
		else
			dev_dbg(&spi->dev, "%d Hz (max)\n", tmp);
		spi->max_speed_hz = save;
	}
	break;

default:
	/* segmented and/or full-duplex I/O request */
	/* Check message and copy into scratch area */
	ioc = spidev_get_ioc_message(cmd,
			(struct spi_ioc_transfer __user *)arg, &n_ioc);
	if (IS_ERR(ioc)) {
		retval = PTR_ERR(ioc);
		break;
	}
	if (!ioc)
		break;	/* n_ioc is also 0 */

	/* translate to spi_message, execute */
	retval = spidev_message(spidev, ioc, n_ioc);
	kfree(ioc);
	break;
}

mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;

}

#ifdef CONFIG_COMPAT
static long
spidev_compat_ioc_message(struct file *filp, unsigned int cmd,
unsigned long arg)
{
struct spi_ioc_transfer __user *u_ioc;
int retval = 0;
struct spidev_data *spidev;
struct spi_device *spi;
unsigned n_ioc, n;
struct spi_ioc_transfer *ioc;

u_ioc = (struct spi_ioc_transfer __user *) compat_ptr(arg);
if (!access_ok(VERIFY_READ, u_ioc, _IOC_SIZE(cmd)))
	return -EFAULT;

/* guard against device removal before, or while,
 * we issue this ioctl.
 */
spidev = filp->private_data;
spin_lock_irq(&spidev->spi_lock);
spi = spi_dev_get(spidev->spi);
spin_unlock_irq(&spidev->spi_lock);

if (spi == NULL)
	return -ESHUTDOWN;

/* SPI_IOC_MESSAGE needs the buffer locked "normally" */
mutex_lock(&spidev->buf_lock);

/* Check message and copy into scratch area */
ioc = spidev_get_ioc_message(cmd, u_ioc, &n_ioc);
if (IS_ERR(ioc)) {
	retval = PTR_ERR(ioc);
	goto done;
}
if (!ioc)
	goto done;	/* n_ioc is also 0 */

/* Convert buffer pointers */
for (n = 0; n < n_ioc; n++) {
	ioc[n].rx_buf = (uintptr_t) compat_ptr(ioc[n].rx_buf);
	ioc[n].tx_buf = (uintptr_t) compat_ptr(ioc[n].tx_buf);
}

/* translate to spi_message, execute */
retval = spidev_message(spidev, ioc, n_ioc);
kfree(ioc);

done:
mutex_unlock(&spidev->buf_lock);
spi_dev_put(spi);
return retval;
}

static long
spidev_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
if (_IOC_TYPE(cmd) == SPI_IOC_MAGIC
&& _IOC_NR(cmd) == _IOC_NR(SPI_IOC_MESSAGE(0))
&& _IOC_DIR(cmd) == _IOC_WRITE)
return spidev_compat_ioc_message(filp, cmd, arg);

return spidev_ioctl(filp, cmd, (unsigned long)compat_ptr(arg));

}
#else
#define spidev_compat_ioctl NULL
#endif /* CONFIG_COMPAT */

static int spidev_open(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;
int status = -ENXIO;

mutex_lock(&device_list_lock);

list_for_each_entry(spidev, &device_list, device_entry) {
	if (spidev->devt == inode->i_rdev) {
		status = 0;
		break;
	}
}

if (status) {
	pr_debug("spidev: nothing for minor %d\n", iminor(inode));
	goto err_find_dev;
}

if (!spidev->tx_buffer) {
	spidev->tx_buffer = kmalloc(bufsiz, GFP_KERNEL);
	if (!spidev->tx_buffer) {
		dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
		status = -ENOMEM;
		goto err_find_dev;
	}
}

if (!spidev->rx_buffer) {
	spidev->rx_buffer = kmalloc(bufsiz, GFP_KERNEL);
	if (!spidev->rx_buffer) {
		dev_dbg(&spidev->spi->dev, "open/ENOMEM\n");
		status = -ENOMEM;
		goto err_alloc_rx_buf;
	}
}

spidev->users++;
filp->private_data = spidev;
nonseekable_open(inode, filp);

mutex_unlock(&device_list_lock);
return 0;

err_alloc_rx_buf:
kfree(spidev->tx_buffer);
spidev->tx_buffer = NULL;
err_find_dev:
mutex_unlock(&device_list_lock);
return status;
}

static int spidev_release(struct inode *inode, struct file *filp)
{
struct spidev_data *spidev;

mutex_lock(&device_list_lock);
spidev = filp->private_data;
filp->private_data = NULL;

/* last close? */
spidev->users--;
if (!spidev->users) {
	int		dofree;

	kfree(spidev->tx_buffer);
	spidev->tx_buffer = NULL;

	kfree(spidev->rx_buffer);
	spidev->rx_buffer = NULL;

	spin_lock_irq(&spidev->spi_lock);
	if (spidev->spi)
		spidev->speed_hz = spidev->spi->max_speed_hz;

	/* ... after we unbound from the underlying device? */
	dofree = (spidev->spi == NULL);
	spin_unlock_irq(&spidev->spi_lock);

	if (dofree)
		kfree(spidev);
}
mutex_unlock(&device_list_lock);

return 0;

}

static const struct file_operations spidev_fops = {
.owner = THIS_MODULE,
/* REVISIT switch to aio primitives, so that userspace
* gets more complete API coverage. It’ll simplify things
* too, except for the locking.
*/
.write = spidev_write,
.read = spidev_read,
.unlocked_ioctl = spidev_ioctl,
.compat_ioctl = spidev_compat_ioctl,
.open = spidev_open,
.release = spidev_release,
.llseek = no_llseek,
};

/-------------------------------------------------------------------------/

/* The main reason to have this class is to make mdev/udev create the

  • /dev/spidevB.C character device nodes exposing our userspace API.
  • It also simplifies memory management.
    */

#ifdef CONFIG_OF
static const struct of_device_id fm175xx_dt_ids = {
{ .compatible = “fdw,nfc_fm175xx” },
{},
};
MODULE_DEVICE_TABLE(of, fm175xx_dt_ids);
#endif

/-------------------------------------------------------------------------/

static int fm175xx_probe(struct spi_device *spi)
{
struct spidev_data *spidev;
int status;
unsigned long minor;

printk("fm175xx probe function successful!\n");
/*
 * spidev should never be referenced in DT without a specific
 * compatible string, it is a Linux implementation thing
 * rather than a description of the hardware.
 */
if (spi->dev.of_node && !of_match_device(fm175xx_dt_ids, &spi->dev)) {
	dev_err(&spi->dev, "buggy DT: spidev listed directly in DT\n");
	WARN_ON(spi->dev.of_node &&
		!of_match_device(fm175xx_dt_ids, &spi->dev));
}

/* Allocate driver data */
spidev = kzalloc(sizeof(*spidev), GFP_KERNEL);
if (!spidev)
	return -ENOMEM;

/* Initialize the driver data */
spidev->spi = spi;
spin_lock_init(&spidev->spi_lock);
mutex_init(&spidev->buf_lock);

INIT_LIST_HEAD(&spidev->device_entry);

/* If we can allocate a minor number, hook up this device.
 * Reusing minors is fine so long as udev or mdev is working.
 */
mutex_lock(&device_list_lock);
minor = find_first_zero_bit(minors, N_SPI_MINORS);
if (minor < N_SPI_MINORS) {
	struct device *dev;

	spidev->devt = MKDEV(SPIDEV_MAJOR, minor);
	dev = device_create(fm175xx_class, &spi->dev, spidev->devt,
			    spidev, "spidev%d.%d",
			    spi->master->bus_num, spi->chip_select);
	status = PTR_ERR_OR_ZERO(dev);
} else {
	dev_dbg(&spi->dev, "no minor number available!\n");
	status = -ENODEV;
}
if (status == 0) {
	set_bit(minor, minors);
	list_add(&spidev->device_entry, &device_list);
}
mutex_unlock(&device_list_lock);

spidev->speed_hz = spi->max_speed_hz;

if (status == 0)
	spi_set_drvdata(spi, spidev);
else
	kfree(spidev);

return status;

}

static int fm175xx_remove(struct spi_device *spi)
{
struct spidev_data *spidev = spi_get_drvdata(spi);

/* make sure ops on existing fds can abort cleanly */
spin_lock_irq(&spidev->spi_lock);
spidev->spi = NULL;
spin_unlock_irq(&spidev->spi_lock);

/* prevent new opens */
mutex_lock(&device_list_lock);
list_del(&spidev->device_entry);
device_destroy(fm175xx_class, spidev->devt);
clear_bit(MINOR(spidev->devt), minors);
if (spidev->users == 0)
	kfree(spidev);
mutex_unlock(&device_list_lock);

return 0;

}

static struct spi_driver fm175xx_spi_driver = {
.driver = {
.name = “fdw,nfc_fm175xx”,
.of_match_table = of_match_ptr(fm175xx_dt_ids),
},
.probe = fm175xx_probe,
.remove = fm175xx_remove,

/* NOTE:  suspend/resume methods are not necessary here.
 * We don't do anything except pass the requests to/from
 * the underlying controller.  The refrigerator handles
 * most issues; the controller driver handles the rest.
 */

};

/-------------------------------------------------------------------------/

static int __init fm175xx_md_init(void)
{
int status;

/* Claim our 256 reserved device numbers.  Then register a class
 * that will key udev/mdev to add/remove /dev nodes.  Last, register
 * the driver which manages those device numbers.
 */
BUILD_BUG_ON(N_SPI_MINORS > 256);
status = register_chrdev(SPIDEV_MAJOR, "fm175xx", &spidev_fops);
if (status < 0)
	return status;

fm175xx_class = class_create(THIS_MODULE, "fm175xx");
if (IS_ERR(fm175xx_class)) {
	unregister_chrdev(SPIDEV_MAJOR, fm175xx_spi_driver.driver.name);
	return PTR_ERR(fm175xx_class);
}

status = spi_register_driver(&fm175xx_spi_driver);
if (status < 0) {
	class_destroy(fm175xx_class);
	unregister_chrdev(SPIDEV_MAJOR, fm175xx_spi_driver.driver.name);
}
printk("spi module init successful.\n");

return status;

}
module_init(fm175xx_md_init);

static void __exit fm175xx_md_exit(void)
{
spi_unregister_driver(&fm175xx_spi_driver);
class_destroy(fm175xx_class);
unregister_chrdev(SPIDEV_MAJOR, fm175xx_spi_driver.driver.name);
printk(“spi module exit successful.\n”);
}
module_exit(fm175xx_md_exit);

MODULE_AUTHOR(“WJ wang<wangwenjun@inmyshow.com>”);
MODULE_DESCRIPTION(“User mode nfc fm175xx interface”);
MODULE_LICENSE(“GPL”);

dmesg 查看信息

楼主,问题解决了没?我遇到跟你一样的问题

Kconfig Makefile和设备树comptible 配置成功没啥问题