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 <jon.lin@rock-chips.com>
This commit is contained in:
@ -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
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
#include <linux/dmaengine.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/miscdevice.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/pinctrl/consumer.h>
|
||||
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user