|
23 | 23 |
|
24 | 24 | #include "i2c-designware-core.h"
|
25 | 25 |
|
| 26 | +#define AMD_TIMEOUT_MIN_US 25 |
| 27 | +#define AMD_TIMEOUT_MAX_US 250 |
| 28 | +#define AMD_MASTERCFG_MASK GENMASK(15, 0) |
| 29 | + |
26 | 30 | static void i2c_dw_configure_fifo_master(struct dw_i2c_dev *dev)
|
27 | 31 | {
|
28 | 32 | /* Configure Tx/Rx FIFO threshold levels */
|
@@ -259,6 +263,108 @@ static void i2c_dw_xfer_init(struct dw_i2c_dev *dev)
|
259 | 263 | regmap_write(dev->map, DW_IC_INTR_MASK, DW_IC_INTR_MASTER_MASK);
|
260 | 264 | }
|
261 | 265 |
|
| 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 | + |
262 | 368 | /*
|
263 | 369 | * Initiate (and continue) low level master read/write transaction.
|
264 | 370 | * 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)
|
462 | 568 |
|
463 | 569 | pm_runtime_get_sync(dev->dev);
|
464 | 570 |
|
| 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 | + |
465 | 581 | if (dev_WARN_ONCE(dev->dev, dev->suspended, "Transfer while suspended\n")) {
|
466 | 582 | ret = -ESHUTDOWN;
|
467 | 583 | goto done_nolock;
|
@@ -738,6 +854,20 @@ static int i2c_dw_init_recovery_info(struct dw_i2c_dev *dev)
|
738 | 854 | return 0;
|
739 | 855 | }
|
740 | 856 |
|
| 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 | + |
741 | 871 | int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
742 | 872 | {
|
743 | 873 | struct i2c_adapter *adap = &dev->adapter;
|
@@ -774,6 +904,9 @@ int i2c_dw_probe_master(struct dw_i2c_dev *dev)
|
774 | 904 | adap->dev.parent = dev->dev;
|
775 | 905 | i2c_set_adapdata(adap, dev);
|
776 | 906 |
|
| 907 | + if ((dev->flags & MODEL_MASK) == MODEL_AMD_NAVI_GPU) |
| 908 | + return amd_i2c_adap_quirk(dev); |
| 909 | + |
777 | 910 | if (dev->flags & ACCESS_NO_IRQ_SUSPEND) {
|
778 | 911 | irq_flags = IRQF_NO_SUSPEND;
|
779 | 912 | } else {
|
|
0 commit comments