From a8b8bfcdf5b2b55f136e45387b7152a3707afd99 Mon Sep 17 00:00:00 2001 From: Jon Lin Date: Fri, 16 Sep 2022 14:07:05 +0800 Subject: [PATCH] spi: rockchip: Support rkspi-devN misc devices Reach controller register in user space by mmap operation, And only do spi transmission in user space. Change-Id: I37e22ad04813c2cd10d97324339afc5a11da59bd Signed-off-by: Jon Lin --- drivers/spi/Kconfig | 10 +++++ drivers/spi/spi-rockchip.c | 79 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 0a7fd56c1ed9..f5e93c37106b 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -508,6 +508,16 @@ config SPI_ROCKCHIP The main usecase of this controller is to use spi flash as boot device. +config SPI_ROCKCHIP_MISCDEV + bool "Rockchip SPI controller misc devices" + depends on SPI_ROCKCHIP + help + This selects a misc driver for Rockchip SPI controller. + + If you say yes to this option, It will register rkspi-devN misc device + for each spi controller and support to get the controller register + resource by calling mmap. + config SPI_RB4XX tristate "Mikrotik RB4XX SPI master" depends on SPI_MASTER && ATH79 diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index 36986a708949..86042911aa54 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,8 @@ #define ROCKCHIP_SPI_VER2_TYPE1 0x05EC0002 #define ROCKCHIP_SPI_VER2_TYPE2 0x00110002 +#define ROCKCHIP_SPI_REGISTER_SIZE 0x1000 + struct rockchip_spi_quirks { u32 max_baud_div_in_cpha; }; @@ -216,6 +219,8 @@ struct rockchip_spi { bool gpio_requested; bool cs_inactive; /* spi slave tansmition stop when cs inactive */ struct spi_transfer *xfer; /* Store xfer temporarily */ + phys_addr_t base_addr_phy; + struct miscdevice miscdev; /* quirks */ u32 max_baud_div_in_cpha; @@ -813,6 +818,60 @@ static void rockchip_spi_cleanup(struct spi_device *spi) gpio_free(spi->cs_gpio); } +static int rockchip_spi_misc_open(struct inode *inode, struct file *filp) +{ + struct miscdevice *misc = filp->private_data; + struct spi_controller *ctlr = dev_get_drvdata(misc->parent); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + + pm_runtime_get_sync(rs->dev); + + return 0; +} + +static int rockchip_spi_misc_release(struct inode *inode, struct file *filp) +{ + struct miscdevice *misc = filp->private_data; + struct spi_controller *ctlr = dev_get_drvdata(misc->parent); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + + pm_runtime_put(rs->dev); + + return 0; +} + +static int rockchip_spi_mmap(struct file *filp, struct vm_area_struct *vma) +{ + struct miscdevice *misc = filp->private_data; + struct spi_controller *ctlr = dev_get_drvdata(misc->parent); + struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + size_t size = vma->vm_end - vma->vm_start; + int err; + + if (size > ROCKCHIP_SPI_REGISTER_SIZE) { + dev_warn(misc->parent, "mmap size is out of limitation\n"); + return -EINVAL; + } + + vma->vm_flags |= VM_IO; + vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + err = remap_pfn_range(vma, vma->vm_start, + __phys_to_pfn(rs->base_addr_phy), + size, vma->vm_page_prot); + if (err) + return -EAGAIN; + + return 0; +} + +static const struct file_operations rockchip_spi_misc_fops = { + .open = rockchip_spi_misc_open, + .release = rockchip_spi_misc_release, + .mmap = rockchip_spi_mmap, +}; + static int rockchip_spi_probe(struct platform_device *pdev) { int ret; @@ -849,6 +908,7 @@ static int rockchip_spi_probe(struct platform_device *pdev) ret = PTR_ERR(rs->regs); goto err_put_ctlr; } + rs->base_addr_phy = mem->start; rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk"); if (IS_ERR(rs->apb_pclk)) { @@ -997,6 +1057,22 @@ static int rockchip_spi_probe(struct platform_device *pdev) goto err_free_dma_rx; } + if (IS_ENABLED(CONFIG_SPI_ROCKCHIP_MISCDEV)) { + char misc_name[20]; + + snprintf(misc_name, sizeof(misc_name), "rkspi-dev%d", ctlr->bus_num); + rs->miscdev.minor = MISC_DYNAMIC_MINOR; + rs->miscdev.name = misc_name; + rs->miscdev.fops = &rockchip_spi_misc_fops; + rs->miscdev.parent = &pdev->dev; + + ret = misc_register(&rs->miscdev); + if (ret) + dev_err(&pdev->dev, "failed to register misc device %s\n", misc_name); + else + dev_info(&pdev->dev, "register misc device %s\n", misc_name); + } + return 0; err_free_dma_rx: @@ -1022,6 +1098,9 @@ static int rockchip_spi_remove(struct platform_device *pdev) struct spi_controller *ctlr = spi_controller_get(platform_get_drvdata(pdev)); struct rockchip_spi *rs = spi_controller_get_devdata(ctlr); + if (IS_ENABLED(CONFIG_SPI_ROCKCHIP_MISCDEV)) + misc_deregister(&rs->miscdev); + pm_runtime_get_sync(&pdev->dev); clk_disable_unprepare(rs->spiclk);