linguini1 commented on code in PR #18499:
URL: https://github.com/apache/nuttx/pull/18499#discussion_r2891433839
##########
arch/arm64/include/bcm2711/chip.h:
##########
@@ -75,6 +75,8 @@
#if defined(CONFIG_RPI4B_RAM_4GB) || defined(CONFIG_RPI4B_RAM_8GB)
#define CONFIG_RAMBANK1_SIZE GB(4) - MB(64)
+#elif defined(CONFIG_RPI4B_RAM_2GB)
Review Comment:
Do you have a 2GB Pi? Have you tested on it?
##########
boards/arm64/bcm2711/raspberrypi-4b/Kconfig:
##########
@@ -60,4 +60,151 @@ config RPI4B_FRAMEBUFFER
---help---
Registers a frame buffer character driver for graphical output.
+menu "Raspberry Pi 4B Pin Configuration"
+ config RPI4B_GPIO0
+ int "GPIO 0 (0:SDA0, 1:SA5, 2:PCLK, 3:SPI3_CE0_N, 4:TXD2, 5:SDA6, 6:IN,
7:OUT)"
+ range 0 7
+ default 0
+ ---help---
+ Reserved for I2C to Raspberry Pi HAT EEPROM. Disable EEPROM
forced read and FAN I/O to use it otherwise.
+
+ config RPI4B_GPIO1
+ int "GPIO 1 (0:SCL0, 1:SA4, 2:DE, 3:SPI3_MISO, 4:RXD2, 5:SCL6, 6:IN,
7:OUT)"
+ range 0 7
+ default 0
+ ---help---
+ Reserved for I2C to Raspberry Pi HAT EEPROM. Disable EEPROM
forced read and FAN I/O to use it otherwise.
+
+ config RPI4B_GPIO2
Review Comment:
I don't think this a good approach, because it implies that the GPIO is
going to be configured to the alternate function when a number 1-7 is selected.
Configuring the gpio pin for use as SPI3_MISO (for example) should be the
responsibility of the SPI driver.
##########
arch/arm64/src/bcm2711/hardware/bcm2711_gpio.h:
##########
@@ -37,38 +37,38 @@
/* GPIO register offset definitions */
-#define BCM_GPIO_GPFSEL0_OFFSET 0x00 /* GPIO function select 0 */
-#define BCM_GPIO_GPFSEL1_OFFSET 0x04 /* GPIO function select 1 */
-#define BCM_GPIO_GPFSEL2_OFFSET 0x08 /* GPIO function select 2 */
-#define BCM_GPIO_GPFSEL3_OFFSET 0x0c /* GPIO function select 3 */
-#define BCM_GPIO_GPFSEL4_OFFSET 0x10 /* GPIO function select 4 */
-#define BCM_GPIO_GPFSEL5_OFFSET 0x14 /* GPIO function select 5 */
-#define BCM_GPIO_GPSET0_OFFSET 0x1c /* GPIO pin output set 0 */
-#define BCM_GPIO_GPSET1_OFFSET 0x20 /* GPIO pin output set 1 */
-#define BCM_GPIO_GPCLR0_OFFSET 0x28 /* GPIO pin output clear 0 */
-#define BCM_GPIO_GPCLR1_OFFSET 0x2c /* GPIO pin output clear 1 */
-#define BCM_GPIO_GPLEV0_OFFSET 0x34 /* GPIO pin level 0 */
-#define BCM_GPIO_GPLEV1_OFFSET 0x38 /* GPIO pin level 1 */
-
-#define BCM_GPIO_GPEDS0_OFFSET 0x40 /* GPIO pin event detect status 0 */
-#define BCM_GPIO_GPEDS1_OFFSET 0x44 /* GPIO pin event detect status 1 */
-#define BCM_GPIO_GPREN0_OFFSET 0x4c /* GPIO pin rise edge detect enable 0 */
-#define BCM_GPIO_GPREN1_OFFSET 0x50 /* GPIO pin rise edge detect enable 1 */
-#define BCM_GPIO_GPFEN0_OFFSET 0x58 /* GPIO pin fall edge detect enable 0 */
-#define BCM_GPIO_GPFEN1_OFFSET 0x5c /* GPIO pin fall edge detect enable 1 */
-#define BCM_GPIO_GPHEN0_OFFSET 0x64 /* GPIO pin high detect enable 0 */
-#define BCM_GPIO_GPHEN1_OFFSET 0x68 /* GPIO pin high detect enable 1 */
-#define BCM_GPIO_GPLEN0_OFFSET 0x70 /* GPIO pin low detect enable 0 */
-#define BCM_GPIO_GPLEN1_OFFSET 0x74 /* GPIO pin low detect enable 1 */
-#define BCM_GPIO_GPAREN0_OFFSET 0x7c /* GPIO pin async rise edge detect 0 */
-#define BCM_GPIO_GPAREN1_OFFSET 0x80 /* GPIO pin async rise edge detect 1 */
-#define BCM_GPIO_GPAFEN0_OFFSET 0x88 /* GPIO pin async fall edge detect 0 */
-#define BCM_GPIO_GPAFEN1_OFFSET 0x8c /* GPIO pin async fall edge detect 1 */
-
-#define BCM_GPIO_PUP_PDN_REG0_OFFSET 0xe4 /* GPIO pullup/down reg 0 */
-#define BCM_GPIO_PUP_PDN_REG1_OFFSET 0xe8 /* GPIO pullup/down reg 1 */
-#define BCM_GPIO_PUP_PDN_REG2_OFFSET 0xec /* GPIO pullup/down reg 2 */
-#define BCM_GPIO_PUP_PDN_REG3_OFFSET 0xf0 /* GPIO pullup/down reg 3 */
+#define BCM_GPIO_GPFSEL0_OFFSET 0x00UL /* GPIO function select 0 */
Review Comment:
Why is this change necessary?
##########
arch/arm64/src/bcm2711/hardware/bcm2711_memmap.h:
##########
@@ -58,17 +58,17 @@
/* Base addresses for chip registers */
#define BCM_ARMT_BASEADDR \
- (BCM_PERIPHERAL_BASEADDR + 0x0000b000) /* ARM timer */
+ (BCM_PERIPHERAL_BASEADDR + 0x0000b000UL) /* ARM timer */
Review Comment:
Why is this change necessary?
##########
boards/arm64/bcm2711/raspberrypi-4b/src/rpi4b.h:
##########
@@ -61,4 +61,57 @@ int rpi4b_sdmmc_initialize(void);
int bcm2711_dev_gpio_init(void);
#endif /* defined(CONFIG_DEV_GPIO) */
+/* == UART == */
+
+/* UART 0: GPIO 14 (ALT0=0) and GPIO 15 (ALT0=0) */
+#if (CONFIG_RPI4B_GPIO14 == 0) && (CONFIG_RPI4B_GPIO15 == 0)
+# define RPI4B_UART0
+#endif
+
+/* UART 1: GPIO 14 (ALT5=5) and GPIO 15 (ALT5=5) */
+#if (CONFIG_RPI4B_GPIO14 == 5) && (CONFIG_RPI4B_GPIO15 == 5)
+# define RPI4B_UART1
+#endif
+
+/* UART 2: GPIO 0 (ALT4=4) and GPIO 1 (ALT4=4) */
+#if (CONFIG_RPI4B_GPIO0 == 4) && (CONFIG_RPI4B_GPIO1 == 4)
+# define RPI4B_UART2
+#endif
+
+/* UART 3: GPIO 4 (ALT4=4) and GPIO 5 (ALT4=4) */
+#if (CONFIG_RPI4B_GPIO4 == 4) && (CONFIG_RPI4B_GPIO5 == 4)
+# define RPI4B_UART3
+#endif
+
+/* UART 4: GPIO 8 (ALT4=4) and GPIO 9 (ALT4=4) */
+#if (CONFIG_RPI4B_GPIO8 == 4) && (CONFIG_RPI4B_GPIO9 == 4)
+# define RPI4B_UART4
+#endif
+
+/* UART 5: GPIO 12 (ALT4=4) and GPIO 13 (ALT4=4) */
+#if (CONFIG_RPI4B_GPIO12 == 4) && (CONFIG_RPI4B_GPIO13 == 4)
+# define RPI4B_UART5
+#endif
+
+/* == I2C == */
+
+/* I2C 0: GPIO 0 (ALT0=0) and GPIO 1 (ALT0=0) */
+#if (CONFIG_RPI4B_GPIO0 == 0) && (CONFIG_RPI4B_GPIO1 == 0)
+# define RPI4B_I2C0
Review Comment:
What is the purpose of these definitions? The user can already
enable/disable the BCM2711 I2C/SPI/UART interfaces via Kconfig.
##########
boards/arm64/bcm2711/raspberrypi-4b/src/rpi4b_gpio.c:
##########
@@ -248,92 +217,161 @@ static int gpout_write(struct gpio_dev_s *dev, bool
value)
* value - A pointer to the location to store the pin status.
****************************************************************************/
-static int gpin_read(struct gpio_dev_s *dev, bool *value)
+static int rpi4b_gpin_read(struct gpio_dev_s *dev, bool *value)
{
- struct bcm2711_gpio_dev_s *bcm2711gpio =
+ struct bcm2711_gpio_dev_s *bcm2711_gpio =
(struct bcm2711_gpio_dev_s *)(dev);
- DEBUGASSERT(bcm2711gpio != NULL);
+ DEBUGASSERT(bcm2711_gpio != NULL);
DEBUGASSERT(value != NULL);
- DEBUGASSERT(bcm2711gpio->id < BOARD_NGPIOIN);
+ DEBUGASSERT(bcm2711_gpio->pin < RPI4B_NGPIO);
- *value = bcm2711_gpio_pin_get(g_gpioinputs[bcm2711gpio->id]);
+ *value = bcm2711_gpio_pin_get(bcm2711_gpio->pin);
return 0;
}
-#endif /* BOARD_NGPIOIN > 0 */
+static enum bcm2711_gpio_pull_e rpi4b_gpio_pull_status(int pin)
+{
+ DEBUGASSERT(pin < RPI4B_NGPIO);
-/****************************************************************************
- * Public Functions
- ****************************************************************************/
+ /* BCM2711 Peripheral Doc */
-/****************************************************************************
- * Name: bcm2711_dev_gpio_init
- ****************************************************************************/
+ if (pin <= 8)
+ {
+ return BCM2711_GPIO_PULL_HIGH;
+ }
+ else
+ {
+ return BCM2711_GPIO_PULL_LOW;
+ }
-int bcm2711_dev_gpio_init(void)
+ /* 0-8 are HIGH, 9-27 are LOW. Other pins are not supported in RPI 4B */
+}
+
+static int rpi4b_register_gpio_output(uint8_t pin)
{
- int i;
- int ret = OK;
+ int ret = OK;
+ enum bcm2711_gpio_pull_e pull_status;
- /* Register output pins. */
+ gpioinfo("Registering GPIO pin %" PRIu8 " as OUTPUT", pin);
-#if BOARD_NGPIOOUT > 0
- for (i = 0; i < BOARD_NGPIOOUT; i++)
+ rpi4b_gpio_ops[pin].gpio.gp_pintype = GPIO_OUTPUT_PIN;
+ rpi4b_gpio_ops[pin].gpio.gp_ops = &gpout_ops;
+ rpi4b_gpio_ops[pin].pin = pin;
+
+ ret = gpio_pin_register(&rpi4b_gpio_ops[pin].gpio, pin);
+ if (predict_false(ret != OK))
{
- /* Setup and register the GPIO pin */
+ goto errout;
+ }
- g_gpout[i].gpio.gp_pintype = GPIO_OUTPUT_PIN;
- g_gpout[i].gpio.gp_ops = &gpout_ops;
- g_gpout[i].id = i;
- ret = gpio_pin_register(&g_gpout[i].gpio, g_gpiooutputs[i]);
- if (ret < 0)
- {
- gpioerr("Failed to register output pin %d (BCM2711 #%u): %d\n", i,
- g_gpiooutputs[i], ret);
- return ret;
- }
+ pull_status = rpi4b_gpio_pull_status(pin);
+ switch (pull_status)
+ {
+ case BCM2711_GPIO_PULL_NO:
+ bcm2711_gpio_set_pulls(pin, false, false);
+ break;
- /* Configure the pins that will be used as output.
- * They will start low and have no pull-up or pull-down resistors.
- */
+ case BCM2711_GPIO_PULL_HIGH:
+ bcm2711_gpio_set_pulls(pin, true, false);
+ break;
- bcm2711_gpio_set_pulls(g_gpiooutputs[i], false, false);
- bcm2711_gpio_set_func(g_gpiooutputs[i], BCM_GPIO_OUTPUT);
- bcm2711_gpio_pin_set(g_gpiooutputs[i], false);
+ case BCM2711_GPIO_PULL_LOW:
+ bcm2711_gpio_set_pulls(pin, false, true);
+ break;
}
-#endif
- /* Register input pins. */
+ bcm2711_gpio_set_func(pin, BCM_GPIO_OUTPUT);
+ bcm2711_gpio_pin_set(pin, false);
+ return ret;
-#if BOARD_NGPIOIN > 0
- for (i = 0; i < BOARD_NGPIOIN; i++)
+errout:
+ return ret;
+}
+
+static int rpi4b_register_gpio_input(uint8_t pin)
+{
+ int ret = OK;
Review Comment:
Don't space variables like this.
##########
boards/arm64/bcm2711/raspberrypi-4b/src/rpi4b_gpio.c:
##########
@@ -67,121 +66,94 @@
struct bcm2711_gpio_dev_s
{
struct gpio_dev_s gpio; /* Underlying GPIO device */
- uint8_t id; /* The index of the pin in its list. */
+ uint8_t pin; /* The index of the pin in its list. */
};
-/* GPIO device with interrupt capabilities on the BCM2711 */
-
-struct bcm2711_gpioint_dev_s
+enum bcm2711_gpio_pull_e
{
- struct bcm2711_gpio_dev_s bcm2711_gpio; /* BCM2711 GPIO device */
+ BCM2711_GPIO_PULL_NO,
+ BCM2711_GPIO_PULL_HIGH,
+ BCM2711_GPIO_PULL_LOW
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
-#if BOARD_NGPIOOUT > 0
-static int gpout_read(struct gpio_dev_s *dev, bool *value);
-static int gpout_write(struct gpio_dev_s *dev, bool value);
-#endif
-
-#if BOARD_NGPIOIN > 0
-static int gpin_read(struct gpio_dev_s *dev, bool *value);
-#endif
-
-#if BOARD_NGPIOINT > 0
-static int gpint_read(struct gpio_dev_s *dev, bool *value);
-static int gpint_attach(struct gpio_dev_s *dev, pin_interrupt_t callback);
-static int gpint_enable(struct gpio_dev_s *dev, bool enable);
-#endif
+static int rpi4b_gpout_read(struct gpio_dev_s *dev, bool *value);
+static int rpi4b_gpout_write(struct gpio_dev_s *dev, bool value);
+static int rpi4b_gpin_read(struct gpio_dev_s *dev, bool *value);
+static void rpi4b_set_gpio_funcs(void);
+static int rpi4b_register_gpio_output(uint8_t pin);
+static int rpi4b_register_gpio_input(uint8_t pin);
+static void rpi4b_register_pins(void);
/****************************************************************************
* Private Data
****************************************************************************/
-#if BOARD_NGPIOOUT > 0
-
/* GPIO operations for output pins. */
static const struct gpio_operations_s gpout_ops =
{
- .go_read = gpout_read,
- .go_write = gpout_write,
- .go_attach = NULL,
- .go_enable = NULL,
+ .go_read = rpi4b_gpout_read,
+ .go_write = rpi4b_gpout_write,
+ .go_attach = NULL,
+ .go_enable = NULL,
};
-/* This array maps the GPIO pins used as OUTPUT */
-
-static const uint32_t g_gpiooutputs[BOARD_NGPIOOUT] =
-{
- GPIO_OUT1,
-};
-
-/* GPIO output pin devices */
-
-static struct bcm2711_gpio_dev_s g_gpout[BOARD_NGPIOOUT];
-
-#endif /* BOARD_NGPIOOUT > 0 */
-
-#if BOARD_NGPIOIN > 0
-
/* GPIO operations for input pins. */
static const struct gpio_operations_s gpin_ops =
{
- .go_read = gpin_read,
- .go_write = NULL,
- .go_attach = NULL,
- .go_enable = NULL,
-};
-
-/* This array maps the GPIO pins used as INTERRUPT INPUTS */
-
-static const uint32_t g_gpioinputs[BOARD_NGPIOIN] =
-{
- GPIO_IN1,
-};
-
-/* GPIO input pin devices */
-
-static struct bcm2711_gpio_dev_s g_gpin[BOARD_NGPIOIN];
-
-#endif /* BOARD_NGPIOIN > 0 */
-
-#if BOARD_NGPIOINT > 0
-
-#warn "Missing functionality for interrupt GPIO pins."
-
-/* GPIO operations for interrupt pins. */
-
-static const struct gpio_operations_s gpint_ops =
-{
- .go_read = gpint_read,
- .go_write = NULL,
- .go_attach = gpint_attach,
- .go_enable = gpint_enable,
+ .go_read = rpi4b_gpin_read,
+ .go_write = NULL,
+ .go_attach = NULL,
+ .go_enable = NULL,
};
-/* This array maps the GPIO pins used as INTERRUPT INPUTS */
+/* TODO: Other types of ops */
-static const uint32_t g_gpiointinputs[BOARD_NGPIOINT] =
-{
- GPIO_IRQPIN1,
-};
-
-/* GPIO interrupt pin devices */
-
-static struct bcm2711_gpioint_dev_s g_gpint[BOARD_NGPIOINT];
+/* GPIO pin functions */
-#endif /* BOARD_NGPIOINT > 0 */
+static enum bcm2711_gpio_func_e rpi4b_gpios[RPI4B_NGPIO];
+static struct bcm2711_gpio_dev_s rpi4b_gpio_ops[RPI4B_NGPIO];
/****************************************************************************
* Private Functions
****************************************************************************/
-#if BOARD_NGPIOOUT > 0
+static void rpi4b_set_gpio_funcs(void)
+{
+ rpi4b_gpios[0] = (enum bcm2711_gpio_func_e) CONFIG_RPI4B_GPIO0;
Review Comment:
Why is this done in a function instead of statically?
##########
arch/arm64/src/bcm2711/bcm2711_gpio.c:
##########
@@ -370,35 +372,50 @@ void bcm2711_gpio_set_func(uint32_t gpio, enum
bcm2711_gpio_func_e func)
DEBUGASSERT(gpio < BCM_GPIO_NUM);
uint32_t value = 0;
+ uint32_t mask = 0;
+ uint32_t shift = 0;
+
if (gpio <= 9)
{
- value = (g_fsel_map[func] << (gpio * 3));
- modreg32(value, value, BCM_GPIO_GPFSEL0);
+ shift = (gpio * 3);
+ mask = (0x7 << shift);
+ value = (g_fsel_map[func] << shift);
+ modreg32(value, mask, BCM_GPIO_GPFSEL0);
}
- else if (gpio <= 19 && gpio > 9)
+ else if (gpio <= 19 && gpio >= 10)
{
- value = (g_fsel_map[func] << ((gpio - 10) * 3));
- modreg32(value, value, BCM_GPIO_GPFSEL1);
+ shift = ((gpio - 10) * 3);
+ mask = (0x7 << shift);
+ value = (g_fsel_map[func] << shift);
+ modreg32(value, mask, BCM_GPIO_GPFSEL1);
}
- else if (gpio <= 29 && gpio > 20)
+ else if (gpio <= 29 && gpio >= 20)
{
- value = (g_fsel_map[func] << ((gpio - 20) * 3));
- modreg32(value, value, BCM_GPIO_GPFSEL2);
+ shift = ((gpio - 20) * 3);
+ mask = (0x7 << shift);
+ value = (g_fsel_map[func] << shift);
+ modreg32(value, mask, BCM_GPIO_GPFSEL2);
Review Comment:
What's the benefit of this refactor? The additional mask isn't needed.
##########
boards/arm64/bcm2711/raspberrypi-4b/src/rpi4b_gpio.c:
##########
@@ -67,121 +66,94 @@
struct bcm2711_gpio_dev_s
{
struct gpio_dev_s gpio; /* Underlying GPIO device */
- uint8_t id; /* The index of the pin in its list. */
+ uint8_t pin; /* The index of the pin in its list. */
};
-/* GPIO device with interrupt capabilities on the BCM2711 */
-
-struct bcm2711_gpioint_dev_s
+enum bcm2711_gpio_pull_e
{
- struct bcm2711_gpio_dev_s bcm2711_gpio; /* BCM2711 GPIO device */
+ BCM2711_GPIO_PULL_NO,
+ BCM2711_GPIO_PULL_HIGH,
+ BCM2711_GPIO_PULL_LOW
};
/****************************************************************************
* Private Function Prototypes
****************************************************************************/
-#if BOARD_NGPIOOUT > 0
-static int gpout_read(struct gpio_dev_s *dev, bool *value);
-static int gpout_write(struct gpio_dev_s *dev, bool value);
-#endif
-
-#if BOARD_NGPIOIN > 0
-static int gpin_read(struct gpio_dev_s *dev, bool *value);
-#endif
-
-#if BOARD_NGPIOINT > 0
-static int gpint_read(struct gpio_dev_s *dev, bool *value);
-static int gpint_attach(struct gpio_dev_s *dev, pin_interrupt_t callback);
-static int gpint_enable(struct gpio_dev_s *dev, bool enable);
-#endif
+static int rpi4b_gpout_read(struct gpio_dev_s *dev, bool *value);
+static int rpi4b_gpout_write(struct gpio_dev_s *dev, bool value);
+static int rpi4b_gpin_read(struct gpio_dev_s *dev, bool *value);
+static void rpi4b_set_gpio_funcs(void);
+static int rpi4b_register_gpio_output(uint8_t pin);
+static int rpi4b_register_gpio_input(uint8_t pin);
+static void rpi4b_register_pins(void);
/****************************************************************************
* Private Data
****************************************************************************/
-#if BOARD_NGPIOOUT > 0
-
/* GPIO operations for output pins. */
static const struct gpio_operations_s gpout_ops =
{
- .go_read = gpout_read,
- .go_write = gpout_write,
- .go_attach = NULL,
- .go_enable = NULL,
+ .go_read = rpi4b_gpout_read,
+ .go_write = rpi4b_gpout_write,
+ .go_attach = NULL,
+ .go_enable = NULL,
};
-/* This array maps the GPIO pins used as OUTPUT */
-
-static const uint32_t g_gpiooutputs[BOARD_NGPIOOUT] =
-{
- GPIO_OUT1,
-};
-
-/* GPIO output pin devices */
-
-static struct bcm2711_gpio_dev_s g_gpout[BOARD_NGPIOOUT];
-
-#endif /* BOARD_NGPIOOUT > 0 */
-
-#if BOARD_NGPIOIN > 0
-
/* GPIO operations for input pins. */
static const struct gpio_operations_s gpin_ops =
{
- .go_read = gpin_read,
- .go_write = NULL,
- .go_attach = NULL,
- .go_enable = NULL,
-};
-
-/* This array maps the GPIO pins used as INTERRUPT INPUTS */
-
-static const uint32_t g_gpioinputs[BOARD_NGPIOIN] =
-{
- GPIO_IN1,
-};
-
-/* GPIO input pin devices */
-
-static struct bcm2711_gpio_dev_s g_gpin[BOARD_NGPIOIN];
-
-#endif /* BOARD_NGPIOIN > 0 */
-
-#if BOARD_NGPIOINT > 0
-
-#warn "Missing functionality for interrupt GPIO pins."
-
-/* GPIO operations for interrupt pins. */
-
-static const struct gpio_operations_s gpint_ops =
-{
- .go_read = gpint_read,
- .go_write = NULL,
- .go_attach = gpint_attach,
- .go_enable = gpint_enable,
+ .go_read = rpi4b_gpin_read,
+ .go_write = NULL,
+ .go_attach = NULL,
+ .go_enable = NULL,
};
-/* This array maps the GPIO pins used as INTERRUPT INPUTS */
+/* TODO: Other types of ops */
-static const uint32_t g_gpiointinputs[BOARD_NGPIOINT] =
-{
- GPIO_IRQPIN1,
-};
-
-/* GPIO interrupt pin devices */
-
-static struct bcm2711_gpioint_dev_s g_gpint[BOARD_NGPIOINT];
+/* GPIO pin functions */
-#endif /* BOARD_NGPIOINT > 0 */
+static enum bcm2711_gpio_func_e rpi4b_gpios[RPI4B_NGPIO];
+static struct bcm2711_gpio_dev_s rpi4b_gpio_ops[RPI4B_NGPIO];
Review Comment:
Why are there ops for each gpio pin?
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]