Thread (15 messages) 15 messages, 5 authors, 2017-02-02

[PATCH V11 3/3] irqchip: qcom: Add IRQ combiner driver

From: Agustin Vega-Frias <hidden>
Date: 2017-02-02 22:20:52
Also in: linux-acpi, lkml

Hi Andy,

On 2017-01-25 19:44, Andy Shevchenko wrote:
On Fri, Jan 20, 2017 at 4:34 AM, Agustin Vega-Frias
[off-list ref] wrote:
quoted
Driver for interrupt combiners in the Top-level Control and Status
Registers (TCSR) hardware block in Qualcomm Technologies chips.

An interrupt combiner in this block combines a set of interrupts by
OR'ing the individual interrupt signals into a summary interrupt
signal routed to a parent interrupt controller, and provides read-
only, 32-bit registers to query the status of individual interrupts.
The status bit for IRQ n is bit (n % 32) within register (n / 32)
of the given combiner. Thus, each combiner can be described as a set
of register offsets and the number of IRQs managed.
quoted
+static inline u32 irq_register(int irq)
+{
+       return irq / REG_SIZE;
+}
+
+static inline u32 irq_bit(int irq)
+{
+       return irq % REG_SIZE;
+
+}
Besides extra line I do not see a benefit of those helpers. On first
glance they even increase characters to type.
Will remove these.
quoted
+static inline int irq_nr(u32 reg, u32 bit)
+{
+       return reg * REG_SIZE + bit;
+}
This one might make sense.
quoted
+static void combiner_handle_irq(struct irq_desc *desc)
+{
+       struct combiner *combiner = irq_desc_get_handler_data(desc);
+       struct irq_chip *chip = irq_desc_get_chip(desc);
+       u32 reg;
+
+       chained_irq_enter(chip, desc);
+
+       for (reg = 0; reg < combiner->nregs; reg++) {
+               int virq;
+               int hwirq;
+               u32 bit;
+               u32 status;
+
+               bit = readl_relaxed(combiner->regs[reg].addr);
+               status = bit & combiner->regs[reg].enabled;
+               if (!status)
+                       pr_warn_ratelimited("Unexpected IRQ on CPU%d: 
(%08x %08lx %p)\n",
+                                           smp_processor_id(), bit,
+                                           
combiner->regs[reg].enabled,
+                                           combiner->regs[reg].addr);
+
quoted
+               while (status) {
+                       bit = __ffs(status);
+                       status &= ~(1 << bit);
Interesting way of for_each_set_bit() ?
I'm leaving this as-is since in arm64 using __ffs can be optimized
better by using the bic instruction.
quoted
+                       hwirq = irq_nr(reg, bit);
+                       virq = irq_find_mapping(combiner->domain, 
hwirq);
+                       if (virq > 0)
+                               generic_handle_irq(virq);
+
+               }
+       }
+
+       chained_irq_exit(chip, desc);
+}
+
quoted
+/*
+ * irqchip callbacks
+ */
Useless.
Will remove
quoted
+/*
+ * irq_domain_ops callbacks
+ */
Ditto.
Will remove
quoted
+/*
+ * Device probing
+ */
Ditto.
Will remove
quoted
+static acpi_status count_registers_cb(struct acpi_resource *ares, 
void *context)
+{
+       int *count = context;
I would consider to define a struct. It would be easy to extend if 
needed and...
The intent here is always to get the count so I don't see value
in adding the struct.
quoted
+
+       if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+               ++(*count);
...allows not to use such of constructions. (I think above is
equivalent to ++*count).
IMHO having the parentheses makes the code clearer.
quoted
+       return AE_OK;
quoted
+}
+
+static int count_registers(struct platform_device *pdev)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
You don't use adev, so, ACPI_HANDLE() ?
Will change
quoted
+       acpi_status status;
+       int count = 0;
+
+       if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
+               return -EINVAL;
+
+       status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+                                    count_registers_cb, &count);
+       if (ACPI_FAILURE(status))
+               return -EINVAL;
+       return count;
+}
Oh, since you are using this just as a helper to get count first, why
not to combine this in one callback?
What's the benefit of separation?
This is because we do an allocation based on the count first.
quoted
+
+struct get_registers_context {
+       struct device *dev;
+       struct combiner *combiner;
+       int err;
+};
+
+static acpi_status get_registers_cb(struct acpi_resource *ares, void 
*context)
+{
+       struct get_registers_context *ctx = context;
+       struct acpi_resource_generic_register *reg;
+       phys_addr_t paddr;
+       void __iomem *vaddr;
+
+       if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER)
+               return AE_OK;
+
+       reg = &ares->data.generic_reg;
+       paddr = reg->address;
+       if ((reg->space_id != ACPI_SPACE_MEM) ||
+           (reg->bit_offset != 0) ||
+           (reg->bit_width > REG_SIZE)) {
+               dev_err(ctx->dev, "Bad register resource @%pa\n", 
&paddr);
+               ctx->err = -EINVAL;
+               return AE_ERROR;
+       }
+
+       vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE);
+       if (IS_ERR(vaddr)) {
+               dev_err(ctx->dev, "Can't map register @%pa\n", 
&paddr);
+               ctx->err = PTR_ERR(vaddr);
+               return AE_ERROR;
+       }
This all sounds to me like an OperationalRegion. But I'm not sure it's
suitable here.
Do you have ACPI table carved in stone?
I decided to go with registers because in some cases these might be non-
contiguous.
quoted
+
+       ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr;
+       ctx->combiner->nirqs += reg->bit_width;
+       ctx->combiner->nregs++;
+       return AE_OK;
+}
+
+static int get_registers(struct platform_device *pdev, struct 
combiner *comb)
+{
+       struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
+       acpi_status status;
+       struct get_registers_context ctx;
+
+       if (!acpi_has_method(adev->handle, METHOD_NAME__CRS))
+               return -EINVAL;
+
+       ctx.dev = &pdev->dev;
+       ctx.combiner = comb;
+       ctx.err = 0;
+
+       status = acpi_walk_resources(adev->handle, METHOD_NAME__CRS,
+                                    get_registers_cb, &ctx);
+       if (ACPI_FAILURE(status))
+               return ctx.err;
+       return 0;
+}
+
+static int __init combiner_probe(struct platform_device *pdev)
+{
+       struct combiner *combiner;
+       size_t alloc_sz;
+       u32 nregs;
+       int err;
+
+       nregs = count_registers(pdev);
+       if (nregs <= 0) {
+               dev_err(&pdev->dev, "Error reading register 
resources\n");
+               return -EINVAL;
+       }
+
+       alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * 
nregs;
+       combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL);
+       if (!combiner)
+               return -ENOMEM;
+
+       err = get_registers(pdev, combiner);
+       if (err < 0)
+               return err;
+
+       combiner->parent_irq = platform_get_irq(pdev, 0);
+       if (combiner->parent_irq <= 0) {
+               dev_err(&pdev->dev, "Error getting IRQ resource\n");
+               return -EPROBE_DEFER;
+       }
+
+       combiner->domain = irq_domain_create_linear(pdev->dev.fwnode, 
combiner->nirqs,
+                                                   &domain_ops, 
combiner);
+       if (!combiner->domain)
+               /* Errors printed by irq_domain_create_linear */
+               return -ENODEV;
+
+       irq_set_chained_handler_and_data(combiner->parent_irq,
+                                        combiner_handle_irq, 
combiner);
+
+       dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n",
+                combiner->parent_irq, combiner->nirqs, 
combiner->regs[0].addr);
+       return 0;
+}
+
+static const struct acpi_device_id qcom_irq_combiner_ids[] 
__dsdt_irqchip = {
+       { "QCOM80B1", },
+       { }
+};
+
+static struct platform_driver qcom_irq_combiner_probe = {
+       .driver = {
+               .name = "qcom-irq-combiner",
quoted
+               .owner = THIS_MODULE,
Do you still need this?
Will remove

Thanks,
Agustin
quoted
+               .acpi_match_table = ACPI_PTR(qcom_irq_combiner_ids),
+       },
--
With Best Regards,
Andy Shevchenko
-- 
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm 
Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a 
Linux Foundation Collaborative Project.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help