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

Configure Feed

Select the types of activity you want to include in your feed.

i2c: spacemit: introduce pio for k1

This patch introduces I2C PIO functionality for the Spacemit K1 SoC,
enabling the use of I2C in atomic context.

When i2c xfer_atomic is invoked, use_pio is set accordingly.

Since an atomic context is required, all interrupts are disabled when
operating in PIO mode. Even with interrupts disabled, the bits in the
ISR (Interrupt Status Register) will still be set, so error handling can
be performed by polling the relevant status bits in the ISR.

Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Tested-by: Aurelien Jarno <aurelien@aurel32.net>
Reviewed-by: Aurelien Jarno <aurelien@aurel32.net>
Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
Link: https://lore.kernel.org/r/20260207-b4-k3-i2c-pio-v7-2-626942d94d91@linux.spacemit.com

authored by

Troy Mitchell and committed by
Andi Shyti
5dd75dac 5b74da8e

+226 -70
+226 -70
drivers/i2c/busses/i2c-k1.c
··· 98 98 99 99 #define SPACEMIT_BUS_RESET_CLK_CNT_MAX 9 100 100 101 + #define SPACEMIT_WAIT_TIMEOUT 1000 /* ms */ 102 + #define SPACEMIT_POLL_TIMEOUT 1000 /* us */ 103 + #define SPACEMIT_POLL_INTERVAL 30 /* us */ 104 + 101 105 enum spacemit_i2c_state { 102 106 SPACEMIT_STATE_IDLE, 103 107 SPACEMIT_STATE_START, ··· 130 126 131 127 enum spacemit_i2c_state state; 132 128 bool read; 129 + bool use_pio; 133 130 struct completion complete; 134 131 u32 status; 135 132 }; ··· 177 172 return i2c->status & SPACEMIT_SR_ACKNAK ? -ENXIO : -EIO; 178 173 } 179 174 175 + static inline void spacemit_i2c_delay(struct spacemit_i2c_dev *i2c, unsigned int us) 176 + { 177 + if (i2c->use_pio) 178 + udelay(us); 179 + else 180 + fsleep(us); 181 + } 182 + 180 183 static void spacemit_i2c_conditionally_reset_bus(struct spacemit_i2c_dev *i2c) 181 184 { 182 185 u32 status; ··· 196 183 return; 197 184 198 185 spacemit_i2c_reset(i2c); 199 - usleep_range(10, 20); 186 + 187 + spacemit_i2c_delay(i2c, 10); 200 188 201 189 for (clk_cnt = 0; clk_cnt < SPACEMIT_BUS_RESET_CLK_CNT_MAX; clk_cnt++) { 202 190 status = readl(i2c->base + SPACEMIT_IBMR); ··· 226 212 if (!(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB))) 227 213 return 0; 228 214 229 - ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, 230 - val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), 231 - 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); 215 + if (i2c->use_pio) 216 + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, 217 + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), 218 + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); 219 + else 220 + ret = readl_poll_timeout(i2c->base + SPACEMIT_ISR, 221 + val, !(val & (SPACEMIT_SR_UB | SPACEMIT_SR_IBB)), 222 + 1500, SPACEMIT_I2C_BUS_BUSY_TIMEOUT); 223 + 232 224 if (ret) 233 225 spacemit_i2c_reset(i2c); 234 226 ··· 246 226 /* in case bus is not released after transfer completes */ 247 227 if (readl(i2c->base + SPACEMIT_ISR) & SPACEMIT_SR_EBB) { 248 228 spacemit_i2c_conditionally_reset_bus(i2c); 249 - usleep_range(90, 150); 229 + spacemit_i2c_delay(i2c, 90); 250 230 } 251 231 } 252 232 ··· 258 238 259 239 static void spacemit_i2c_init(struct spacemit_i2c_dev *i2c) 260 240 { 261 - u32 val; 241 + u32 val = 0; 262 242 263 - /* 264 - * Unmask interrupt bits for all xfer mode: 265 - * bus error, arbitration loss detected. 266 - * For transaction complete signal, we use master stop 267 - * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. 268 - */ 269 - val = SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; 243 + if (!i2c->use_pio) { 244 + /* 245 + * Enable interrupt bits for all xfer mode: 246 + * bus error, arbitration loss detected. 247 + */ 248 + val |= SPACEMIT_CR_BEIE | SPACEMIT_CR_ALDIE; 270 249 271 - /* 272 - * Unmask interrupt bits for interrupt xfer mode: 273 - * When IDBR receives a byte, an interrupt is triggered. 274 - * 275 - * For the tx empty interrupt, it will be enabled in the 276 - * i2c_start function. 277 - * Otherwise, it will cause an erroneous empty interrupt before i2c_start. 278 - */ 279 - val |= SPACEMIT_CR_DRFIE; 250 + /* 251 + * Unmask interrupt bits for interrupt xfer mode: 252 + * When IDBR receives a byte, an interrupt is triggered. 253 + * 254 + * For the tx empty interrupt, it will be enabled in the 255 + * i2c_start(). 256 + * We don't want a TX empty interrupt until we start 257 + * a transfer in i2c_start(). 258 + */ 259 + val |= SPACEMIT_CR_DRFIE; 260 + 261 + /* 262 + * Enable master stop interrupt bit. 263 + * For transaction complete signal, we use master stop 264 + * interrupt, so we don't need to unmask SPACEMIT_CR_TXDONEIE. 265 + */ 266 + val |= SPACEMIT_CR_MSDIE; 267 + } 280 268 281 269 if (i2c->clock_freq == SPACEMIT_I2C_MAX_FAST_MODE_FREQ) 282 270 val |= SPACEMIT_CR_MODE_FAST; ··· 296 268 val |= SPACEMIT_CR_SCLE; 297 269 298 270 /* enable master stop detected */ 299 - val |= SPACEMIT_CR_MSDE | SPACEMIT_CR_MSDIE; 271 + val |= SPACEMIT_CR_MSDE; 300 272 301 273 writel(val, i2c->base + SPACEMIT_ICR); 302 274 ··· 329 301 /* send start pulse */ 330 302 val = readl(i2c->base + SPACEMIT_ICR); 331 303 val &= ~SPACEMIT_CR_STOP; 332 - val |= SPACEMIT_CR_START | SPACEMIT_CR_TB | SPACEMIT_CR_DTEIE; 304 + val |= SPACEMIT_CR_START | SPACEMIT_CR_TB; 305 + 306 + /* Enable the TX empty interrupt */ 307 + if (!i2c->use_pio) 308 + val |= SPACEMIT_CR_DTEIE; 309 + 333 310 writel(val, i2c->base + SPACEMIT_ICR); 334 311 } 335 312 ··· 349 316 return !i2c->unprocessed; 350 317 } 351 318 319 + static inline void spacemit_i2c_complete(struct spacemit_i2c_dev *i2c) 320 + { 321 + /* SPACEMIT_STATE_IDLE avoids triggering the next byte */ 322 + i2c->state = SPACEMIT_STATE_IDLE; 323 + 324 + if (i2c->use_pio) 325 + return; 326 + 327 + complete(&i2c->complete); 328 + } 329 + 352 330 static void spacemit_i2c_handle_write(struct spacemit_i2c_dev *i2c) 353 331 { 332 + /* If there's no space in the IDBR, we're done */ 333 + if (!(i2c->status & SPACEMIT_SR_ITE)) 334 + return; 335 + 354 336 /* if transfer completes, SPACEMIT_ISR will handle it */ 355 337 if (i2c->status & SPACEMIT_SR_MSD) 356 338 return; ··· 376 328 return; 377 329 } 378 330 379 - /* SPACEMIT_STATE_IDLE avoids trigger next byte */ 380 - i2c->state = SPACEMIT_STATE_IDLE; 381 - complete(&i2c->complete); 331 + spacemit_i2c_complete(i2c); 382 332 } 383 333 384 334 static void spacemit_i2c_handle_read(struct spacemit_i2c_dev *i2c) 385 335 { 336 + /* If there's nothing in the IDBR, we're done */ 337 + if (!(i2c->status & SPACEMIT_SR_IRF)) 338 + return; 339 + 386 340 if (i2c->unprocessed) { 387 341 *i2c->msg_buf++ = readl(i2c->base + SPACEMIT_IDBR); 388 342 i2c->unprocessed--; 343 + return; 389 344 } 390 345 391 346 /* if transfer completes, SPACEMIT_ISR will handle it */ ··· 399 348 if (i2c->unprocessed) 400 349 return; 401 350 402 - /* SPACEMIT_STATE_IDLE avoids trigger next byte */ 403 - i2c->state = SPACEMIT_STATE_IDLE; 404 - complete(&i2c->complete); 351 + spacemit_i2c_complete(i2c); 405 352 } 406 353 407 354 static void spacemit_i2c_handle_start(struct spacemit_i2c_dev *i2c) ··· 433 384 434 385 spacemit_i2c_clear_int_status(i2c, SPACEMIT_I2C_INT_STATUS_MASK); 435 386 436 - i2c->state = SPACEMIT_STATE_IDLE; 437 - complete(&i2c->complete); 387 + spacemit_i2c_complete(i2c); 388 + } 389 + 390 + static void spacemit_i2c_handle_state(struct spacemit_i2c_dev *i2c) 391 + { 392 + u32 val; 393 + 394 + if (i2c->status & SPACEMIT_SR_ERR) 395 + goto err_out; 396 + 397 + switch (i2c->state) { 398 + case SPACEMIT_STATE_START: 399 + spacemit_i2c_handle_start(i2c); 400 + break; 401 + case SPACEMIT_STATE_READ: 402 + spacemit_i2c_handle_read(i2c); 403 + break; 404 + case SPACEMIT_STATE_WRITE: 405 + spacemit_i2c_handle_write(i2c); 406 + break; 407 + default: 408 + break; 409 + } 410 + 411 + if (i2c->state != SPACEMIT_STATE_IDLE) { 412 + val = readl(i2c->base + SPACEMIT_ICR); 413 + val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | 414 + SPACEMIT_CR_STOP | SPACEMIT_CR_START); 415 + val |= SPACEMIT_CR_TB; 416 + if (!i2c->use_pio) 417 + val |= SPACEMIT_CR_ALDIE; 418 + 419 + if (spacemit_i2c_is_last_msg(i2c)) { 420 + /* trigger next byte with stop */ 421 + val |= SPACEMIT_CR_STOP; 422 + 423 + if (i2c->read) 424 + val |= SPACEMIT_CR_ACKNAK; 425 + } 426 + writel(val, i2c->base + SPACEMIT_ICR); 427 + } 428 + 429 + err_out: 430 + spacemit_i2c_err_check(i2c); 431 + } 432 + 433 + /* 434 + * In PIO mode, this function is used as a replacement for 435 + * wait_for_completion_timeout(), whose return value indicates 436 + * the remaining time. 437 + * 438 + * We do not have a meaningful remaining-time value here, so 439 + * return a non-zero value on success to indicate "not timed out". 440 + * Returning 1 ensures callers treating the return value as 441 + * time_left will not incorrectly report a timeout. 442 + */ 443 + static int spacemit_i2c_wait_pio_xfer(struct spacemit_i2c_dev *i2c) 444 + { 445 + u32 mask, msec = jiffies_to_msecs(i2c->adapt.timeout); 446 + ktime_t timeout = ktime_add_ms(ktime_get(), msec); 447 + int ret; 448 + 449 + mask = SPACEMIT_SR_IRF | SPACEMIT_SR_ITE; 450 + 451 + do { 452 + i2c->status = readl(i2c->base + SPACEMIT_ISR); 453 + 454 + spacemit_i2c_clear_int_status(i2c, i2c->status); 455 + 456 + if (i2c->status & mask) 457 + spacemit_i2c_handle_state(i2c); 458 + else 459 + udelay(SPACEMIT_POLL_INTERVAL); 460 + } while (i2c->unprocessed && ktime_compare(ktime_get(), timeout) < 0); 461 + 462 + if (i2c->unprocessed) 463 + return 0; 464 + 465 + if (i2c->read) 466 + return 1; 467 + 468 + /* 469 + * If this is the last byte to write of the current message, 470 + * we have to wait here. Otherwise, control will proceed directly 471 + * to start(), which would overwrite the current data. 472 + */ 473 + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, 474 + i2c->status, i2c->status & SPACEMIT_SR_ITE, 475 + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); 476 + if (ret) 477 + return 0; 478 + 479 + /* 480 + * For writes: in interrupt mode, an ITE (write-empty) interrupt is triggered 481 + * after the last byte, and the MSD-related handling takes place there. 482 + * In PIO mode, however, we need to explicitly call err_check() to emulate this 483 + * step, otherwise the next transfer will fail. 484 + */ 485 + if (i2c->msg_idx == i2c->msg_num - 1) { 486 + mask = SPACEMIT_SR_MSD | SPACEMIT_SR_ERR; 487 + /* 488 + * In some cases, MSD may not arrive immediately; 489 + * wait here to handle that. 490 + */ 491 + ret = readl_poll_timeout_atomic(i2c->base + SPACEMIT_ISR, 492 + i2c->status, i2c->status & mask, 493 + SPACEMIT_POLL_INTERVAL, SPACEMIT_POLL_TIMEOUT); 494 + if (ret) 495 + return 0; 496 + 497 + spacemit_i2c_err_check(i2c); 498 + } 499 + 500 + return 1; 501 + } 502 + 503 + static int spacemit_i2c_wait_xfer_complete(struct spacemit_i2c_dev *i2c) 504 + { 505 + if (i2c->use_pio) 506 + return spacemit_i2c_wait_pio_xfer(i2c); 507 + 508 + return wait_for_completion_timeout(&i2c->complete, 509 + i2c->adapt.timeout); 438 510 } 439 511 440 512 static int spacemit_i2c_xfer_msg(struct spacemit_i2c_dev *i2c) ··· 573 403 574 404 spacemit_i2c_start(i2c); 575 405 576 - time_left = wait_for_completion_timeout(&i2c->complete, 577 - i2c->adapt.timeout); 406 + time_left = spacemit_i2c_wait_xfer_complete(i2c); 407 + 578 408 if (!time_left) { 579 409 dev_err(i2c->dev, "msg completion timeout\n"); 580 410 spacemit_i2c_conditionally_reset_bus(i2c); ··· 592 422 static irqreturn_t spacemit_i2c_irq_handler(int irq, void *devid) 593 423 { 594 424 struct spacemit_i2c_dev *i2c = devid; 595 - u32 status, val; 425 + u32 status; 596 426 597 427 status = readl(i2c->base + SPACEMIT_ISR); 598 428 if (!status) ··· 602 432 603 433 spacemit_i2c_clear_int_status(i2c, status); 604 434 605 - if (i2c->status & SPACEMIT_SR_ERR) 606 - goto err_out; 435 + spacemit_i2c_handle_state(i2c); 607 436 608 - val = readl(i2c->base + SPACEMIT_ICR); 609 - val &= ~(SPACEMIT_CR_TB | SPACEMIT_CR_ACKNAK | SPACEMIT_CR_STOP | SPACEMIT_CR_START); 610 - 611 - switch (i2c->state) { 612 - case SPACEMIT_STATE_START: 613 - spacemit_i2c_handle_start(i2c); 614 - break; 615 - case SPACEMIT_STATE_READ: 616 - spacemit_i2c_handle_read(i2c); 617 - break; 618 - case SPACEMIT_STATE_WRITE: 619 - spacemit_i2c_handle_write(i2c); 620 - break; 621 - default: 622 - break; 623 - } 624 - 625 - if (i2c->state != SPACEMIT_STATE_IDLE) { 626 - val |= SPACEMIT_CR_TB | SPACEMIT_CR_ALDIE; 627 - 628 - if (spacemit_i2c_is_last_msg(i2c)) { 629 - /* trigger next byte with stop */ 630 - val |= SPACEMIT_CR_STOP; 631 - 632 - if (i2c->read) 633 - val |= SPACEMIT_CR_ACKNAK; 634 - } 635 - writel(val, i2c->base + SPACEMIT_ICR); 636 - } 637 - 638 - err_out: 639 - spacemit_i2c_err_check(i2c); 640 437 return IRQ_HANDLED; 641 438 } 642 439 ··· 611 474 { 612 475 unsigned long timeout; 613 476 int idx = 0, cnt = 0; 477 + 478 + if (i2c->use_pio) { 479 + i2c->adapt.timeout = msecs_to_jiffies(SPACEMIT_WAIT_TIMEOUT); 480 + return; 481 + } 614 482 615 483 for (; idx < i2c->msg_num; idx++) 616 484 cnt += (i2c->msgs + idx)->len + 1; ··· 629 487 i2c->adapt.timeout = usecs_to_jiffies(timeout + USEC_PER_SEC / 10) / i2c->msg_num; 630 488 } 631 489 632 - static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) 490 + static inline int 491 + spacemit_i2c_xfer_common(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num, bool use_pio) 633 492 { 634 493 struct spacemit_i2c_dev *i2c = i2c_get_adapdata(adapt); 635 494 int ret; 495 + 496 + i2c->use_pio = use_pio; 636 497 637 498 i2c->msgs = msgs; 638 499 i2c->msg_num = num; ··· 664 519 return ret < 0 ? ret : num; 665 520 } 666 521 522 + static int spacemit_i2c_xfer(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) 523 + { 524 + return spacemit_i2c_xfer_common(adapt, msgs, num, false); 525 + } 526 + 527 + static int spacemit_i2c_pio_xfer_atomic(struct i2c_adapter *adapt, struct i2c_msg *msgs, int num) 528 + { 529 + return spacemit_i2c_xfer_common(adapt, msgs, num, true); 530 + } 531 + 667 532 static u32 spacemit_i2c_func(struct i2c_adapter *adap) 668 533 { 669 534 return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK); ··· 681 526 682 527 static const struct i2c_algorithm spacemit_i2c_algo = { 683 528 .xfer = spacemit_i2c_xfer, 529 + .xfer_atomic = spacemit_i2c_pio_xfer_atomic, 684 530 .functionality = spacemit_i2c_func, 685 531 }; 686 532