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

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.auSigned-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 66b32e3d
...@@ -21,6 +21,8 @@ ...@@ -21,6 +21,8 @@
#include <linux/reset.h> #include <linux/reset.h>
#include <linux/slab.h> #include <linux/slab.h>
#include "dw-i3c-master.h"
#define DEVICE_CTRL 0x0 #define DEVICE_CTRL 0x0
#define DEV_CTRL_ENABLE BIT(31) #define DEV_CTRL_ENABLE BIT(31)
#define DEV_CTRL_RESUME BIT(30) #define DEV_CTRL_RESUME BIT(30)
...@@ -189,8 +191,6 @@ ...@@ -189,8 +191,6 @@
#define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0)) #define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0))
#define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2)) #define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2))
#define MAX_DEVS 32
#define I3C_BUS_SDR1_SCL_RATE 8000000 #define I3C_BUS_SDR1_SCL_RATE 8000000
#define I3C_BUS_SDR2_SCL_RATE 6000000 #define I3C_BUS_SDR2_SCL_RATE 6000000
#define I3C_BUS_SDR3_SCL_RATE 4000000 #define I3C_BUS_SDR3_SCL_RATE 4000000
...@@ -201,11 +201,6 @@ ...@@ -201,11 +201,6 @@
#define XFER_TIMEOUT (msecs_to_jiffies(1000)) #define XFER_TIMEOUT (msecs_to_jiffies(1000))
struct dw_i3c_master_caps {
u8 cmdfifodepth;
u8 datafifodepth;
};
struct dw_i3c_cmd { struct dw_i3c_cmd {
u32 cmd_lo; u32 cmd_lo;
u32 cmd_hi; u32 cmd_hi;
...@@ -224,25 +219,6 @@ struct dw_i3c_xfer { ...@@ -224,25 +219,6 @@ struct dw_i3c_xfer {
struct dw_i3c_cmd cmds[]; 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 { struct dw_i3c_i2c_dev_data {
u8 index; u8 index;
}; };
...@@ -602,6 +578,10 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m) ...@@ -602,6 +578,10 @@ static int dw_i3c_master_bus_init(struct i3c_master_controller *m)
u32 thld_ctrl; u32 thld_ctrl;
int ret; int ret;
ret = master->platform_ops->init(master);
if (ret)
return ret;
switch (bus->mode) { switch (bus->mode) {
case I3C_BUS_MODE_MIXED_FAST: case I3C_BUS_MODE_MIXED_FAST:
case I3C_BUS_MODE_MIXED_LIMITED: case I3C_BUS_MODE_MIXED_LIMITED:
...@@ -1124,14 +1104,23 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = { ...@@ -1124,14 +1104,23 @@ static const struct i3c_master_controller_ops dw_mipi_i3c_ops = {
.i2c_xfers = dw_i3c_master_i2c_xfers, .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; int ret, irq;
master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); if (!master->platform_ops)
if (!master) master->platform_ops = &dw_i3c_platform_ops_default;
return -ENOMEM;
master->regs = devm_platform_ioremap_resource(pdev, 0); master->regs = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(master->regs)) if (IS_ERR(master->regs))
...@@ -1192,17 +1181,37 @@ static int dw_i3c_probe(struct platform_device *pdev) ...@@ -1192,17 +1181,37 @@ static int dw_i3c_probe(struct platform_device *pdev)
return ret; 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); i3c_master_unregister(&master->base);
reset_control_assert(master->core_rst); reset_control_assert(master->core_rst);
clk_disable_unprepare(master->core_clk); 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[] = { static const struct of_device_id dw_i3c_master_of_match[] = {
{ .compatible = "snps,dw-i3c-master-1.00a", }, { .compatible = "snps,dw-i3c-master-1.00a", },
......
/* 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);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment