Skip to content

Commit 17631e8

Browse files
sgoswami45wsakernel
authored andcommitted
i2c: designware: Add driver support for AMD NAVI GPU
The Latest AMD NAVI GPU card has an integrated Type-C controller and Designware I2C with PCI Interface. The PD controller for USB Type-C can be accessed over I2C. The client driver is part of the USB Type-C UCSI driver. Also, there exists a couple of notable IP limitations that are dealt as workarounds: - I2C transaction work on a polling mode as IP does not generate interrupt. - I2C read command sent twice to address the IP issues. - AMD NAVI GPU based products are already in the commercial market, hence some of the I2C parameters are statically programmed as they can not be part of the ACPI table. Reviewed-by: Shyam Sundar S K <[email protected]> Co-developed-by: Nehal Bakulchandra Shah <[email protected]> Signed-off-by: Nehal Bakulchandra Shah <[email protected]> Signed-off-by: Sanket Goswami <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Signed-off-by: Wolfram Sang <[email protected]>
1 parent 4a76954 commit 17631e8

File tree

4 files changed

+205
-0
lines changed

4 files changed

+205
-0
lines changed

drivers/i2c/busses/i2c-designware-common.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,9 @@ int i2c_dw_init_regmap(struct dw_i2c_dev *dev)
150150
reg = readl(dev->base + DW_IC_COMP_TYPE);
151151
i2c_dw_release_lock(dev);
152152

153+
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
154+
map_cfg.max_register = AMD_UCSI_INTR_REG;
155+
153156
if (reg == swab32(DW_IC_COMP_TYPE_VALUE)) {
154157
map_cfg.reg_read = dw_reg_read_swab;
155158
map_cfg.reg_write = dw_reg_write_swab;

drivers/i2c/busses/i2c-designware-core.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,8 +295,16 @@ struct dw_i2c_dev {
295295

296296
#define MODEL_MSCC_OCELOT BIT(8)
297297
#define MODEL_BAIKAL_BT1 BIT(9)
298+
#define MODEL_AMD_NAVI_GPU BIT(10)
298299
#define MODEL_MASK GENMASK(11, 8)
299300

301+
/*
302+
* Enable UCSI interrupt by writing 0xd at register
303+
* offset 0x474 specified in hardware specification.
304+
*/
305+
#define AMD_UCSI_INTR_REG 0x474
306+
#define AMD_UCSI_INTR_EN 0xd
307+
300308
int i2c_dw_init_regmap(struct dw_i2c_dev *dev);
301309
u32 i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset);
302310
u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset);

drivers/i2c/busses/i2c-designware-master.c

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323

2424
#include "i2c-designware-core.h"
2525

26+
#define AMD_TIMEOUT_MIN_US 25
27+
#define AMD_TIMEOUT_MAX_US 250
28+
#define AMD_MASTERCFG_MASK GENMASK(15, 0)
29+
2630
static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
2731
{
2832
/* Configure Tx/Rx FIFO threshold levels */
@@ -259,6 +263,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
259263
regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
260264
}
261265

266+
static int i2c_dw_check_stopbit(struct dw_i2c_dev *dev)
267+
{
268+
u32 val;
269+
int ret;
270+
271+
ret = regmap_read_poll_timeout(dev->map, DW_IC_INTR_STAT, val,
272+
!(val & DW_IC_INTR_STOP_DET),
273+
1100, 20000);
274+
if (ret)
275+
dev_err(dev->dev, "i2c timeout error %d\n", ret);
276+
277+
return ret;
278+
}
279+
280+
static int i2c_dw_status(struct dw_i2c_dev *dev)
281+
{
282+
int status;
283+
284+
status = i2c_dw_wait_bus_not_busy(dev);
285+
if (status)
286+
return status;
287+
288+
return i2c_dw_check_stopbit(dev);
289+
}
290+
291+
/*
292+
* Initiate and continue master read/write transaction with polling
293+
* based transfer routine afterward write messages into the Tx buffer.
294+
*/
295+
static int amd_i2c_dw_xfer_quirk(struct i2c_adapter *adap, struct i2c_msg *msgs, int num_msgs)
296+
{
297+
struct dw_i2c_dev *dev = i2c_get_adapdata(adap);
298+
int msg_wrt_idx, msg_itr_lmt, buf_len, data_idx;
299+
int cmd = 0, status;
300+
u8 *tx_buf;
301+
u32 val;
302+
303+
/*
304+
* In order to enable the interrupt for UCSI i.e. AMD NAVI GPU card,
305+
* it is mandatory to set the right value in specific register
306+
* (offset:0x474) as per the hardware IP specification.
307+
*/
308+
regmap_write(dev->map, AMD_UCSI_INTR_REG, AMD_UCSI_INTR_EN);
309+
310+
dev->msgs = msgs;
311+
dev->msgs_num = num_msgs;
312+
i2c_dw_xfer_init(dev);
313+
i2c_dw_disable_int(dev);
314+
315+
/* Initiate messages read/write transaction */
316+
for (msg_wrt_idx = 0; msg_wrt_idx < num_msgs; msg_wrt_idx++) {
317+
tx_buf = msgs[msg_wrt_idx].buf;
318+
buf_len = msgs[msg_wrt_idx].len;
319+
320+
if (!(msgs[msg_wrt_idx].flags & I2C_M_RD))
321+
regmap_write(dev->map, DW_IC_TX_TL, buf_len - 1);
322+
/*
323+
* Initiate the i2c read/write transaction of buffer length,
324+
* and poll for bus busy status. For the last message transfer,
325+
* update the command with stopbit enable.
326+
*/
327+
for (msg_itr_lmt = buf_len; msg_itr_lmt > 0; msg_itr_lmt--) {
328+
if (msg_wrt_idx == num_msgs - 1 && msg_itr_lmt == 1)
329+
cmd |= BIT(9);
330+
331+
if (msgs[msg_wrt_idx].flags & I2C_M_RD) {
332+
/* Due to hardware bug, need to write the same command twice. */
333+
regmap_write(dev->map, DW_IC_DATA_CMD, 0x100);
334+
regmap_write(dev->map, DW_IC_DATA_CMD, 0x100 | cmd);
335+
if (cmd) {
336+
regmap_write(dev->map, DW_IC_TX_TL, 2 * (buf_len - 1));
337+
regmap_write(dev->map, DW_IC_RX_TL, 2 * (buf_len - 1));
338+
/*
339+
* Need to check the stop bit. However, it cannot be
340+
* detected from the registers so we check it always
341+
* when read/write the last byte.
342+
*/
343+
status = i2c_dw_status(dev);
344+
if (status)
345+
return status;
346+
347+
for (data_idx = 0; data_idx < buf_len; data_idx++) {
348+
regmap_read(dev->map, DW_IC_DATA_CMD, &val);
349+
tx_buf[data_idx] = val;
350+
}
351+
status = i2c_dw_check_stopbit(dev);
352+
if (status)
353+
return status;
354+
}
355+
} else {
356+
regmap_write(dev->map, DW_IC_DATA_CMD, *tx_buf++ | cmd);
357+
usleep_range(AMD_TIMEOUT_MIN_US, AMD_TIMEOUT_MAX_US);
358+
}
359+
}
360+
status = i2c_dw_check_stopbit(dev);
361+
if (status)
362+
return status;
363+
}
364+
365+
return 0;
366+
}
367+
262368
/*
263369
* Initiate (and continue) low level master read/write transaction.
264370
* This function is only called from i2c_dw_isr, and pumping i2c_msg
@@ -462,6 +568,16 @@ i2c_dw_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
462568

463569
pm_runtime_get_sync(dev->dev);
464570

571+
/*
572+
* Initiate I2C message transfer when AMD NAVI GPU card is enabled,
573+
* As it is polling based transfer mechanism, which does not support
574+
* interrupt based functionalities of existing DesignWare driver.
575+
*/
576+
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
577+
ret = amd_i2c_dw_xfer_quirk(adap, msgs, num);
578+
goto done_nolock;
579+
}
580+
465581
if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
466582
ret = -ESHUTDOWN;
467583
goto done_nolock;
@@ -738,6 +854,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
738854
return 0;
739855
}
740856

857+
static int amd_i2c_adap_quirk(struct dw_i2c_dev *dev)
858+
{
859+
struct i2c_adapter *adap = &dev->adapter;
860+
int ret;
861+
862+
pm_runtime_get_noresume(dev->dev);
863+
ret = i2c_add_numbered_adapter(adap);
864+
if (ret)
865+
dev_err(dev->dev, "Failed to add adapter: %d\n", ret);
866+
pm_runtime_put_noidle(dev->dev);
867+
868+
return ret;
869+
}
870+
741871
int i2c_dw_probe_master(struct dw_i2c_dev *dev)
742872
{
743873
struct i2c_adapter *adap = &dev->adapter;
@@ -774,6 +904,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
774904
adap->dev.parent = dev->dev;
775905
i2c_set_adapdata(adap, dev);
776906

907+
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU)
908+
return amd_i2c_adap_quirk(dev);
909+
777910
if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
778911
irq_flags = IRQF_NO_SUSPEND;
779912
} else {

drivers/i2c/busses/i2c-designware-pcidrv.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
#include "i2c-designware-core.h"
2727

2828
#define DRIVER_NAME "i2c-designware-pci"
29+
#define AMD_CLK_RATE_HZ 100000
2930

3031
enum dw_pci_ctl_id_t {
3132
medfield,
@@ -34,6 +35,7 @@ enum dw_pci_ctl_id_t {
3435
cherrytrail,
3536
haswell,
3637
elkhartlake,
38+
navi_amd,
3739
};
3840

3941
struct dw_scl_sda_cfg {
@@ -78,11 +80,23 @@ static struct dw_scl_sda_cfg hsw_config = {
7880
.sda_hold = 0x9,
7981
};
8082

83+
/* NAVI-AMD HCNT/LCNT/SDA hold time */
84+
static struct dw_scl_sda_cfg navi_amd_config = {
85+
.ss_hcnt = 0x1ae,
86+
.ss_lcnt = 0x23a,
87+
.sda_hold = 0x9,
88+
};
89+
8190
static u32 mfld_get_clk_rate_khz(struct dw_i2c_dev *dev)
8291
{
8392
return 25000;
8493
}
8594

95+
static u32 navi_amd_get_clk_rate_khz(struct dw_i2c_dev *dev)
96+
{
97+
return AMD_CLK_RATE_HZ;
98+
}
99+
86100
static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
87101
{
88102
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
@@ -104,6 +118,35 @@ static int mfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
104118
return -ENODEV;
105119
}
106120

121+
/*
122+
* TODO find a better way how to deduplicate instantiation
123+
* of USB PD slave device from nVidia GPU driver.
124+
*/
125+
static int navi_amd_register_client(struct dw_i2c_dev *dev)
126+
{
127+
struct i2c_board_info info;
128+
129+
memset(&info, 0, sizeof(struct i2c_board_info));
130+
strscpy(info.type, "ccgx-ucsi", I2C_NAME_SIZE);
131+
info.addr = 0x08;
132+
info.irq = dev->irq;
133+
134+
dev->slave = i2c_new_client_device(&dev->adapter, &info);
135+
if (!dev->slave)
136+
return -ENODEV;
137+
138+
return 0;
139+
}
140+
141+
static int navi_amd_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
142+
{
143+
struct dw_i2c_dev *dev = dev_get_drvdata(&pdev->dev);
144+
145+
dev->flags |= MODEL_AMD_NAVI_GPU;
146+
dev->timings.bus_freq_hz = I2C_MAX_STANDARD_MODE_FREQ;
147+
return 0;
148+
}
149+
107150
static int mrfld_setup(struct pci_dev *pdev, struct dw_pci_controller *c)
108151
{
109152
/*
@@ -155,6 +198,12 @@ static struct dw_pci_controller dw_pci_controllers[] = {
155198
.bus_num = -1,
156199
.get_clk_rate_khz = ehl_get_clk_rate_khz,
157200
},
201+
[navi_amd] = {
202+
.bus_num = -1,
203+
.scl_sda_cfg = &navi_amd_config,
204+
.setup = navi_amd_setup,
205+
.get_clk_rate_khz = navi_amd_get_clk_rate_khz,
206+
},
158207
};
159208

160209
#ifdef CONFIG_PM
@@ -274,6 +323,14 @@ static int i2c_dw_pci_probe(struct pci_dev *pdev,
274323
return r;
275324
}
276325

326+
if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) {
327+
r = navi_amd_register_client(dev);
328+
if (r) {
329+
dev_err(dev->dev, "register client failed with %d\n", r);
330+
return r;
331+
}
332+
}
333+
277334
pm_runtime_set_autosuspend_delay(&pdev->dev, 1000);
278335
pm_runtime_use_autosuspend(&pdev->dev);
279336
pm_runtime_put_autosuspend(&pdev->dev);
@@ -337,6 +394,10 @@ static const struct pci_device_id i2_designware_pci_ids[] = {
337394
{ PCI_VDEVICE(INTEL, 0x4bbe), elkhartlake },
338395
{ PCI_VDEVICE(INTEL, 0x4bbf), elkhartlake },
339396
{ PCI_VDEVICE(INTEL, 0x4bc0), elkhartlake },
397+
{ PCI_VDEVICE(ATI, 0x7314), navi_amd },
398+
{ PCI_VDEVICE(ATI, 0x73a4), navi_amd },
399+
{ PCI_VDEVICE(ATI, 0x73e4), navi_amd },
400+
{ PCI_VDEVICE(ATI, 0x73c4), navi_amd },
340401
{ 0,}
341402
};
342403
MODULE_DEVICE_TABLE(pci, i2_designware_pci_ids);

0 commit comments

Comments
 (0)