265 lines
7.2 KiB
C

// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for Rockchip Flexbus
*
* Copyright (C) 2024 Rockchip Electronics Co., Ltd.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_platform.h>
#include <linux/regmap.h>
#include <linux/err.h>
#include <linux/mfd/syscon.h>
#include <linux/io.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <dt-bindings/mfd/rockchip-flexbus.h>
#include <linux/mfd/rockchip-flexbus.h>
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);
static struct rockchip_flexbus_dfs_reg rockchip_flexbus_dfs_reg_v0 = {
.dfs_2bit = 0x0,
.dfs_4bit = 0x1,
.dfs_8bit = 0x2,
.dfs_16bit = 0x3,
.dfs_mask = 0x3,
};
static struct rockchip_flexbus_dfs_reg rockchip_flexbus_dfs_reg_v1 = {
.dfs_1bit = (0x0 << 29),
.dfs_2bit = (0x1 << 29),
.dfs_4bit = (0x2 << 29),
.dfs_8bit = (0x3 << 29),
.dfs_16bit = (0x4 << 29),
.dfs_mask = (0x7 << 29),
};
#define RK3506_GRF_SOC_CON1 0x0004
static void rk3506_flexbus_init_config(struct rockchip_flexbus *rkfb)
{
regmap_write(rkfb->regmap_grf, RK3506_GRF_SOC_CON1, BIT(4 + 16));
}
static void rk3506_flexbus_grf_config(struct rockchip_flexbus *rkfb, bool slave_mode, bool cpol,
bool cpha)
{
u32 val = 0x3 << 16;
if (slave_mode) {
if ((!cpol && cpha) || (cpol && !cpha))
val |= BIT(0);
} else {
val |= BIT(1);
}
regmap_write(rkfb->regmap_grf, RK3506_GRF_SOC_CON1, val);
}
#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_err(&pdev->dev, "failed to get rockchip,grf node.\n");
return -ENODEV;
}
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->config->init_config)
rkfb->config->init_config(rkfb);
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);
switch (rockchip_flexbus_readl(rkfb, FLEXBUS_REVISION) >> 24 & 0xff) {
case 0x0:
rkfb->dfs_reg = &rockchip_flexbus_dfs_reg_v0;
break;
case 0x1:
rkfb->dfs_reg = &rockchip_flexbus_dfs_reg_v1;
break;
default:
dev_err(&pdev->dev, "failed to get large version.\n");
return -EINVAL;
}
return devm_of_platform_populate(&pdev->dev);
}
static const struct rockchip_flexbus_config rk3506_flexbus_config = {
.init_config = rk3506_flexbus_init_config,
.grf_config = rk3506_flexbus_grf_config,
.txwat_start_max = 255,
};
static const struct rockchip_flexbus_config rk3576_flexbus_config = {
.init_config = NULL,
.grf_config = rk3576_flexbus_grf_config,
.txwat_start_max = 511,
};
static const struct of_device_id rockchip_flexbus_of_match[] = {
{ .compatible = "rockchip,rk3506-flexbus", .data = &rk3506_flexbus_config},
{ .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");