Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 52 additions & 8 deletions drivers/pinctrl/sunxi/pinctrl-sunxi.c
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,21 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev,
writel((readl(pctl->membase + reg) & ~mask) | config << shift,
pctl->membase + reg);

/*
* A pin muxed to gpio_out directly through a pinmux node bypasses
* sunxi_pinctrl_gpio_set() and drives whatever its output latch
* holds. Now that the pin is in output mode the data register
* reads back the latch, so refresh the shadow to keep such pins
* driving their pre-existing level.
*/
if (config == SUN4I_FUNC_OUTPUT) {
u32 *shadow = &pctl->dat_shadow[pin / PINS_PER_BANK];

sunxi_data_reg(pctl, pin, &reg, &shift, &mask);
*shadow = (*shadow & ~mask) |
(readl(pctl->membase + reg) & mask);
}

raw_spin_unlock_irqrestore(&pctl->lock, flags);
}

Expand Down Expand Up @@ -943,21 +958,29 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
unsigned offset, int value)
{
struct sunxi_pinctrl *pctl = gpiochip_get_data(chip);
u32 reg, shift, mask, val;
u32 *shadow = &pctl->dat_shadow[offset / PINS_PER_BANK];
u32 reg, shift, mask;
unsigned long flags;

sunxi_data_reg(pctl, offset, &reg, &shift, &mask);

raw_spin_lock_irqsave(&pctl->lock, flags);

val = readl(pctl->membase + reg);

/*
* Reading the data register returns the pin level, not the output
* latch, for pins muxed as inputs. A read-modify-write based on
* the register would therefore corrupt the latches of input-muxed
* pins in the same bank (e.g. an emulated open-drain I2C line
* released high), making them drive the wrong level once switched
* to output. Base the read-modify-write on a shadow copy of the
* latches instead.
*/
if (value)
val |= mask;
*shadow |= mask;
else
val &= ~mask;
*shadow &= ~mask;

writel(val, pctl->membase + reg);
writel(*shadow, pctl->membase + reg);

raw_spin_unlock_irqrestore(&pctl->lock, flags);
}
Expand Down Expand Up @@ -1481,7 +1504,7 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
struct pinctrl_pin_desc *pins;
struct sunxi_pinctrl *pctl;
struct pinmux_ops *pmxops;
int i, ret, last_pin, pin_idx;
int i, ret, last_pin, pin_idx, nbanks;
struct clk *clk;

pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
Expand Down Expand Up @@ -1515,6 +1538,28 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
if (!pctl->irq_array)
return -ENOMEM;

/*
* Seed the output latch shadow from the hardware so pins the
* bootloader left in output mode keep their state; see
* sunxi_pinctrl_gpio_set() for why a shadow is needed. This must
* happen before the pinctrl device registers, as pin hogs can mux
* pins to gpio_out and thereby update the shadow.
*/
last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
nbanks = (round_up(last_pin, PINS_PER_BANK) - pctl->desc->pin_base) /
PINS_PER_BANK;
pctl->dat_shadow = devm_kcalloc(&pdev->dev, nbanks,
sizeof(*pctl->dat_shadow), GFP_KERNEL);
if (!pctl->dat_shadow)
return -ENOMEM;

for (i = 0; i < nbanks; i++) {
u32 reg, shift, mask;

sunxi_data_reg(pctl, i * PINS_PER_BANK, &reg, &shift, &mask);
pctl->dat_shadow[i] = readl(pctl->membase + reg);
}

ret = sunxi_pinctrl_build_state(pdev);
if (ret) {
dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
Expand Down Expand Up @@ -1569,7 +1614,6 @@ int sunxi_pinctrl_init_with_variant(struct platform_device *pdev,
if (!pctl->chip)
return -ENOMEM;

last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
pctl->chip->owner = THIS_MODULE;
pctl->chip->request = gpiochip_generic_request;
pctl->chip->free = gpiochip_generic_free;
Expand Down
7 changes: 7 additions & 0 deletions drivers/pinctrl/sunxi/pinctrl-sunxi.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
#define IO_BIAS_MASK GENMASK(3, 0)

#define SUN4I_FUNC_INPUT 0
#define SUN4I_FUNC_OUTPUT 1
#define SUN4I_FUNC_IRQ 6

#define PINCTRL_SUN5I_A10S BIT(1)
Expand Down Expand Up @@ -173,6 +174,12 @@ struct sunxi_pinctrl {
int *irq;
unsigned *irq_array;
raw_spinlock_t lock;
/*
* Output latch shadow, one word per bank. Seeded lockless at
* probe before the pinctrl device registers, protected by @lock
* afterwards.
*/
u32 *dat_shadow;
struct pinctrl_dev *pctl_dev;
unsigned long variant;
u32 bank_mem_size;
Expand Down