changeset 3952:01577c807d28

device/irq: use sink_update to disable irq on unlink, fix device refcount on icu
author Alexandre Becoulet <alexandre.becoulet@free.fr>
date Wed, 06 Jun 2018 21:33:00 +0200
parents a4161bb5ee6e
children e68724364949
files arch/efm32/drivers/gpio/gpio.c libdevice/include/device/class/icu.h libdevice/irq.c
diffstat 3 files changed, 58 insertions(+), 58 deletions(-) [+]
line wrap: on
line diff
--- a/arch/efm32/drivers/gpio/gpio.c	Wed Jun 06 21:29:54 2018 +0200
+++ b/arch/efm32/drivers/gpio/gpio.c	Wed Jun 06 21:33:00 2018 +0200
@@ -646,6 +646,7 @@
   struct efm32_gpio_private_s *pv = dev->drv_pv;
   uint_fast8_t sink_id = sink - pv->sink;
   uint_fast8_t bank = efm32_gpio_icupv_bank(sink);
+  uint_fast8_t h = (sink_id & 8) >> 1; /* 4 when using high registers */
 
 #ifdef CONFIG_DEVICE_IRQ_SHARING
   if (sink->base.link_count > 1)
@@ -660,8 +661,6 @@
     return 0;
 #endif
 
-  uintptr_t h = (sink_id & 8) >> 1; /* 4 when using high registers */
-
   /* Select bank */
   uint32_t x = endian_le32(cpu_mem_read_32(EFM32_GPIO_ADDR + EFM32_GPIO_EXTIPSELL_ADDR + h));
   EFM32_GPIO_EXTIPSELL_EXT_SETVAL(sink_id & 7, x, bank);
@@ -684,9 +683,6 @@
 
   /* Clear interrupt */
   cpu_mem_write_32(EFM32_GPIO_ADDR + EFM32_GPIO_IFC_ADDR, endian_le32(bit(sink_id)));
-  x = endian_le32(cpu_mem_read_32(EFM32_GPIO_ADDR + EFM32_GPIO_IEN_ADDR));
-  x |= bit(sink_id);
-  cpu_mem_write_32(EFM32_GPIO_ADDR + EFM32_GPIO_IEN_ADDR, endian_le32(x));
 
   return 0;
 }
--- a/libdevice/include/device/class/icu.h	Wed Jun 06 21:29:54 2018 +0200
+++ b/libdevice/include/device/class/icu.h	Wed Jun 06 21:33:00 2018 +0200
@@ -55,10 +55,10 @@
 #define DEV_ICU_LINK(n)	error_t (n) (struct device_icu_s *accessor, struct dev_irq_sink_s *sink, \
                                      struct dev_irq_src_s *src, dev_irq_route_t *route_mask, \
                                      struct dev_irq_src_s **bypass)
-/** @This configure the hardware after the link between a sink and a
-    source endpoints have changed. This is called from the @ref
-    device_irq_source_link and @ref device_irq_source_unlink functions.
-
+/** @This configure the hardware when a link between a sink and a
+    source endpoint has changed. This is called after linking from the
+    @ref device_irq_source_link function and before unlinking from the
+    @ref device_irq_source_unlink functions.
 
     @This is called with a @tt NULL pointer for the @tt route_mask
     parameter when a link between two endpoint is to be broken. When
--- a/libdevice/irq.c	Wed Jun 06 21:29:54 2018 +0200
+++ b/libdevice/irq.c	Wed Jun 06 21:33:00 2018 +0200
@@ -56,16 +56,6 @@
   .process = device_irq_dummy_process
 };
 
-static void device_irq_sink_cleanup(struct dev_irq_sink_s *sink)
-{
-  if (!sink->base.link_count)
-    {
-      if (sink->sense_all != DEV_IRQ_SENSE_ID_BUS)
-        sink->update(sink, DEV_IRQ_SENSE_NONE, 0);
-      sink->base.links.single = (void*)&device_irq_dummy_src_ep;
-    }
-}
-
 /****************************************/
 
 static error_t device_irq_ep_link_half(struct dev_irq_ep_s *a, struct dev_irq_ep_s *b)
@@ -155,32 +145,6 @@
   return -ENOENT;
 }
 
-static void device_irq_ep_unlink_all(struct dev_irq_ep_s *a)
-{
-  switch (a->link_count)
-    {
-    case 0:
-      return;
-
-    case 1:
-      device_irq_ep_unlink_half(a->links.single, a);
-      a->link_count = 0;
-      return;
-
-#if defined(CONFIG_DEVICE_IRQ_SHARING) || defined(CONFIG_DEVICE_IRQ_MULTI_SINK)
-    default: {
-      struct dev_irq_ep_s **r = a->links.array;
-      uint_fast8_t i;
-      for (i = 0; i < a->link_count; i++)
-        ensure(device_irq_ep_unlink_half(r[i], a));
-      mem_free(r);
-      a->link_count = 0;
-      return;
-    }
-#endif
-    }
-}
-
 static
 error_t device_irq_ep_link(struct dev_irq_src_s *source, struct dev_irq_sink_s *sink)
 {
@@ -207,19 +171,30 @@
   return 0;
 }
 
-static
-error_t device_irq_ep_unlink(struct dev_irq_src_s *source, struct dev_irq_sink_s *sink)
+static void device_irq_sink_unlink(struct dev_irq_src_s *src,
+                                   struct dev_irq_sink_s *sink)
 {
-  assert(source != NULL || sink != NULL);
+#ifdef CONFIG_DEVICE_IRQ_SHARING
+  if (sink->base.link_count == 1)
+#endif
+    if (sink->sense_all != DEV_IRQ_SENSE_ID_BUS)
+      sink->update(sink, DEV_IRQ_SENSE_NONE, 0);
 
-  if (device_irq_ep_unlink_half(&source->base, &sink->base))
-    return -ENOENT;
-  ensure(!device_irq_ep_unlink_half(&sink->base, &source->base));
-  device_irq_sink_cleanup(sink);
+  struct device_icu_s icu;
+  if (!device_get_accessor(&icu.base, sink->base.dev, DRIVER_CLASS_ICU, 0))
+    {
+      DEVICE_OP(&icu, link, sink, src, NULL, NULL);
+      device_put_accessor(&icu.base);
+    }
 
   sink->base.dev->ref_count--;
 
-  return 0;
+  ensure(!device_irq_ep_unlink_half(&sink->base, &src->base));
+
+#ifdef CONFIG_DEVICE_IRQ_SHARING
+  if (sink->base.link_count == 0)
+#endif
+    sink->base.links.single = (void*)&device_irq_dummy_src_ep;
 }
 
 void device_irq_source_init(struct device_s *dev, struct dev_irq_src_s *sources,
@@ -318,15 +293,43 @@
   return err;
 }
 
-void device_irq_source_unlink(struct device_s *dev, struct dev_irq_src_s *sources, uint_fast8_t src_count)
+void device_irq_source_unlink(struct device_s *dev, struct dev_irq_src_s *srcs,
+                              uint_fast8_t src_count)
 {
   uint_fast8_t i = 0;
 
+  /* drop all links to this source ep */
   for (; i < src_count; i++)
     {
-      struct dev_irq_src_s *src = sources + i;
-      device_icu_source_link(src, NULL);
-      device_irq_ep_unlink_all(dev_irq_src_s_base(src));
+      struct dev_irq_src_s *src = srcs + i;
+
+      switch (src->base.link_count)
+        {
+        case 0:
+          return;
+
+        case 1: {
+          struct dev_irq_sink_s *sink = (void*)src->base.links.single;
+          device_irq_sink_unlink(src, sink);
+          src->base.link_count = 0;
+          return;
+        }
+
+#if defined(CONFIG_DEVICE_IRQ_SHARING) || defined(CONFIG_DEVICE_IRQ_MULTI_SINK)
+        default: {
+          struct dev_irq_ep_s **r = src->base.links.array;
+          uint_fast8_t i;
+          for (i = 0; i < src->base.link_count; i++)
+            {
+              struct dev_irq_sink_s *sink = (void*)r[i];
+              device_irq_sink_unlink(src, sink);
+            }
+          mem_free(r);
+          src->base.link_count = 0;
+          return;
+        }
+#endif
+        }
     }
 }
 
@@ -668,7 +671,8 @@
 
     if (device_icu_source_link(src, &route_mask))
       {
-        device_irq_ep_unlink(src, sink);
+        device_irq_ep_unlink_half(&src->base, &sink->base);
+        device_irq_sink_unlink(src, sink);
         err = -EBUSY;
         goto out;
       }