ast2600-i3c-master.c 5.0 KB

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