[PATCH 1/4] ARM: BCM2835: Add a function for doing an rmb() between device reads.

Lee Jones lee at kernel.org
Wed Mar 18 01:26:03 PDT 2015


On Thu, 12 Mar 2015, Eric Anholt wrote:

> Stephen Warren was concerned that the rmb() present in the new mailbox
> driver was unnecessary, and after seeing the docs, that it was just so
> surprising that somebody would come along and remove it later.  The
> explanation for the need for the rmb() is long enough that we won't
> want to place it at every callsite.  Make a wrapper with the whole
> explanation in it, so that anyone wondering what's going on sees the
> docs right there.
> 
> Signed-off-by: Eric Anholt <eric at anholt.net>
> ---
>  include/soc/bcm2835/peripheral-workaround.h | 75 +++++++++++++++++++++++++++++
>  1 file changed, 75 insertions(+)
>  create mode 100644 include/soc/bcm2835/peripheral-workaround.h
> 
> diff --git a/include/soc/bcm2835/peripheral-workaround.h b/include/soc/bcm2835/peripheral-workaround.h
> new file mode 100644
> index 0000000..4541a13
> --- /dev/null
> +++ b/include/soc/bcm2835/peripheral-workaround.h
> @@ -0,0 +1,75 @@
> +/*
> + * Copyright © 2015 Broadcom
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +static inline void bcm2835_peripheral_read_workaround(void)
> +{
> +#ifdef CONFIG_ARCH_BCM2835
> +	/*
> +	 * The BCM2835 bus is unusual in that it doesn't guarantee
> +	 * ordering between reads from different peripherals (where
> +	 * peripherals roughly correspond to Linux devices).  From
> +	 * BCM2835 ARM Peripherals.pdf, page 7:
> +	 *
> +	 *     "In order to keep the system complexity low and data
> +	 *      throughput high, the BCM2835 AXI system does not
> +	 *      always return read data in-order. The GPU has special
> +	 *      logic to cope with data arriving out-of-order; however
> +	 *      the ARM core does not contain such logic. Therefore
> +	 *      some precautions must be taken when using the ARM to
> +	 *      access peripherals.
> +	 *
> +	 *      Accesses to the same peripheral will always arrive and
> +	 *      return in-order. It is only when switching from one
> +	 *      peripheral to another that data can arrive
> +	 *      out-of-order. The simplest way to make sure that data
> +	 *      is processed in-order is to place a memory barrier
> +	 *      instruction at critical positions in the code. You
> +	 *      should place:
> +	 *
> +	 *      • A memory write barrier before the first write to a
> +	 *        peripheral.
> +	 *      • A memory read barrier after the last read of a
> +	 *        peripheral."
> +	 *
> +	 * The footnote explicitly says that:
> +	 *
> +	 *      "For example:
> +	 *
> +	 *         a_status = *pointer_to_peripheral_a;
> +	 *         b_status = *pointer_to_peripheral_b;
> +	 *
> +	 *       Without precuations the values ending up in the
> +	 *       variables a_status and b_status can be swapped
> +	 *       around."
> +	 *
> +	 * However, it also notes that, somewhat contrary to the first
> +	 * bullet point:
> +	 *
> +	 *     "It is theoretical possible for writes to go ‘wrong’
> +	 *      but that is far more difficult to achieve. The AXI
> +	 *      system makes sure the data always arrives in-order at
> +	 *      its intended destination. So:
> +	 *
> +	 *        *pointer_to_peripheral_a = value_a;
> +	 *        *pointer_to_peripheral_b = value_b;
> +	 *
> +	 *      will always give the expected result. The only time
> +	 *      write data can arrive out-of-order is if two different
> +	 *      peripherals are connected to the same external
> +	 *      equipment"
> +	 *
> +	 * Since we aren't interacting with multiple peripherals
> +	 * connected to the same external equipment as far as we know,
> +	 * that means that we only need to handle the read workaround
> +	 * case.  We do so by placing an rmb() at the first device
> +	 * read acceess in a given driver path, including the
> +	 * interrupt handlers.
> +	 */
> +	rmb();
> +#endif
> +}

The format:

#ifdef CONFIG_ARCH_BCM2835
static inline void bcm2835_peripheral_read_workaround(void)
{
        <stuff>
}
#else
static inline void bcm2835_peripheral_read_workaround(void) {}
#endif

... is more traditional in the Linux kernel.



More information about the linux-rpi-kernel mailing list