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/mfd/syscon.h> 9 #include <linux/module.h> 10 #include <linux/of.h> 11 #include <linux/of_device.h> 12 #include <linux/platform_device.h> 13 #include <linux/regmap.h> 14 15 #include "dw-i3c-master.h" 16 17 /* AST2600-specific global register set */ 18 #define AST2600_I3CG_REG0(idx) (((idx) * 4 * 4) + 0x10) 19 #define AST2600_I3CG_REG1(idx) (((idx) * 4 * 4) + 0x14) 20 21 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_MASK GENMASK(29, 28) 22 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_2K (0x0 << 28) 23 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_750 (0x2 << 28) 24 #define AST2600_I3CG_REG0_SDA_PULLUP_EN_545 (0x3 << 28) 25 26 #define AST2600_I3CG_REG1_I2C_MODE BIT(0) 27 #define AST2600_I3CG_REG1_TEST_MODE BIT(1) 28 #define AST2600_I3CG_REG1_ACT_MODE_MASK GENMASK(3, 2) 29 #define AST2600_I3CG_REG1_ACT_MODE(x) (((x) << 2) & AST2600_I3CG_REG1_ACT_MODE_MASK) 30 #define AST2600_I3CG_REG1_PENDING_INT_MASK GENMASK(7, 4) 31 #define AST2600_I3CG_REG1_PENDING_INT(x) (((x) << 4) & AST2600_I3CG_REG1_PENDING_INT_MASK) 32 #define AST2600_I3CG_REG1_SA_MASK GENMASK(14, 8) 33 #define AST2600_I3CG_REG1_SA(x) (((x) << 8) & AST2600_I3CG_REG1_SA_MASK) 34 #define AST2600_I3CG_REG1_SA_EN BIT(15) 35 #define AST2600_I3CG_REG1_INST_ID_MASK GENMASK(19, 16) 36 #define AST2600_I3CG_REG1_INST_ID(x) (((x) << 16) & AST2600_I3CG_REG1_INST_ID_MASK) 37 38 #define AST2600_DEFAULT_SDA_PULLUP_OHMS 2000 39 40 #define DEV_ADDR_TABLE_IBI_PEC BIT(11) 41 42 struct ast2600_i3c { 43 struct dw_i3c_master dw; 44 struct regmap *global_regs; 45 unsigned int global_idx; 46 unsigned int sda_pullup; 47 }; 48 49 static struct ast2600_i3c *to_ast2600_i3c(struct dw_i3c_master *dw) 50 { 51 return container_of(dw, struct ast2600_i3c, dw); 52 } 53 54 static int ast2600_i3c_pullup_to_reg(unsigned int ohms, u32 *regp) 55 { 56 u32 reg; 57 58 switch (ohms) { 59 case 2000: 60 reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_2K; 61 break; 62 case 750: 63 reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_750; 64 break; 65 case 545: 66 reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_545; 67 break; 68 default: 69 return -EINVAL; 70 } 71 72 if (regp) 73 *regp = reg; 74 75 return 0; 76 } 77 78 static int ast2600_i3c_init(struct dw_i3c_master *dw) 79 { 80 struct ast2600_i3c *i3c = to_ast2600_i3c(dw); 81 u32 reg = 0; 82 int rc; 83 84 /* reg0: set SDA pullup values */ 85 rc = ast2600_i3c_pullup_to_reg(i3c->sda_pullup, ®); 86 if (rc) 87 return rc; 88 89 rc = regmap_write(i3c->global_regs, 90 AST2600_I3CG_REG0(i3c->global_idx), reg); 91 if (rc) 92 return rc; 93 94 /* reg1: set up the instance id, but leave everything else disabled, 95 * as it's all for client mode 96 */ 97 reg = AST2600_I3CG_REG1_INST_ID(i3c->global_idx); 98 rc = regmap_write(i3c->global_regs, 99 AST2600_I3CG_REG1(i3c->global_idx), reg); 100 101 return rc; 102 } 103 104 static void ast2600_i3c_set_dat_ibi(struct dw_i3c_master *i3c, 105 struct i3c_dev_desc *dev, 106 bool enable, u32 *dat) 107 { 108 /* 109 * The ast2600 i3c controller will lock up on receiving 4n+1-byte IBIs 110 * if the PEC is disabled. We have no way to restrict the length of 111 * IBIs sent to the controller, so we need to unconditionally enable 112 * PEC checking, which means we drop a byte of payload data 113 */ 114 if (enable && dev->info.bcr & I3C_BCR_IBI_PAYLOAD) { 115 dev_warn_once(&i3c->base.dev, 116 "Enabling PEC workaround. IBI payloads will be truncated\n"); 117 *dat |= DEV_ADDR_TABLE_IBI_PEC; 118 } 119 } 120 121 static const struct dw_i3c_platform_ops ast2600_i3c_ops = { 122 .init = ast2600_i3c_init, 123 .set_dat_ibi = ast2600_i3c_set_dat_ibi, 124 }; 125 126 static int ast2600_i3c_probe(struct platform_device *pdev) 127 { 128 struct device_node *np = pdev->dev.of_node; 129 struct of_phandle_args gspec; 130 struct ast2600_i3c *i3c; 131 int rc; 132 133 i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL); 134 if (!i3c) 135 return -ENOMEM; 136 137 rc = of_parse_phandle_with_fixed_args(np, "aspeed,global-regs", 1, 0, 138 &gspec); 139 if (rc) 140 return -ENODEV; 141 142 i3c->global_regs = syscon_node_to_regmap(gspec.np); 143 of_node_put(gspec.np); 144 145 if (IS_ERR(i3c->global_regs)) 146 return PTR_ERR(i3c->global_regs); 147 148 i3c->global_idx = gspec.args[0]; 149 150 rc = of_property_read_u32(np, "sda-pullup-ohms", &i3c->sda_pullup); 151 if (rc) 152 i3c->sda_pullup = AST2600_DEFAULT_SDA_PULLUP_OHMS; 153 154 rc = ast2600_i3c_pullup_to_reg(i3c->sda_pullup, NULL); 155 if (rc) 156 dev_err(&pdev->dev, "invalid sda-pullup value %d\n", 157 i3c->sda_pullup); 158 159 i3c->dw.platform_ops = &ast2600_i3c_ops; 160 i3c->dw.ibi_capable = true; 161 return dw_i3c_common_probe(&i3c->dw, pdev); 162 } 163 164 static void ast2600_i3c_remove(struct platform_device *pdev) 165 { 166 struct dw_i3c_master *dw_i3c = platform_get_drvdata(pdev); 167 168 dw_i3c_common_remove(dw_i3c); 169 } 170 171 static const struct of_device_id ast2600_i3c_master_of_match[] = { 172 { .compatible = "aspeed,ast2600-i3c", }, 173 {}, 174 }; 175 MODULE_DEVICE_TABLE(of, ast2600_i3c_master_of_match); 176 177 static struct platform_driver ast2600_i3c_driver = { 178 .probe = ast2600_i3c_probe, 179 .remove_new = ast2600_i3c_remove, 180 .driver = { 181 .name = "ast2600-i3c-master", 182 .of_match_table = ast2600_i3c_master_of_match, 183 }, 184 }; 185 module_platform_driver(ast2600_i3c_driver); 186 187 MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 188 MODULE_DESCRIPTION("ASPEED AST2600 I3C driver"); 189 MODULE_LICENSE("GPL"); 190