[v3,2/2] gpio: loongson: add firmware offset parse support
Commit Message
Loongson GPIO controllers come in multiple variants that are compatible
except for certain register offset values. Add support for device
properties allowing to specify them in ACPI or DT.
Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
---
drivers/gpio/gpio-loongson-64bit.c | 74 +++++++++++++++++++++++++++---
1 file changed, 67 insertions(+), 7 deletions(-)
Comments
Hi Yinbo,
thanks for your patch!
On Mon, Aug 7, 2023 at 9:41 AM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
> Loongson GPIO controllers come in multiple variants that are compatible
> except for certain register offset values. Add support for device
> properties allowing to specify them in ACPI or DT.
>
> Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
(...)
> @@ -26,6 +26,7 @@ struct loongson_gpio_chip_data {
> unsigned int conf_offset;
> unsigned int out_offset;
> unsigned int in_offset;
> + unsigned int inten_offset;
Consider just changing all of these from unsigned int to u32.
(...)
> + if (device_property_read_u32(dev, "loongson,gpio-conf-offset", (u32 *)&d->conf_offset)
> + || device_property_read_u32(dev, "loongson,gpio-in-offset", (u32 *)&d->in_offset)
> + || device_property_read_u32(dev, "loongson,gpio-out-offset", (u32 *)&d->out_offset)
> + || device_property_read_u32(dev, "loongson,gpio-ctrl-mode", (u32 *)&d->mode))
Because then you can get rid of this annoying forest of cast.
I'm fine with doing this change in this patch without a need for a separate
refactoring, as it's just a contained driver and clearly just about typing.
Yours,
Linus Walleij
在 2023/8/10 下午4:27, Linus Walleij 写道:
> Hi Yinbo,
>
> thanks for your patch!
>
> On Mon, Aug 7, 2023 at 9:41 AM Yinbo Zhu <zhuyinbo@loongson.cn> wrote:
>
>> Loongson GPIO controllers come in multiple variants that are compatible
>> except for certain register offset values. Add support for device
>> properties allowing to specify them in ACPI or DT.
>>
>> Signed-off-by: Yinbo Zhu <zhuyinbo@loongson.cn>
>
> (...)
>> @@ -26,6 +26,7 @@ struct loongson_gpio_chip_data {
>> unsigned int conf_offset;
>> unsigned int out_offset;
>> unsigned int in_offset;
>> + unsigned int inten_offset;
>
> Consider just changing all of these from unsigned int to u32.
okay, I got it.
>
> (...)
>> + if (device_property_read_u32(dev, "loongson,gpio-conf-offset", (u32 *)&d->conf_offset)
>> + || device_property_read_u32(dev, "loongson,gpio-in-offset", (u32 *)&d->in_offset)
>> + || device_property_read_u32(dev, "loongson,gpio-out-offset", (u32 *)&d->out_offset)
>> + || device_property_read_u32(dev, "loongson,gpio-ctrl-mode", (u32 *)&d->mode))
>
> Because then you can get rid of this annoying forest of cast.
Change offset to u32 and here still need use a (u32 *) cast, because the
chip_data is const type so &chip_data->offset will be (const u32 *) type
and need a (u32 *) cast.
>
> I'm fine with doing this change in this patch without a need for a separate
> refactoring, as it's just a contained driver and clearly just about typing.
okay, I got it.
Thanks,
Yinbo.
Mon, Aug 07, 2023 at 03:40:43PM +0800, Yinbo Zhu kirjoitti:
> Loongson GPIO controllers come in multiple variants that are compatible
> except for certain register offset values. Add support for device
> properties allowing to specify them in ACPI or DT.
> + if (device_property_read_u32(dev, "ngpios", &ngpios) || !ngpios)
> + return -EINVAL;
> +
> + ret = DIV_ROUND_UP(ngpios, 8);
> + switch (ret) {
> + case 1 ... 2:
> + io_width = ret;
> + break;
> + case 3 ... 4:
> + io_width = 0x4;
> + break;
> + case 5 ... 8:
> + io_width = 0x8;
> + break;
> + default:
> + dev_err(dev, "unsupported io width\n");
> + return -EINVAL;
> + }
Why? We have bgpio_init() handle this.
https://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git/commit/?h=gpio/for-next&id=55b2395e4e92adc492c6b30ac109eb78250dcd9d
...
> + lgpio->chip.can_sleep = 0;
It's boolean, use boolean initializer.
...
> + if (lgpio->chip_data->label)
> + lgpio->chip.label = lgpio->chip_data->label;
> + else
> + lgpio->chip.label = kstrdup(to_platform_device(dev)->name, GFP_KERNEL);
No error check? Not a devm_*() variant, so leaking memory?
...
> + {
> + .id = "LOON0007",
> + },
How does DSDT excerpt for this device look like?
Hi andy,
Sorry, I lost this email due to issues with my company's email server. I
will adopt your suggestion in v5 version.
在 2023/8/15 上午5:48, andy.shevchenko@gmail.com 写道:
> Mon, Aug 07, 2023 at 03:40:43PM +0800, Yinbo Zhu kirjoitti:
>> Loongson GPIO controllers come in multiple variants that are compatible
>> except for certain register offset values. Add support for device
>> properties allowing to specify them in ACPI or DT.
>
>> + if (device_property_read_u32(dev, "ngpios", &ngpios) || !ngpios)
>> + return -EINVAL;
>> +
>> + ret = DIV_ROUND_UP(ngpios, 8);
>> + switch (ret) {
>> + case 1 ... 2:
>> + io_width = ret;
>> + break;
>> + case 3 ... 4:
>> + io_width = 0x4;
>> + break;
>> + case 5 ... 8:
>> + io_width = 0x8;
>> + break;
>> + default:
>> + dev_err(dev, "unsupported io width\n");
>> + return -EINVAL;
>> + }
>
> Why? We have bgpio_init() handle this.
> https://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux.git/commit/?h=gpio/for-next&id=55b2395e4e92adc492c6b30ac109eb78250dcd9d
okay, I got it.
>
> ...
>
>> + lgpio->chip.can_sleep = 0;
>
> It's boolean, use boolean initializer.
okay, I got it.
>
> ...
>
>> + if (lgpio->chip_data->label)
>> + lgpio->chip.label = lgpio->chip_data->label;
>> + else
>> + lgpio->chip.label = kstrdup(to_platform_device(dev)->name, GFP_KERNEL);
>
> No error check? Not a devm_*() variant, so leaking memory?
This code had been removed in v4.
>
> ...
>
>> + {
>> + .id = "LOON0007",
>> + },
>
> How does DSDT excerpt for this device look like?
LOON0007 and LOON000A are similar, LOON000A is for 2k2000 gpio0.
Device (GPO0)
{
Name (_HID, "LOON000A") // _HID: Hardware ID
Name (_ADR, Zero) // _ADR: Address
Name (_UID, One) // _UID: Unique ID
Name (_CRS, ResourceTemplate () // _CRS: Current Resource
Settings
{
QWordMemory (ResourceConsumer, PosDecode, MinFixed,
MaxFixed, NonCacheable, ReadWrite,
0x0000000000000000, // Granularity
0x000000001FE00500, // Range Minimum
0x000000001FE00520, // Range Maximum
0x0000000000000000, // Translation Offset
0x0000000000000021, // Length
,, , AddressRangeMemory, TypeStatic)
})
Name (_DSD, Package (0x02) // _DSD: Device-Specific Data
{
ToUUID ("daffd814-6eba-4d8c-8a91-bc9bbf4aa301") /*
Device Properties for _DSD */,
Package (0x01)
{
Package (0x02)
{
"ngpios",
0x20
}
}
})
}
Thanks,
Yinbo
@@ -26,6 +26,7 @@ struct loongson_gpio_chip_data {
unsigned int conf_offset;
unsigned int out_offset;
unsigned int in_offset;
+ unsigned int inten_offset;
};
struct loongson_gpio_chip {
@@ -117,7 +118,17 @@ static void loongson_gpio_set(struct gpio_chip *chip, unsigned int pin, int valu
static int loongson_gpio_to_irq(struct gpio_chip *chip, unsigned int offset)
{
+ unsigned int u;
struct platform_device *pdev = to_platform_device(chip->parent);
+ struct loongson_gpio_chip *lgpio = to_loongson_gpio_chip(chip);
+
+ if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
+ u = readl(lgpio->reg_base + lgpio->chip_data->inten_offset + offset / 32 * 4);
+ u |= BIT(offset % 32);
+ writel(u, lgpio->reg_base + lgpio->chip_data->inten_offset + offset / 32 * 4);
+ } else {
+ writeb(1, lgpio->reg_base + lgpio->chip_data->inten_offset + offset);
+ }
return platform_get_irq(pdev, offset);
}
@@ -127,11 +138,30 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp
{
int ret;
u32 ngpios;
+ unsigned int io_width;
lgpio->reg_base = reg_base;
+ if (device_property_read_u32(dev, "ngpios", &ngpios) || !ngpios)
+ return -EINVAL;
+
+ ret = DIV_ROUND_UP(ngpios, 8);
+ switch (ret) {
+ case 1 ... 2:
+ io_width = ret;
+ break;
+ case 3 ... 4:
+ io_width = 0x4;
+ break;
+ case 5 ... 8:
+ io_width = 0x8;
+ break;
+ default:
+ dev_err(dev, "unsupported io width\n");
+ return -EINVAL;
+ }
if (lgpio->chip_data->mode == BIT_CTRL_MODE) {
- ret = bgpio_init(&lgpio->chip, dev, 8,
+ ret = bgpio_init(&lgpio->chip, dev, io_width,
lgpio->reg_base + lgpio->chip_data->in_offset,
lgpio->reg_base + lgpio->chip_data->out_offset,
NULL, NULL,
@@ -151,16 +181,35 @@ static int loongson_gpio_init(struct device *dev, struct loongson_gpio_chip *lgp
spin_lock_init(&lgpio->lock);
}
- device_property_read_u32(dev, "ngpios", &ngpios);
-
- lgpio->chip.can_sleep = 0;
lgpio->chip.ngpio = ngpios;
- lgpio->chip.label = lgpio->chip_data->label;
- lgpio->chip.to_irq = loongson_gpio_to_irq;
+ lgpio->chip.can_sleep = 0;
+ if (lgpio->chip_data->label)
+ lgpio->chip.label = lgpio->chip_data->label;
+ else
+ lgpio->chip.label = kstrdup(to_platform_device(dev)->name, GFP_KERNEL);
+
+ if (lgpio->chip_data->inten_offset)
+ lgpio->chip.to_irq = loongson_gpio_to_irq;
return devm_gpiochip_add_data(dev, &lgpio->chip, lgpio);
}
+static int loongson_gpio_get_props(struct device *dev,
+ struct loongson_gpio_chip *lgpio)
+{
+ const struct loongson_gpio_chip_data *d = lgpio->chip_data;
+
+ if (device_property_read_u32(dev, "loongson,gpio-conf-offset", (u32 *)&d->conf_offset)
+ || device_property_read_u32(dev, "loongson,gpio-in-offset", (u32 *)&d->in_offset)
+ || device_property_read_u32(dev, "loongson,gpio-out-offset", (u32 *)&d->out_offset)
+ || device_property_read_u32(dev, "loongson,gpio-ctrl-mode", (u32 *)&d->mode))
+ return -EINVAL;
+
+ device_property_read_u32(dev, "loongson,gpio-inten-offset", (u32 *)&d->inten_offset);
+
+ return 0;
+}
+
static int loongson_gpio_probe(struct platform_device *pdev)
{
void __iomem *reg_base;
@@ -172,7 +221,12 @@ static int loongson_gpio_probe(struct platform_device *pdev)
if (!lgpio)
return -ENOMEM;
- lgpio->chip_data = device_get_match_data(dev);
+ lgpio->chip_data = devm_kzalloc(dev, sizeof(*lgpio->chip_data), GFP_KERNEL);
+ if (!lgpio->chip_data)
+ return -ENOMEM;
+
+ if (loongson_gpio_get_props(dev, lgpio))
+ lgpio->chip_data = device_get_match_data(dev);
reg_base = devm_platform_ioremap_resource(pdev, 0);
if (IS_ERR(reg_base))
@@ -206,6 +260,9 @@ static const struct of_device_id loongson_gpio_of_match[] = {
.compatible = "loongson,ls7a-gpio",
.data = &loongson_gpio_ls7a_data,
},
+ {
+ .compatible = "loongson,ls2k1000-gpio",
+ },
{}
};
MODULE_DEVICE_TABLE(of, loongson_gpio_of_match);
@@ -215,6 +272,9 @@ static const struct acpi_device_id loongson_gpio_acpi_match[] = {
.id = "LOON0002",
.driver_data = (kernel_ulong_t)&loongson_gpio_ls7a_data,
},
+ {
+ .id = "LOON0007",
+ },
{}
};
MODULE_DEVICE_TABLE(acpi, loongson_gpio_acpi_match);