Skip to content

Commit e0906f1

Browse files
committed
Merge tag 'i3c/for-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
Pull i3c updates from Alexandre Belloni: "Subsystem: - OF alias bus numbering - convert to platform remove callback returning void New driver: - AST2600 controller, based on Synopsys DesignWare IP Driver update: - dw: add infrastructure to support different platform integrations" * tag 'i3c/for-6.4' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux: i3c: ast2600: set variable ast2600_i3c_ops storage-class-specifier to static i3c: ast2600: fix register setting for 545 ohm pullups i3c: ast2600: enable IBI support i3c: dw: Add a platform facility for IBI PEC workarounds i3c: dw: Add support for in-band interrupts i3c: dw: Turn DAT array entry into a struct i3c: dw: Create a generic fifo read function i3c: Allow OF-alias-based persistent bus numbering i3c: ast2600: Add AST2600 platform-specific driver dt-bindings: i3c: Add AST2600 i3c controller i3c: dw: Add infrastructure for platform-specific implementations i3c: dw: use bus mode rather than device reg for conditional tCAS setting i3c: dw: Return the length from a read priv_xfer i3c: svc: Convert to platform remove callback returning void i3c: mipi-i3c-hci: Convert to platform remove callback returning void i3c: cdns: Convert to platform remove callback returning void i3c: dw: Convert to platform remove callback returning void i3c: Make i3c_master_unregister() return void i3c: dw: drop of_match_ptr for ID table i3c: Correct reference to the I²C device data type
2 parents 06936aa + 6b496a9 commit e0906f1

12 files changed

Lines changed: 779 additions & 91 deletions

File tree

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause
2+
%YAML 1.2
3+
---
4+
$id: http://devicetree.org/schemas/i3c/aspeed,ast2600-i3c.yaml#
5+
$schema: http://devicetree.org/meta-schemas/core.yaml#
6+
7+
title: ASPEED AST2600 i3c controller
8+
9+
maintainers:
10+
- Jeremy Kerr <jk@codeconstruct.com.au>
11+
12+
allOf:
13+
- $ref: i3c.yaml#
14+
15+
properties:
16+
compatible:
17+
const: aspeed,ast2600-i3c
18+
19+
reg:
20+
maxItems: 1
21+
22+
clocks:
23+
maxItems: 1
24+
25+
resets:
26+
maxItems: 1
27+
28+
interrupts:
29+
maxItems: 1
30+
31+
sda-pullup-ohms:
32+
enum: [545, 750, 2000]
33+
default: 2000
34+
description: |
35+
Value to configure SDA pullup resistor, in Ohms.
36+
37+
aspeed,global-regs:
38+
$ref: /schemas/types.yaml#/definitions/phandle-array
39+
items:
40+
- items:
41+
- description: phandle to i3c global register syscon node
42+
- description: index of this i3c controller in the global register set
43+
description: |
44+
A (phandle, controller index) reference to the i3c global register set
45+
used for this device.
46+
47+
required:
48+
- compatible
49+
- reg
50+
- clocks
51+
- interrupts
52+
- aspeed,global-regs
53+
54+
unevaluatedProperties: false
55+
56+
examples:
57+
- |
58+
#include <dt-bindings/interrupt-controller/arm-gic.h>
59+
60+
i3c-master@2000 {
61+
compatible = "aspeed,ast2600-i3c";
62+
reg = <0x2000 0x1000>;
63+
#address-cells = <3>;
64+
#size-cells = <0>;
65+
clocks = <&syscon 0>;
66+
resets = <&syscon 0>;
67+
aspeed,global-regs = <&i3c_global 0>;
68+
pinctrl-names = "default";
69+
pinctrl-0 = <&pinctrl_i3c1_default>;
70+
interrupts = <GIC_SPI 102 IRQ_TYPE_LEVEL_HIGH>;
71+
};
72+
...

MAINTAINERS

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9778,6 +9778,12 @@ S: Orphan
97789778
F: Documentation/devicetree/bindings/i3c/snps,dw-i3c-master.yaml
97799779
F: drivers/i3c/master/dw*
97809780

9781+
I3C DRIVER FOR ASPEED AST2600
9782+
M: Jeremy Kerr <jk@codeconstruct.com.au>
9783+
S: Maintained
9784+
F: Documentation/devicetree/bindings/i3c/aspeed,ast2600-i3c.yaml
9785+
F: drivers/i3c/master/ast2600-i3c-master.c
9786+
97819787
I3C SUBSYSTEM
97829788
M: Alexandre Belloni <alexandre.belloni@bootlin.com>
97839789
L: linux-i3c@lists.infradead.org (moderated for non-subscribers)

drivers/i3c/master.c

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
static DEFINE_IDR(i3c_bus_idr);
2323
static DEFINE_MUTEX(i3c_core_lock);
24+
static int __i3c_first_dynamic_bus_num;
2425

2526
/**
2627
* i3c_bus_maintenance_lock - Lock the bus for a maintenance operation
@@ -419,18 +420,29 @@ static void i3c_bus_cleanup(struct i3c_bus *i3cbus)
419420
mutex_unlock(&i3c_core_lock);
420421
}
421422

422-
static int i3c_bus_init(struct i3c_bus *i3cbus)
423+
static int i3c_bus_init(struct i3c_bus *i3cbus, struct device_node *np)
423424
{
424-
int ret;
425+
int ret, start, end, id = -1;
425426

426427
init_rwsem(&i3cbus->lock);
427428
INIT_LIST_HEAD(&i3cbus->devs.i2c);
428429
INIT_LIST_HEAD(&i3cbus->devs.i3c);
429430
i3c_bus_init_addrslots(i3cbus);
430431
i3cbus->mode = I3C_BUS_MODE_PURE;
431432

433+
if (np)
434+
id = of_alias_get_id(np, "i3c");
435+
432436
mutex_lock(&i3c_core_lock);
433-
ret = idr_alloc(&i3c_bus_idr, i3cbus, 0, 0, GFP_KERNEL);
437+
if (id >= 0) {
438+
start = id;
439+
end = start + 1;
440+
} else {
441+
start = __i3c_first_dynamic_bus_num;
442+
end = 0;
443+
}
444+
445+
ret = idr_alloc(&i3c_bus_idr, i3cbus, start, end, GFP_KERNEL);
434446
mutex_unlock(&i3c_core_lock);
435447

436448
if (ret < 0)
@@ -2606,7 +2618,7 @@ int i3c_master_register(struct i3c_master_controller *master,
26062618
INIT_LIST_HEAD(&master->boardinfo.i2c);
26072619
INIT_LIST_HEAD(&master->boardinfo.i3c);
26082620

2609-
ret = i3c_bus_init(i3cbus);
2621+
ret = i3c_bus_init(i3cbus, master->dev.of_node);
26102622
if (ret)
26112623
return ret;
26122624

@@ -2695,17 +2707,13 @@ EXPORT_SYMBOL_GPL(i3c_master_register);
26952707
* @master: master used to send frames on the bus
26962708
*
26972709
* Basically undo everything done in i3c_master_register().
2698-
*
2699-
* Return: 0 in case of success, a negative error code otherwise.
27002710
*/
2701-
int i3c_master_unregister(struct i3c_master_controller *master)
2711+
void i3c_master_unregister(struct i3c_master_controller *master)
27022712
{
27032713
i3c_master_i2c_adapter_cleanup(master);
27042714
i3c_master_unregister_i3c_devs(master);
27052715
i3c_master_bus_cleanup(master);
27062716
device_unregister(&master->dev);
2707-
2708-
return 0;
27092717
}
27102718
EXPORT_SYMBOL_GPL(i3c_master_unregister);
27112719

@@ -2834,8 +2842,16 @@ void i3c_dev_free_ibi_locked(struct i3c_dev_desc *dev)
28342842

28352843
static int __init i3c_init(void)
28362844
{
2837-
int res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
2845+
int res;
2846+
2847+
res = of_alias_get_highest_id("i3c");
2848+
if (res >= 0) {
2849+
mutex_lock(&i3c_core_lock);
2850+
__i3c_first_dynamic_bus_num = res + 1;
2851+
mutex_unlock(&i3c_core_lock);
2852+
}
28382853

2854+
res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);
28392855
if (res)
28402856
return res;
28412857

drivers/i3c/master/Kconfig

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,20 @@ config DW_I3C_MASTER
2222
This driver can also be built as a module. If so, the module
2323
will be called dw-i3c-master.
2424

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+
2539
config SVC_I3C_MASTER
2640
tristate "Silvaco I3C Dual-Role Master driver"
2741
depends on I3C

drivers/i3c/master/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# SPDX-License-Identifier: GPL-2.0-only
22
obj-$(CONFIG_CDNS_I3C_MASTER) += i3c-master-cdns.o
33
obj-$(CONFIG_DW_I3C_MASTER) += dw-i3c-master.o
4+
obj-$(CONFIG_AST2600_I3C_MASTER) += ast2600-i3c-master.o
45
obj-$(CONFIG_SVC_I3C_MASTER) += svc-i3c-master.o
56
obj-$(CONFIG_MIPI_I3C_HCI) += mipi-i3c-hci/
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
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, &reg);
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");

0 commit comments

Comments
 (0)