[RFC PATCH v1 11/20] drivers: gpio: add support of RP1 GPIO for Raspberry PI 5
Marek Behún
kabel at kernel.org
Wed Feb 5 12:09:02 CET 2025
On Wed, Feb 05, 2025 at 10:15:45AM +0000, Oleksii Moisieiev wrote:
...
> +#define RP1_PAD_DRIVE_LSB 4
> +#define RP1_PAD_IN_ENABLE_MASK 0x00000040
> +#define RP1_PAD_IN_ENABLE_LSB 6
> +#define RP1_PAD_OUT_DISABLE_MASK 0x00000080
> +#define RP1_PAD_OUT_DISABLE_LSB 7
> +
> +#define RP1_PAD_DRIVE_2MA 0x00000000
> +#define RP1_PAD_DRIVE_4MA 0x00000010
> +#define RP1_PAD_DRIVE_8MA 0x00000020
> +#define RP1_PAD_DRIVE_12MA 0x00000030
Can we use enumerator for these, instead of preprocessor macros?
Also in addition to BI(), can we use GENMASK() and FIELD_PREP_CONST(),
i.e.:
enum {
...
RP1_PAD_DRIVE_MASK = GENMASK(7, 4),
RP1_PAD_DRIVE_2MA = FIELD_PREP_CONST(RP1_PAD_DRIVE_MASK, 0),
RP1_PAD_DRIVE_4MA = FIELD_PREP_CONST(RP1_PAD_DRIVE_MASK, 1),
RP1_PAD_DRIVE_8MA = FIELD_PREP_CONST(RP1_PAD_DRIVE_MASK, 2),
RP1_PAD_DRIVE_12MA = FIELD_PREP_CONST(RP1_PAD_DRIVE_MASK, 3),
...
};
You need to import FIELD_PREP_CONST() into include/linux/bitfield.h,
from Linux sources, since we don't have it in U-Boot yet.
> +#define FLD_GET(r, f) (((r) & (f ## _MASK)) >> (f ## _LSB))
> +#define FLD_SET(r, f, v) r = (((r) & ~(f ## _MASK)) | ((v) << (f ## _LSB)))
Use FIELD_GET and FIELD_SET.
...
> +static void rp1_pad_update(struct rp1_pin_info *pin, u32 clr, u32 set)
> +{
> + u32 padctrl = readl(pin->pad);
> +
> + padctrl &= ~clr;
> + padctrl |= set;
> +
> + writel(padctrl, pin->pad);
> +}
Instead of implementing your own function, you can just use
clrsetbits_le32() ...
...
> +static void rp1_set_fsel(struct rp1_pin_info *pin, u32 fsel)
> +{
> + u32 ctrl = readl(pin->gpio + RP1_GPIO_CTRL);
> +
> + if (fsel >= RP1_FSEL_COUNT)
> + fsel = RP1_FSEL_NONE_HW;
> +
> + rp1_input_enable(pin, 1);
> + rp1_output_enable(pin, 1);
Shouldn't this be done with one read-modify-write? It seems to be doing
two read-modify-writes to one register.
> +
> + if (fsel == RP1_FSEL_NONE) {
> + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_DISABLE);
> + } else {
> + FLD_SET(ctrl, RP1_GPIO_CTRL_OUTOVER, RP1_OUTOVER_PERI);
> + FLD_SET(ctrl, RP1_GPIO_CTRL_OEOVER, RP1_OEOVER_PERI);
> + }
> + FLD_SET(ctrl, RP1_GPIO_CTRL_FUNCSEL, fsel);
> +
> + writel(ctrl, pin->gpio + RP1_GPIO_CTRL);
> +}
> +
> +static int rp1_get_dir(struct rp1_pin_info *pin)
> +{
> + return !(readl(pin->rio + RP1_RIO_OE) & (1 << pin->offset)) ?
> + RP1_DIR_INPUT : RP1_DIR_OUTPUT;
Why use own constants (RP1_DIR_INPUT/OUTPUT) ?
1 << pin->offset should be BIT(pin->offset). This happens more time in
the subsequent code.
> +}
> +
> +static void rp1_set_dir(struct rp1_pin_info *pin, bool is_input)
> +{
> + int offset = is_input ? RP1_CLR_OFFSET : RP1_SET_OFFSET;
size_t offset
> +
> + writel(1 << pin->offset, pin->rio + RP1_RIO_OE + offset);
> +}
> +
> +static int rp1_get_value(struct rp1_pin_info *pin)
> +{
> + return !!(readl(pin->rio + RP1_RIO_IN) & (1 << pin->offset));
> +}
> +
> +static void rp1_set_value(struct rp1_pin_info *pin, int value)
> +{
> + /* Assume the pin is already an output */
> + writel(1 << pin->offset,
> + pin->rio + RP1_RIO_OUT + (value ? RP1_SET_OFFSET : RP1_CLR_OFFSET));
In rp1_set_dir() you have helper variable offset, why not here?
> +}
> +
> +static int rp1_gpio_get(struct udevice *dev, unsigned int offset)
> +{
> + struct rp1_pin_info *pin = rp1_get_pin(dev, offset);
> + int ret;
> +
> + if (!pin)
> + return -EINVAL;
> +
> + ret = rp1_get_value(pin);
> + return ret;
return rp1_get_value(pin); ? And you can drop the ret variable and save
two lines.
> +}
> +
> +static int rp1_gpio_set(struct udevice *dev, unsigned int offset, int value)
> +{
> + struct rp1_pin_info *pin = rp1_get_pin(dev, offset);
> +
> + if (pin)
> + rp1_set_value(pin, value);
Shouldn't this return some error if pin is NULL?
> + return 0;
> +}
> +
> +static int rp1_gpio_direction_input(struct udevice *dev, unsigned int offset)
> +{
> + struct rp1_pin_info *pin = rp1_get_pin(dev, offset);
> +
> + if (!pin)
> + return -EINVAL;
> +
> + rp1_set_dir(pin, RP1_DIR_INPUT);
> + rp1_set_fsel(pin, RP1_FSEL_GPIO);
Empty line before return.
> + return 0;
> +}
> +
> +static int rp1_gpio_direction_output(struct udevice *dev, unsigned int offset, int value)
> +{
> + struct rp1_pin_info *pin = rp1_get_pin(dev, offset);
> +
> + if (!pin)
> + return -EINVAL;
> +
> + rp1_set_value(pin, value);
> + rp1_set_dir(pin, RP1_DIR_OUTPUT);
> + rp1_set_fsel(pin, RP1_FSEL_GPIO);
Empty line before return.
> + return 0;
> +}
> +
> +static int rp1_gpio_get_direction(struct udevice *dev, unsigned int offset)
> +{
> + struct rp1_pin_info *pin = rp1_get_pin(dev, offset);
> + u32 fsel;
> +
> + if (!pin)
> + return -EINVAL;
> +
> + fsel = rp1_get_fsel(pin);
> + if (fsel != RP1_FSEL_GPIO)
> + return -EINVAL;
> +
> + return (rp1_get_dir(pin) == RP1_DIR_OUTPUT) ?
> + GPIOF_OUTPUT :
> + GPIOF_INPUT;
rp1_get_dir() could have already returned GPIOF_INPUT/OUTPUT instead of
the custom constants.
> +}
> +
> +static const struct dm_gpio_ops rp1_gpio_ops = {
> + .direction_input = rp1_gpio_direction_input,
> + .direction_output = rp1_gpio_direction_output,
> + .get_value = rp1_gpio_get,
> + .set_value = rp1_gpio_set,
> + .get_function = rp1_gpio_get_direction,
> +};
Align the '=' with tabs.
> +
> +static int rp1_gpio_probe(struct udevice *dev)
> +{
> + int i;
> + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + struct rp1_gpio_priv *priv = dev_get_priv(dev);
Reverse christmas tree variables declaration if possible:
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct rp1_gpio_priv *priv = dev_get_priv(dev);
int i;
But you don't need declaring i here, see below.
> +
> + priv->gpio_base = dev_remap_addr_index(dev, 0);
> + if (!priv->gpio_base)
> + return -EINVAL;
> +
> + priv->rio_base = dev_remap_addr_index(dev, 1);
> + if (!priv->rio_base)
> + return -EINVAL;
> +
> + priv->pads_base = dev_remap_addr_index(dev, 2);
> + if (!priv->pads_base)
> + return -EINVAL;
> +
> + uc_priv->gpio_count = RP1_NUM_GPIOS;
> + uc_priv->bank_name = dev->name;
> +
> + for (i = 0; i < RP1_NUM_BANKS; i++) {
for (int i = 0; ...)
> + const struct rp1_iobank_desc *bank = &rp1_iobanks[i];
> + int j;
> +
> + for (j = 0; j < bank->num_gpios; j++) {
for (int j = 0; ...)
> + struct rp1_pin_info *pin =
> + &priv->pins[bank->min_gpio + j];
> +
> + pin->num = bank->min_gpio + j;
> + pin->bank = i;
> + pin->offset = j;
> +
> + pin->gpio = priv->gpio_base + bank->gpio_offset +
> + j * sizeof(u32) * 2;
> + pin->inte = priv->gpio_base + bank->inte_offset;
> + pin->ints = priv->gpio_base + bank->ints_offset;
> + pin->rio = priv->rio_base + bank->rio_offset;
> + pin->pad = priv->pads_base + bank->pads_offset +
> + j * sizeof(u32);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct udevice_id rp1_gpio_ids[] = {
> + { .compatible = "raspberrypi,rp1-gpio" },
> + { /* sentinel */ }
No need to write the sentinel comment. IMO we should drop this from all
drivers that use it.
> +};
> +
> +U_BOOT_DRIVER(rp1_gpio) = {
> + .name = "rp1-gpio",
> + .id = UCLASS_GPIO,
> + .of_match = rp1_gpio_ids,
> + .ops = &rp1_gpio_ops,
> + .priv_auto = sizeof(struct rp1_gpio_priv),
> + .probe = rp1_gpio_probe,
Align '=' with tabs.
Marek
More information about the U-Boot
mailing list