i3c: dw: Add infrastructure for platform-specific implementations

The dw i3c core can be integrated into various SoC devices. Platforms
that use this core may need a little configuration that is specific to
that platform.

Add some infrastructure to allow platform-specific behaviour: common
probe/remove functions, a set of platform hook operations, and a pointer
for platform-specific data in struct dw_i3c_master. Move the common api
into a new (i3c local) header file.

Platforms will provide their own struct platform_driver, which allocates
struct dw_i3c_master, does any platform-specific probe behaviour, and
calls into the common probe.

A future change will add new platform support that uses this
infrastructure.

Signed-off-by: Jeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: Joel Stanley <joel@jms.id.au>
Link: https://lore.kernel.org/r/20230331091501.3800299-2-jk@codeconstruct.com.au
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Jeremy Kerr 2023-03-31 17:14:59 +08:00 committed by Alexandre Belloni
parent 66b32e3d2c
commit d782188cbb
2 changed files with 97 additions and 34 deletions

View File

@ -21,6 +21,8 @@
#include <linux/reset.h>
#include <linux/slab.h>
#include "dw-i3c-master.h"
#define DEVICE_CTRL 0x0
#define DEV_CTRL_ENABLE BIT(31)
#define DEV_CTRL_RESUME BIT(30)
@ -189,8 +191,6 @@
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
#define MAX_DEVS 32
#define I3C_BUS_SDR1_SCL_RATE 8000000
#define I3C_BUS_SDR2_SCL_RATE 6000000
#define I3C_BUS_SDR3_SCL_RATE 4000000
@ -201,11 +201,6 @@
#define XFER_TIMEOUT (msecs_to_jiffies(1000))
struct dw_i3c_master_caps {
u8 cmdfifodepth;
u8 datafifodepth;
};
struct dw_i3c_cmd {
u32 cmd_lo;
u32 cmd_hi;
@ -224,25 +219,6 @@ struct dw_i3c_xfer {
struct dw_i3c_cmd cmds[];
};
struct dw_i3c_master {
struct i3c_master_controller base;
u16 maxdevs;
u16 datstartaddr;
u32 free_pos;
struct {
struct list_head list;
struct dw_i3c_xfer *cur;
spinlock_t lock;
} xferqueue;
struct dw_i3c_master_caps caps;
void __iomem *regs;
struct reset_control *core_rst;
struct clk *core_clk;
char version[5];
char type[5];
u8 addrs[MAX_DEVS];
};
struct dw_i3c_i2c_dev_data {
u8 index;
};
@ -602,6 +578,10 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
u32 thld_ctrl;
int ret;
ret = master->platform_ops->init(master);
if (ret)
return ret;
switch (bus->mode) {
case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED:
@ -1124,14 +1104,23 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.i2c_xfers = dw_i3c_master_i2c_xfers,
};
static int dw_i3c_probe(struct platform_device *pdev)
/* default platform ops implementations */
static int dw_i3c_platform_init_nop(struct dw_i3c_master *i3c)
{
return 0;
}
static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = {
.init = dw_i3c_platform_init_nop,
};
int dw_i3c_common_probe(struct dw_i3c_master *master,
struct platform_device *pdev)
{
struct dw_i3c_master *master;
int ret, irq;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
if (!master->platform_ops)
master->platform_ops = &dw_i3c_platform_ops_default;
master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs))
@ -1192,17 +1181,37 @@ err_disable_core_clk:
return ret;
}
EXPORT_SYMBOL_GPL(dw_i3c_common_probe);
static void dw_i3c_remove(struct platform_device *pdev)
void dw_i3c_common_remove(struct dw_i3c_master *master)
{
struct dw_i3c_master *master = platform_get_drvdata(pdev);
i3c_master_unregister(&master->base);
reset_control_assert(master->core_rst);
clk_disable_unprepare(master->core_clk);
}
EXPORT_SYMBOL_GPL(dw_i3c_common_remove);
/* base platform implementation */
static int dw_i3c_probe(struct platform_device *pdev)
{
struct dw_i3c_master *master;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
if (!master)
return -ENOMEM;
return dw_i3c_common_probe(master, pdev);
}
static void dw_i3c_remove(struct platform_device *pdev)
{
struct dw_i3c_master *master = platform_get_drvdata(pdev);
dw_i3c_common_remove(master);
}
static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", },

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2023 Code Construct
*
* Author: Jeremy Kerr <jk@codeconstruct.com.au>
*/
#include <linux/clk.h>
#include <linux/i3c/master.h>
#include <linux/reset.h>
#include <linux/types.h>
#define DW_I3C_MAX_DEVS 32
struct dw_i3c_master_caps {
u8 cmdfifodepth;
u8 datafifodepth;
};
struct dw_i3c_master {
struct i3c_master_controller base;
u16 maxdevs;
u16 datstartaddr;
u32 free_pos;
struct {
struct list_head list;
struct dw_i3c_xfer *cur;
spinlock_t lock;
} xferqueue;
struct dw_i3c_master_caps caps;
void __iomem *regs;
struct reset_control *core_rst;
struct clk *core_clk;
char version[5];
char type[5];
u8 addrs[DW_I3C_MAX_DEVS];
/* platform-specific data */
const struct dw_i3c_platform_ops *platform_ops;
};
struct dw_i3c_platform_ops {
/*
* Called on early bus init: the i3c has been set up, but before any
* transactions have taken place. Platform implementations may use to
* perform actual device enabling with the i3c core ready.
*/
int (*init)(struct dw_i3c_master *i3c);
};
extern int dw_i3c_common_probe(struct dw_i3c_master *master,
struct platform_device *pdev);
extern void dw_i3c_common_remove(struct dw_i3c_master *master);