From e144358375d07d13fe2e896a87b0346f4ce54728 Mon Sep 17 00:00:00 2001 From: Wesley Yao Date: Wed, 8 May 2024 20:36:15 +0800 Subject: [PATCH] mfd: Add driver for Rockchip Flexbus Change-Id: I14529b18c2adb06bf71cd669b75f5f277e727637 Signed-off-by: Wesley Yao --- drivers/mfd/Kconfig | 8 + drivers/mfd/Makefile | 1 + drivers/mfd/rockchip-flexbus.c | 209 +++++++++++++++++++++++++++ include/linux/mfd/rockchip-flexbus.h | 136 +++++++++++++++++ 4 files changed, 354 insertions(+) create mode 100644 drivers/mfd/rockchip-flexbus.c create mode 100644 include/linux/mfd/rockchip-flexbus.h diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b92a40aa623f..7c0bcd83e21e 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1304,6 +1304,14 @@ config MFD_RK1000 source "drivers/mfd/display-serdes/Kconfig" source "drivers/mfd/rkx110_x120/Kconfig" +config MFD_ROCKCHIP_FLEXBUS + tristate "Rockchip Flexbus" + depends on ARCH_ROCKCHIP + depends on OF + select MFD_CORE + help + Select this to get support for Rockchip Flexbus. + config MFD_RN5T618 tristate "Ricoh RN5T567/618 PMIC" depends on I2C diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index e9f51a5df98f..edd4e673dd38 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -234,6 +234,7 @@ obj-$(CONFIG_MFD_RK806_I2C) += rk806-i2c.o obj-$(CONFIG_MFD_RK806_SPI) += rk806-spi.o obj-$(CONFIG_MFD_RK808) += rk808.o obj-$(CONFIG_MFD_RK1000) += rk1000-core.o +obj-$(CONFIG_MFD_ROCKCHIP_FLEXBUS) += rockchip-flexbus.o obj-$(CONFIG_MFD_SERDES_DISPLAY) += display-serdes/ obj-y += rkx110_x120/ obj-$(CONFIG_MFD_RN5T618) += rn5t618.o diff --git a/drivers/mfd/rockchip-flexbus.c b/drivers/mfd/rockchip-flexbus.c new file mode 100644 index 000000000000..485c1d6a3d51 --- /dev/null +++ b/drivers/mfd/rockchip-flexbus.c @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for Rockchip Flexbus + * + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +enum rockchip_flexbus_tx_rx_mode { + FLEXBUS_TX_AND_RX = 0x0, + FLEXBUS_TX_ONLY = 0x1, + FLEXBUS_RX_ONLY = 0x2, + FLEXBUS_TX_THEN_RX = 0x3, +}; + +unsigned int rockchip_flexbus_readl(struct rockchip_flexbus *rkfb, unsigned int reg) +{ + return readl_relaxed(rkfb->base + reg); +} +EXPORT_SYMBOL_GPL(rockchip_flexbus_readl); + +void rockchip_flexbus_writel(struct rockchip_flexbus *rkfb, unsigned int reg, unsigned int val) +{ + writel_relaxed(val, rkfb->base + reg); +} +EXPORT_SYMBOL_GPL(rockchip_flexbus_writel); + +void rockchip_flexbus_clrbits(struct rockchip_flexbus *rkfb, unsigned int reg, unsigned int clr_val) +{ + unsigned int reg_val; + + reg_val = rockchip_flexbus_readl(rkfb, reg); + reg_val &= ~(clr_val); + rockchip_flexbus_writel(rkfb, reg, reg_val); +} +EXPORT_SYMBOL_GPL(rockchip_flexbus_clrbits); + +void rockchip_flexbus_setbits(struct rockchip_flexbus *rkfb, unsigned int reg, unsigned int set_val) +{ + unsigned int reg_val; + + reg_val = rockchip_flexbus_readl(rkfb, reg); + reg_val |= set_val; + rockchip_flexbus_writel(rkfb, reg, reg_val); +} +EXPORT_SYMBOL_GPL(rockchip_flexbus_setbits); + +void rockchip_flexbus_clrsetbits(struct rockchip_flexbus *rkfb, unsigned int reg, + unsigned int clr_val, unsigned int set_val) +{ + unsigned int reg_val; + + reg_val = rockchip_flexbus_readl(rkfb, reg); + reg_val &= ~(clr_val); + reg_val |= set_val; + rockchip_flexbus_writel(rkfb, reg, reg_val); +} +EXPORT_SYMBOL_GPL(rockchip_flexbus_clrsetbits); + +#define RK3576_VCCIO_IOC_MISC_CON0 0x6400 +static void rk3576_flexbus_grf_config(struct rockchip_flexbus *rkfb, bool slave_mode, bool cpol, + bool cpha) +{ + u32 val = 0x3 << (16 + 7); + + if (slave_mode) { + val |= BIT(8); + if ((!cpol && cpha) || (cpol && !cpha)) + val |= BIT(7); + } + regmap_write(rkfb->regmap_grf, RK3576_VCCIO_IOC_MISC_CON0, val); +} + +static irqreturn_t rockchip_flexbus_isr(int irq, void *dev_id) +{ + struct rockchip_flexbus *rkfb = dev_id; + u32 isr; + + isr = rockchip_flexbus_readl(rkfb, FLEXBUS_ISR); + + if (rkfb->opmode0 != ROCKCHIP_FLEXBUS0_OPMODE_NULL && rkfb->fb0_isr) + rkfb->fb0_isr(rkfb, isr); + if (rkfb->opmode1 != ROCKCHIP_FLEXBUS1_OPMODE_NULL && rkfb->fb1_isr) + rkfb->fb1_isr(rkfb, isr); + + return IRQ_HANDLED; +} + +static void rockchip_flexbus_clk_bulk_disable(void *data) +{ + struct rockchip_flexbus *rkfb = data; + + clk_bulk_disable_unprepare(rkfb->num_clks, rkfb->clks); +} + +static int rockchip_flexbus_probe(struct platform_device *pdev) +{ + struct rockchip_flexbus *rkfb; + int ret; + + rkfb = devm_kzalloc(&pdev->dev, sizeof(*rkfb), GFP_KERNEL); + if (!rkfb) + return -ENOMEM; + + platform_set_drvdata(pdev, rkfb); + + ret = device_property_read_u32(&pdev->dev, "rockchip,flexbus0-opmode", &rkfb->opmode0); + if (ret) + rkfb->opmode0 = ROCKCHIP_FLEXBUS0_OPMODE_NULL; + if (rkfb->opmode0 < ROCKCHIP_FLEXBUS0_OPMODE_NULL || + rkfb->opmode0 > ROCKCHIP_FLEXBUS0_OPMODE_SPI) + return -EINVAL; + + ret = device_property_read_u32(&pdev->dev, "rockchip,flexbus1-opmode", &rkfb->opmode1); + if (ret) + rkfb->opmode1 = ROCKCHIP_FLEXBUS1_OPMODE_NULL; + if (rkfb->opmode1 < ROCKCHIP_FLEXBUS1_OPMODE_NULL || + rkfb->opmode1 > ROCKCHIP_FLEXBUS1_OPMODE_CIF) + return -EINVAL; + + rkfb->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(rkfb->base)) + return PTR_ERR(rkfb->base); + rkfb->dev = &pdev->dev; + + ret = platform_get_irq(pdev, 0); + if (ret < 0) + return dev_err_probe(&pdev->dev, ret, "failed to get irq\n"); + + ret = devm_request_irq(&pdev->dev, ret, rockchip_flexbus_isr, 0, dev_name(&pdev->dev), + rkfb); + if (ret < 0) { + dev_err(&pdev->dev, "failed to request irq %d\n", ret); + return ret; + } + + rkfb->config = device_get_match_data(&pdev->dev); + + rkfb->regmap_grf = syscon_regmap_lookup_by_phandle_optional(pdev->dev.of_node, + "rockchip,grf"); + if (!rkfb->regmap_grf) + dev_warn(&pdev->dev, "failed to get rockchip,grf node.\n"); + + rkfb->num_clks = devm_clk_bulk_get_all(&pdev->dev, &rkfb->clks); + if (rkfb->num_clks <= 0) { + dev_err(&pdev->dev, "bus clock not found\n"); + return -ENODEV; + } + ret = clk_bulk_prepare_enable(rkfb->num_clks, rkfb->clks); + if (ret) { + dev_err(&pdev->dev, "failed to enable clocks.\n"); + return ret; + } + + ret = devm_add_action_or_reset(&pdev->dev, rockchip_flexbus_clk_bulk_disable, rkfb); + if (ret) { + dev_err(&pdev->dev, "failed to register devm action, %d\n", ret); + return ret; + } + + if (rkfb->opmode0 != ROCKCHIP_FLEXBUS0_OPMODE_NULL && + rkfb->opmode1 != ROCKCHIP_FLEXBUS1_OPMODE_NULL) + rockchip_flexbus_writel(rkfb, FLEXBUS_COM_CTL, FLEXBUS_TX_AND_RX); + else if (rkfb->opmode0 != ROCKCHIP_FLEXBUS0_OPMODE_NULL) + rockchip_flexbus_writel(rkfb, FLEXBUS_COM_CTL, FLEXBUS_TX_ONLY); + else + rockchip_flexbus_writel(rkfb, FLEXBUS_COM_CTL, FLEXBUS_RX_ONLY); + + return devm_of_platform_populate(&pdev->dev); +} + +static const struct rockchip_flexbus_config rk3576_flexbus_config = { + .grf_config = rk3576_flexbus_grf_config, + .txwat_start_max = 511, +}; + +static const struct of_device_id rockchip_flexbus_of_match[] = { + { .compatible = "rockchip,rk3576-flexbus", .data = &rk3576_flexbus_config}, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, rockchip_flexbus_of_match); + +static struct platform_driver rockchip_flexbus_driver = { + .probe = rockchip_flexbus_probe, + .driver = { + .name = "rockchip_flexbus", + .of_match_table = rockchip_flexbus_of_match, + }, +}; + +module_platform_driver(rockchip_flexbus_driver); + +MODULE_DESCRIPTION("Rockchip Flexbus driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/rockchip-flexbus.h b/include/linux/mfd/rockchip-flexbus.h new file mode 100644 index 000000000000..fb92a63a1440 --- /dev/null +++ b/include/linux/mfd/rockchip-flexbus.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2024 Rockchip Electronics Co., Ltd. + */ + +#ifndef __LINUX_MFD_ROCKCHIP_FLEXBUS_H__ +#define __LINUX_MFD_ROCKCHIP_FLEXBUS_H__ + +#define FLEXBUS_ENR 0x000 +#define FLEXBUS_FREE_SCLK 0x004 +#define FLEXBUS_CSN_CFG 0x008 +#define FLEXBUS_COM_CTL 0x00C +#define FLEXBUS_REMAP 0x010 +#define FLEXBUS_STOP 0x014 +#define FLEXBUS_SLAVE_MODE 0x018 +#define FLEXBUS_DVP_POL 0x01C +#define FLEXBUS_DVP_CROP_SIZE 0x020 +#define FLEXBUS_DVP_CROP_START 0x024 +#define FLEXBUS_DVP_ORDER 0x028 +#define FLEXBUS_TX_CTL 0x040 +#define FLEXBUS_TX_NUM 0x044 +#define FLEXBUS_TXWAT_START 0x048 +#define FLEXBUS_TXFIFO_DNUM 0x04C +#define FLEXBUS_RX_CTL 0x080 +#define FLEXBUS_RX_NUM 0x084 +#define FLEXBUS_RXFIFO_DNUM 0x088 +#define FLEXBUS_DLL_EN 0x08C +#define FLEXBUS_DLL_NUM 0x090 +#define FLEXBUS_RXCLK_DUMMY 0x094 +#define FLEXBUS_RXCLK_CAP_CNT 0x098 +#define FLEXBUS_DMA_RD_OUTSTD 0x100 +#define FLEXBUS_DMA_WR_OUTSTD 0x104 +#define FLEXBUS_DMA_SRC_ADDR0 0x108 +#define FLEXBUS_DMA_DST_ADDR0 0x10C +#define FLEXBUS_DMA_SRC_ADDR1 0x110 +#define FLEXBUS_DMA_DST_ADDR1 0x114 +#define FLEXBUS_DMA_SRC_LEN0 0x118 +#define FLEXBUS_DMA_DST_LEN0 0x11C +#define FLEXBUS_DMA_SRC_LEN1 0x120 +#define FLEXBUS_DMA_DST_LEN1 0x124 +#define FLEXBUS_DMA_WAT_INT 0x128 +#define FLEXBUS_DMA_TIMEOUT 0x12C +#define FLEXBUS_DMA_RD_LEN 0x130 +#define FLEXBUS_STATUS 0x160 +#define FLEXBUS_IMR 0x164 +#define FLEXBUS_RISR 0x168 +#define FLEXBUS_ISR 0x16C +#define FLEXBUS_ICR 0x170 +#define FLEXBUS_TESTCLK 0x190 +#define FLEXBUS_TESTDAT 0x194 +#define FLEXBUS_REVISION 0x1F0 + +/* Bit fields in ENR */ +#define FLEXBUS_RX_ENR (BIT(16 + 1) | BIT(1)) +#define FLEXBUS_RX_DIS BIT(16 + 1) +#define FLEXBUS_TX_ENR (BIT(16) | BIT(0)) +#define FLEXBUS_TX_DIS BIT(16) + +/* Bit fields in FREE_SCLK */ +#define FLEXBUS_RX_FREE_MODE (BIT(16 + 1) | BIT(1)) +#define FLEXBUS_TX_FREE_MODE (BIT(16) | BIT(0)) + +/* Bit fields in SLAVE_MODE */ +#define FLEXBUS_DVP_SEL BIT(1) +#define FLEXBUS_CLK1_IN BIT(0) + +/* Bit fields in TX_CTL and RX_CTL */ +#define FLEXBUS_CONTINUE_MODE BIT(4) +#define FLEXBUS_CPOL BIT(3) +#define FLEXBUS_CPHA BIT(2) +#define FLEXBUS_DFS_SHIFT 0 + +/* Bit fields in RX_CTL */ +#define FLEXBUS_AUTOPAD BIT(14) + +/* Bit fields in DMA_WAT_INT */ +#define FLEXBUS_SRC_WAT_LVL_MASK 0x3 +#define FLEXBUS_SRC_WAT_LVL_SHIFT 2 +#define FLEXBUS_DST_WAT_LVL_MASK 0x3 +#define FLEXBUS_DST_WAT_LVL_SHIFT 0 + +/* Bit fields in IMR, RISR, ISR and ICR */ +#define FLEXBUS_DMA_TIMEOUT_ISR BIT(13) +#define FLEXBUS_DMA_ERR_ISR BIT(12) +#define FLEXBUS_DMA_DST1_ISR BIT(11) +#define FLEXBUS_DMA_DST0_ISR BIT(10) +#define FLEXBUS_DMA_SRC1_ISR BIT(9) +#define FLEXBUS_DMA_SRC0_ISR BIT(8) +#define FLEXBUS_DVP_FRAME_START_ISR BIT(7) +#define FLEXBUS_DVP_FRAME_AB_ISR BIT(6) +#define FLEXBUS_RX_DONE_ISR BIT(5) +#define FLEXBUS_RX_UDF_ISR BIT(4) +#define FLEXBUS_RX_OVF_ISR BIT(3) +#define FLEXBUS_TX_DONE_ISR BIT(2) +#define FLEXBUS_TX_UDF_ISR BIT(1) +#define FLEXBUS_TX_OVF_ISR BIT(0) + +struct rockchip_flexbus; + +struct rockchip_flexbus_config { + void (*grf_config)(struct rockchip_flexbus *rkfb, bool slave_mode, bool cpol, bool cpha); + u32 txwat_start_max; +}; + +struct rockchip_flexbus { + struct device *dev; + void __iomem *base; + struct regmap *regmap_grf; + unsigned int opmode0; + unsigned int opmode1; + struct clk_bulk_data *clks; + int num_clks; + void *fb0_data; + void *fb1_data; + void (*fb0_isr)(struct rockchip_flexbus *rkfb, u32 isr); + void (*fb1_isr)(struct rockchip_flexbus *rkfb, u32 isr); + const struct rockchip_flexbus_config *config; +}; + +enum rockchip_flexbus_dfs { + FLEXBUS_DFS_2BIT = 0x0, + FLEXBUS_DFS_4BIT, + FLEXBUS_DFS_8BIT, + FLEXBUS_DFS_16BIT, +}; + +unsigned int rockchip_flexbus_readl(struct rockchip_flexbus *rkfb, unsigned int reg); +void rockchip_flexbus_writel(struct rockchip_flexbus *rkfb, unsigned int reg, unsigned int val); +void rockchip_flexbus_clrbits(struct rockchip_flexbus *rkfb, unsigned int reg, + unsigned int clr_val); +void rockchip_flexbus_setbits(struct rockchip_flexbus *rkfb, unsigned int reg, + unsigned int set_val); +void rockchip_flexbus_clrsetbits(struct rockchip_flexbus *rkfb, unsigned int reg, + unsigned int clr_val, unsigned int set_val); + +#endif /* __LINUX_MFD_ROCKCHIP_FLEXBUS_H__ */