Skip to content

Commit 45c11a9

Browse files
jwrdegoedeandy-shev
authored andcommitted
pinctrl: baytrail: Fix pin being driven low for a while on gpiod_get(..., GPIOD_OUT_HIGH)
The pins on the Bay Trail SoC have separate input-buffer and output-buffer enable bits and a read of the level bit of the value register will always return the value from the input-buffer. The BIOS of a device may configure a pin in output-only mode, only enabling the output buffer, and write 1 to the level bit to drive the pin high. This 1 written to the level bit will be stored inside the data-latch of the output buffer. But a subsequent read of the value register will return 0 for the level bit because the input-buffer is disabled. This causes a read-modify-write as done by byt_gpio_set_direction() to write 0 to the level bit, driving the pin low! Before this commit byt_gpio_direction_output() relied on pinctrl_gpio_direction_output() to set the direction, followed by a call to byt_gpio_set() to apply the selected value. This causes the pin to go low between the pinctrl_gpio_direction_output() and byt_gpio_set() calls. Change byt_gpio_direction_output() to directly make the register modifications itself instead. Replacing the 2 subsequent writes to the value register with a single write. Note that the pinctrl code does not keep track internally of the direction, so not going through pinctrl_gpio_direction_output() is not an issue. This issue was noticed on a Trekstor SurfTab Twin 10.1. When the panel is already on at boot (no external monitor connected), then the i915 driver does a gpiod_get(..., GPIOD_OUT_HIGH) for the panel-enable GPIO. The temporarily going low of that GPIO was causing the panel to reset itself after which it would not show an image until it was turned off and back on again (until a full modeset was done on it). This commit fixes this. This commit also updates the byt_gpio_direction_input() to use direct register accesses instead of going through pinctrl_gpio_direction_input(), to keep it consistent with byt_gpio_direction_output(). Note for backporting, this commit depends on: commit e2b7441 ("pinctrl: baytrail: Replace WARN with dev_info_once when setting direct-irq pin to output") Cc: [email protected] Fixes: 86e3ef8 ("pinctrl: baytrail: Update gpio chip operations") Signed-off-by: Hans de Goede <[email protected]> Acked-by: Mika Westerberg <[email protected]> Signed-off-by: Andy Shevchenko <[email protected]>
1 parent b3a9e3b commit 45c11a9

File tree

1 file changed

+53
-14
lines changed

1 file changed

+53
-14
lines changed

drivers/pinctrl/intel/pinctrl-baytrail.c

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -800,14 +800,28 @@ static void byt_gpio_disable_free(struct pinctrl_dev *pctl_dev,
800800
pm_runtime_put(vg->dev);
801801
}
802802

803+
static void byt_gpio_direct_irq_check(struct intel_pinctrl *vg,
804+
unsigned int offset)
805+
{
806+
void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG);
807+
808+
/*
809+
* Before making any direction modifications, do a check if gpio is set
810+
* for direct IRQ. On Bay Trail, setting GPIO to output does not make
811+
* sense, so let's at least inform the caller before they shoot
812+
* themselves in the foot.
813+
*/
814+
if (readl(conf_reg) & BYT_DIRECT_IRQ_EN)
815+
dev_info_once(vg->dev, "Potential Error: Setting GPIO with direct_irq_en to output");
816+
}
817+
803818
static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev,
804819
struct pinctrl_gpio_range *range,
805820
unsigned int offset,
806821
bool input)
807822
{
808823
struct intel_pinctrl *vg = pinctrl_dev_get_drvdata(pctl_dev);
809824
void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG);
810-
void __iomem *conf_reg = byt_gpio_reg(vg, offset, BYT_CONF0_REG);
811825
unsigned long flags;
812826
u32 value;
813827

@@ -817,14 +831,8 @@ static int byt_gpio_set_direction(struct pinctrl_dev *pctl_dev,
817831
value &= ~BYT_DIR_MASK;
818832
if (input)
819833
value |= BYT_OUTPUT_EN;
820-
else if (readl(conf_reg) & BYT_DIRECT_IRQ_EN)
821-
/*
822-
* Before making any direction modifications, do a check if gpio
823-
* is set for direct IRQ. On baytrail, setting GPIO to output
824-
* does not make sense, so let's at least inform the caller before
825-
* they shoot themselves in the foot.
826-
*/
827-
dev_info_once(vg->dev, "Potential Error: Setting GPIO with direct_irq_en to output");
834+
else
835+
byt_gpio_direct_irq_check(vg, offset);
828836

829837
writel(value, val_reg);
830838

@@ -1165,19 +1173,50 @@ static int byt_gpio_get_direction(struct gpio_chip *chip, unsigned int offset)
11651173

11661174
static int byt_gpio_direction_input(struct gpio_chip *chip, unsigned int offset)
11671175
{
1168-
return pinctrl_gpio_direction_input(chip->base + offset);
1176+
struct intel_pinctrl *vg = gpiochip_get_data(chip);
1177+
void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG);
1178+
unsigned long flags;
1179+
u32 reg;
1180+
1181+
raw_spin_lock_irqsave(&byt_lock, flags);
1182+
1183+
reg = readl(val_reg);
1184+
reg &= ~BYT_DIR_MASK;
1185+
reg |= BYT_OUTPUT_EN;
1186+
writel(reg, val_reg);
1187+
1188+
raw_spin_unlock_irqrestore(&byt_lock, flags);
1189+
return 0;
11691190
}
11701191

1192+
/*
1193+
* Note despite the temptation this MUST NOT be converted into a call to
1194+
* pinctrl_gpio_direction_output() + byt_gpio_set() that does not work this
1195+
* MUST be done as a single BYT_VAL_REG register write.
1196+
* See the commit message of the commit adding this comment for details.
1197+
*/
11711198
static int byt_gpio_direction_output(struct gpio_chip *chip,
11721199
unsigned int offset, int value)
11731200
{
1174-
int ret = pinctrl_gpio_direction_output(chip->base + offset);
1201+
struct intel_pinctrl *vg = gpiochip_get_data(chip);
1202+
void __iomem *val_reg = byt_gpio_reg(vg, offset, BYT_VAL_REG);
1203+
unsigned long flags;
1204+
u32 reg;
11751205

1176-
if (ret)
1177-
return ret;
1206+
raw_spin_lock_irqsave(&byt_lock, flags);
1207+
1208+
byt_gpio_direct_irq_check(vg, offset);
11781209

1179-
byt_gpio_set(chip, offset, value);
1210+
reg = readl(val_reg);
1211+
reg &= ~BYT_DIR_MASK;
1212+
if (value)
1213+
reg |= BYT_LEVEL;
1214+
else
1215+
reg &= ~BYT_LEVEL;
11801216

1217+
writel(reg, val_reg);
1218+
1219+
raw_spin_unlock_irqrestore(&byt_lock, flags);
11811220
return 0;
11821221
}
11831222

0 commit comments

Comments
 (0)