diff --git a/drivers/mtd/spi-nor/Kconfig b/drivers/mtd/spi-nor/Kconfig index ffc4b380f2b1..ed4a5640866a 100644 --- a/drivers/mtd/spi-nor/Kconfig +++ b/drivers/mtd/spi-nor/Kconfig @@ -24,6 +24,13 @@ config MTD_SPI_NOR_USE_4K_SECTORS Please note that some tools/drivers/filesystems may not work with 4096 B erase size (e.g. UBIFS requires 15 KiB as a minimum). +config MTD_SPI_NOR_MISC + bool "Support SPI NOR misc device" + default n + help + Support obtaining flash information through the ioctl interface + of the misc device. + source "drivers/mtd/spi-nor/controllers/Kconfig" endif # MTD_SPI_NOR diff --git a/drivers/mtd/spi-nor/core.c b/drivers/mtd/spi-nor/core.c index 2f38ca1e5568..fdec7c815937 100644 --- a/drivers/mtd/spi-nor/core.c +++ b/drivers/mtd/spi-nor/core.c @@ -21,9 +21,17 @@ #include #include #include +#include + +#include #include "core.h" +struct spi_nor_misc_dev { + struct miscdevice dev; + struct spi_nor *nor; +}; + /* Define max times to check status register before we give up. */ /* @@ -3462,6 +3470,78 @@ static int spi_nor_create_write_dirmap(struct spi_nor *nor) return PTR_ERR_OR_ZERO(nor->dirmap.wdesc); } +static int spi_nor_misc_open(struct inode *inode, struct file *file) +{ + struct miscdevice *miscdev = file->private_data; + struct spi_nor_misc_dev *nor_dev; + + nor_dev = container_of(miscdev, struct spi_nor_misc_dev, dev); + file->private_data = nor_dev->nor; + + return 0; +} + +static long spi_nor_misc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct spi_nor *nor = (struct spi_nor *)file->private_data; + struct nor_flash_user_info info; + void __user *uarg = (void __user *)arg; + int i, ret; + + switch (cmd) { + case NOR_GET_FLASH_INFO: + for (i = 0; i < SPI_NOR_MAX_ID_LEN; i++) + info.id[i] = nor->info->id[i]; + + ret = copy_to_user(uarg, &info, sizeof(info)); + if (ret) { + dev_err(nor->dev, "failed to get elbi data\n"); + return -EFAULT; + } + break; + default: + break; + } + return 0; +} + +static const struct file_operations spi_nor_misc_ops = { + .owner = THIS_MODULE, + .open = spi_nor_misc_open, + .unlocked_ioctl = spi_nor_misc_ioctl, +}; + +static int spi_nor_add_misc(struct spi_nor *nor) +{ + int ret; + struct spi_nor_misc_dev *nor_dev; + char name[24]; + + nor_dev = devm_kzalloc(nor->dev, sizeof(struct spi_nor_misc_dev), + GFP_KERNEL); + if (!nor_dev) + return -ENOMEM; + + nor_dev->dev.minor = MISC_DYNAMIC_MINOR; + snprintf(name, sizeof(name), "%s%s", "nor_misc_", dev_name(nor->dev)); + nor_dev->dev.name = devm_kstrdup(nor->dev, name, GFP_KERNEL); + nor_dev->dev.fops = &spi_nor_misc_ops; + nor_dev->dev.parent = nor->dev; + + ret = misc_register(&nor_dev->dev); + if (ret) { + dev_err(nor->dev, "failed to register misc device.\n"); + return ret; + } + + nor_dev->nor = nor; + nor->misc_dev = &nor_dev->dev; + + dev_info(nor->dev, "register misc device\n"); + + return 0; +} + static int spi_nor_probe(struct spi_mem *spimem) { struct spi_device *spi = spimem->spi; @@ -3531,6 +3611,9 @@ static int spi_nor_probe(struct spi_mem *spimem) if (ret) return ret; + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_MISC)) + spi_nor_add_misc(nor); + return mtd_device_register(&nor->mtd, data ? data->parts : NULL, data ? data->nr_parts : 0); } @@ -3541,6 +3624,9 @@ static int spi_nor_remove(struct spi_mem *spimem) spi_nor_restore(nor); + if (IS_ENABLED(CONFIG_MTD_SPI_NOR_MISC) && nor->misc_dev) + misc_deregister(nor->misc_dev); + /* Clean up MTD stuff. */ return mtd_device_unregister(&nor->mtd); } diff --git a/include/linux/mtd/spi-nor.h b/include/linux/mtd/spi-nor.h index bfe4d492c6bf..36d354a36077 100644 --- a/include/linux/mtd/spi-nor.h +++ b/include/linux/mtd/spi-nor.h @@ -388,6 +388,7 @@ struct spi_nor { struct spi_mem_dirmap_desc *wdesc; } dirmap; + struct miscdevice *misc_dev; void *priv; }; diff --git a/include/uapi/linux/spi_nor_misc.h b/include/uapi/linux/spi_nor_misc.h new file mode 100644 index 000000000000..4b6e604cc0fc --- /dev/null +++ b/include/uapi/linux/spi_nor_misc.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * Copyright (c) 2022 Rockchip Electronics Co., Ltd. + */ + +#ifndef _UAPI__SPI_NOR_MISC_H__ +#define _UAPI__SPI_NOR_MISC_H__ + +#include + +#define SPI_NOR_MAX_ID_LEN 6 + +struct nor_flash_user_info { + __u8 id[SPI_NOR_MAX_ID_LEN]; +}; + +#define NOR_BASE 'P' +#define NOR_GET_FLASH_INFO _IOR(NOR_BASE, 0, struct nor_flash_user_info) + +#endif