Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Jeremy Kerr and committed by
Alexandre Belloni
d782188c 66b32e3d

+97 -34
+43 -34
drivers/i3c/master/dw-i3c-master.c
··· 21 21 #include <linux/reset.h> 22 22 #include <linux/slab.h> 23 23 24 + #include "dw-i3c-master.h" 25 + 24 26 #define DEVICE_CTRL 0x0 25 27 #define DEV_CTRL_ENABLE BIT(31) 26 28 #define DEV_CTRL_RESUME BIT(30) ··· 191 189 #define DEV_ADDR_TABLE_STATIC_ADDR(x) ((x) & GENMASK(6, 0)) 192 190 #define DEV_ADDR_TABLE_LOC(start, idx) ((start) + ((idx) << 2)) 193 191 194 - #define MAX_DEVS 32 195 - 196 192 #define I3C_BUS_SDR1_SCL_RATE 8000000 197 193 #define I3C_BUS_SDR2_SCL_RATE 6000000 198 194 #define I3C_BUS_SDR3_SCL_RATE 4000000 ··· 200 200 #define I3C_BUS_THIGH_MAX_NS 41 201 201 202 202 #define XFER_TIMEOUT (msecs_to_jiffies(1000)) 203 - 204 - struct dw_i3c_master_caps { 205 - u8 cmdfifodepth; 206 - u8 datafifodepth; 207 - }; 208 203 209 204 struct dw_i3c_cmd { 210 205 u32 cmd_lo; ··· 217 222 int ret; 218 223 unsigned int ncmds; 219 224 struct dw_i3c_cmd cmds[]; 220 - }; 221 - 222 - struct dw_i3c_master { 223 - struct i3c_master_controller base; 224 - u16 maxdevs; 225 - u16 datstartaddr; 226 - u32 free_pos; 227 - struct { 228 - struct list_head list; 229 - struct dw_i3c_xfer *cur; 230 - spinlock_t lock; 231 - } xferqueue; 232 - struct dw_i3c_master_caps caps; 233 - void __iomem *regs; 234 - struct reset_control *core_rst; 235 - struct clk *core_clk; 236 - char version[5]; 237 - char type[5]; 238 - u8 addrs[MAX_DEVS]; 239 225 }; 240 226 241 227 struct dw_i3c_i2c_dev_data { ··· 577 601 struct i3c_device_info info = { }; 578 602 u32 thld_ctrl; 579 603 int ret; 604 + 605 + ret = master->platform_ops->init(master); 606 + if (ret) 607 + return ret; 580 608 581 609 switch (bus->mode) { 582 610 case I3C_BUS_MODE_MIXED_FAST: ··· 1104 1124 .i2c_xfers = dw_i3c_master_i2c_xfers, 1105 1125 }; 1106 1126 1107 - static int dw_i3c_probe(struct platform_device *pdev) 1127 + /* default platform ops implementations */ 1128 + static int dw_i3c_platform_init_nop(struct dw_i3c_master *i3c) 1108 1129 { 1109 - struct dw_i3c_master *master; 1130 + return 0; 1131 + } 1132 + 1133 + static const struct dw_i3c_platform_ops dw_i3c_platform_ops_default = { 1134 + .init = dw_i3c_platform_init_nop, 1135 + }; 1136 + 1137 + int dw_i3c_common_probe(struct dw_i3c_master *master, 1138 + struct platform_device *pdev) 1139 + { 1110 1140 int ret, irq; 1111 1141 1112 - master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); 1113 - if (!master) 1114 - return -ENOMEM; 1142 + if (!master->platform_ops) 1143 + master->platform_ops = &dw_i3c_platform_ops_default; 1115 1144 1116 1145 master->regs = devm_platform_ioremap_resource(pdev, 0); 1117 1146 if (IS_ERR(master->regs)) ··· 1181 1192 1182 1193 return ret; 1183 1194 } 1195 + EXPORT_SYMBOL_GPL(dw_i3c_common_probe); 1184 1196 1185 - static void dw_i3c_remove(struct platform_device *pdev) 1197 + void dw_i3c_common_remove(struct dw_i3c_master *master) 1186 1198 { 1187 - struct dw_i3c_master *master = platform_get_drvdata(pdev); 1188 - 1189 1199 i3c_master_unregister(&master->base); 1190 1200 1191 1201 reset_control_assert(master->core_rst); 1192 1202 1193 1203 clk_disable_unprepare(master->core_clk); 1204 + } 1205 + EXPORT_SYMBOL_GPL(dw_i3c_common_remove); 1206 + 1207 + /* base platform implementation */ 1208 + 1209 + static int dw_i3c_probe(struct platform_device *pdev) 1210 + { 1211 + struct dw_i3c_master *master; 1212 + 1213 + master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL); 1214 + if (!master) 1215 + return -ENOMEM; 1216 + 1217 + return dw_i3c_common_probe(master, pdev); 1218 + } 1219 + 1220 + static void dw_i3c_remove(struct platform_device *pdev) 1221 + { 1222 + struct dw_i3c_master *master = platform_get_drvdata(pdev); 1223 + 1224 + dw_i3c_common_remove(master); 1194 1225 } 1195 1226 1196 1227 static const struct of_device_id dw_i3c_master_of_match[] = {
+54
drivers/i3c/master/dw-i3c-master.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (c) 2023 Code Construct 4 + * 5 + * Author: Jeremy Kerr <jk@codeconstruct.com.au> 6 + */ 7 + 8 + #include <linux/clk.h> 9 + #include <linux/i3c/master.h> 10 + #include <linux/reset.h> 11 + #include <linux/types.h> 12 + 13 + #define DW_I3C_MAX_DEVS 32 14 + 15 + struct dw_i3c_master_caps { 16 + u8 cmdfifodepth; 17 + u8 datafifodepth; 18 + }; 19 + 20 + struct dw_i3c_master { 21 + struct i3c_master_controller base; 22 + u16 maxdevs; 23 + u16 datstartaddr; 24 + u32 free_pos; 25 + struct { 26 + struct list_head list; 27 + struct dw_i3c_xfer *cur; 28 + spinlock_t lock; 29 + } xferqueue; 30 + struct dw_i3c_master_caps caps; 31 + void __iomem *regs; 32 + struct reset_control *core_rst; 33 + struct clk *core_clk; 34 + char version[5]; 35 + char type[5]; 36 + u8 addrs[DW_I3C_MAX_DEVS]; 37 + 38 + /* platform-specific data */ 39 + const struct dw_i3c_platform_ops *platform_ops; 40 + }; 41 + 42 + struct dw_i3c_platform_ops { 43 + /* 44 + * Called on early bus init: the i3c has been set up, but before any 45 + * transactions have taken place. Platform implementations may use to 46 + * perform actual device enabling with the i3c core ready. 47 + */ 48 + int (*init)(struct dw_i3c_master *i3c); 49 + }; 50 + 51 + extern int dw_i3c_common_probe(struct dw_i3c_master *master, 52 + struct platform_device *pdev); 53 + extern void dw_i3c_common_remove(struct dw_i3c_master *master); 54 +