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

i3c: ast2600: Add AST2600 platform-specific driver

Now that we have platform-specific infrastructure for the dw i3c driver,
add platform support for the ASPEED AST2600 SoC.

The AST2600 has a small set of "i3c global" registers, providing
platform-level i3c configuration outside of the i3c core.

For the ast2600, we need a couple of extra setup operations:

- on probe: find the i3c global register set and parse the SDA pullup
resistor values

- on init: set the pullups accordingly, and set the i3c instance IDs

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-4-jk@codeconstruct.com.au
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>

authored by

Jeremy Kerr and committed by
Alexandre Belloni
58445641 21203e09

+189
+6
MAINTAINERS
··· 9713 9713 F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml 9714 9714 F: drivers/i3c/master/dw* 9715 9715 9716 + I3C DRIVER FOR ASPEED AST2600 9717 + M: Jeremy Kerr <jk@codeconstruct.com.au> 9718 + S: Maintained 9719 + F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml 9720 + F: drivers/i3c/master/ast2600-i3c-master.c 9721 + 9716 9722 I3C SUBSYSTEM 9717 9723 M: Alexandre Belloni <alexandre.belloni@bootlin.com> 9718 9724 L: linux-i3c@lists.infradead.org (moderated for non-subscribers)
+14
drivers/i3c/master/Kconfig
··· 22 22 This driver can also be built as a module. If so, the module 23 23 will be called dw-i3c-master. 24 24 25 + config AST2600_I3C_MASTER 26 + tristate "ASPEED AST2600 I3C master driver" 27 + depends on DW_I3C_MASTER 28 + depends on ARCH_ASPEED || COMPILE_TEST 29 + select MFD_SYSCON 30 + help 31 + Support for ASPEED AST2600 I3C Controller. 32 + 33 + This hardware is an instance of the DW I3C controller; this 34 + driver adds platform- specific support for AST2600 hardware. 35 + 36 + This driver can also be built as a module. If so, the module 37 + will be called ast2600-i3c-master. 38 + 25 39 config SVC_I3C_MASTER 26 40 tristate "Silvaco I3C Dual-Role Master driver" 27 41 depends on I3C
+1
drivers/i3c/master/Makefile
··· 1 1 # SPDX-License-Identifier: GPL-2.0-only 2 2 obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o 3 3 obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o 4 + obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o 4 5 obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o 5 6 obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/
+168
drivers/i3c/master/ast2600-i3c-master.c
··· 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 + 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) 36 + 37 + #define AST2600_DEFAULT_SDA_PULLUP_OHMS 2000 38 + 39 + struct ast2600_i3c { 40 + struct dw_i3c_master dw; 41 + struct regmap *global_regs; 42 + unsigned int global_idx; 43 + unsigned int sda_pullup; 44 + }; 45 + 46 + static struct ast2600_i3c *to_ast2600_i3c(struct dw_i3c_master *dw) 47 + { 48 + return container_of(dw, struct ast2600_i3c, dw); 49 + } 50 + 51 + static int ast2600_i3c_pullup_to_reg(unsigned int ohms, u32 *regp) 52 + { 53 + u32 reg; 54 + 55 + switch (ohms) { 56 + case 2000: 57 + reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_2K; 58 + break; 59 + case 750: 60 + reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_750; 61 + break; 62 + case 545: 63 + reg = AST2600_I3CG_REG0_SDA_PULLUP_EN_2K | 64 + AST2600_I3CG_REG0_SDA_PULLUP_EN_750; 65 + break; 66 + default: 67 + return -EINVAL; 68 + } 69 + 70 + if (regp) 71 + *regp = reg; 72 + 73 + return 0; 74 + } 75 + 76 + static int ast2600_i3c_init(struct dw_i3c_master *dw) 77 + { 78 + struct ast2600_i3c *i3c = to_ast2600_i3c(dw); 79 + u32 reg = 0; 80 + int rc; 81 + 82 + /* reg0: set SDA pullup values */ 83 + rc = ast2600_i3c_pullup_to_reg(i3c->sda_pullup, &reg); 84 + if (rc) 85 + return rc; 86 + 87 + rc = regmap_write(i3c->global_regs, 88 + AST2600_I3CG_REG0(i3c->global_idx), reg); 89 + if (rc) 90 + return rc; 91 + 92 + /* reg1: set up the instance id, but leave everything else disabled, 93 + * as it's all for client mode 94 + */ 95 + reg = AST2600_I3CG_REG1_INST_ID(i3c->global_idx); 96 + rc = regmap_write(i3c->global_regs, 97 + AST2600_I3CG_REG1(i3c->global_idx), reg); 98 + 99 + return rc; 100 + } 101 + 102 + const struct dw_i3c_platform_ops ast2600_i3c_ops = { 103 + .init = ast2600_i3c_init, 104 + }; 105 + 106 + static int ast2600_i3c_probe(struct platform_device *pdev) 107 + { 108 + struct device_node *np = pdev->dev.of_node; 109 + struct of_phandle_args gspec; 110 + struct ast2600_i3c *i3c; 111 + int rc; 112 + 113 + i3c = devm_kzalloc(&pdev->dev, sizeof(*i3c), GFP_KERNEL); 114 + if (!i3c) 115 + return -ENOMEM; 116 + 117 + rc = of_parse_phandle_with_fixed_args(np, "aspeed,global-regs", 1, 0, 118 + &gspec); 119 + if (rc) 120 + return -ENODEV; 121 + 122 + i3c->global_regs = syscon_node_to_regmap(gspec.np); 123 + of_node_put(gspec.np); 124 + 125 + if (IS_ERR(i3c->global_regs)) 126 + return PTR_ERR(i3c->global_regs); 127 + 128 + i3c->global_idx = gspec.args[0]; 129 + 130 + rc = of_property_read_u32(np, "sda-pullup-ohms", &i3c->sda_pullup); 131 + if (rc) 132 + i3c->sda_pullup = AST2600_DEFAULT_SDA_PULLUP_OHMS; 133 + 134 + rc = ast2600_i3c_pullup_to_reg(i3c->sda_pullup, NULL); 135 + if (rc) 136 + dev_err(&pdev->dev, "invalid sda-pullup value %d\n", 137 + i3c->sda_pullup); 138 + 139 + i3c->dw.platform_ops = &ast2600_i3c_ops; 140 + return dw_i3c_common_probe(&i3c->dw, pdev); 141 + } 142 + 143 + static void ast2600_i3c_remove(struct platform_device *pdev) 144 + { 145 + struct dw_i3c_master *dw_i3c = platform_get_drvdata(pdev); 146 + 147 + dw_i3c_common_remove(dw_i3c); 148 + } 149 + 150 + static const struct of_device_id ast2600_i3c_master_of_match[] = { 151 + { .compatible = "aspeed,ast2600-i3c", }, 152 + {}, 153 + }; 154 + MODULE_DEVICE_TABLE(of, ast2600_i3c_master_of_match); 155 + 156 + static struct platform_driver ast2600_i3c_driver = { 157 + .probe = ast2600_i3c_probe, 158 + .remove_new = ast2600_i3c_remove, 159 + .driver = { 160 + .name = "ast2600-i3c-master", 161 + .of_match_table = ast2600_i3c_master_of_match, 162 + }, 163 + }; 164 + module_platform_driver(ast2600_i3c_driver); 165 + 166 + MODULE_AUTHOR("Jeremy Kerr <jk@codeconstruct.com.au>"); 167 + MODULE_DESCRIPTION("ASPEED AST2600 I3C driver"); 168 + MODULE_LICENSE("GPL");