1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2023 Code Construct
5 * Author: Jeremy Kerr <jk@codeconstruct.com.au>
8 #include <linux/mfd/syscon.h>
9 #include <linux/module.h>
11 #include <linux/platform_device.h>
12 #include <linux/regmap.h>
14 #include "dw-i3c-master.h"
16 /* AST2600-specific global register set */
17 #define AST2600_I3CG_REG0(idx) (((idx) * 4 * 4) + 0x10)
18 #define AST2600_I3CG_REG1(idx) (((idx) * 4 * 4) + 0x14)
20 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_MASK GENMASK(29, 28)
21 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_2K (0x0 << 28)
22 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_750 (0x2 << 28)
23 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_545 (0x3 << 28)
25 #define AST2600_I3CG_REG1_I2C_MODE BIT(0)
26 #define AST2600_I3CG_REG1_TEST_MODE BIT(1)
27 #define AST2600_I3CG_REG1_ACT_MODE_MASK GENMASK(3, 2)
28 #define AST2600_I3CG_REG1_ACT_MODE(x) (((x) << 2) & AST2600_I3CG_REG1_ACT_MODE_MASK)
29 #define AST2600_I3CG_REG1_PENDING_INT_MASK GENMASK(7, 4)
30 #define AST2600_I3CG_REG1_PENDING_INT(x) (((x) << 4) & AST2600_I3CG_REG1_PENDING_INT_MASK)
31 #define AST2600_I3CG_REG1_SA_MASK GENMASK(14, 8)
32 #define AST2600_I3CG_REG1_SA(x) (((x) << 8) & AST2600_I3CG_REG1_SA_MASK)
33 #define AST2600_I3CG_REG1_SA_EN BIT(15)
34 #define AST2600_I3CG_REG1_INST_ID_MASK GENMASK(19, 16)
35 #define AST2600_I3CG_REG1_INST_ID(x) (((x) << 16) & AST2600_I3CG_REG1_INST_ID_MASK)
37 #define AST2600_DEFAULT_SDA_PULLUP_OHMS 2000
39 #define DEV_ADDR_TABLE_IBI_PEC BIT(11)
42 struct dw_i3c_master dw
;
43 struct regmap
*global_regs
;
44 unsigned int global_idx
;
45 unsigned int sda_pullup
;
48 static struct ast2600_i3c
*to_ast2600_i3c(struct dw_i3c_master
*dw
)
50 return container_of(dw
, struct ast2600_i3c
, dw
);
53 static int ast2600_i3c_pullup_to_reg(unsigned int ohms
, u32
*regp
)
59 reg
= AST2600_I3CG_REG0_SDA_PULLUP_EN_2K
;
62 reg
= AST2600_I3CG_REG0_SDA_PULLUP_EN_750
;
65 reg
= AST2600_I3CG_REG0_SDA_PULLUP_EN_545
;
77 static int ast2600_i3c_init(struct dw_i3c_master
*dw
)
79 struct ast2600_i3c
*i3c
= to_ast2600_i3c(dw
);
83 /* reg0: set SDA pullup values */
84 rc
= ast2600_i3c_pullup_to_reg(i3c
->sda_pullup
, ®
);
88 rc
= regmap_write(i3c
->global_regs
,
89 AST2600_I3CG_REG0(i3c
->global_idx
), reg
);
93 /* reg1: set up the instance id, but leave everything else disabled,
94 * as it's all for client mode
96 reg
= AST2600_I3CG_REG1_INST_ID(i3c
->global_idx
);
97 rc
= regmap_write(i3c
->global_regs
,
98 AST2600_I3CG_REG1(i3c
->global_idx
), reg
);
103 static void ast2600_i3c_set_dat_ibi(struct dw_i3c_master
*i3c
,
104 struct i3c_dev_desc
*dev
,
105 bool enable
, u32
*dat
)
108 * The ast2600 i3c controller will lock up on receiving 4n+1-byte IBIs
109 * if the PEC is disabled. We have no way to restrict the length of
110 * IBIs sent to the controller, so we need to unconditionally enable
111 * PEC checking, which means we drop a byte of payload data
113 if (enable
&& dev
->info
.bcr
& I3C_BCR_IBI_PAYLOAD
) {
114 dev_warn_once(&i3c
->base
.dev
,
115 "Enabling PEC workaround. IBI payloads will be truncated\n");
116 *dat
|= DEV_ADDR_TABLE_IBI_PEC
;
120 static const struct dw_i3c_platform_ops ast2600_i3c_ops
= {
121 .init
= ast2600_i3c_init
,
122 .set_dat_ibi
= ast2600_i3c_set_dat_ibi
,
125 static int ast2600_i3c_probe(struct platform_device
*pdev
)
127 struct device_node
*np
= pdev
->dev
.of_node
;
128 struct of_phandle_args gspec
;
129 struct ast2600_i3c
*i3c
;
132 i3c
= devm_kzalloc(&pdev
->dev
, sizeof(*i3c
), GFP_KERNEL
);
136 rc
= of_parse_phandle_with_fixed_args(np
, "aspeed,global-regs", 1, 0,
141 i3c
->global_regs
= syscon_node_to_regmap(gspec
.np
);
142 of_node_put(gspec
.np
);
144 if (IS_ERR(i3c
->global_regs
))
145 return PTR_ERR(i3c
->global_regs
);
147 i3c
->global_idx
= gspec
.args
[0];
149 rc
= of_property_read_u32(np
, "sda-pullup-ohms", &i3c
->sda_pullup
);
151 i3c
->sda_pullup
= AST2600_DEFAULT_SDA_PULLUP_OHMS
;
153 rc
= ast2600_i3c_pullup_to_reg(i3c
->sda_pullup
, NULL
);
155 dev_err(&pdev
->dev
, "invalid sda-pullup value %d\n",
158 i3c
->dw
.platform_ops
= &ast2600_i3c_ops
;
159 return dw_i3c_common_probe(&i3c
->dw
, pdev
);
162 static void ast2600_i3c_remove(struct platform_device
*pdev
)
164 struct dw_i3c_master
*dw_i3c
= platform_get_drvdata(pdev
);
166 dw_i3c_common_remove(dw_i3c
);
169 static const struct of_device_id ast2600_i3c_master_of_match
[] = {
170 { .compatible
= "aspeed,ast2600-i3c", },
173 MODULE_DEVICE_TABLE(of
, ast2600_i3c_master_of_match
);
175 static struct platform_driver ast2600_i3c_driver
= {
176 .probe
= ast2600_i3c_probe
,
177 .remove
= ast2600_i3c_remove
,
179 .name
= "ast2600-i3c-master",
180 .of_match_table
= ast2600_i3c_master_of_match
,
183 module_platform_driver(ast2600_i3c_driver
);
185 MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>");
186 MODULE_DESCRIPTION("ASPEED AST2600 I3C driver");
187 MODULE_LICENSE("GPL");