Previously, samsung_gpio_drection_in/output function were not covered with a spinlock. For example, samsung_gpio_direction_output function consists of two functions. 1. samsung_gpio_set 2. samsung_gpio_set_direction When 2 CPUs try to control the same gpio pin heavily, (situation like i2c control with gpio emulation) This situation can cause below problem. CPU 0 | CPU1 | samsung_gpio_direction_output | samsung_gpio_set(pin A as 1) | samsung_gpio_direction_output | samsung_gpio_set(pin A as 0) samsung_gpio_set_direction | | samsung_gpio_set_direction The initial value of pin A will be set as 0 while we wanted to set pin A as 1. This patch modifies samsung_gpio_direction_in/output function to be done in one spinlock to fix race condition. Additionally, the new samsung_gpio_set_value was added to implement gpio set callback(samsung_gpio_set) with spinlock using this function.
Unified · Split
| @@ -514,25 +514,35 @@ static const struct pinconf_ops samsung_pinconf_ops = { · 513 unmodified lines | ||
| 514 | 514 | .pin_config_group_set = samsung_pinconf_group_set, |
| 515 | 515 | }; |
| 516 | 516 | |
| 517 | −/* gpiolib gpio_set callback function */ | |
| 518 | −static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) | |
| 517 | +/* | |
| 518 | + * The samsung_gpio_set_vlaue() should be called with "bank->slock" held | |
| 519 | + * to avoid race condition. | |
| 520 | + */ | |
| 521 | +static void samsung_gpio_set_value(struct gpio_chip *gc, | |
| 522 | + unsigned offset, int value) | |
| 519 | 523 | { |
| 520 | 524 | struct samsung_pin_bank *bank = gpiochip_get_data(gc); |
| 521 | 525 | const struct samsung_pin_bank_type *type = bank->type; |
| 522 | − unsigned long flags; | |
| 523 | 526 | void __iomem *reg; |
| 524 | 527 | u32 data; |
| 525 | 528 | |
| 526 | 529 | reg = bank->drvdata->virt_base + bank->pctl_offset; |
| 527 | 530 | |
| 528 | − spin_lock_irqsave(&bank->slock, flags); | |
| 529 | − | |
| 530 | 531 | data = readl(reg + type->reg_offset[PINCFG_TYPE_DAT]); |
| 531 | 532 | data &= ~(1 << offset); |
| 532 | 533 | if (value) |
| 533 | 534 | data |= 1 << offset; |
| 534 | 535 | writel(data, reg + type->reg_offset[PINCFG_TYPE_DAT]); |
| 536 | +} | |
| 537 | + | |
| 538 | +/* gpiolib gpio_set callback function */ | |
| 539 | +static void samsung_gpio_set(struct gpio_chip *gc, unsigned offset, int value) | |
| 540 | +{ | |
| 541 | + struct samsung_pin_bank *bank = gpiochip_get_data(gc); | |
| 542 | + unsigned long flags; | |
| 535 | 543 | |
| 544 | + spin_lock_irqsave(&bank->slock, flags); | |
| 545 | + samsung_gpio_set_value(gc, offset, value); | |
| 536 | 546 | spin_unlock_irqrestore(&bank->slock, flags); |
| 537 | 547 | } |
| 538 | 548 | |
| @@ -553,6 +563,8 @@ static int samsung_gpio_get(struct gpio_chip *gc, unsigned offset) · 14 unmodified lines | ||
| 553 | 563 | } |
| 554 | 564 | |
| 555 | 565 | /* |
| 566 | + * The samsung_gpio_set_direction() should be called with "bank->slock" held | |
| 567 | + * to avoid race condition. | |
| 556 | 568 | * The calls to gpio_direction_output() and gpio_direction_input() |
| 557 | 569 | * leads to this function call. |
| 558 | 570 | */ |
| @@ -564,7 +576,6 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc, · 5 unmodified lines | ||
| 564 | 576 | struct samsung_pinctrl_drv_data *drvdata; |
| 565 | 577 | void __iomem *reg; |
| 566 | 578 | u32 data, mask, shift; |
| 567 | − unsigned long flags; | |
| 568 | 579 | |
| 569 | 580 | bank = gpiochip_get_data(gc); |
| 570 | 581 | type = bank->type; |
| @@ -581,31 +592,42 @@ static int samsung_gpio_set_direction(struct gpio_chip *gc, · 10 unmodified lines | ||
| 581 | 592 | reg += 4; |
| 582 | 593 | } |
| 583 | 594 | |
| 584 | − spin_lock_irqsave(&bank->slock, flags); | |
| 585 | − | |
| 586 | 595 | data = readl(reg); |
| 587 | 596 | data &= ~(mask << shift); |
| 588 | 597 | if (!input) |
| 589 | 598 | data |= FUNC_OUTPUT << shift; |
| 590 | 599 | writel(data, reg); |
| 591 | 600 | |
| 592 | − spin_unlock_irqrestore(&bank->slock, flags); | |
| 593 | − | |
| 594 | 601 | return 0; |
| 595 | 602 | } |
| 596 | 603 | |
| 597 | 604 | /* gpiolib gpio_direction_input callback function. */ |
| 598 | 605 | static int samsung_gpio_direction_input(struct gpio_chip *gc, unsigned offset) |
| 599 | 606 | { |
| 600 | − return samsung_gpio_set_direction(gc, offset, true); | |
| 607 | + struct samsung_pin_bank *bank = gpiochip_get_data(gc); | |
| 608 | + unsigned long flags; | |
| 609 | + int ret; | |
| 610 | + | |
| 611 | + spin_lock_irqsave(&bank->slock, flags); | |
| 612 | + ret = samsung_gpio_set_direction(gc, offset, true); | |
| 613 | + spin_unlock_irqrestore(&bank->slock, flags); | |
| 614 | + return ret; | |
| 601 | 615 | } |
| 602 | 616 | |
| 603 | 617 | /* gpiolib gpio_direction_output callback function. */ |
| 604 | 618 | static int samsung_gpio_direction_output(struct gpio_chip *gc, unsigned offset, |
| 605 | 619 | int value) |
| 606 | 620 | { |
| 607 | − samsung_gpio_set(gc, offset, value); | |
| 608 | − return samsung_gpio_set_direction(gc, offset, false); | |
| 621 | + struct samsung_pin_bank *bank = gpiochip_get_data(gc); | |
| 622 | + unsigned long flags; | |
| 623 | + int ret; | |
| 624 | + | |
| 625 | + spin_lock_irqsave(&bank->slock, flags); | |
| 626 | + samsung_gpio_set_value(gc, offset, value); | |
| 627 | + ret = samsung_gpio_set_direction(gc, offset, false); | |
| 628 | + spin_unlock_irqrestore(&bank->slock, flags); | |
| 629 | + | |
| 630 | + return ret; | |
| 609 | 631 | } |
| 610 | 632 | |
| 611 | 633 | /* |