diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index 4469a177751a..0bdd5fe31d66 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -640,6 +640,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_ROCKCHIP_SFC tristate "Rockchip Serial Flash Controller (SFC)" imply ROCKCHIP_MTD_VENDOR_STORAGE diff --git a/drivers/spi/spi-rockchip.c b/drivers/spi/spi-rockchip.c index be61a77901e4..c8302e38f4c2 100644 --- a/drivers/spi/spi-rockchip.c +++ b/drivers/spi/spi-rockchip.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -169,6 +170,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; }; @@ -209,6 +212,8 @@ struct rockchip_spi { bool cs_high_supported; /* native CS supports active-high polarity */ struct spi_transfer *xfer; /* Store xfer temporarily */ + phys_addr_t base_addr_phy; + struct miscdevice miscdev; /* quirks */ u32 max_baud_div_in_cpha; @@ -785,6 +790,60 @@ static int rockchip_spi_setup(struct spi_device *spi) return 0; } +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; @@ -821,6 +880,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; if (!has_acpi_companion(&pdev->dev)) rs->apb_pclk = devm_clk_get(&pdev->dev, "apb_pclk"); @@ -999,6 +1059,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: @@ -1026,6 +1102,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->sclk_in);