Commit d782188c authored by Jeremy Kerr's avatar Jeremy Kerr Committed by Alexandre Belloni
Browse files

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: default avatarJeremy Kerr <jk@codeconstruct.com.au>
Reviewed-by: default avatarJoel Stanley <joel@jms.id.au>
Link: https://lore.kernel.org/r/20230331091501.3800299-2-jk@codeconstruct.com.au


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 66b32e3d
Loading
Loading
Loading
Loading
+43 −34
Original line number Diff line number Diff line
@@ -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 @@ static int dw_i3c_probe(struct platform_device *pdev)

	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", },
+54 −0
Original line number Diff line number Diff line
/* 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);