#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)
 #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
 
 #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;
        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;
 };
        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:
        .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))
 
        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", },
 
--- /dev/null
+/* 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);
+