All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 2/2] dt-bindings: Add DT bindings info for FlexRM ring manager
From: Anup Patel @ 2017-01-04  5:34 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483508082-7008-1-git-send-email-anup.patel@broadcom.com>

This patch adds device tree bindings document for the FlexRM
ring manager found on Broadcom iProc SoCs.

Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Signed-off-by: Anup Patel <anup.patel@broadcom.com>
---
 .../bindings/mailbox/brcm,iproc-flexrm-mbox.txt    | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt

diff --git a/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
new file mode 100644
index 0000000..ca51a39
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
@@ -0,0 +1,60 @@
+Broadcom FlexRM Ring Manager
+============================
+The Broadcom FlexRM ring manager provides a set of rings which can be
+used to submit work to offload engines. An SoC may have multiple FlexRM
+hardware blocks. There is one device tree entry per FlexRM block. The
+FlexRM driver will create a mailbox-controller instance for given FlexRM
+hardware block where each mailbox channel is a separate FlexRM ring.
+
+Required properties:
+--------------------
+- compatible:	Should be "brcm,iproc-flexrm-mbox"
+- reg:		Specifies base physical address and size of the FlexRM
+		ring registers
+- msi-parent:	Phandles (and potential Device IDs) to MSI controllers
+		The FlexRM engine will send MSIs (instead of wired
+		interrupts) to CPU. There is one MSI for each FlexRM ring.
+		Refer devicetree/bindings/interrupt-controller/msi.txt
+- #mbox-cells:	Specifies the number of cells needed to encode a mailbox
+		channel. This should be 3.
+
+		The 1st cell is the mailbox channel number.
+
+		The 2nd cell contains MSI completion threshold. This is the
+		number of completion messages for which FlexRM will inject
+		one MSI interrupt to CPU.
+
+		The 3nd cell contains MSI timer value representing time for
+		which FlexRM will wait to accumulate N completion messages
+		where N is the value specified by 2nd cell above. If FlexRM
+		does not get required number of completion messages in time
+		specified by this cell then it will inject one MSI interrupt
+		to CPU provided atleast one completion message is available.
+
+Optional properties:
+--------------------
+- dma-coherent:	Present if DMA operations made by the FlexRM engine (such
+		as DMA descriptor access, access to buffers pointed by DMA
+		descriptors and read/write pointer updates to DDR) are
+		cache coherent with the CPU.
+
+Example:
+--------
+crypto_mbox: mbox at 67000000 {
+	compatible = "brcm,iproc-flexrm-mbox";
+	reg = <0x67000000 0x200000>;
+	msi-parent = <&gic_its 0x7f00>;
+	#mbox-cells = <3>;
+};
+
+crypto_client {
+	...
+	mboxes = <&crypto_mbox 0 0x1 0xffff>,
+		 <&crypto_mbox 1 0x1 0xffff>,
+		 <&crypto_mbox 16 0x1 0xffff>,
+		 <&crypto_mbox 17 0x1 0xffff>,
+		 <&crypto_mbox 30 0x1 0xffff>,
+		 <&crypto_mbox 31 0x1 0xffff>;
+	};
+	...
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 2/2] dt-bindings: Add DT bindings info for FlexRM ring manager
From: Anup Patel @ 2017-01-04  5:34 UTC (permalink / raw)
  To: Jassi Brar, Rob Herring
  Cc: Mark Rutland, Ray Jui, Scott Branden, Pramod KUMAR, Rob Rice,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	bcm-kernel-feedback-list-dY08KVG/lbpWk0Htik3J/w, Anup Patel
In-Reply-To: <1483508082-7008-1-git-send-email-anup.patel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>

This patch adds device tree bindings document for the FlexRM
ring manager found on Broadcom iProc SoCs.

Reviewed-by: Ray Jui <ray.jui-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Reviewed-by: Scott Branden <scott.branden-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
Signed-off-by: Anup Patel <anup.patel-dY08KVG/lbpWk0Htik3J/w@public.gmane.org>
---
 .../bindings/mailbox/brcm,iproc-flexrm-mbox.txt    | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt

diff --git a/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
new file mode 100644
index 0000000..ca51a39
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
@@ -0,0 +1,60 @@
+Broadcom FlexRM Ring Manager
+============================
+The Broadcom FlexRM ring manager provides a set of rings which can be
+used to submit work to offload engines. An SoC may have multiple FlexRM
+hardware blocks. There is one device tree entry per FlexRM block. The
+FlexRM driver will create a mailbox-controller instance for given FlexRM
+hardware block where each mailbox channel is a separate FlexRM ring.
+
+Required properties:
+--------------------
+- compatible:	Should be "brcm,iproc-flexrm-mbox"
+- reg:		Specifies base physical address and size of the FlexRM
+		ring registers
+- msi-parent:	Phandles (and potential Device IDs) to MSI controllers
+		The FlexRM engine will send MSIs (instead of wired
+		interrupts) to CPU. There is one MSI for each FlexRM ring.
+		Refer devicetree/bindings/interrupt-controller/msi.txt
+- #mbox-cells:	Specifies the number of cells needed to encode a mailbox
+		channel. This should be 3.
+
+		The 1st cell is the mailbox channel number.
+
+		The 2nd cell contains MSI completion threshold. This is the
+		number of completion messages for which FlexRM will inject
+		one MSI interrupt to CPU.
+
+		The 3nd cell contains MSI timer value representing time for
+		which FlexRM will wait to accumulate N completion messages
+		where N is the value specified by 2nd cell above. If FlexRM
+		does not get required number of completion messages in time
+		specified by this cell then it will inject one MSI interrupt
+		to CPU provided atleast one completion message is available.
+
+Optional properties:
+--------------------
+- dma-coherent:	Present if DMA operations made by the FlexRM engine (such
+		as DMA descriptor access, access to buffers pointed by DMA
+		descriptors and read/write pointer updates to DDR) are
+		cache coherent with the CPU.
+
+Example:
+--------
+crypto_mbox: mbox@67000000 {
+	compatible = "brcm,iproc-flexrm-mbox";
+	reg = <0x67000000 0x200000>;
+	msi-parent = <&gic_its 0x7f00>;
+	#mbox-cells = <3>;
+};
+
+crypto_client {
+	...
+	mboxes = <&crypto_mbox 0 0x1 0xffff>,
+		 <&crypto_mbox 1 0x1 0xffff>,
+		 <&crypto_mbox 16 0x1 0xffff>,
+		 <&crypto_mbox 17 0x1 0xffff>,
+		 <&crypto_mbox 30 0x1 0xffff>,
+		 <&crypto_mbox 31 0x1 0xffff>;
+	};
+	...
+};
-- 
2.7.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related

* Re: [Qemu-arm] [PATCH v4 6/8] hw/timer: Add Epson RX8900 RTC support
From: Alastair D'Silva @ 2017-01-04  5:34 UTC (permalink / raw)
  To: Andrew Jeffery, qemu-arm
  Cc: Peter Maydell, Joel Stanley, qemu-devel, Cédric Le Goater
In-Reply-To: <1483505971.22609.6.camel@aj.id.au>

On Wed, 2017-01-04 at 15:29 +1030, Andrew Jeffery wrote:

> Hi Alastair,
> 
> I have some mostly minor comments below.

Ok, I'm removing Chris from the CC list since he's left the team.

> 
> On Thu, 2016-12-15 at 16:48 +1100, Alastair D'Silva wrote:
> > > From: Alastair D'Silva <alastair@d-silva.org>
> > 
> > This patch adds support for the Epson RX8900 I2C RTC.
> > 
> > The following chip features are implemented:
> >  - RTC (wallclock based, ptimer 10x oversampling to pick up
> >         wallclock transitions)
> >  - Time update interrupt (per second/minute, wallclock based)
> >  - Alarms (wallclock based)
> >  - Temperature (set via a property)
> >  - Countdown timer (emulated clock via ptimer)
> >  - FOUT via GPIO (emulated clock via ptimer)
> > 
> > The following chip features are unimplemented:
> >  - Low voltage detection
> >  - i2c timeout
> > 
> > The implementation exports the following named GPIOs:
> > rx8900-interrupt-out
> > rx8900-fout-enable
> > rx8900-fout
> > 
> > > Signed-off-by: Alastair D'Silva <alastair@d-silva.org>
> > > Signed-off-by: Chris Smart <chris@distroguy.com>
> > 
> > ---
> >  default-configs/arm-softmmu.mak |   1 +
> >  hw/timer/Makefile.objs          |   2 +
> >  hw/timer/rx8900.c               | 912
> > ++++++++++++++++++++++++++++++++++++++++
> >  hw/timer/rx8900_regs.h          | 141 +++++++
> >  hw/timer/trace-events           |  31 ++
> >  5 files changed, 1087 insertions(+)
> >  create mode 100644 hw/timer/rx8900.c
> >  create mode 100644 hw/timer/rx8900_regs.h
> > 
> > diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-
> > softmmu.mak
> > index 6de3e16..adb600e 100644
> > --- a/default-configs/arm-softmmu.mak
> > +++ b/default-configs/arm-softmmu.mak
> > @@ -29,6 +29,7 @@ CONFIG_SMC91C111=y
> >  CONFIG_ALLWINNER_EMAC=y
> >  CONFIG_IMX_FEC=y
> >  CONFIG_DS1338=y
> > +CONFIG_RX8900=y
> >  CONFIG_PFLASH_CFI01=y
> >  CONFIG_PFLASH_CFI02=y
> >  CONFIG_MICRODRIVE=y
> > diff --git a/hw/timer/Makefile.objs b/hw/timer/Makefile.objs
> > index 7ba8c23..fa028ac 100644
> > --- a/hw/timer/Makefile.objs
> > +++ b/hw/timer/Makefile.objs
> > @@ -3,6 +3,7 @@ common-obj-$(CONFIG_ARM_MPTIMER) += arm_mptimer.o
> >  common-obj-$(CONFIG_A9_GTIMER) += a9gtimer.o
> >  common-obj-$(CONFIG_CADENCE) += cadence_ttc.o
> >  common-obj-$(CONFIG_DS1338) += ds1338.o
> > +common-obj-$(CONFIG_RX8900) += rx8900.o
> >  common-obj-$(CONFIG_HPET) += hpet.o
> >  common-obj-$(CONFIG_I8254) += i8254_common.o i8254.o
> >  common-obj-$(CONFIG_M48T59) += m48t59.o
> > @@ -17,6 +18,7 @@ common-obj-$(CONFIG_IMX) += imx_epit.o
> >  common-obj-$(CONFIG_IMX) += imx_gpt.o
> >  common-obj-$(CONFIG_LM32) += lm32_timer.o
> >  common-obj-$(CONFIG_MILKYMIST) += milkymist-sysctl.o
> > +common-obj-$(CONFIG_RX8900) += rx8900.o
> >  
> >  obj-$(CONFIG_EXYNOS4) += exynos4210_mct.o
> >  obj-$(CONFIG_EXYNOS4) += exynos4210_pwm.o
> > diff --git a/hw/timer/rx8900.c b/hw/timer/rx8900.c
> > new file mode 100644
> > index 0000000..cb1a2c8
> > --- /dev/null
> > +++ b/hw/timer/rx8900.c
> > @@ -0,0 +1,912 @@
> > +/*
> > + * Epson RX8900SA/CE Realtime Clock Module
> > + *
> > + * Copyright (c) 2016 IBM Corporation
> > + * Authors:
> > > + *  Alastair D'Silva <alastair@d-silva.org>
> > > + *  Chris Smart <chris@distroguy.com>
> > 
> > + *
> > + * This code is licensed under the GPL version 2 or later.  See
> > + * the COPYING file in the top-level directory.
> > + *
> > + * Datasheet available at:
> > + *  https://support.epson.biz/td/api/doc_check.php?dl=app_RX8900CE
> > &lang=en
> > + *
> > + * Not implemented:
> > + *  Implement i2c timeout
> > + */
> > +
> > +#include "qemu/osdep.h"
> > +#include "qemu-common.h"
> > +#include "hw/i2c/i2c.h"
> > +#include "hw/timer/rx8900_regs.h"
> > +#include "hw/ptimer.h"
> > +#include "qemu/main-loop.h"
> > +#include "qemu/bcd.h"
> > +#include "qemu/log.h"
> > +#include "qapi/error.h"
> > +#include "qapi/visitor.h"
> > +#include "trace.h"
> > +
> > +#define TYPE_RX8900 "rx8900"
> > +#define RX8900(obj) OBJECT_CHECK(RX8900State, (obj), TYPE_RX8900)
> > +
> > +typedef struct RX8900State {
> > +    I2CSlave parent_obj;
> > +
> > +    ptimer_state *sec_timer; /* triggered once per second */
> > +    ptimer_state *fout_timer;
> > +    ptimer_state *countdown_timer;
> > +    bool fout_state;
> > +    int64_t offset;
> > +    uint8_t weekday; /* Saved for deferred offset calculation, 0-6 
> > */
> > +    uint8_t wday_offset;
> > +    uint8_t nvram[RX8900_NVRAM_SIZE];
> > +    int32_t nvram_offset; /* Wrapped to stay within
> > RX8900_NVRAM_SIZE */
> > +    bool addr_byte;
> > +    uint8_t last_interrupt_seconds; /* The last time the second
> > timer ticked */
> > +    /* the last minute the timer update interrupt was triggered
> > (if enabled) */
> > +    uint8_t last_update_interrupt_minutes;
> > +    double supply_voltage;
> > +    qemu_irq interrupt_pin;
> > +    qemu_irq fout_pin;
> > +} RX8900State;
> > +
> > +static const VMStateDescription vmstate_rx8900 = {
> > +    .name = "rx8900",
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_I2C_SLAVE(parent_obj, RX8900State),
> > +        VMSTATE_PTIMER(sec_timer, RX8900State),
> > +        VMSTATE_PTIMER(fout_timer, RX8900State),
> > +        VMSTATE_PTIMER(countdown_timer, RX8900State),
> > +        VMSTATE_BOOL(fout_state, RX8900State),
> > +        VMSTATE_INT64(offset, RX8900State),
> > +        VMSTATE_UINT8(weekday, RX8900State),
> > +        VMSTATE_UINT8(wday_offset, RX8900State),
> > +        VMSTATE_UINT8_ARRAY(nvram, RX8900State,
> > RX8900_NVRAM_SIZE),
> > +        VMSTATE_INT32(nvram_offset, RX8900State),
> > +        VMSTATE_BOOL(addr_byte, RX8900State),
> > +        VMSTATE_UINT8(last_interrupt_seconds, RX8900State),
> > +        VMSTATE_UINT8(last_update_interrupt_minutes, RX8900State),
> > +        VMSTATE_END_OF_LIST()
> > +    }
> > +};
> > +
> > +static void rx8900_reset(DeviceState *dev);
> > +
> > +static void capture_current_time(RX8900State *s)
> > +{
> > +    /* Capture the current time into the secondary registers
> > +     * which will be actually read by the data transfer operation.
> > +     */
> > +    struct tm now;
> > +    qemu_get_timedate(&now, s->offset);
> > +    s->nvram[SECONDS] = to_bcd(now.tm_sec);
> > +    s->nvram[MINUTES] = to_bcd(now.tm_min);
> > +    s->nvram[HOURS] = to_bcd(now.tm_hour);
> > +
> > +    s->nvram[WEEKDAY] = 0x01 << ((now.tm_wday + s->wday_offset) %
> > 7);
> > +    s->nvram[DAY] = to_bcd(now.tm_mday);
> > +    s->nvram[MONTH] = to_bcd(now.tm_mon + 1);
> > +    s->nvram[YEAR] = to_bcd(now.tm_year % 100);
> > +
> > +    s->nvram[EXT_SECONDS] = s->nvram[SECONDS];
> > +    s->nvram[EXT_MINUTES] = s->nvram[MINUTES];
> > +    s->nvram[EXT_HOURS] = s->nvram[HOURS];
> > +    s->nvram[EXT_WEEKDAY] = s->nvram[WEEKDAY];
> > +    s->nvram[EXT_DAY] = s->nvram[DAY];
> > +    s->nvram[EXT_MONTH] = s->nvram[MONTH];
> > +    s->nvram[EXT_YEAR] = s->nvram[YEAR];
> 
> +
> > +    trace_rx8900_capture_current_time(now.tm_hour, now.tm_min,
> > now.tm_sec,
> > +            (now.tm_wday + s->wday_offset) % 7,
> > +            now.tm_mday, now.tm_mon, now.tm_year + 1900,
> > +            s->nvram[HOURS], s->nvram[MINUTES], s->nvram[SECONDS],
> > +            s->nvram[WEEKDAY], s->nvram[DAY], s->nvram[MONTH], s-
> > >nvram[YEAR]);
> > +}
> > +
> > +/**
> > + * Increment the internal register pointer, dealing with wrapping
> > + * @param s the RTC to operate on
> > + */
> > +static void inc_regptr(RX8900State *s)
> > +{
> > +    /* The register pointer wraps around after 0x1F
> > +     */
> > +    s->nvram_offset = (s->nvram_offset + 1) & (RX8900_NVRAM_SIZE -
> > 1);
> > +    trace_rx8900_regptr_update(s->nvram_offset);
> > +
> > +    if (s->nvram_offset == START_ADDRESS) {
> > +        trace_rx8900_regptr_overflow();
> > +        capture_current_time(s);
> > +    }
> > +}
> > +
> > +#define INVALID_WEEKDAY 0xff
> > +
> > +/**
> > + * Receive an I2C Event
> > + * @param i2c the i2c device instance
> > + * @param event the event to handle
> > + */
> > +static void rx8900_event(I2CSlave *i2c, enum i2c_event event)
> > +{
> > +    RX8900State *s = RX8900(i2c);
> > +
> > +    switch (event) {
> > +    case I2C_START_RECV:
> > +        /* In h/w, time capture happens on any START condition,
> > not just a
> > +         * START_RECV. For the emulation, it doesn't actually
> > matter,
> > +         * since a START_RECV has to occur before the data can be
> > read.
> > +         */
> > +        capture_current_time(s);
> > +        break;
> > +    case I2C_START_SEND:
> > +        s->addr_byte = true;
> > +        break;
> > +    case I2C_FINISH:
> > +        if (s->weekday < 7) {
> > +            /* We defer the weekday calculation as it is handed to
> > us before
> > +             * the date has been updated. If we calculate the
> > weekday offset
> > +             * when it is passed to us, we will incorrectly
> > determine it
> > +             * based on the current emulated date, rather than the
> > date that
> > +             * has been written.
> > +             */
> > +            struct tm now;
> > +            qemu_get_timedate(&now, s->offset);
> > +
> > +            s->wday_offset = (s->weekday - now.tm_wday + 7) % 7;
> > +
> > +            trace_rx8900_event_weekday(s->weekday, BIT(s-
> > >weekday),
> > +                    s->wday_offset);
> > +
> > +            s->weekday = INVALID_WEEKDAY;
> > +        }
> > +        break;
> > +
> > +    default:
> > +        break;
> > +    }
> > +}
> > +
> > +/**
> > + * Perform an i2c receive action
> > + * @param i2c the i2c device instance
> > + * @return the value of the current register
> > + * @post the internal register pointer is incremented
> > + */
> > +static int rx8900_recv(I2CSlave *i2c)
> > +{
> > +    RX8900State *s = RX8900(i2c);
> > +    uint8_t res = s->nvram[s->nvram_offset];
> > +    trace_rx8900_read_register(s->nvram_offset, res);
> > +    inc_regptr(s);
> > +    return res;
> > +}
> > +
> > +/**
> > + * Disable the countdown timer
> > + * @param s the RTC to operate on
> > + */
> > +static void disable_countdown_timer(RX8900State *s)
> > +{
> > +    trace_rx8900_disable_countdown_timer();
> > +    ptimer_stop(s->countdown_timer);
> > +}
> > +
> > +/**
> > + * Enable the countdown timer
> > + * @param s the RTC to operate on
> > + */
> > +static void enable_countdown_timer(RX8900State *s)
> > +{
> > +    trace_rx8900_enable_countdown_timer();
> > +    ptimer_run(s->countdown_timer, 0);
> > +}
> > +
> > +/**
> > + * Tick the countdown timer
> > + * @param opaque the device instance
> > + */
> > +static void rx8900_countdown_tick(void *opaque)
> > +{
> > +    RX8900State *s = (RX8900State *)opaque;
> > +
> > +    uint16_t count = s->nvram[TIMER_COUNTER_0] |
> > +            ((s->nvram[TIMER_COUNTER_1] & 0x0F) << 8);
> > +    trace_rx8900_countdown_tick(count);
> > +    count--;
> > +
> > +    s->nvram[TIMER_COUNTER_0] = (uint8_t)(count & 0x00ff);
> > +    s->nvram[TIMER_COUNTER_1] = (uint8_t)((count & 0x0f00) >> 8);
> > +
> > +    if (count == 0) {
> > +        trace_rx8900_countdown_elapsed();
> > +
> > +        disable_countdown_timer(s);
> > +
> > +        s->nvram[FLAG_REGISTER] |= FLAG_MASK_TF;
> > +
> > +        if (s->nvram[CONTROL_REGISTER] & CTRL_MASK_TIE) {
> > +            trace_rx8900_fire_interrupt();
> > +            qemu_irq_pulse(s->interrupt_pin);
> > +        }
> > +    }
> > +}
> > +
> > +/**
> > + * Disable the per second timer
> > + * @param s the RTC to operate on
> > + */
> > +static void disable_timer(RX8900State *s)
> > +{
> > +    trace_rx8900_disable_timer();
> > +    ptimer_stop(s->sec_timer);
> > +}
> > +
> > +/**
> > + * Enable the per second timer
> > + * @param s the RTC to operate on
> > + */
> > +static void enable_timer(RX8900State *s)
> > +{
> > +    trace_rx8900_enable_timer();
> > +    ptimer_run(s->sec_timer, 0);
> > +}
> > +
> > +/**
> > + * Tick the per second timer (can be called more frequently as it
> > early exits
> > + * if the wall clock has not progressed)
> > + * @param opaque the RTC to tick
> > + */
> > +static void rx8900_timer_tick(void *opaque)
> > +{
> > +    RX8900State *s = (RX8900State *)opaque;
> > +    struct tm now;
> > +    bool fire_interrupt = false;
> > +    bool alarm_week_day_matches;
> > +
> > +    qemu_get_timedate(&now, s->offset);
> > +
> > +    if (now.tm_sec == s->last_interrupt_seconds) {
> > +        return;
> > +    }
> > +
> > +    s->last_interrupt_seconds = now.tm_sec;
> > +
> > +    trace_rx8900_tick();
> > +
> > +    /* Update timer interrupt */
> > +    if (s->nvram[CONTROL_REGISTER] & CTRL_MASK_UIE) {
> > +        if ((s->nvram[EXTENSION_REGISTER] & EXT_MASK_USEL) &&
> > +                now.tm_min != s->last_update_interrupt_minutes) {
> > +            s->last_update_interrupt_minutes = now.tm_min;
> > +            s->nvram[FLAG_REGISTER] |= FLAG_MASK_UF;
> > +            fire_interrupt = true;
> > +        } else if (!(s->nvram[EXTENSION_REGISTER] &
> > EXT_MASK_USEL)) {
> > +            /* per second update interrupt */
> > +            s->nvram[FLAG_REGISTER] |= FLAG_MASK_UF;
> > +            fire_interrupt = true;
> > +        }
> > +    }
> > +
> > +    /* Alarm interrupt */
> > +    if ((s->nvram[EXTENSION_REGISTER] & EXT_MASK_WADA)) {
> > +        alarm_week_day_matches =
> > +            s->nvram[ALARM_WEEK_DAY] == to_bcd(now.tm_mday);
> > +    } else {
> > +        alarm_week_day_matches =
> > +            s->nvram[ALARM_WEEK_DAY] ==
> > +                0x01 << ((now.tm_wday + s->wday_offset) % 7);
> > +    }
> > +
> > +    if ((s->nvram[CONTROL_REGISTER] & CTRL_MASK_AIE) && now.tm_sec
> > == 0 &&
> > +            s->nvram[ALARM_MINUTE] == to_bcd(now.tm_min) &&
> > +            s->nvram[ALARM_HOUR] == to_bcd(now.tm_hour) &&
> > +            alarm_week_day_matches) {
> > +        trace_rx8900_trigger_alarm();
> > +        s->nvram[FLAG_REGISTER] |= FLAG_MASK_AF;
> > +        fire_interrupt = true;
> > +    }
> > +
> > +    if (fire_interrupt) {
> > +        trace_rx8900_fire_interrupt();
> > +        qemu_irq_pulse(s->interrupt_pin);
> > +    }
> > +}
> > +
> > +
> > +#define COUNTDOWN_TIMER_FREQ 4096
> > +
> > +/**
> > + * Validate the extension register and perform actions based on
> > the bits
> > + * @param s the RTC to operate on
> > + * @param data the new data for the extension register
> > + */
> > +static void update_extension_register(RX8900State *s, uint8_t
> > data)
> > +{
> > +    if (data & EXT_MASK_TEST) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                "Test bit is enabled but is forbidden by the
> > manufacturer");
> > +    }
> > +
> > +    if ((data ^ s->nvram[EXTENSION_REGISTER]) &
> > +            (EXT_MASK_FSEL0 | EXT_MASK_FSEL1)) {
> > +        /* FSELx has changed */
> > +
> > +        switch (data & (EXT_MASK_FSEL0 | EXT_MASK_FSEL1)) {
> > +        case EXT_MASK_FSEL0:
> > +            trace_rx8900_set_fout(1024);
> > +            ptimer_set_limit(s->fout_timer, 32, 1);
> > +            break;
> > +        case EXT_MASK_FSEL1:
> > +            trace_rx8900_set_fout(1);
> > +            ptimer_set_limit(s->fout_timer, 32768, 1);
> > +            break;
> > +        case 0:
> > +        case (EXT_MASK_FSEL0 | EXT_MASK_FSEL1):
> > +            trace_rx8900_set_fout(32768);
> > +            ptimer_set_limit(s->fout_timer, 1, 1);
> > +            break;
> > +        }
> > +    }
> > +
> > +    if ((data ^ s->nvram[EXTENSION_REGISTER]) &
> > +            (EXT_MASK_TSEL0 | EXT_MASK_TSEL1)) {
> > +        /* TSELx has changed */
> > +        switch (data & (EXT_MASK_TSEL0 | EXT_MASK_TSEL1)) {
> > +        case 0:
> > +            trace_rx8900_set_countdown_timer(64);
> > +            ptimer_set_limit(s->countdown_timer,
> > COUNTDOWN_TIMER_FREQ / 64, 1);
> > +            break;
> > +        case EXT_MASK_TSEL0:
> > +            trace_rx8900_set_countdown_timer(1);
> > +            ptimer_set_limit(s->countdown_timer,
> > COUNTDOWN_TIMER_FREQ, 1);
> > +            break;
> > +        case EXT_MASK_TSEL1:
> > +            trace_rx8900_set_countdown_timer_per_minute();
> > +            ptimer_set_limit(s->countdown_timer,
> > COUNTDOWN_TIMER_FREQ * 60, 1);
> > +            break;
> > +        case (EXT_MASK_TSEL0 | EXT_MASK_TSEL1):
> > +            trace_rx8900_set_countdown_timer(COUNTDOWN_TIMER_FREQ)
> > ;
> > +            ptimer_set_limit(s->countdown_timer, 1, 1);
> > +            break;
> > +        }
> > +    }
> > +
> > +    if (data & EXT_MASK_TE) {
> > +        enable_countdown_timer(s);
> > +    }
> > +
> > +    s->nvram[EXTENSION_REGISTER] = data;
> > +    s->nvram[EXT_EXTENSION_REGISTER] = data;
> > +
> > +}
> > +/**
> > + * Validate the control register and perform actions based on the
> > bits
> > + * @param s the RTC to operate on
> > + * @param data the new value for the control register
> > + */
> > +
> > +static void update_control_register(RX8900State *s, uint8_t data)
> > +{
> > +    uint8_t diffmask = ~s->nvram[CONTROL_REGISTER] & data;
> > +
> > +    if (diffmask & CTRL_MASK_WP0) {
> > +        data &= ~CTRL_MASK_WP0;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Attempt to write to write protected bit %d in control
> > register",
> > +            CTRL_REG_WP0);
> > +    }
> > +
> > +    if (diffmask & CTRL_MASK_WP1) {
> > +        data &= ~CTRL_MASK_WP1;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Attempt to write to write protected bit %d in control
> > register",
> > +            CTRL_REG_WP1);
> > +    }
> > +
> > +    if (data & CTRL_MASK_RESET) {
> > +        data &= ~CTRL_MASK_RESET;
> > +        rx8900_reset(DEVICE(s));
> > +    }
> > +
> > +    if (diffmask & CTRL_MASK_UIE) {
> > +        /* Update interrupts were off and are now on */
> > +        struct tm now;
> > +
> > +        trace_rx8900_enable_update_timer();
> > +
> > +        qemu_get_timedate(&now, s->offset);
> > +
> > +        s->last_update_interrupt_minutes = now.tm_min;
> > +        s->last_interrupt_seconds = now.tm_sec;
> > +        enable_timer(s);
> > +    }
> > +
> > +    if (diffmask & CTRL_MASK_AIE) {
> > +        /* Alarm interrupts were off and are now on */
> > +        struct tm now;
> > +
> > +        trace_rx8900_enable_alarm();
> > +
> > +        qemu_get_timedate(&now, s->offset);
> > +
> > +        s->last_interrupt_seconds = now.tm_sec;
> > +        enable_timer(s);
> > +    }
> > +
> > +    if (!(data & (CTRL_MASK_UIE | CTRL_MASK_AIE))) {
> > +        disable_timer(s);
> > +    }
> > +
> > +    s->nvram[CONTROL_REGISTER] = data;
> > +    s->nvram[EXT_CONTROL_REGISTER] = data;
> > +}
> > +
> > +/**
> > + * Validate the flag register
> > + * @param s the RTC to operate on
> > + * @param data the new value for the flag register
> > + */
> > +static void validate_flag_register(RX8900State *s, uint8_t *data)
> > +{
> > +    uint8_t diffmask = ~s->nvram[FLAG_REGISTER] & *data;
> > +
> > +    if (diffmask & FLAG_MASK_VDET) {
> > +        *data &= ~FLAG_MASK_VDET;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Only 0 can be written to VDET bit %d in the flag
> > register",
> > +            FLAG_REG_VDET);
> > +    }
> > +
> > +    if (diffmask & FLAG_MASK_VLF) {
> > +        *data &= ~FLAG_MASK_VLF;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Only 0 can be written to VLF bit %d in the flag
> > register",
> > +            FLAG_REG_VLF);
> > +    }
> > +
> > +    if (diffmask & FLAG_MASK_UNUSED_2) {
> > +        *data &= ~FLAG_MASK_UNUSED_2;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Only 0 can be written to unused bit %d in the flag
> > register",
> > +            FLAG_REG_UNUSED_2);
> > +    }
> > +
> > +    if (diffmask & FLAG_MASK_UNUSED_6) {
> > +        *data &= ~FLAG_MASK_UNUSED_6;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Only 0 can be written to unused bit %d in the flag
> > register",
> > +            FLAG_REG_UNUSED_6);
> > +    }
> > +
> > +    if (diffmask & FLAG_MASK_UNUSED_7) {
> > +        *data &= ~FLAG_MASK_UNUSED_7;
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +            "Only 0 can be written to unused bit %d in the flag
> > register",
> > +            FLAG_REG_UNUSED_7);
> > +    }
> > +}
> > +
> > +/**
> > + * Handle FOUT_ENABLE (FOE) line
> > + * Enables/disables the FOUT line
> > + * @param opaque the device instance
> > + * @param n the IRQ number
> > + * @param level true if the line has been raised
> > + */
> > +static void rx8900_fout_enable_handler(void *opaque, int n, int
> > level)
> > +{
> > +    RX8900State *s = RX8900(opaque);
> > +
> > +    if (level) {
> > +        trace_rx8900_enable_fout();
> > +        ptimer_run(s->fout_timer, 0);
> > +    } else {
> > +        /* disable fout */
> > +        trace_rx8900_disable_fout();
> > +        ptimer_stop(s->fout_timer);
> > +    }
> > +}
> > +
> > +/**
> > + * Tick the FOUT timer
> > + * @param opaque the device instance
> > + */
> > +static void rx8900_fout_tick(void *opaque)
> > +{
> > +    RX8900State *s = (RX8900State *)opaque;
> > +
> > +    trace_rx8900_fout_toggle();
> > +    s->fout_state = !s->fout_state;
> > +
> > +    if (s->fout_state) {
> > +        qemu_irq_raise(s->fout_pin);
> > +    } else {
> > +        qemu_irq_lower(s->fout_pin);
> > +    }
> 
> We could just use qemu_set_irq() here.
> 

Ok

> > +}
> > +
> > +/**
> > + * Verify the current voltage and raise flags if it is low
> > + * @param s the RTC to operate on
> > + */
> > +static void check_voltage(RX8900State *s)
> > +{
> > +    if (!(s->nvram[BACKUP_FUNCTION] & BACKUP_MASK_VDETOFF)) {
> > +        if (s->supply_voltage < 2.0) {
> > +            s->nvram[FLAG_REGISTER] |= FLAG_MASK_VDET;
> > +        }
> > +
> > +        if (s->supply_voltage < 1.6) {
> > +            s->nvram[FLAG_REGISTER] |= FLAG_MASK_VLF;
> > +        }
> > +    }
> > +}
> > +
> > +/**
> > + * Receive a byte of data from i2c
> > + * @param i2c the i2c device that is receiving data
> > + * @param data the data that was received
> > + */
> > +static int rx8900_send(I2CSlave *i2c, uint8_t data)
> > +{
> > +    RX8900State *s = RX8900(i2c);
> > +    struct tm now;
> > +
> > +    trace_rx8900_i2c_data_receive(data);
> > +
> > +    if (s->addr_byte) {
> > +        s->nvram_offset = data & (RX8900_NVRAM_SIZE - 1);
> > +        trace_rx8900_regptr_update(s->nvram_offset);
> > +        s->addr_byte = false;
> > +        return 0;
> > +    }
> > +
> > +    trace_rx8900_set_register(s->nvram_offset, data);
> > +
> > +    qemu_get_timedate(&now, s->offset);
> > +    switch (s->nvram_offset) {
> > +    case SECONDS:
> > +    case EXT_SECONDS:
> > +        now.tm_sec = from_bcd(data & 0x7f);
> > +        if (now.tm_sec > 59) { /* RX8900 does not support leap
> > seconds */
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "RX8900 - second data '%x' is out of range, "
> > +                    "undefined behavior will result", data);
> > +        }
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case MINUTES:
> > +    case EXT_MINUTES:
> > +        now.tm_min = from_bcd(data & 0x7f);
> > +        if (now.tm_min > 59) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "RX8900 - minute data '%x' is out of range, "
> > +                    "undefined behavior will result", data);
> > +        }
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case HOURS:
> > +    case EXT_HOURS:
> > +        now.tm_hour = from_bcd(data & 0x3f);
> > +        if (now.tm_hour > 24) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "RX8900 - hour data '%x' is out of range, "
> > +                    "undefined behavior will result", data);
> > +        }
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case WEEKDAY:
> > +    case EXT_WEEKDAY: {
> > +        int user_wday = ctz32(data);
> > +        /* The day field is supposed to contain a value in
> > +         * with only 1 of bits 0-6 set. Otherwise behavior is
> > undefined.
> > +         */
> > +        switch (data) {
> > +        case 0x01:
> > +        case 0x02:
> > +        case 0x04:
> > +        case 0x08:
> > +        case 0x10:
> > +        case 0x20:
> > +        case 0x40:
> > +            break;
> > +        default:
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                "RX8900 - weekday data '%x' is out of range, "
> > +                        "undefined behavior will result", data);
> > +            break;
> > +        }
> 
> I think it might be clearer to do:
> 
>     if (data == 0x80 || ctpop8(data) != 1) {
>         qemu_log_mask(...);
>         break;
>     }

Ok, I've also factored this out.

> > +        s->weekday = user_wday;
> > +        break;
> > +    }
> > +
> > +    case DAY:
> > +    case EXT_DAY:
> > +        now.tm_mday = from_bcd(data & 0x3f);
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case MONTH:
> > +    case EXT_MONTH:
> > +        now.tm_mon = from_bcd(data & 0x1f) - 1;
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case YEAR:
> > +    case EXT_YEAR:
> > +        now.tm_year = from_bcd(data) + 100;
> > +        s->offset = qemu_timedate_diff(&now);
> > +        break;
> > +
> > +    case EXTENSION_REGISTER:
> > +    case EXT_EXTENSION_REGISTER:
> > +        update_extension_register(s, data);
> > +        break;
> > +
> > +    case FLAG_REGISTER:
> > +    case EXT_FLAG_REGISTER:
> > +        validate_flag_register(s, &data);
> > +
> > +        s->nvram[FLAG_REGISTER] = data;
> > +        s->nvram[EXT_FLAG_REGISTER] = data;
> > +
> > +        check_voltage(s);
> > +        break;
> > +
> > +    case CONTROL_REGISTER:
> > +    case EXT_CONTROL_REGISTER:
> > +        update_control_register(s, data);
> > +        break;
> > +
> > +    default:
> > +        s->nvram[s->nvram_offset] = data;
> > +    }
> > +
> > +    inc_regptr(s);
> > +    return 0;
> > +}
> > +
> > +/**
> > + * Get the device temperature in Celsius as a property
> > + * @param obj the device
> > + * @param v
> > + * @param name the property name
> > + * @param opaque
> > + * @param errp an error object to populate on failure
> > + */
> > +static void rx8900_get_temperature(Object *obj, Visitor *v, const
> > char *name,
> > +                                   void *opaque, Error **errp)
> > +{
> > +    RX8900State *s = RX8900(obj);
> > +    double value = (s->nvram[TEMPERATURE] * 2.0 - 187.1) / 3.218;
> 
> You have encode_temperature() below, should we not have
> decode_temperature()?
> 

This was factored out as it was needed in multiple places, the decoding
is only done in a single place.

> > +
> > +    trace_rx8900_get_temperature(s->nvram[TEMPERATURE], value);
> > +
> > +    visit_type_number(v, name, &value, errp);
> > +}
> > +
> > +/**
> > + * Encode a temperature in Celsius
> > + * @param celsius the temperature
> > + * @return the encoded temperature
> > + */
> > +static inline uint8_t encode_temperature(double celsius)
> > +{
> > +    return (uint8_t) ((celsius * 3.218 + 187.19) / 2);
> > +}
> > +
> > +/**
> > + * Set the device temperature in Celsius as a property
> > + * @param obj the device
> > + * @param v
> > + * @param name the property name
> > + * @param opaque
> > + * @param errp an error object to populate on failure
> > + */
> > +static void rx8900_set_temperature(Object *obj, Visitor *v, const
> > char *name,
> > +                                   void *opaque, Error **errp)
> > +{
> > +    RX8900State *s = RX8900(obj);
> > +    Error *local_err = NULL;
> > +    double temp; /* degrees Celsius */
> > +    visit_type_number(v, name, &temp, &local_err);
> > +    if (local_err) {
> > +        error_propagate(errp, local_err);
> > +        return;
> > +    }
> > +    if (temp >= 100 || temp < -58) {
> > +        error_setg(errp, "value %fC is out of range", temp);
> > +        return;
> > +    }
> > +
> > +    s->nvram[TEMPERATURE] = encode_temperature(temp);
> > +
> > +    trace_rx8900_set_temperature(s->nvram[TEMPERATURE], temp);
> > +}
> > +
> > +/**
> > + * Get the device supply voltage as a property
> > + * @param obj the device
> > + * @param v
> > + * @param name the property name
> > + * @param opaque
> > + * @param errp an error object to populate on failure
> > + */
> > +static void rx8900_get_voltage(Object *obj, Visitor *v, const char
> > *name,
> > +                                   void *opaque, Error **errp)
> > +{
> > +    RX8900State *s = RX8900(obj);
> > +
> > +    visit_type_number(v, name, &s->supply_voltage, errp);
> > +}
> > +
> > +/**
> > + * Set the device supply voltage as a property
> > + * @param obj the device
> > + * @param v
> > + * @param name the property name
> > + * @param opaque
> > + * @param errp an error object to populate on failure
> > + */
> > +static void rx8900_set_voltage(Object *obj, Visitor *v, const char
> > *name,
> > +                                   void *opaque, Error **errp)
> > +{
> > +    RX8900State *s = RX8900(obj);
> > +    Error *local_err = NULL;
> > +    double temp;
> 
> Maybe voltage? It's ambiguous as to whether it's a copy/paste error
> from 'temperature' or if you mean 'temporary'...

Ok, it meant temporary, but voltage is more meaningful.

> > +    visit_type_number(v, name, &temp, &local_err);
> > +    if (local_err) {
> > +        error_propagate(errp, local_err);
> > +        return;
> > +    }
> > +
> > +    s->supply_voltage = temp;
> 
> ... as this almost looks like a bug.
> 
> > +    trace_rx8900_set_voltage(s->supply_voltage);
> > +
> > +    check_voltage(s);
> > +}
> > +
> > +
> > +/**
> > + * Configure device properties
> > + * @param obj the device
> > + */
> > +static void rx8900_initfn(Object *obj)
> > +{
> > +    object_property_add(obj, "temperature", "number",
> > +                        rx8900_get_temperature,
> > +                        rx8900_set_temperature, NULL, NULL, NULL);
> > +
> > +    object_property_add(obj, "voltage", "number",
> > +                        rx8900_get_voltage,
> > +                        rx8900_set_voltage, NULL, NULL, NULL);
> > +}
> > +
> > +/**
> > + * Reset the device
> > + * @param dev the RX8900 device to reset
> > + */
> > +static void rx8900_reset(DeviceState *dev)
> 
> Is there a reason we can't define rx8900_reset() further up the file
> to
> avoid forward-declaring it?
> 

We could, but I wanted to keep it close to the other init code.

> > +{
> > +    RX8900State *s = RX8900(dev);
> > +
> > +    trace_rx8900_reset();
> > +
> > +    /* The clock is running and synchronized with the host */
> > +    s->offset = 0;
> > +    s->weekday = 7; /* Set to an invalid value */
> 
> Can you explain why we do that rather than just stating that 7 is
> invalid?

Hmm, that should have been a #define.

> 
> > +
> > +    s->nvram[EXTENSION_REGISTER] = EXT_MASK_TSEL1;
> > +    s->nvram[CONTROL_REGISTER] = CTRL_MASK_CSEL0;
> > +    s->nvram[FLAG_REGISTER] &= FLAG_MASK_VDET | FLAG_MASK_VLF;
> > +
> > +    s->nvram_offset = 0;
> > +
> > +    trace_rx8900_regptr_update(s->nvram_offset);
> > +
> > +    s->addr_byte = false;
> > +}
> > +
> > +/**
> > + * Realize an RX8900 device instance
> > + * Set up timers
> > + * Configure GPIO lines
> > + * @param dev the device instance to realize
> > + * @param errp an error object to populate on error
> > + */
> > +static void rx8900_realize(DeviceState *dev, Error **errp)
> > +{
> > +    RX8900State *s = RX8900(dev);
> > +    I2CSlave *i2c = I2C_SLAVE(dev);
> > +    QEMUBH *bh;
> > +    char name[64];
> > +
> > +    s->fout_state = false;
> > +
> > +    memset(s->nvram, 0, RX8900_NVRAM_SIZE);
> > +    /* Set the initial state to 25 degrees Celsius */
> > +    s->nvram[TEMPERATURE] = encode_temperature(25.0);
> > +
> > +    /* Set up timers */
> > +    bh = qemu_bh_new(rx8900_timer_tick, s);
> > +    s->sec_timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
> > +    /* we trigger the timer at 10Hz and check for rollover, as the
> > qemu
> > +     * clock does not advance in realtime in the test environment,
> > +     * leading to unstable test results
> > +     */
> 
> It seems unfortunate that some of the code seems dedicated to working
> around test suite issues (e.g. early return in some functions). Ah
> well.
> 
> > +    ptimer_set_freq(s->sec_timer, 10);
> > +    ptimer_set_limit(s->sec_timer, 1, 1);
> > +
> > +    bh = qemu_bh_new(rx8900_fout_tick, s);
> > +    s->fout_timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
> > +    /* frequency doubled to generate 50% duty cycle square wave */
> > +    ptimer_set_freq(s->fout_timer, 32768 * 2);
> > +    ptimer_set_limit(s->fout_timer, 1, 1);
> > +
> > +    bh = qemu_bh_new(rx8900_countdown_tick, s);
> > +    s->countdown_timer = ptimer_init(bh, PTIMER_POLICY_DEFAULT);
> > +    ptimer_set_freq(s->countdown_timer, COUNTDOWN_TIMER_FREQ);
> > +    ptimer_set_limit(s->countdown_timer, COUNTDOWN_TIMER_FREQ, 1);
> > +
> > +
> > +    /* set up GPIO */
> > +    snprintf(name, sizeof(name), "rx8900-interrupt-out");
> 
> Why the snprintf()s?

These were constructed in an earlier version, but it's not necessary
now.

> 
> > +    qdev_init_gpio_out_named(&i2c->qdev, &s->interrupt_pin, name,
> > 1);
> > +    trace_rx8900_pin_name("Interrupt", name);
> > +
> > +    snprintf(name, sizeof(name), "rx8900-fout-enable");
> > +    qdev_init_gpio_in_named(&i2c->qdev,
> > rx8900_fout_enable_handler, name, 1);
> > +    trace_rx8900_pin_name("Fout-enable", name);
> > +
> > +    snprintf(name, sizeof(name), "rx8900-fout");
> > +    qdev_init_gpio_out_named(&i2c->qdev, &s->fout_pin, name, 1);
> > +    trace_rx8900_pin_name("Fout", name);
> > +
> > +    /* Set up default voltage */
> > +    s->supply_voltage = 3.3f;
> > +    trace_rx8900_set_voltage(s->supply_voltage);
> > +}
> > +
> > +/**
> > + * Set up the device callbacks
> > + * @param klass the device class
> > + * @param data
> > + */
> > +static void rx8900_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +    I2CSlaveClass *k = I2C_SLAVE_CLASS(klass);
> > +
> > +    k->event = rx8900_event;
> > +    k->recv = rx8900_recv;
> > +    k->send = rx8900_send;
> > +    dc->realize = rx8900_realize;
> > +    dc->reset = rx8900_reset;
> > +    dc->vmsd = &vmstate_rx8900;
> > +}
> > +
> > +static const TypeInfo rx8900_info = {
> > +    .name = TYPE_RX8900,
> > +    .parent = TYPE_I2C_SLAVE,
> > +    .instance_size = sizeof(RX8900State),
> > +    .instance_init = rx8900_initfn,
> > +    .class_init = rx8900_class_init,
> > +};
> > +
> > +/**
> > + * Register the device with QEMU
> > + */
> > +static void rx8900_register_types(void)
> > +{
> > +    type_register_static(&rx8900_info);
> > +}
> > +
> > +type_init(rx8900_register_types)
> > diff --git a/hw/timer/rx8900_regs.h b/hw/timer/rx8900_regs.h
> > new file mode 100644
> > index 0000000..0aaa9a3
> > --- /dev/null
> > +++ b/hw/timer/rx8900_regs.h
> > @@ -0,0 +1,141 @@
> > +/*
> > + * Epson RX8900SA/CE Realtime Clock Module
> > + *
> > + * Copyright (c) 2016 IBM Corporation
> > + * Authors:
> > > + *  Alastair D'Silva <alastair@d-silva.org>
> > 
> > + *
> > + * This code is licensed under the GPL version 2 or later.  See
> > + * the COPYING file in the top-level directory.
> > + *
> > + * Datasheet available at:
> > + *  https://support.epson.biz/td/api/doc_check.php?dl=app_RX8900CE
> > &lang=en
> > + *
> > + */
> > +
> > +#ifndef RX8900_REGS_H
> > +#define RX8900_REGS_H
> > +
> > +#include "qemu/bitops.h"
> > +
> > +#define RX8900_NVRAM_SIZE 0x20
> 
> Do all the enums below need to be defined in the header or are they
> only relevant to the implementation? If it's the latter, then they
> should go in the .c file.
> 

They are also used in the test.

> Also I would've reached for #define for all these values, not enums,
> but that might be a personal thing.

I prefer enums as they add type safety.

> > +
> > +typedef enum RX8900Addresses {
> > +    START_ADDRESS = 0x00,
> > +    SECONDS = 0x00,
> > +    MINUTES = 0x01,
> > +    HOURS = 0x02,
> > +    WEEKDAY = 0x03, /* a walking bit with bit 0 set for Sunday, 1
> > for Monday...
> > +                       6 for Saturday, see the datasheet for
> > details */
> > +    DAY = 0x04,
> > +    MONTH = 0x05,
> > +    YEAR = 0x06,
> > +    RAM = 0x07,
> > +    ALARM_MINUTE = 0x08,
> > +    ALARM_HOUR = 0x09,
> > +    ALARM_WEEK_DAY = 0x0A,
> > +    TIMER_COUNTER_0 = 0x0B,
> > +    TIMER_COUNTER_1 = 0x0C,
> > +    EXTENSION_REGISTER = 0x0D,
> > +    FLAG_REGISTER = 0X0E,
> > +    CONTROL_REGISTER = 0X0F,
> > +    EXT_SECONDS = 0x010, /* Alias of SECONDS */
> > +    EXT_MINUTES = 0x11, /* Alias of MINUTES */
> > +    EXT_HOURS = 0x12, /* Alias of HOURS */
> > +    EXT_WEEKDAY = 0x13, /* Alias of WEEKDAY */
> > +    EXT_DAY = 0x14, /* Alias of DAY */
> > +    EXT_MONTH = 0x15, /* Alias of MONTH */
> > +    EXT_YEAR = 0x16, /* Alias of YEAR */
> > +    TEMPERATURE = 0x17,
> > +    BACKUP_FUNCTION = 0x18,
> > +    NO_USE_1 = 0x19,
> > +    NO_USE_2 = 0x1A,
> > +    EXT_TIMER_COUNTER_0 = 0x1B, /* Alias of TIMER_COUNTER_0 */
> > +    EXT_TIMER_COUNTER_1 = 0x1C, /* Alias of TIMER_COUNTER_1 */
> > +    EXT_EXTENSION_REGISTER = 0x1D, /* Alias of EXTENSION_REGISTER
> > */
> > +    EXT_FLAG_REGISTER = 0X1E, /* Alias of FLAG_REGISTER */
> > +    EXT_CONTROL_REGISTER = 0X1F /* Alias of CONTROL_REGISTER */
> > +} RX8900Addresses;
> > +
> > +typedef enum ExtRegBits {
> > +    EXT_REG_TSEL0 = 0,
> > +    EXT_REG_TSEL1 = 1,
> > +    EXT_REG_FSEL0 = 2,
> > +    EXT_REG_FSEL1 = 3,
> > +    EXT_REG_TE = 4,
> > +    EXT_REG_USEL = 5,
> > +    EXT_REG_WADA = 6,
> > +    EXT_REG_TEST = 7
> > +} ExtRegBits;
> > +
> > +typedef enum ExtRegMasks {
> > +    EXT_MASK_TSEL0 = BIT(0),
> > +    EXT_MASK_TSEL1 = BIT(1),
> > +    EXT_MASK_FSEL0 = BIT(2),
> > +    EXT_MASK_FSEL1 = BIT(3),
> > +    EXT_MASK_TE = BIT(4),
> > +    EXT_MASK_USEL = BIT(5),
> > +    EXT_MASK_WADA = BIT(6),
> > +    EXT_MASK_TEST = BIT(7)
> > +} ExtRegMasks;
> > +
> > +typedef enum CtrlRegBits {
> > +    CTRL_REG_RESET = 0,
> > +    CTRL_REG_WP0 = 1,
> > +    CTRL_REG_WP1 = 2,
> > +    CTRL_REG_AIE = 3,
> > +    CTRL_REG_TIE = 4,
> > +    CTRL_REG_UIE = 5,
> > +    CTRL_REG_CSEL0 = 6,
> > +    CTRL_REG_CSEL1 = 7
> > +} CtrlRegBits;
> > +
> > +typedef enum CtrlRegMask {
> > +    CTRL_MASK_RESET = BIT(0),
> > +    CTRL_MASK_WP0 = BIT(1),
> > +    CTRL_MASK_WP1 = BIT(2),
> > +    CTRL_MASK_AIE = BIT(3),
> > +    CTRL_MASK_TIE = BIT(4),
> > +    CTRL_MASK_UIE = BIT(5),
> > +    CTRL_MASK_CSEL0 = BIT(6),
> > +    CTRL_MASK_CSEL1 = BIT(7)
> > +} CtrlRegMask;
> > +
> > +typedef enum FlagRegBits {
> > +    FLAG_REG_VDET = 0,
> > +    FLAG_REG_VLF = 1,
> > +    FLAG_REG_UNUSED_2 = 2,
> > +    FLAG_REG_AF = 3,
> > +    FLAG_REG_TF = 4,
> > +    FLAG_REG_UF = 5,
> > +    FLAG_REG_UNUSED_6 = 6,
> > +    FLAG_REG_UNUSED_7 = 7
> > +} FlagRegBits;
> > +
> > +#define RX8900_INTERRUPT_SOURCES 6
> > +typedef enum FlagRegMask {
> > +    FLAG_MASK_VDET = BIT(0),
> > +    FLAG_MASK_VLF = BIT(1),
> > +    FLAG_MASK_UNUSED_2 = BIT(2),
> > +    FLAG_MASK_AF = BIT(3),
> > +    FLAG_MASK_TF = BIT(4),
> > +    FLAG_MASK_UF = BIT(5),
> > +    FLAG_MASK_UNUSED_6 = BIT(6),
> > +    FLAG_MASK_UNUSED_7 = BIT(7)
> > +} FlagRegMask;
> > +
> > +typedef enum BackupRegBits {
> > +    BACKUP_REG_BKSMP0 = 0,
> > +    BACKUP_REG_BKSMP1 = 1,
> > +    BACKUP_REG_SWOFF = 2,
> > +    BACKUP_REG_VDETOFF = 3
> > +} BackupRegBits;
> > +
> > +typedef enum BackupRegMask {
> > +    BACKUP_MASK_BKSMP0 = BIT(0),
> > +    BACKUP_MASK_BKSMP1 = BIT(1),
> > +    BACKUP_MASK_SWOFF = BIT(2),
> > +    BACKUP_MASK_VDETOFF = BIT(3)
> > +} BackupRegMask;
> > +
> > +#endif
> > diff --git a/hw/timer/trace-events b/hw/timer/trace-events
> > index 3495c41..8f4969b 100644
> > --- a/hw/timer/trace-events
> > +++ b/hw/timer/trace-events
> > @@ -49,3 +49,34 @@ aspeed_timer_ctrl_pulse_enable(uint8_t i, bool
> > enable) "Timer %" PRIu8 ": %d"
> >  aspeed_timer_set_ctrl2(uint32_t value) "Value: 0x%" PRIx32
> >  aspeed_timer_set_value(int timer, int reg, uint32_t value) "Timer
> > %d register %d: 0x%" PRIx32
> >  aspeed_timer_read(uint64_t offset, unsigned size, uint64_t value)
> > "From 0x%" PRIx64 ": of size %u: 0x%" PRIx64
> > +
> > +# hw/timer/rx8900.c
> > +rx8900_capture_current_time(int hour, int minute, int second, int
> > weekday, int mday, int month, int year, int raw_hours, int
> > raw_minutes, int raw_seconds, int raw_weekday, int raw_day, int
> > raw_month, int raw_year) "Update current time to %02d:%02d:%02d %d
> > %d/%d/%d (0x%02x%02x%02x%02x%02x%02x%02x)"
> > +rx8900_regptr_update(uint32_t ptr) "Operating on register 0x%02x"
> > +rx8900_regptr_overflow(void) "Register pointer has overflowed,
> > wrapping to 0"
> > +rx8900_event_weekday(int weekday, int weekmask, int
> > weekday_offset) "Set weekday to %d (0x%02x), wday_offset=%d"
> > +rx8900_read_register(int address, int val) "Read register 0x%x =
> > 0x%x"
> > +rx8900_set_fout(int hz) "Setting fout to %dHz"
> > +rx8900_set_countdown_timer(int hz) "Setting countdown timer to %d
> > Hz"
> > +rx8900_set_countdown_timer_per_minute(void) "Setting countdown
> > timer to per minute updates"
> > +rx8900_enable_update_timer(void) ""
> > +rx8900_enable_alarm(void) ""
> > +rx8900_trigger_alarm(void) ""
> > +rx8900_tick(void) ""
> > +rx8900_fire_interrupt(void) ""
> > +rx8900_disable_timer(void) ""
> > +rx8900_enable_timer(void) ""
> > +rx8900_disable_fout(void) ""
> > +rx8900_enable_fout(void) ""
> > +rx8900_fout_toggle(void) ""
> > +rx8900_disable_countdown_timer(void) ""
> > +rx8900_enable_countdown_timer(void) ""
> > +rx8900_countdown_tick(int count) "Countdown tick, count=%d"
> > +rx8900_countdown_elapsed(void) ""
> > +rx8900_i2c_data_receive(uint8_t data) "Received I2C data 0x%02x"
> > +rx8900_set_register(uint32_t addr, uint8_t data) "Set data
> > 0x%02x=0x%02x"
> > +rx8900_get_temperature(uint8_t raw, double val) "0x%x = %fC"
> > +rx8900_set_temperature(uint8_t raw, double val) "0x%x = %fC"
> > +rx8900_reset(void) ""
> > +rx8900_pin_name(const char *type, const char *name) "'%s' pin is
> > '%s'"
> > +rx8900_set_voltage(double voltage) "Device voltage set to %f"
> 
> Is there a justification for all the empty traces?

They are used to show the function is called.

-- 
Alastair D'Silva
Open Source Developer
Linux Technology Centre, IBM Australia
mob: 0423 762 819


^ permalink raw reply

* [GIT PULL] Amlogic fixes for v4.10-rc
From: Kevin Hilman @ 2017-01-04  5:35 UTC (permalink / raw)
  To: linus-amlogic

Arnd, Olof,

This pull has one real fix, as a couple non-critical ones.  The DRM
DT/defconfig patches are coming now because I didn't expect the new
driver to make it for the v4.10 merge window, but since it did[1], the
DT and defconfig should go into the same release.

Thanks,

Kevin

[1] bbbe775ec5b5 drm: Add support for Amlogic Meson Graphic Controller

The following changes since commit 7ce7d89f48834cefece7804d38fc5d85382edf77:

  Linux 4.10-rc1 (2016-12-25 16:13:08 -0800)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git tags/amlogic-fixes

for you to fetch changes up to fcdaf1a2a7a042a290f4c7de28bcdebd5de18445:

  ARM64: defconfig: enable DRM_MESON as module (2017-01-03 09:31:13 -0800)

----------------------------------------------------------------
Amlogic fixes for v4.10
- DT: GXL: fix GPIO include
- add DT and defconfig for newly merged DRM driver

----------------------------------------------------------------
Kevin Hilman (2):
      ARM64: dts: meson-gxl: fix GPIO include
      ARM64: defconfig: enable DRM_MESON as module

Neil Armstrong (1):
      ARM64: dts: meson-gx: Add Graphic Controller nodes

 arch/arm64/boot/dts/amlogic/meson-gx.dtsi              | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi       | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi            |  4 ++++
 arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts  | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi             |  6 +++++-
 arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts    | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi             |  4 ++++
 arch/arm64/configs/defconfig                           |  1 +
 9 files changed, 94 insertions(+), 1 deletion(-)

^ permalink raw reply

* [GIT PULL] Amlogic fixes for v4.10-rc
From: Kevin Hilman @ 2017-01-04  5:35 UTC (permalink / raw)
  To: linux-arm-kernel

Arnd, Olof,

This pull has one real fix, as a couple non-critical ones.  The DRM
DT/defconfig patches are coming now because I didn't expect the new
driver to make it for the v4.10 merge window, but since it did[1], the
DT and defconfig should go into the same release.

Thanks,

Kevin

[1] bbbe775ec5b5 drm: Add support for Amlogic Meson Graphic Controller

The following changes since commit 7ce7d89f48834cefece7804d38fc5d85382edf77:

  Linux 4.10-rc1 (2016-12-25 16:13:08 -0800)

are available in the git repository at:

  git://git.kernel.org/pub/scm/linux/kernel/git/khilman/linux-amlogic.git tags/amlogic-fixes

for you to fetch changes up to fcdaf1a2a7a042a290f4c7de28bcdebd5de18445:

  ARM64: defconfig: enable DRM_MESON as module (2017-01-03 09:31:13 -0800)

----------------------------------------------------------------
Amlogic fixes for v4.10
- DT: GXL: fix GPIO include
- add DT and defconfig for newly merged DRM driver

----------------------------------------------------------------
Kevin Hilman (2):
      ARM64: dts: meson-gxl: fix GPIO include
      ARM64: defconfig: enable DRM_MESON as module

Neil Armstrong (1):
      ARM64: dts: meson-gx: Add Graphic Controller nodes

 arch/arm64/boot/dts/amlogic/meson-gx.dtsi              | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb-nexbox-a95x.dts | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb-p20x.dtsi       | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxbb.dtsi            |  4 ++++
 arch/arm64/boot/dts/amlogic/meson-gxl-nexbox-a95x.dts  | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxl.dtsi             |  6 +++++-
 arch/arm64/boot/dts/amlogic/meson-gxm-nexbox-a1.dts    | 16 ++++++++++++++++
 arch/arm64/boot/dts/amlogic/meson-gxm.dtsi             |  4 ++++
 arch/arm64/configs/defconfig                           |  1 +
 9 files changed, 94 insertions(+), 1 deletion(-)

^ permalink raw reply

* [PATCH v3 2/2] dt-bindings: Add DT bindings info for FlexRM ring manager
From: Anup Patel @ 2017-01-04  5:34 UTC (permalink / raw)
  To: Jassi Brar, Rob Herring
  Cc: Mark Rutland, Ray Jui, Scott Branden, Pramod KUMAR, Rob Rice,
	devicetree, linux-kernel, linux-arm-kernel,
	bcm-kernel-feedback-list, Anup Patel
In-Reply-To: <1483508082-7008-1-git-send-email-anup.patel@broadcom.com>

This patch adds device tree bindings document for the FlexRM
ring manager found on Broadcom iProc SoCs.

Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Signed-off-by: Anup Patel <anup.patel@broadcom.com>
---
 .../bindings/mailbox/brcm,iproc-flexrm-mbox.txt    | 60 ++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt

diff --git a/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
new file mode 100644
index 0000000..ca51a39
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/brcm,iproc-flexrm-mbox.txt
@@ -0,0 +1,60 @@
+Broadcom FlexRM Ring Manager
+============================
+The Broadcom FlexRM ring manager provides a set of rings which can be
+used to submit work to offload engines. An SoC may have multiple FlexRM
+hardware blocks. There is one device tree entry per FlexRM block. The
+FlexRM driver will create a mailbox-controller instance for given FlexRM
+hardware block where each mailbox channel is a separate FlexRM ring.
+
+Required properties:
+--------------------
+- compatible:	Should be "brcm,iproc-flexrm-mbox"
+- reg:		Specifies base physical address and size of the FlexRM
+		ring registers
+- msi-parent:	Phandles (and potential Device IDs) to MSI controllers
+		The FlexRM engine will send MSIs (instead of wired
+		interrupts) to CPU. There is one MSI for each FlexRM ring.
+		Refer devicetree/bindings/interrupt-controller/msi.txt
+- #mbox-cells:	Specifies the number of cells needed to encode a mailbox
+		channel. This should be 3.
+
+		The 1st cell is the mailbox channel number.
+
+		The 2nd cell contains MSI completion threshold. This is the
+		number of completion messages for which FlexRM will inject
+		one MSI interrupt to CPU.
+
+		The 3nd cell contains MSI timer value representing time for
+		which FlexRM will wait to accumulate N completion messages
+		where N is the value specified by 2nd cell above. If FlexRM
+		does not get required number of completion messages in time
+		specified by this cell then it will inject one MSI interrupt
+		to CPU provided atleast one completion message is available.
+
+Optional properties:
+--------------------
+- dma-coherent:	Present if DMA operations made by the FlexRM engine (such
+		as DMA descriptor access, access to buffers pointed by DMA
+		descriptors and read/write pointer updates to DDR) are
+		cache coherent with the CPU.
+
+Example:
+--------
+crypto_mbox: mbox@67000000 {
+	compatible = "brcm,iproc-flexrm-mbox";
+	reg = <0x67000000 0x200000>;
+	msi-parent = <&gic_its 0x7f00>;
+	#mbox-cells = <3>;
+};
+
+crypto_client {
+	...
+	mboxes = <&crypto_mbox 0 0x1 0xffff>,
+		 <&crypto_mbox 1 0x1 0xffff>,
+		 <&crypto_mbox 16 0x1 0xffff>,
+		 <&crypto_mbox 17 0x1 0xffff>,
+		 <&crypto_mbox 30 0x1 0xffff>,
+		 <&crypto_mbox 31 0x1 0xffff>;
+	};
+	...
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH v3 1/2] mailbox: Add driver for Broadcom FlexRM ring manager
From: Anup Patel @ 2017-01-04  5:34 UTC (permalink / raw)
  To: Jassi Brar, Rob Herring
  Cc: Mark Rutland, Ray Jui, Scott Branden, Pramod KUMAR, Rob Rice,
	devicetree, linux-kernel, linux-arm-kernel,
	bcm-kernel-feedback-list, Anup Patel
In-Reply-To: <1483508082-7008-1-git-send-email-anup.patel@broadcom.com>

Some of the Broadcom iProc SoCs have FlexRM ring manager
which provides a ring-based programming interface to various
offload engines (e.g. RAID, Crypto, etc).

This patch adds a common mailbox driver for Broadcom FlexRM
ring manager which can be shared by various offload engine
drivers (implemented as mailbox clients).

Reviewed-by: Ray Jui <ray.jui@broadcom.com>
Reviewed-by: Scott Branden <scott.branden@broadcom.com>
Reviewed-by: Pramod KUMAR <pramod.kumar@broadcom.com>
Signed-off-by: Anup Patel <anup.patel@broadcom.com>
---
 drivers/mailbox/Kconfig                      |  11 +
 drivers/mailbox/Makefile                     |   2 +
 drivers/mailbox/mailbox-flexrm/Makefile      |   6 +
 drivers/mailbox/mailbox-flexrm/flexrm-desc.c | 764 ++++++++++++++++++++++++
 drivers/mailbox/mailbox-flexrm/flexrm-desc.h |  47 ++
 drivers/mailbox/mailbox-flexrm/flexrm-main.c | 829 +++++++++++++++++++++++++++
 include/linux/mailbox/brcm-message.h         |  14 +-
 7 files changed, 1669 insertions(+), 4 deletions(-)
 create mode 100644 drivers/mailbox/mailbox-flexrm/Makefile
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.c
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-desc.h
 create mode 100644 drivers/mailbox/mailbox-flexrm/flexrm-main.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index ceff415..305018c 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -152,4 +152,15 @@ config BCM_PDC_MBOX
 	  Mailbox implementation for the Broadcom PDC ring manager,
 	  which provides access to various offload engines on Broadcom
 	  SoCs. Say Y here if you want to use the Broadcom PDC.
+
+config BCM_FLEXRM_MBOX
+	tristate "Broadcom FlexRM Mailbox"
+	depends on ARM64 || COMPILE_TEST
+	depends on HAS_DMA
+	select GENERIC_MSI_IRQ_DOMAIN
+	default ARCH_BCM_IPROC
+	help
+	  Mailbox implementation of the Broadcom FlexRM ring manager,
+	  which provides access to various offload engines on Broadcom
+	  SoCs. Say Y here if you want to use the Broadcom FlexRM.
 endif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 7dde4f6..45083c0 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -30,4 +30,6 @@ obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
 
 obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
 
+obj-$(CONFIG_BCM_FLEXRM_MBOX)	+= mailbox-flexrm/
+
 obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
diff --git a/drivers/mailbox/mailbox-flexrm/Makefile b/drivers/mailbox/mailbox-flexrm/Makefile
new file mode 100644
index 0000000..f5bf069
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for Broadcom FlexRM Mailbox Driver.
+#
+
+flexrm-mbox-objs := flexrm-main.o flexrm-desc.o
+obj-$(CONFIG_BCM_FLEXRM_MBOX) += flexrm-mbox.o
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-desc.c b/drivers/mailbox/mailbox-flexrm/flexrm-desc.c
new file mode 100644
index 0000000..b0449eb
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-desc.c
@@ -0,0 +1,764 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 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.
+ *
+ * FlexRM descriptor library
+ */
+
+#include <asm/barrier.h>
+#include <asm/byteorder.h>
+#include <linux/dma-mapping.h>
+#include <linux/printk.h>
+
+#include "flexrm-desc.h"
+
+/* Completion descriptor format */
+#define CMPL_OPAQUE_SHIFT			0
+#define CMPL_OPAQUE_MASK			0xffff
+#define CMPL_ENGINE_STATUS_SHIFT		16
+#define CMPL_ENGINE_STATUS_MASK			0xffff
+#define CMPL_DME_STATUS_SHIFT			32
+#define CMPL_DME_STATUS_MASK			0xffff
+#define CMPL_RM_STATUS_SHIFT			48
+#define CMPL_RM_STATUS_MASK			0xffff
+
+/* Completion DME status code */
+#define DME_STATUS_MEM_COR_ERR			BIT(0)
+#define DME_STATUS_MEM_UCOR_ERR			BIT(1)
+#define DME_STATUS_FIFO_UNDERFLOW		BIT(2)
+#define DME_STATUS_FIFO_OVERFLOW		BIT(3)
+#define DME_STATUS_RRESP_ERR			BIT(4)
+#define DME_STATUS_BRESP_ERR			BIT(5)
+#define DME_STATUS_ERROR_MASK			(DME_STATUS_MEM_COR_ERR | \
+						 DME_STATUS_MEM_UCOR_ERR | \
+						 DME_STATUS_FIFO_UNDERFLOW | \
+						 DME_STATUS_FIFO_OVERFLOW | \
+						 DME_STATUS_RRESP_ERR | \
+						 DME_STATUS_BRESP_ERR)
+
+/* Completion RM status code */
+#define RM_STATUS_CODE_SHIFT			0
+#define RM_STATUS_CODE_MASK			0x3ff
+#define RM_STATUS_CODE_GOOD			0x0
+#define RM_STATUS_CODE_AE_TIMEOUT		0x3ff
+
+/* General descriptor format */
+#define DESC_TYPE_SHIFT				60
+#define DESC_TYPE_MASK				0xf
+#define DESC_PAYLOAD_SHIFT			0
+#define DESC_PAYLOAD_MASK			0x0fffffffffffffff
+
+/* Null descriptor format  */
+#define NULL_TYPE				0
+#define NULL_TOGGLE_SHIFT			58
+#define NULL_TOGGLE_MASK			0x1
+
+/* Header descriptor format */
+#define HEADER_TYPE				1
+#define HEADER_TOGGLE_SHIFT			58
+#define HEADER_TOGGLE_MASK			0x1
+#define HEADER_ENDPKT_SHIFT			57
+#define HEADER_ENDPKT_MASK			0x1
+#define HEADER_STARTPKT_SHIFT			56
+#define HEADER_STARTPKT_MASK			0x1
+#define HEADER_BDCOUNT_SHIFT			36
+#define HEADER_BDCOUNT_MASK			0x1f
+#define HEADER_BDCOUNT_MAX			HEADER_BDCOUNT_MASK
+#define HEADER_FLAGS_SHIFT			16
+#define HEADER_FLAGS_MASK			0xffff
+#define HEADER_OPAQUE_SHIFT			0
+#define HEADER_OPAQUE_MASK			0xffff
+
+/* Source (SRC) descriptor format */
+#define SRC_TYPE				2
+#define SRC_LENGTH_SHIFT			44
+#define SRC_LENGTH_MASK				0xffff
+#define SRC_ADDR_SHIFT				0
+#define SRC_ADDR_MASK				0x00000fffffffffff
+
+/* Destination (DST) descriptor format */
+#define DST_TYPE				3
+#define DST_LENGTH_SHIFT			44
+#define DST_LENGTH_MASK				0xffff
+#define DST_ADDR_SHIFT				0
+#define DST_ADDR_MASK				0x00000fffffffffff
+
+/* Immediate (IMM) descriptor format */
+#define IMM_TYPE				4
+#define IMM_DATA_SHIFT				0
+#define IMM_DATA_MASK				0x0fffffffffffffff
+
+/* Next pointer (NPTR) descriptor format */
+#define NPTR_TYPE				5
+#define NPTR_TOGGLE_SHIFT			58
+#define NPTR_TOGGLE_MASK			0x1
+#define NPTR_ADDR_SHIFT				0
+#define NPTR_ADDR_MASK				0x00000fffffffffff
+
+/* Mega source (MSRC) descriptor format */
+#define MSRC_TYPE				6
+#define MSRC_LENGTH_SHIFT			44
+#define MSRC_LENGTH_MASK			0xffff
+#define MSRC_ADDR_SHIFT				0
+#define MSRC_ADDR_MASK				0x00000fffffffffff
+
+/* Mega destination (MDST) descriptor format */
+#define MDST_TYPE				7
+#define MDST_LENGTH_SHIFT			44
+#define MDST_LENGTH_MASK			0xffff
+#define MDST_ADDR_SHIFT				0
+#define MDST_ADDR_MASK				0x00000fffffffffff
+
+/* Source with tlast (SRCT) descriptor format */
+#define SRCT_TYPE				8
+#define SRCT_LENGTH_SHIFT			44
+#define SRCT_LENGTH_MASK			0xffff
+#define SRCT_ADDR_SHIFT				0
+#define SRCT_ADDR_MASK				0x00000fffffffffff
+
+/* Destination with tlast (DSTT) descriptor format */
+#define DSTT_TYPE				9
+#define DSTT_LENGTH_SHIFT			44
+#define DSTT_LENGTH_MASK			0xffff
+#define DSTT_ADDR_SHIFT				0
+#define DSTT_ADDR_MASK				0x00000fffffffffff
+
+/* Immediate with tlast (IMMT) descriptor format */
+#define IMMT_TYPE				10
+#define IMMT_DATA_SHIFT				0
+#define IMMT_DATA_MASK				0x0fffffffffffffff
+
+/* Descriptor helper macros */
+#define DESC_DEC(_d, _s, _m)			(((_d) >> (_s)) & (_m))
+#define DESC_ENC(_d, _v, _s, _m)		\
+			do { \
+				(_d) &= ~((u64)(_m) << (_s)); \
+				(_d) |= (((u64)(_v) & (_m)) << (_s)); \
+			} while (0)
+
+u64 flexrm_read_desc(void *desc_ptr)
+{
+	return le64_to_cpu(*((u64 *)desc_ptr));
+}
+
+void flexrm_write_desc(void *desc_ptr, u64 desc)
+{
+	*((u64 *)desc_ptr) = cpu_to_le64(desc);
+}
+
+u32 flexrm_cmpl_desc_to_reqid(u64 cmpl_desc)
+{
+	return (u32)(cmpl_desc & CMPL_OPAQUE_MASK);
+}
+
+int flexrm_cmpl_desc_to_error(u64 cmpl_desc)
+{
+	u32 status;
+
+	status = DESC_DEC(cmpl_desc, CMPL_DME_STATUS_SHIFT,
+			  CMPL_DME_STATUS_MASK);
+	if (status & DME_STATUS_ERROR_MASK)
+		return -EIO;
+
+	status = DESC_DEC(cmpl_desc, CMPL_RM_STATUS_SHIFT,
+			  CMPL_RM_STATUS_MASK);
+	status &= RM_STATUS_CODE_MASK;
+	if (status == RM_STATUS_CODE_AE_TIMEOUT)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+bool flexrm_is_next_table_desc(void *desc_ptr)
+{
+	u64 desc = flexrm_read_desc(desc_ptr);
+	u32 type = DESC_DEC(desc, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+
+	return (type == NPTR_TYPE) ? true : false;
+}
+
+u64 flexrm_next_table_desc(u32 toggle, dma_addr_t next_addr)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, NPTR_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, NPTR_TOGGLE_SHIFT, NPTR_TOGGLE_MASK);
+	DESC_ENC(desc, next_addr, NPTR_ADDR_SHIFT, NPTR_ADDR_MASK);
+
+	return desc;
+}
+
+u64 flexrm_null_desc(u32 toggle)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, NULL_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, NULL_TOGGLE_SHIFT, NULL_TOGGLE_MASK);
+
+	return desc;
+}
+
+u32 flexrm_estimate_header_desc_count(u32 nhcnt)
+{
+	u32 hcnt = nhcnt / HEADER_BDCOUNT_MAX;
+
+	if (!(nhcnt % HEADER_BDCOUNT_MAX))
+		hcnt += 1;
+
+	return hcnt;
+}
+
+static void flexrm_flip_header_toogle(void *desc_ptr)
+{
+	u64 desc = flexrm_read_desc(desc_ptr);
+
+	if (desc & ((u64)0x1 << HEADER_TOGGLE_SHIFT))
+		desc &= ~((u64)0x1 << HEADER_TOGGLE_SHIFT);
+	else
+		desc |= ((u64)0x1 << HEADER_TOGGLE_SHIFT);
+
+	flexrm_write_desc(desc_ptr, desc);
+}
+
+static u64 flexrm_header_desc(u32 toggle, u32 startpkt, u32 endpkt,
+			       u32 bdcount, u32 flags, u32 opaque)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, HEADER_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, toggle, HEADER_TOGGLE_SHIFT, HEADER_TOGGLE_MASK);
+	DESC_ENC(desc, startpkt, HEADER_STARTPKT_SHIFT, HEADER_STARTPKT_MASK);
+	DESC_ENC(desc, endpkt, HEADER_ENDPKT_SHIFT, HEADER_ENDPKT_MASK);
+	DESC_ENC(desc, bdcount, HEADER_BDCOUNT_SHIFT, HEADER_BDCOUNT_MASK);
+	DESC_ENC(desc, flags, HEADER_FLAGS_SHIFT, HEADER_FLAGS_MASK);
+	DESC_ENC(desc, opaque, HEADER_OPAQUE_SHIFT, HEADER_OPAQUE_MASK);
+
+	return desc;
+}
+
+static void flexrm_enqueue_desc(u32 nhpos, u32 nhcnt, u32 reqid,
+				 u64 desc, void **desc_ptr, u32 *toggle,
+				 void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 nhavail, _toggle, _startpkt, _endpkt, _bdcount;
+
+	/* Sanity check */
+	if (nhcnt <= nhpos)
+		return;
+
+	/*
+	 * Each request or packet start with a HEADER descriptor followed
+	 * by one or more non-HEADER descriptors (SRC, SRCT, MSRC, DST,
+	 * DSTT, MDST, IMM, and IMMT). The number of non-HEADER descriptors
+	 * following a HEADER descriptor is represented by BDCOUNT field
+	 * of HEADER descriptor. The max value of BDCOUNT field is 31 which
+	 * means we can only have 31 non-HEADER descriptors following one
+	 * HEADER descriptor.
+	 *
+	 * In general use, number of non-HEADER descriptors can easily go
+	 * beyond 31. To tackle this situation, we have packet (or request)
+	 * extenstion bits (STARTPKT and ENDPKT) in the HEADER descriptor.
+	 *
+	 * To use packet extension, the first HEADER descriptor of request
+	 * (or packet) will have STARTPKT=1 and ENDPKT=0. The intermediate
+	 * HEADER descriptors will have STARTPKT=0 and ENDPKT=0. The last
+	 * HEADER descriptor will have STARTPKT=0 and ENDPKT=1. Also, the
+	 * TOGGLE bit of the first HEADER will be set to invalid state to
+	 * ensure that FlexRM does not start fetching descriptors till all
+	 * descriptors are enqueued. The user of this function will flip
+	 * the TOGGLE bit of first HEADER after all descriptors are
+	 * enqueued.
+	 */
+
+	if ((nhpos % HEADER_BDCOUNT_MAX == 0) && (nhcnt - nhpos)) {
+		/* Prepare the header descriptor */
+		nhavail = (nhcnt - nhpos);
+		_toggle = (nhpos == 0) ? !(*toggle) : (*toggle);
+		_startpkt = (nhpos == 0) ? 0x1 : 0x0;
+		_endpkt = (nhavail <= HEADER_BDCOUNT_MAX) ? 0x1 : 0x0;
+		_bdcount = (nhavail <= HEADER_BDCOUNT_MAX) ?
+				nhavail : HEADER_BDCOUNT_MAX;
+		if (nhavail <= HEADER_BDCOUNT_MAX)
+			_bdcount = nhavail;
+		else
+			_bdcount = HEADER_BDCOUNT_MAX;
+		d = flexrm_header_desc(_toggle, _startpkt, _endpkt,
+					_bdcount, 0x0, reqid);
+
+		/* Write header descriptor */
+		flexrm_write_desc(*desc_ptr, d);
+
+		/* Point to next descriptor */
+		*desc_ptr += sizeof(desc);
+		if (*desc_ptr == end_desc)
+			*desc_ptr = start_desc;
+
+		/* Skip next pointer descriptors */
+		while (flexrm_is_next_table_desc(*desc_ptr)) {
+			*toggle = (*toggle) ? 0 : 1;
+			*desc_ptr += sizeof(desc);
+			if (*desc_ptr == end_desc)
+				*desc_ptr = start_desc;
+		}
+	}
+
+	/* Write desired descriptor */
+	flexrm_write_desc(*desc_ptr, desc);
+
+	/* Point to next descriptor */
+	*desc_ptr += sizeof(desc);
+	if (*desc_ptr == end_desc)
+		*desc_ptr = start_desc;
+
+	/* Skip next pointer descriptors */
+	while (flexrm_is_next_table_desc(*desc_ptr)) {
+		*toggle = (*toggle) ? 0 : 1;
+		*desc_ptr += sizeof(desc);
+		if (*desc_ptr == end_desc)
+			*desc_ptr = start_desc;
+	}
+}
+
+static u64 flexrm_src_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, SRC_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, SRC_LENGTH_SHIFT, SRC_LENGTH_MASK);
+	DESC_ENC(desc, addr, SRC_ADDR_SHIFT, SRC_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_msrc_desc(dma_addr_t addr, unsigned int length_div_16)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, MSRC_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length_div_16, MSRC_LENGTH_SHIFT, MSRC_LENGTH_MASK);
+	DESC_ENC(desc, addr, MSRC_ADDR_SHIFT, MSRC_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_dst_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, DST_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, DST_LENGTH_SHIFT, DST_LENGTH_MASK);
+	DESC_ENC(desc, addr, DST_ADDR_SHIFT, DST_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_mdst_desc(dma_addr_t addr, unsigned int length_div_16)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, MDST_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length_div_16, MDST_LENGTH_SHIFT, MDST_LENGTH_MASK);
+	DESC_ENC(desc, addr, MDST_ADDR_SHIFT, MDST_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_imm_desc(u64 data)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, IMM_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, data, IMM_DATA_SHIFT, IMM_DATA_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_srct_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, SRCT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, SRCT_LENGTH_SHIFT, SRCT_LENGTH_MASK);
+	DESC_ENC(desc, addr, SRCT_ADDR_SHIFT, SRCT_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_dstt_desc(dma_addr_t addr, unsigned int length)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, DSTT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, length, DSTT_LENGTH_SHIFT, DSTT_LENGTH_MASK);
+	DESC_ENC(desc, addr, DSTT_ADDR_SHIFT, DSTT_ADDR_MASK);
+
+	return desc;
+}
+
+static u64 flexrm_immt_desc(u64 data)
+{
+	u64 desc = 0;
+
+	DESC_ENC(desc, IMMT_TYPE, DESC_TYPE_SHIFT, DESC_TYPE_MASK);
+	DESC_ENC(desc, data, IMMT_DATA_SHIFT, IMMT_DATA_MASK);
+
+	return desc;
+}
+
+static bool flexrm_spu_sanity_check(struct brcm_message *msg)
+{
+	struct scatterlist *sg;
+
+	if (!msg->spu.src || !msg->spu.dst)
+		return false;
+	for (sg = msg->spu.src; sg; sg = sg_next(sg)) {
+		if (sg->length & 0xf) {
+			if (sg->length > SRC_LENGTH_MASK)
+				return false;
+		} else {
+			if (sg->length > (MSRC_LENGTH_MASK * 16))
+				return false;
+		}
+	}
+	for (sg = msg->spu.dst; sg; sg = sg_next(sg)) {
+		if (sg->length & 0xf) {
+			if (sg->length > DST_LENGTH_MASK)
+				return false;
+		} else {
+			if (sg->length > (MDST_LENGTH_MASK * 16))
+				return false;
+		}
+	}
+
+	return true;
+}
+
+static u32 flexrm_spu_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	u32 cnt = 0;
+	unsigned int dst_target = 0;
+	struct scatterlist *src_sg = msg->spu.src, *dst_sg = msg->spu.dst;
+
+	while (src_sg || dst_sg) {
+		if (src_sg) {
+			cnt++;
+			dst_target = src_sg->length;
+			src_sg = sg_next(src_sg);
+		} else
+			dst_target = UINT_MAX;
+
+		while (dst_target && dst_sg) {
+			cnt++;
+			if (dst_sg->length < dst_target)
+				dst_target -= dst_sg->length;
+			else
+				dst_target = 0;
+			dst_sg = sg_next(dst_sg);
+		}
+	}
+
+	return cnt;
+}
+
+static int flexrm_spu_dma_map(struct device *dev, struct brcm_message *msg)
+{
+	int rc;
+
+	rc = dma_map_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+			DMA_TO_DEVICE);
+	if (rc < 0)
+		return rc;
+
+	rc = dma_map_sg(dev, msg->spu.dst, sg_nents(msg->spu.dst),
+			DMA_FROM_DEVICE);
+	if (rc < 0) {
+		dma_unmap_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+			     DMA_TO_DEVICE);
+		return rc;
+	}
+
+	return 0;
+}
+
+static void flexrm_spu_dma_unmap(struct device *dev, struct brcm_message *msg)
+{
+	dma_unmap_sg(dev, msg->spu.dst, sg_nents(msg->spu.dst),
+		     DMA_FROM_DEVICE);
+	dma_unmap_sg(dev, msg->spu.src, sg_nents(msg->spu.src),
+		     DMA_TO_DEVICE);
+}
+
+static void *flexrm_spu_write_descs(struct brcm_message *msg, u32 nhcnt,
+				     u32 reqid, void *desc_ptr, u32 toggle,
+				     void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 nhpos = 0;
+	void *orig_desc_ptr = desc_ptr;
+	unsigned int dst_target = 0;
+	struct scatterlist *src_sg = msg->spu.src, *dst_sg = msg->spu.dst;
+
+	while (src_sg || dst_sg) {
+		if (src_sg) {
+			if (sg_dma_len(src_sg) & 0xf)
+				d = flexrm_src_desc(sg_dma_address(src_sg),
+						     sg_dma_len(src_sg));
+			else
+				d = flexrm_msrc_desc(sg_dma_address(src_sg),
+						      sg_dma_len(src_sg)/16);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+			dst_target = sg_dma_len(src_sg);
+			src_sg = sg_next(src_sg);
+		} else
+			dst_target = UINT_MAX;
+
+		while (dst_target && dst_sg) {
+			if (sg_dma_len(dst_sg) & 0xf)
+				d = flexrm_dst_desc(sg_dma_address(dst_sg),
+						     sg_dma_len(dst_sg));
+			else
+				d = flexrm_mdst_desc(sg_dma_address(dst_sg),
+						      sg_dma_len(dst_sg)/16);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+			if (sg_dma_len(dst_sg) < dst_target)
+				dst_target -= sg_dma_len(dst_sg);
+			else
+				dst_target = 0;
+			dst_sg = sg_next(dst_sg);
+		}
+	}
+
+	/* Null descriptor with invalid toggle bit */
+	flexrm_write_desc(desc_ptr, flexrm_null_desc(!toggle));
+
+	/* Ensure that descriptors have been written to memory */
+	wmb();
+
+	/* Flip toggle bit in header */
+	flexrm_flip_header_toogle(orig_desc_ptr);
+
+	return desc_ptr;
+}
+
+static bool flexrm_sba_sanity_check(struct brcm_message *msg)
+{
+	u32 i;
+
+	if (!msg->sba.cmds || !msg->sba.cmds_count)
+		return false;
+
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		if (((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) ||
+		     (msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C)) &&
+		    (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) &&
+		    (msg->sba.cmds[i].data_len > SRCT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C) &&
+		    (msg->sba.cmds[i].data_len > SRCT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_RESP) &&
+		    (msg->sba.cmds[i].resp_len > DSTT_LENGTH_MASK))
+			return false;
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT) &&
+		    (msg->sba.cmds[i].data_len > DSTT_LENGTH_MASK))
+			return false;
+	}
+
+	return true;
+}
+
+static u32 flexrm_sba_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	u32 i, cnt;
+
+	cnt = 0;
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		cnt++;
+
+		if ((msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_B) ||
+		    (msg->sba.cmds[i].flags & BRCM_SBA_CMD_TYPE_C))
+			cnt++;
+
+		if (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_RESP)
+			cnt++;
+
+		if (msg->sba.cmds[i].flags & BRCM_SBA_CMD_HAS_OUTPUT)
+			cnt++;
+	}
+
+	return cnt;
+}
+
+static void *flexrm_sba_write_descs(struct brcm_message *msg, u32 nhcnt,
+				     u32 reqid, void *desc_ptr, u32 toggle,
+				     void *start_desc, void *end_desc)
+{
+	u64 d;
+	u32 i, nhpos = 0;
+	struct brcm_sba_command *c;
+	void *orig_desc_ptr = desc_ptr;
+
+	/* Convert SBA commands into descriptors */
+	for (i = 0; i < msg->sba.cmds_count; i++) {
+		c = &msg->sba.cmds[i];
+
+		if ((c->flags & BRCM_SBA_CMD_HAS_RESP) &&
+		    (c->flags & BRCM_SBA_CMD_HAS_OUTPUT)) {
+			/* Destination response descriptor */
+			d = flexrm_dst_desc(c->resp, c->resp_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		} else if (c->flags & BRCM_SBA_CMD_HAS_RESP) {
+			/* Destination response with tlast descriptor */
+			d = flexrm_dstt_desc(c->resp, c->resp_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if (c->flags & BRCM_SBA_CMD_HAS_OUTPUT) {
+			/* Destination with tlast descriptor */
+			d = flexrm_dstt_desc(c->data, c->data_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if (c->flags & BRCM_SBA_CMD_TYPE_B) {
+			/* Command as immediate descriptor */
+			d = flexrm_imm_desc(c->cmd);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		} else {
+			/* Command as immediate descriptor with tlast */
+			d = flexrm_immt_desc(c->cmd);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+
+		if ((c->flags & BRCM_SBA_CMD_TYPE_B) ||
+		    (c->flags & BRCM_SBA_CMD_TYPE_C)) {
+			/* Source with tlast descriptor */
+			d = flexrm_srct_desc(c->data, c->data_len);
+			flexrm_enqueue_desc(nhpos, nhcnt, reqid,
+					     d, &desc_ptr, &toggle,
+					     start_desc, end_desc);
+			nhpos++;
+		}
+	}
+
+	/* Null descriptor with invalid toggle bit */
+	flexrm_write_desc(desc_ptr, flexrm_null_desc(!toggle));
+
+	/* Ensure that descriptors have been written to memory */
+	wmb();
+
+	/* Flip toggle bit in header */
+	flexrm_flip_header_toogle(orig_desc_ptr);
+
+	return desc_ptr;
+}
+
+bool flexrm_sanity_check(struct brcm_message *msg)
+{
+	if (!msg)
+		return false;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_sanity_check(msg);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_sanity_check(msg);
+	default:
+		return false;
+	};
+}
+
+u32 flexrm_estimate_nonheader_desc_count(struct brcm_message *msg)
+{
+	if (!msg)
+		return 0;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_estimate_nonheader_desc_count(msg);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_estimate_nonheader_desc_count(msg);
+	default:
+		return 0;
+	};
+}
+
+int flexrm_dma_map(struct device *dev, struct brcm_message *msg)
+{
+	if (!dev || !msg)
+		return -EINVAL;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_dma_map(dev, msg);
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+void flexrm_dma_unmap(struct device *dev, struct brcm_message *msg)
+{
+	if (!dev || !msg)
+		return;
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		flexrm_spu_dma_unmap(dev, msg);
+		break;
+	default:
+		break;
+	}
+}
+
+void *flexrm_write_descs(struct brcm_message *msg, u32 nhcnt,
+			  u32 reqid, void *desc_ptr, u32 toggle,
+			  void *start_desc, void *end_desc)
+{
+	if (!msg || !desc_ptr || !start_desc || !end_desc)
+		return ERR_PTR(-ENOTSUPP);
+
+	if ((desc_ptr < start_desc) || (end_desc <= desc_ptr))
+		return ERR_PTR(-ERANGE);
+
+	switch (msg->type) {
+	case BRCM_MESSAGE_SPU:
+		return flexrm_spu_write_descs(msg, nhcnt, reqid,
+					       desc_ptr, toggle,
+					       start_desc, end_desc);
+	case BRCM_MESSAGE_SBA:
+		return flexrm_sba_write_descs(msg, nhcnt, reqid,
+					       desc_ptr, toggle,
+					       start_desc, end_desc);
+	default:
+		return ERR_PTR(-ENOTSUPP);
+	};
+}
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-desc.h b/drivers/mailbox/mailbox-flexrm/flexrm-desc.h
new file mode 100644
index 0000000..a95cf61
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-desc.h
@@ -0,0 +1,47 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 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.
+ *
+ * FlexRM descriptor library
+ */
+
+#ifndef __FLEXRM_DESC_H__
+#define __FLEXRM_DESC_H__
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mailbox/brcm-message.h>
+
+extern u64 flexrm_read_desc(void *desc_ptr);
+
+extern void flexrm_write_desc(void *desc_ptr, u64 desc);
+
+extern u32 flexrm_cmpl_desc_to_reqid(u64 cmpl_desc);
+
+extern int flexrm_cmpl_desc_to_error(u64 cmpl_desc);
+
+extern bool flexrm_is_next_table_desc(void *desc_ptr);
+
+extern u64 flexrm_next_table_desc(u32 toggle, dma_addr_t next_addr);
+
+extern u64 flexrm_null_desc(u32 toogle);
+
+extern u32 flexrm_estimate_header_desc_count(u32 nhcnt);
+
+extern bool flexrm_sanity_check(struct brcm_message *msg);
+
+extern u32 flexrm_estimate_nonheader_desc_count(struct brcm_message *msg);
+
+extern int flexrm_dma_map(struct device *dev, struct brcm_message *msg);
+
+extern void flexrm_dma_unmap(struct device *dev, struct brcm_message *msg);
+
+extern void *flexrm_write_descs(struct brcm_message *msg, u32 nhcnt,
+				 u32 reqid, void *desc_ptr, u32 toggle,
+				 void *start_desc, void *end_desc);
+
+#endif /* __FLEXRM_DESC_H__ */
diff --git a/drivers/mailbox/mailbox-flexrm/flexrm-main.c b/drivers/mailbox/mailbox-flexrm/flexrm-main.c
new file mode 100644
index 0000000..c8890f1
--- /dev/null
+++ b/drivers/mailbox/mailbox-flexrm/flexrm-main.c
@@ -0,0 +1,829 @@
+/* Broadcom FlexRM Mailbox Driver
+ *
+ * Copyright (C) 2016 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.
+ *
+ * Each Broadcom FlexSparx4 offload engine is implemented as an
+ * extension to Broadcom FlexRM ring manager. The FlexRM ring
+ * manager provides a set of rings which can be used to submit
+ * work to a FlexSparx4 offload engine.
+ *
+ * This driver creates a mailbox controller using a set of FlexRM
+ * rings where each mailbox channel represents a separate FlexRM ring.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/mailbox_client.h>
+#include <linux/mailbox/brcm-message.h>
+#include <linux/module.h>
+#include <linux/msi.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include "flexrm-desc.h"
+
+/* FlexRM configuration */
+#define RING_REGS_SIZE					0x10000
+#define RING_DESC_SIZE					8
+#define RING_DESC_INDEX(offset)				\
+			((offset) / RING_DESC_SIZE)
+#define RING_DESC_OFFSET(index)				\
+			((index) * RING_DESC_SIZE)
+#define RING_MAX_REQ_COUNT				1024
+#define RING_BD_ALIGN_ORDER				12
+#define RING_BD_ALIGN_CHECK(addr)			\
+			(!((addr) & ((0x1 << RING_BD_ALIGN_ORDER) - 1)))
+#define RING_BD_TOGGLE_INVALID(offset)			\
+			(((offset) >> RING_BD_ALIGN_ORDER) & 0x1)
+#define RING_BD_TOGGLE_VALID(offset)			\
+			(!RING_BD_TOGGLE_INVALID(offset))
+#define RING_BD_DESC_PER_REQ				32
+#define RING_BD_DESC_COUNT				\
+			(RING_MAX_REQ_COUNT * RING_BD_DESC_PER_REQ)
+#define RING_BD_SIZE					\
+			(RING_BD_DESC_COUNT * RING_DESC_SIZE)
+#define RING_CMPL_ALIGN_ORDER				13
+#define RING_CMPL_DESC_COUNT				RING_MAX_REQ_COUNT
+#define RING_CMPL_SIZE					\
+			(RING_CMPL_DESC_COUNT * RING_DESC_SIZE)
+#define RING_VER_MAGIC					0x76303031
+
+/* Per-Ring register offsets */
+#define RING_VER					0x000
+#define RING_BD_START_ADDR				0x004
+#define RING_BD_READ_PTR				0x008
+#define RING_BD_WRITE_PTR				0x00c
+#define RING_BD_READ_PTR_DDR_LS				0x010
+#define RING_BD_READ_PTR_DDR_MS				0x014
+#define RING_CMPL_START_ADDR				0x018
+#define RING_CMPL_WRITE_PTR				0x01c
+#define RING_NUM_REQ_RECV_LS				0x020
+#define RING_NUM_REQ_RECV_MS				0x024
+#define RING_NUM_REQ_TRANS_LS				0x028
+#define RING_NUM_REQ_TRANS_MS				0x02c
+#define RING_NUM_REQ_OUTSTAND				0x030
+#define RING_CONTROL					0x034
+#define RING_FLUSH_DONE					0x038
+#define RING_MSI_ADDR_LS				0x03c
+#define RING_MSI_ADDR_MS				0x040
+#define RING_MSI_CONTROL				0x048
+#define RING_BD_READ_PTR_DDR_CONTROL			0x04c
+#define RING_MSI_DATA_VALUE				0x064
+
+/* Register RING_BD_START_ADDR fields */
+#define BD_LAST_UPDATE_HW_SHIFT				28
+#define BD_LAST_UPDATE_HW_MASK				0x1
+#define BD_START_ADDR_VALUE(pa)				\
+	((u32)((((dma_addr_t)(pa)) >> RING_BD_ALIGN_ORDER) & 0x0fffffff))
+#define BD_START_ADDR_DECODE(val)			\
+	((dma_addr_t)((val) & 0x0fffffff) << RING_BD_ALIGN_ORDER)
+
+/* Register RING_CMPL_START_ADDR fields */
+#define CMPL_START_ADDR_VALUE(pa)			\
+	((u32)((((u64)(pa)) >> RING_CMPL_ALIGN_ORDER) & 0x03ffffff))
+
+/* Register RING_CONTROL fields */
+#define CONTROL_MASK_DISABLE_CONTROL			12
+#define CONTROL_FLUSH_SHIFT				5
+#define CONTROL_ACTIVE_SHIFT				4
+#define CONTROL_RATE_ADAPT_MASK				0xf
+#define CONTROL_RATE_DYNAMIC				0x0
+#define CONTROL_RATE_FAST				0x8
+#define CONTROL_RATE_MEDIUM				0x9
+#define CONTROL_RATE_SLOW				0xa
+#define CONTROL_RATE_IDLE				0xb
+
+/* Register RING_FLUSH_DONE fields */
+#define FLUSH_DONE_MASK					0x1
+
+/* Register RING_MSI_CONTROL fields */
+#define MSI_TIMER_VAL_SHIFT				16
+#define MSI_TIMER_VAL_MASK				0xffff
+#define MSI_ENABLE_SHIFT				15
+#define MSI_ENABLE_MASK					0x1
+#define MSI_COUNT_SHIFT					0
+#define MSI_COUNT_MASK					0x3ff
+
+/* Register RING_BD_READ_PTR_DDR_CONTROL fields */
+#define BD_READ_PTR_DDR_TIMER_VAL_SHIFT			16
+#define BD_READ_PTR_DDR_TIMER_VAL_MASK			0xffff
+#define BD_READ_PTR_DDR_ENABLE_SHIFT			15
+#define BD_READ_PTR_DDR_ENABLE_MASK			0x1
+
+struct flexrm_ring {
+	/* Unprotected members */
+	int num;
+	struct flexrm_mbox *mbox;
+	void __iomem *regs;
+	bool irq_requested;
+	unsigned int irq;
+	unsigned int msi_timer_val;
+	unsigned int msi_count_threshold;
+	struct ida requests_ida;
+	struct brcm_message *requests[RING_MAX_REQ_COUNT];
+	void *bd_base;
+	dma_addr_t bd_dma_base;
+	u32 bd_write_offset;
+	void *cmpl_base;
+	dma_addr_t cmpl_dma_base;
+	/* Protected members */
+	spinlock_t lock;
+	struct brcm_message *last_pending_msg;
+	u32 cmpl_read_offset;
+};
+
+struct flexrm_mbox {
+	struct device *dev;
+	void __iomem *regs;
+	u32 num_rings;
+	struct flexrm_ring *rings;
+	u64 dma_mask;
+	struct dma_pool *bd_pool;
+	struct dma_pool *cmpl_pool;
+	struct mbox_controller controller;
+};
+
+static int flexrm_new_request(struct flexrm_ring *ring,
+				struct brcm_message *batch_msg,
+				struct brcm_message *msg)
+{
+	void *next;
+	unsigned long flags;
+	u32 val, count, nhcnt;
+	u32 read_offset, write_offset;
+	bool exit_cleanup = false;
+	int ret = 0, reqid;
+
+	/* Do sanity check on message */
+	if (!flexrm_sanity_check(msg))
+		return -EIO;
+	msg->error = 0;
+
+	/* If no requests possible then save data pointer and goto done. */
+	reqid = ida_simple_get(&ring->requests_ida, 0,
+				RING_MAX_REQ_COUNT, GFP_KERNEL);
+	if (reqid < 0) {
+		spin_lock_irqsave(&ring->lock, flags);
+		if (batch_msg)
+			ring->last_pending_msg = batch_msg;
+		else
+			ring->last_pending_msg = msg;
+		spin_unlock_irqrestore(&ring->lock, flags);
+		return 0;
+	}
+	ring->requests[reqid] = msg;
+
+	/* Do DMA mappings for the message */
+	ret = flexrm_dma_map(ring->mbox->dev, msg);
+	if (ret < 0) {
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+		return ret;
+	}
+
+	/* If last_pending_msg is already set then goto done with error */
+	spin_lock_irqsave(&ring->lock, flags);
+	if (ring->last_pending_msg)
+		ret = -ENOSPC;
+	spin_unlock_irqrestore(&ring->lock, flags);
+	if (ret < 0) {
+		dev_warn(ring->mbox->dev, "no space in ring %d\n", ring->num);
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Determine current HW BD read offset */
+	read_offset = readl_relaxed(ring->regs + RING_BD_READ_PTR);
+	val = readl_relaxed(ring->regs + RING_BD_START_ADDR);
+	read_offset *= RING_DESC_SIZE;
+	read_offset += (u32)(BD_START_ADDR_DECODE(val) - ring->bd_dma_base);
+
+	/*
+	 * Number required descriptors = number of non-header descriptors +
+	 *				 number of header descriptors +
+	 *				 1x null descriptor
+	 */
+	nhcnt = flexrm_estimate_nonheader_desc_count(msg);
+	count = flexrm_estimate_header_desc_count(nhcnt) + nhcnt + 1;
+
+	/* Check for available descriptor space. */
+	write_offset = ring->bd_write_offset;
+	while (count) {
+		if (!flexrm_is_next_table_desc(ring->bd_base + write_offset))
+			count--;
+		write_offset += RING_DESC_SIZE;
+		if (write_offset == RING_BD_SIZE)
+			write_offset = 0x0;
+		if (write_offset == read_offset)
+			break;
+	}
+	if (count) {
+		spin_lock_irqsave(&ring->lock, flags);
+		if (batch_msg)
+			ring->last_pending_msg = batch_msg;
+		else
+			ring->last_pending_msg = msg;
+		spin_unlock_irqrestore(&ring->lock, flags);
+		ret = 0;
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Write descriptors to ring */
+	next = flexrm_write_descs(msg, nhcnt, reqid,
+			ring->bd_base + ring->bd_write_offset,
+			RING_BD_TOGGLE_VALID(ring->bd_write_offset),
+			ring->bd_base, ring->bd_base + RING_BD_SIZE);
+	if (IS_ERR(next)) {
+		ret = PTR_ERR(next);
+		exit_cleanup = true;
+		goto exit;
+	}
+
+	/* Save ring BD write offset */
+	ring->bd_write_offset = (unsigned long)(next - ring->bd_base);
+
+exit:
+	/* Update error status in message */
+	msg->error = ret;
+
+	/* Cleanup if we failed */
+	if (exit_cleanup) {
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+	}
+
+	return ret;
+}
+
+static int flexrm_process_completions(struct flexrm_ring *ring)
+{
+	u64 desc;
+	int err, count = 0;
+	unsigned long flags;
+	struct brcm_message *msg = NULL;
+	u32 reqid, cmpl_read_offset, cmpl_write_offset;
+	struct mbox_chan *chan = &ring->mbox->controller.chans[ring->num];
+
+	spin_lock_irqsave(&ring->lock, flags);
+
+	/* Check last_pending_msg */
+	if (ring->last_pending_msg) {
+		msg = ring->last_pending_msg;
+		ring->last_pending_msg = NULL;
+	}
+
+	/*
+	 * Get current completion read and write offset
+	 *
+	 * Note: We should read completion write pointer atleast once
+	 * after we get a MSI interrupt because HW maintains internal
+	 * MSI status which will allow next MSI interrupt only after
+	 * completion write pointer is read.
+	 */
+	cmpl_write_offset = readl_relaxed(ring->regs + RING_CMPL_WRITE_PTR);
+	cmpl_write_offset *= RING_DESC_SIZE;
+	cmpl_read_offset = ring->cmpl_read_offset;
+	ring->cmpl_read_offset = cmpl_write_offset;
+
+	spin_unlock_irqrestore(&ring->lock, flags);
+
+	/* If last_pending_msg was set then queue it back */
+	if (msg)
+		mbox_send_message(chan, msg);
+
+	/* For each completed request notify mailbox clients */
+	reqid = 0;
+	while (cmpl_read_offset != cmpl_write_offset) {
+		/* Dequeue next completion descriptor */
+		desc = *((u64 *)(ring->cmpl_base + cmpl_read_offset));
+
+		/* Next read offset */
+		cmpl_read_offset += RING_DESC_SIZE;
+		if (cmpl_read_offset == RING_CMPL_SIZE)
+			cmpl_read_offset = 0;
+
+		/* Decode error from completion descriptor */
+		err = flexrm_cmpl_desc_to_error(desc);
+		if (err < 0) {
+			dev_warn(ring->mbox->dev,
+				 "got completion desc=0x%lx with error %d",
+				 (unsigned long)desc, err);
+		}
+
+		/* Determine request id from completion descriptor */
+		reqid = flexrm_cmpl_desc_to_reqid(desc);
+
+		/* Determine message pointer based on reqid */
+		msg = ring->requests[reqid];
+		if (!msg) {
+			dev_warn(ring->mbox->dev,
+				 "null msg pointer for completion desc=0x%lx",
+				 (unsigned long)desc);
+			continue;
+		}
+
+		/* Release reqid for recycling */
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+
+		/* Unmap DMA mappings */
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+
+		/* Give-back message to mailbox client */
+		msg->error = err;
+		mbox_chan_received_data(chan, msg);
+
+		/* Increment number of completions processed */
+		count++;
+	}
+
+	return count;
+}
+
+static irqreturn_t flexrm_irq_event(int irq, void *dev_id)
+{
+	/* We only have MSI for completions so just wakeup IRQ thread */
+	/* Ring related errors will be informed via completion descriptors */
+
+	return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t flexrm_irq_thread(int irq, void *dev_id)
+{
+	flexrm_process_completions(dev_id);
+
+	return IRQ_HANDLED;
+}
+
+static int flexrm_send_data(struct mbox_chan *chan, void *data)
+{
+	int i, rc;
+	struct flexrm_ring *ring = chan->con_priv;
+	struct brcm_message *msg = data;
+
+	if (msg->type == BRCM_MESSAGE_BATCH) {
+		for (i = msg->batch.msgs_queued;
+		     i < msg->batch.msgs_count; i++) {
+			rc = flexrm_new_request(ring, msg,
+						 &msg->batch.msgs[i]);
+			if (rc) {
+				msg->error = rc;
+				return rc;
+			}
+			msg->batch.msgs_queued++;
+		}
+		return 0;
+	}
+
+	return flexrm_new_request(ring, NULL, data);
+}
+
+static bool flexrm_peek_data(struct mbox_chan *chan)
+{
+	int cnt = flexrm_process_completions(chan->con_priv);
+
+	return (cnt > 0) ? true : false;
+}
+
+static int flexrm_startup(struct mbox_chan *chan)
+{
+	u64 d;
+	u32 val, off;
+	int ret = 0;
+	dma_addr_t next_addr;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	/* Allocate BD memory */
+	ring->bd_base = dma_pool_alloc(ring->mbox->bd_pool,
+				       GFP_KERNEL, &ring->bd_dma_base);
+	if (!ring->bd_base) {
+		dev_err(ring->mbox->dev, "can't allocate BD memory\n");
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Configure next table pointer entries in BD memory */
+	for (off = 0; off < RING_BD_SIZE; off += RING_DESC_SIZE) {
+		next_addr = off + RING_DESC_SIZE;
+		if (next_addr == RING_BD_SIZE)
+			next_addr = 0;
+		next_addr += ring->bd_dma_base;
+		if (RING_BD_ALIGN_CHECK(next_addr))
+			d = flexrm_next_table_desc(RING_BD_TOGGLE_VALID(off),
+						    next_addr);
+		else
+			d = flexrm_null_desc(RING_BD_TOGGLE_INVALID(off));
+		flexrm_write_desc(ring->bd_base + off, d);
+	}
+
+	/* Allocate completion memory */
+	ring->cmpl_base = dma_pool_alloc(ring->mbox->cmpl_pool,
+					 GFP_KERNEL, &ring->cmpl_dma_base);
+	if (!ring->cmpl_base) {
+		dev_err(ring->mbox->dev, "can't allocate completion memory\n");
+		ret = -ENOMEM;
+		goto fail_free_bd_memory;
+	}
+	memset(ring->cmpl_base, 0, RING_CMPL_SIZE);
+
+	/* Request IRQ */
+	if (ring->irq == UINT_MAX) {
+		dev_err(ring->mbox->dev, "ring IRQ not available\n");
+		ret = -ENODEV;
+		goto fail_free_cmpl_memory;
+	}
+	ret = request_threaded_irq(ring->irq,
+				   flexrm_irq_event,
+				   flexrm_irq_thread,
+				   0, dev_name(ring->mbox->dev), ring);
+	if (ret) {
+		dev_err(ring->mbox->dev, "failed to request ring IRQ\n");
+		goto fail_free_cmpl_memory;
+	}
+	ring->irq_requested = true;
+
+	/* Disable/inactivate ring */
+	writel_relaxed(0x0, ring->regs + RING_CONTROL);
+
+	/* Program BD start address */
+	val = BD_START_ADDR_VALUE(ring->bd_dma_base);
+	writel_relaxed(val, ring->regs + RING_BD_START_ADDR);
+
+	/* BD write pointer will be same as HW write pointer */
+	ring->bd_write_offset =
+			readl_relaxed(ring->regs + RING_BD_WRITE_PTR);
+	ring->bd_write_offset *= RING_DESC_SIZE;
+
+	/* Program completion start address */
+	val = CMPL_START_ADDR_VALUE(ring->cmpl_dma_base);
+	writel_relaxed(val, ring->regs + RING_CMPL_START_ADDR);
+
+	/* Ensure last pending message is cleared */
+	ring->last_pending_msg = NULL;
+
+	/* Completion read pointer will be same as HW write pointer */
+	ring->cmpl_read_offset =
+			readl_relaxed(ring->regs + RING_CMPL_WRITE_PTR);
+	ring->cmpl_read_offset *= RING_DESC_SIZE;
+
+	/* Read ring Tx, Rx, and Outstanding counts to clear */
+	readl_relaxed(ring->regs + RING_NUM_REQ_RECV_LS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_RECV_MS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_TRANS_LS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_TRANS_MS);
+	readl_relaxed(ring->regs + RING_NUM_REQ_OUTSTAND);
+
+	/* Configure RING_MSI_CONTROL */
+	val = 0;
+	val |= (ring->msi_timer_val << MSI_TIMER_VAL_SHIFT);
+	val |= BIT(MSI_ENABLE_SHIFT);
+	val |= (ring->msi_count_threshold & MSI_COUNT_MASK) << MSI_COUNT_SHIFT;
+	writel_relaxed(val, ring->regs + RING_MSI_CONTROL);
+
+	/* Enable/activate ring */
+	val = BIT(CONTROL_ACTIVE_SHIFT);
+	writel_relaxed(val, ring->regs + RING_CONTROL);
+
+	return 0;
+
+fail_free_cmpl_memory:
+	dma_pool_free(ring->mbox->cmpl_pool,
+		      ring->cmpl_base, ring->cmpl_dma_base);
+	ring->cmpl_base = NULL;
+fail_free_bd_memory:
+	dma_pool_free(ring->mbox->bd_pool,
+		      ring->bd_base, ring->bd_dma_base);
+	ring->bd_base = NULL;
+fail:
+	return ret;
+}
+
+static void flexrm_shutdown(struct mbox_chan *chan)
+{
+	u32 reqid;
+	unsigned int timeout;
+	struct brcm_message *msg;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	/* Disable/inactivate ring */
+	writel_relaxed(0x0, ring->regs + RING_CONTROL);
+
+	/* Flush ring with timeout of 1s */
+	timeout = 1000;
+	writel_relaxed(BIT(CONTROL_FLUSH_SHIFT),
+			ring->regs + RING_CONTROL);
+	do {
+		if (readl_relaxed(ring->regs + RING_FLUSH_DONE) &
+		    FLUSH_DONE_MASK)
+			break;
+		mdelay(1);
+	} while (timeout--);
+
+	/* Abort all in-flight requests */
+	for (reqid = 0; reqid < RING_MAX_REQ_COUNT; reqid++) {
+		msg = ring->requests[reqid];
+		if (!msg)
+			continue;
+
+		/* Release reqid for recycling */
+		ring->requests[reqid] = NULL;
+		ida_simple_remove(&ring->requests_ida, reqid);
+
+		/* Unmap DMA mappings */
+		flexrm_dma_unmap(ring->mbox->dev, msg);
+
+		/* Give-back message to mailbox client */
+		msg->error = -EIO;
+		mbox_chan_received_data(chan, msg);
+	}
+
+	/* Release IRQ */
+	if (ring->irq_requested) {
+		free_irq(ring->irq, ring);
+		ring->irq_requested = false;
+	}
+
+	/* Free-up completion descriptor ring */
+	if (ring->cmpl_base) {
+		dma_pool_free(ring->mbox->cmpl_pool,
+			      ring->cmpl_base, ring->cmpl_dma_base);
+		ring->cmpl_base = NULL;
+	}
+
+	/* Free-up BD descriptor ring */
+	if (ring->bd_base) {
+		dma_pool_free(ring->mbox->bd_pool,
+			      ring->bd_base, ring->bd_dma_base);
+		ring->bd_base = NULL;
+	}
+}
+
+static bool flexrm_last_tx_done(struct mbox_chan *chan)
+{
+	bool ret;
+	unsigned long flags;
+	struct flexrm_ring *ring = chan->con_priv;
+
+	spin_lock_irqsave(&ring->lock, flags);
+	ret = (ring->last_pending_msg) ? false : true;
+	spin_unlock_irqrestore(&ring->lock, flags);
+
+	return ret;
+}
+
+static const struct mbox_chan_ops flexrm_mbox_chan_ops = {
+	.send_data	= flexrm_send_data,
+	.startup	= flexrm_startup,
+	.shutdown	= flexrm_shutdown,
+	.last_tx_done	= flexrm_last_tx_done,
+	.peek_data	= flexrm_peek_data,
+};
+
+static void flexrm_mbox_msi_write(struct msi_desc *desc, struct msi_msg *msg)
+{
+	struct device *dev = msi_desc_to_dev(desc);
+	struct flexrm_mbox *mbox = dev_get_drvdata(dev);
+	struct flexrm_ring *ring = &mbox->rings[desc->platform.msi_index];
+
+	/* Configure per-Ring MSI registers */
+	writel_relaxed(msg->address_lo, ring->regs + RING_MSI_ADDR_LS);
+	writel_relaxed(msg->address_hi, ring->regs + RING_MSI_ADDR_MS);
+	writel_relaxed(msg->data, ring->regs + RING_MSI_DATA_VALUE);
+}
+
+static struct mbox_chan *flexrm_mbox_of_xlate(struct mbox_controller *cntlr,
+					const struct of_phandle_args *pa)
+{
+	struct mbox_chan *chan;
+	struct flexrm_ring *ring;
+
+	if (pa->args_count < 3)
+		return ERR_PTR(-EINVAL);
+
+	if (pa->args[0] >= cntlr->num_chans)
+		return ERR_PTR(-ENOENT);
+
+	if (pa->args[1] > MSI_COUNT_MASK)
+		return ERR_PTR(-EINVAL);
+
+	if (pa->args[2] > MSI_TIMER_VAL_MASK)
+		return ERR_PTR(-EINVAL);
+
+	chan = &cntlr->chans[pa->args[0]];
+	ring = chan->con_priv;
+	ring->msi_count_threshold = pa->args[1];
+	ring->msi_timer_val = pa->args[2];
+
+	return chan;
+}
+
+static int flexrm_mbox_probe(struct platform_device *pdev)
+{
+	int index, ret = 0;
+	void __iomem *regs;
+	void __iomem *regs_end;
+	struct msi_desc *desc;
+	struct resource *iomem;
+	struct flexrm_ring *ring;
+	struct flexrm_mbox *mbox;
+	struct device *dev = &pdev->dev;
+
+	/* Allocate driver mailbox struct */
+	mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+	if (!mbox) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	mbox->dev = dev;
+	platform_set_drvdata(pdev, mbox);
+
+	/* Get resource for registers */
+	iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!iomem || (resource_size(iomem) < RING_REGS_SIZE)) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/* Map registers of all rings */
+	mbox->regs = devm_ioremap_resource(&pdev->dev, iomem);
+	if (IS_ERR(mbox->regs)) {
+		ret = PTR_ERR(mbox->regs);
+		dev_err(&pdev->dev, "Failed to remap mailbox regs: %d\n", ret);
+		goto fail;
+	}
+	regs_end = mbox->regs + resource_size(iomem);
+
+	/* Scan and count available rings */
+	mbox->num_rings = 0;
+	for (regs = mbox->regs; regs < regs_end; regs += RING_REGS_SIZE) {
+		if (readl_relaxed(regs + RING_VER) == RING_VER_MAGIC)
+			mbox->num_rings++;
+	}
+	if (!mbox->num_rings) {
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/* Allocate driver ring structs */
+	ring = devm_kcalloc(dev, mbox->num_rings, sizeof(*ring), GFP_KERNEL);
+	if (!ring) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+	mbox->rings = ring;
+
+	/* Initialize members of driver ring structs */
+	regs = mbox->regs;
+	for (index = 0; index < mbox->num_rings; index++) {
+		ring = &mbox->rings[index];
+		ring->num = index;
+		ring->mbox = mbox;
+		while ((regs < regs_end) &&
+		       (readl_relaxed(regs + RING_VER) != RING_VER_MAGIC))
+			regs += RING_REGS_SIZE;
+		if (regs_end <= regs) {
+			ret = -ENODEV;
+			goto fail;
+		}
+		ring->regs = regs;
+		regs += RING_REGS_SIZE;
+		ring->irq = UINT_MAX;
+		ring->irq_requested = false;
+		ring->msi_timer_val = MSI_TIMER_VAL_MASK;
+		ring->msi_count_threshold = 0x1;
+		ida_init(&ring->requests_ida);
+		memset(ring->requests, 0, sizeof(ring->requests));
+		ring->bd_base = NULL;
+		ring->bd_dma_base = 0;
+		ring->cmpl_base = NULL;
+		ring->cmpl_dma_base = 0;
+		spin_lock_init(&ring->lock);
+		ring->last_pending_msg = NULL;
+		ring->cmpl_read_offset = 0;
+	}
+
+	/* FlexRM is capable of 40-bit physical addresses only */
+	mbox->dma_mask = DMA_BIT_MASK(40);
+	dev->dma_mask = &mbox->dma_mask;
+
+	/* Create DMA pool for ring BD memory */
+	mbox->bd_pool = dma_pool_create("bd", dev, RING_BD_SIZE,
+					1 << RING_BD_ALIGN_ORDER, 0);
+	if (!mbox->bd_pool) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	/* Create DMA pool for ring completion memory */
+	mbox->cmpl_pool = dma_pool_create("cmpl", dev, RING_CMPL_SIZE,
+					  1 << RING_CMPL_ALIGN_ORDER, 0);
+	if (!mbox->cmpl_pool) {
+		ret = -ENOMEM;
+		goto fail_destroy_bd_pool;
+	}
+
+	/* Allocate platform MSIs for each ring */
+	ret = platform_msi_domain_alloc_irqs(dev, mbox->num_rings,
+						flexrm_mbox_msi_write);
+	if (ret)
+		goto fail_destroy_cmpl_pool;
+
+	/* Save alloced IRQ numbers for each ring */
+	for_each_msi_entry(desc, dev) {
+		ring = &mbox->rings[desc->platform.msi_index];
+		ring->irq = desc->irq;
+	}
+
+	/* Initialize mailbox controller */
+	mbox->controller.txdone_irq = false;
+	mbox->controller.txdone_poll = true;
+	mbox->controller.txpoll_period = 1;
+	mbox->controller.ops = &flexrm_mbox_chan_ops;
+	mbox->controller.dev = dev;
+	mbox->controller.num_chans = mbox->num_rings;
+	mbox->controller.of_xlate = flexrm_mbox_of_xlate;
+	mbox->controller.chans = devm_kcalloc(dev, mbox->num_rings,
+				sizeof(*mbox->controller.chans), GFP_KERNEL);
+	if (!mbox->controller.chans) {
+		ret = -ENOMEM;
+		goto fail_free_msis;
+	}
+	for (index = 0; index < mbox->num_rings; index++)
+		mbox->controller.chans[index].con_priv = &mbox->rings[index];
+
+	/* Register mailbox controller */
+	ret = mbox_controller_register(&mbox->controller);
+	if (ret)
+		goto fail_free_msis;
+
+	dev_info(dev, "registered flexrm mailbox with %d channels\n",
+			mbox->controller.num_chans);
+
+	return 0;
+
+fail_free_msis:
+	platform_msi_domain_free_irqs(dev);
+fail_destroy_cmpl_pool:
+	dma_pool_destroy(mbox->cmpl_pool);
+fail_destroy_bd_pool:
+	dma_pool_destroy(mbox->bd_pool);
+fail:
+	return ret;
+}
+
+static int flexrm_mbox_remove(struct platform_device *pdev)
+{
+	int index;
+	struct device *dev = &pdev->dev;
+	struct flexrm_ring *ring;
+	struct flexrm_mbox *mbox = platform_get_drvdata(pdev);
+
+	mbox_controller_unregister(&mbox->controller);
+
+	platform_msi_domain_free_irqs(dev);
+
+	dma_pool_destroy(mbox->cmpl_pool);
+	dma_pool_destroy(mbox->bd_pool);
+
+	for (index = 0; index < mbox->num_rings; index++) {
+		ring = &mbox->rings[index];
+		ida_destroy(&ring->requests_ida);
+	}
+
+	return 0;
+}
+
+static const struct of_device_id flexrm_mbox_of_match[] = {
+	{ .compatible = "brcm,iproc-flexrm-mbox", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, flexrm_mbox_of_match);
+
+static struct platform_driver flexrm_mbox_driver = {
+	.driver = {
+		.name = "brcm-flexrm-mbox",
+		.of_match_table = flexrm_mbox_of_match,
+	},
+	.probe		= flexrm_mbox_probe,
+	.remove		= flexrm_mbox_remove,
+};
+module_platform_driver(flexrm_mbox_driver);
+
+MODULE_AUTHOR("Anup Patel <anup.patel@broadcom.com>");
+MODULE_DESCRIPTION("Broadcom FlexRM mailbox driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mailbox/brcm-message.h b/include/linux/mailbox/brcm-message.h
index 6b55c93..c20b484 100644
--- a/include/linux/mailbox/brcm-message.h
+++ b/include/linux/mailbox/brcm-message.h
@@ -16,6 +16,7 @@
 
 enum brcm_message_type {
 	BRCM_MESSAGE_UNKNOWN = 0,
+	BRCM_MESSAGE_BATCH,
 	BRCM_MESSAGE_SPU,
 	BRCM_MESSAGE_SBA,
 	BRCM_MESSAGE_MAX,
@@ -23,24 +24,29 @@ enum brcm_message_type {
 
 struct brcm_sba_command {
 	u64 cmd;
+	u64 *cmd_dma;
+	dma_addr_t cmd_dma_addr;
 #define BRCM_SBA_CMD_TYPE_A		BIT(0)
 #define BRCM_SBA_CMD_TYPE_B		BIT(1)
 #define BRCM_SBA_CMD_TYPE_C		BIT(2)
 #define BRCM_SBA_CMD_HAS_RESP		BIT(3)
 #define BRCM_SBA_CMD_HAS_OUTPUT		BIT(4)
 	u64 flags;
-	dma_addr_t input;
-	size_t input_len;
 	dma_addr_t resp;
 	size_t resp_len;
-	dma_addr_t output;
-	size_t output_len;
+	dma_addr_t data;
+	size_t data_len;
 };
 
 struct brcm_message {
 	enum brcm_message_type type;
 	union {
 		struct {
+			struct brcm_message *msgs;
+			unsigned int msgs_queued;
+			unsigned int msgs_count;
+		} batch;
+		struct {
 			struct scatterlist *src;
 			struct scatterlist *dst;
 		} spu;
-- 
2.7.4

^ permalink raw reply related

* Re: [ULOGD2] Timestamp without year in logemu
From: Petteri Matilainen @ 2017-01-04  5:37 UTC (permalink / raw)
  To: netfilter
In-Reply-To: <1483477124.14368.7.camel@regit.org>

Hello Eric,

Thanks for the reply. Would it be possible to modify the code to
include year in logemu output? I mean, is it something that is easily
modified locally just for me? Can you explain the LOCAL filter, why is
it not there anymore? It's description says that it may print more
accurate timestamp.

Also, if anyone else has tips on how to get the year included in the
log lines, maybe do a search and replace afterwards or something
(although it would be very heavy on large logs), please share.

regards

Petteri Matilainen


On Tue, Jan 3, 2017 at 10:58 PM, Eric Leblond <eric@regit.org> wrote:
> Hello,
>
> On Tue, 2017-01-03 at 21:28 +0200, Petteri Matilainen wrote:
>> Hello list,
>>
>> I'm using iptables firewall and I'm logging blocked packets with
>> NFLOG
>> target and ULOGD2. I have the following configuration in ulogd.conf:
>>
>> # this is a stack for logging packets to firewall.log after a
>> collect
>> via NFLOG
>> stack=firewall6:NFLOG,base6:BASE,ifi6:IFINDEX,ip2str6:IP2STR,print6:P
>> RINTPKT,logemu6:LOGEMU
>>
>> [logemu6]
>> file="/var/log/firewall.log"
>> sync=1
>>
>> The logging itself works just fine, but I noticed the timestamps are
>> missing the year, like so:
>>
>> Apr 23 10:38:04 Router In_New_TCP IN=eth0 OUT=...
>>
>> Any ideas why? This causes problems with the system I'm using to
>> graph
>> the logs and make statistics. My system is Debian with kernel
>> 3.16.7.
>> Ulogd version is 2.0.4. I found an online manual for ulogd2 which
>> mentioned a LOCAL filter but my ulogd does not recognize it.
>
> I've just checked the code and it is using (since 2006) ctime which
> does not return the year.
>
> It may not work for you but if you use the JSON output, you will get
> something nicely formated and will get the year.
>
> BR,
> --
> Eric Leblond <eric@regit.org>

^ permalink raw reply

* adding 32 bit compatibility layer in custom kernel module
From: Anish Kumar @ 2017-01-04  5:37 UTC (permalink / raw)
  To: kernelnewbies
In-Reply-To: <CAHt1LmbsW9HyHbGQ2_BrPwa-EQkZMW+WuvTwAqeriK-zV_VRcw@mail.gmail.com>



> On Jan 3, 2017, at 8:04 PM, Pradeepa Kumar <cdpradeepa@gmail.com> wrote:
> 
> Hi experts
> 
> down vote
> favorite
> in my 64 bit kernel, I have a custom kernel module providing new protocol and providing socket system calls.
> 
> it works fine when 64 bit app runs.
> 
> i am seeing issues when 32 bit app runs (for exp recvmsg() call does not work if msg has cmsghdrs as struct size of cmsghdr is different in 32 bit and 64 bit).
> 
> This is because my custom kernel module does not have 32 bit compatibility layer ( but linux kernel has this in compat.c etc).
> 
> is it simple to add compatibility layer to my custom kernel module
> 
> All you have to do is add compat_ioctl call to your driver.
> how do i do this (any links ?)
> 
> You can see many drivers supports compat call.
> Thanks
> 
> _______________________________________________
> Kernelnewbies mailing list
> Kernelnewbies at kernelnewbies.org
> https://lists.kernelnewbies.org/mailman/listinfo/kernelnewbies
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20170103/d2d8c9a1/attachment.html 

^ permalink raw reply

* [PATCH] usb: gadget: composite: Fix function used to free memory
From: Christophe JAILLET @ 2017-01-04  5:30 UTC (permalink / raw)
  To: balbi, gregkh
  Cc: linux-usb, linux-kernel, kernel-janitors, Christophe JAILLET

'cdev->os_desc_req' has been allocated with 'usb_ep_alloc_request()' so
'usb_ep_free_request()' should be used to free it.

Signed-off-by: Christophe JAILLET <christophe.jaillet@wanadoo.fr>
---
 drivers/usb/gadget/composite.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 002822d98fda..49d685ad0da9 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -2147,7 +2147,7 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
 	cdev->os_desc_req->buf = kmalloc(4096, GFP_KERNEL);
 	if (!cdev->os_desc_req->buf) {
 		ret = -ENOMEM;
-		kfree(cdev->os_desc_req);
+		usb_ep_free_request(ep0, cdev->os_desc_req);
 		goto end;
 	}
 	cdev->os_desc_req->context = cdev;
-- 
2.9.3

^ permalink raw reply related

* Re: [PATCH v5 1/4] soc: zte: Add header for PM domains specifiers
From: Shawn Guo @ 2017-01-04  5:29 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie, robh+dt, mark.rutland, gregkh, davem, geert+renesas,
	akpm, mchehab, linux, krzk, arnd, linus.walleij, pankaj.dubey,
	yangbo.lu, f.fainelli, claudiu.manoil, laurent.pinchart+renesas,
	linux-kernel, linux-arm-kernel, devicetree, xie.baoyou,
	chen.chaokai, wang.qiang01
In-Reply-To: <1483489157-10782-1-git-send-email-baoyou.xie@linaro.org>

On Wed, Jan 04, 2017 at 08:19:14AM +0800, Baoyou Xie wrote:
> This patch adds header with values used for ZTE 2967
> SoC's power domain driver.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawn.guo@linaro.org>

The tags like Reviewed-by, Acked-by etc, can only be added into your
patch after people give it explicitly.  One of the examples is that Jun
Nie gave his Reviewed-by tag on your "[PATCH v2 2/2] soc: zte:
pm_domains: Add support for zx296718 board".

https://www.spinics.net/lists/arm-kernel/msg547282.html

I did not give my Reviewed-by tag on list, so you shouldn't add it here.
Actually, in case of this patch series, I will probably apply them
through ZTE -> arm-soc tree, when I feel it's ready.  So I'm do not even
need to give the Reviewed-by tag.

Shawn

> ---
>  include/dt-bindings/soc/zte,pm_domains.h | 23 +++++++++++++++++++++++
>  1 file changed, 23 insertions(+)
>  create mode 100644 include/dt-bindings/soc/zte,pm_domains.h
> 
> diff --git a/include/dt-bindings/soc/zte,pm_domains.h b/include/dt-bindings/soc/zte,pm_domains.h
> new file mode 100644
> index 0000000..01e9abc
> --- /dev/null
> +++ b/include/dt-bindings/soc/zte,pm_domains.h
> @@ -0,0 +1,23 @@
> +/*
> + * Copyright (C) 2017 Linaro Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +#ifndef _DT_BINDINGS_SOC_ZTE_PM_DOMAINS_H
> +#define _DT_BINDINGS_SOC_ZTE_PM_DOMAINS_H
> +
> +#define DM_ZX296718_SAPPU	0
> +#define DM_ZX296718_VDE		1  /* g1v6 */
> +#define DM_ZX296718_VCE		2  /* h1v6 */
> +#define DM_ZX296718_HDE		3  /* g2v2 */
> +#define DM_ZX296718_VIU		4
> +#define DM_ZX296718_USB20	5
> +#define DM_ZX296718_USB21	6
> +#define DM_ZX296718_USB30	7
> +#define DM_ZX296718_HSIC	8
> +#define DM_ZX296718_GMAC	9
> +#define DM_ZX296718_TS		10
> +#define DM_ZX296718_VOU		11
> +
> +#endif /* _DT_BINDINGS_SOC_ZTE_PM_DOMAINS_H */
> -- 
> 2.7.4
> 

^ permalink raw reply

* adding 32 bit compatibility layer in custom kernel module
From: Pradeepa Kumar @ 2017-01-04  5:39 UTC (permalink / raw)
  To: kernelnewbies
In-Reply-To: <210705.1483504806@turing-police.cc.vt.edu>

Thanks Valdis.
Yes ,  custom kernel module has struct proto_ops pointing to the routines
in the module.
my kernel module routines  do not cal any of linux provided socket* calls.
it gets data from different app and provide that data back to app calling
socket* APIs.

i understand linux kernel calls 32 bit version of syscall handler from sys
call table .
https://kernel.org/doc/html/latest/process/adding-syscalls.html

but how do i do this in custom kernel module.
how to differentiate 32 bit and 64 app in my kernel module.

do i need all stuff from compat.h and compat.c (if i use same files will
 that work) ?
do i need to change some files in std kernel files too like syscall_32.tbl
file , and recompile kernel ?

It would be great if there is any example / any web links for this to
understad
Thanks




On Wed, Jan 4, 2017 at 10:10 AM, <Valdis.Kletnieks@vt.edu> wrote:

> On Wed, 04 Jan 2017 09:34:58 +0530, Pradeepa Kumar said:
>
> >  I have a custom kernel module providing new protocol and providing
> socket system calls.
>
> In general, a loadable module can provide a new protocol, but can't add new
> syscalls.  However, adding support for socket(), connect(), recvmsg() and
> so on for a new protocol *can* be done from a module, as long as the
> protocol provides a suitable struct proto_ops pointing to the routines
> in the module.
>
> > i am seeing issues when 32 bit app runs (for exp recvmsg() call does not
> > work if msg has cmsghdrs as struct size of cmsghdr is different in 32 bit
> > and 64 bit).
>
> That's a good reason to double-check your code to ensure that you don't
> make that same mistake.
>
> > This is because my custom kernel module does not have 32 bit
> compatibility
> > layer ( but linux kernel has this in compat.c etc).
>
> Hold that thought.
>
> >    is it simple to add compatibility layer to my custom kernel module
>
> Given your previous sentence, you should be able to figure that out.
>
> >    how do i do this (any links ?)
>
> /*
>  *  linux/kernel/compat.c
>  *
>  *  Kernel compatibililty routines for e.g. 32 bit syscall support
>  *  on 64 bit kernels.
>  *
>  *  Copyright (C) 2002-2003 Stephen Rothwell, IBM Corporation
>
> May already have the functions you need.  If not, use the code as a guide
> to writing the stuff you're still lacking.
>
> Note that in particular, recvmsg() combatability is already done for you
> via
> this chunk of code in net/compat.c:
>
> COMPAT_SYSCALL_DEFINE3(recvmsg, int, fd, struct compat_msghdr __user *,
> msg, unsigned int, flags)
> {
>         return __sys_recvmsg(fd, (struct user_msghdr __user *)msg, flags |
> MSG_CMSG_COMPAT);
> }
>
> Go look and see what the MSG_CMSG_COMPAT flag does if you want the gory
> details.
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20170104/5d3700e8/attachment.html 

^ permalink raw reply

* Doubt in Eudyptula Challenge
From: Deeksha varshney @ 2017-01-04  5:43 UTC (permalink / raw)
  To: kernelnewbies
In-Reply-To: <100268.1483471562@turing-police.cc.vt.edu>

sir,
i have cleared my doubt thanks for the help
regards
deeksha varshney

On 4 Jan 2017 00:56, <Valdis.Kletnieks@vt.edu> wrote:

> On Mon, 02 Jan 2017 15:52:36 +0530, deeksha varshney said:
> > Hello sir,
> > I am not able to understand what to write in the subject line along with
> > the ID while submitting the task 01.Please help
> >
> > I am getting the following reply from Little
> >
> > I can not understand your Subject: line, please read the introductory
> > email and fix the subject to match exactly as it says in order for me to
> > be able to parse it properly.
>
> So did you read the introductory email?  Did it say what to use as a
> subject?
> Did you actually *do* so?
>
> Did you also read the part where it says do it *on your own*?
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://lists.kernelnewbies.org/pipermail/kernelnewbies/attachments/20170104/8f54f694/attachment.html 

^ permalink raw reply

* [PATCH v5 2/4] soc: zte: pm_domains: Prepare for supporting ARMv8 zx2967 family
From: Shawn Guo @ 2017-01-04  5:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483489157-10782-2-git-send-email-baoyou.xie@linaro.org>

On Wed, Jan 04, 2017 at 08:19:15AM +0800, Baoyou Xie wrote:
> The ARMv8 zx2967 family (296718, 296716 etc) uses different value
> for controlling the power domain on/off registers, Choose the
> value depending on the compatible.
> 
> Multiple domains are prepared for the family, this patch prepares
> the common functions.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawnguo@kernel.org>
> Reviewed-by: Jun Nie <jun.nie@linaro.org>

Same here.  Neither myself nor Jun gave Reviewed-by tag on this patch.

> ---
>  drivers/soc/Kconfig                 |   1 +
>  drivers/soc/Makefile                |   1 +
>  drivers/soc/zte/Kconfig             |  13 ++++
>  drivers/soc/zte/Makefile            |   4 ++
>  drivers/soc/zte/zx2967_pm_domains.c | 139 ++++++++++++++++++++++++++++++++++++
>  drivers/soc/zte/zx2967_pm_domains.h |  46 ++++++++++++
>  6 files changed, 204 insertions(+)
>  create mode 100644 drivers/soc/zte/Kconfig
>  create mode 100644 drivers/soc/zte/Makefile
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.c
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.h
> 
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index f31bceb..f09023f 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -11,5 +11,6 @@ source "drivers/soc/tegra/Kconfig"
>  source "drivers/soc/ti/Kconfig"
>  source "drivers/soc/ux500/Kconfig"
>  source "drivers/soc/versatile/Kconfig"
> +source "drivers/soc/zte/Kconfig"
>  
>  endmenu
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 50c23d0..05eae52 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
>  obj-$(CONFIG_SOC_TI)		+= ti/
>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>  obj-$(CONFIG_PLAT_VERSATILE)	+= versatile/
> +obj-$(CONFIG_ARCH_ZX)		+= zte/
> diff --git a/drivers/soc/zte/Kconfig b/drivers/soc/zte/Kconfig
> new file mode 100644
> index 0000000..20bde38
> --- /dev/null
> +++ b/drivers/soc/zte/Kconfig
> @@ -0,0 +1,13 @@
> +#
> +# ZTE SoC drivers
> +#
> +menuconfig SOC_ZTE
> +	bool "ZTE SoC driver support"
> +
> +if SOC_ZTE
> +
> +config ZX2967_PM_DOMAINS
> +	bool "ZX2967 PM domains"
> +	depends on PM_GENERIC_DOMAINS
> +
> +endif
> diff --git a/drivers/soc/zte/Makefile b/drivers/soc/zte/Makefile
> new file mode 100644
> index 0000000..8a37f2f
> --- /dev/null
> +++ b/drivers/soc/zte/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# ZTE SOC drivers
> +#
> +obj-$(CONFIG_ZX2967_PM_DOMAINS) += zx2967_pm_domains.o
> diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c
> new file mode 100644
> index 0000000..a215875
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.c
> @@ -0,0 +1,139 @@
> +/*
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +
> +#include "zx2967_pm_domains.h"
> +
> +#define PCU_DM_CLKEN(zpd)	((zpd)->reg_offset[REG_CLKEN])
> +#define PCU_DM_ISOEN(zpd)	((zpd)->reg_offset[REG_ISOEN])
> +#define PCU_DM_RSTEN(zpd)	((zpd)->reg_offset[REG_RSTEN])
> +#define PCU_DM_PWREN(zpd)	((zpd)->reg_offset[REG_PWREN])
> +#define PCU_DM_PWRDN(zpd)	((zpd)->reg_offset[REG_PWRDN])

PCU_DM_PWRDN doesn't seem to be used anywhere in this driver.

> +#define PCU_DM_ACK_SYNC(zpd)	((zpd)->reg_offset[REG_ACK_SYNC])
> +
> +static void __iomem *pcubase;
> +
> +int zx2967_power_on(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val |= BIT(zpd->bit);
> +	else
> +		val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && !val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	pr_debug("normal poweron %s\n", domain->name);

Does "normal" in the debug message mean anything here?

> +
> +	return 0;
> +}
> +
> +int zx2967_power_off(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val &= ~BIT(zpd->bit);
> +	else
> +		val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	pr_debug("normal poweroff %s\n", domain->name);

Ditto

> +
> +	return 0;
> +}
> +
> +int zx2967_pd_probe(struct platform_device *pdev,
> +		    struct generic_pm_domain **zx_pm_domains,
> +		    int domain_num)
> +{
> +	struct genpd_onecell_data *genpd_data;
> +	struct resource *res;
> +	int i;
> +
> +	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
> +	if (!genpd_data)
> +		return -ENOMEM;
> +
> +	genpd_data->domains = zx_pm_domains;
> +	genpd_data->num_domains = domain_num;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pcubase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(pcubase)) {
> +		dev_err(&pdev->dev, "ioremap fail.\n");
> +		return PTR_ERR(pcubase);
> +	}
> +
> +	for (i = 0; i < domain_num; ++i)
> +		pm_genpd_init(zx_pm_domains[i], NULL, false);
> +
> +	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
> +	dev_info(&pdev->dev, "powerdomain init ok\n");
> +	return 0;
> +}
> diff --git a/drivers/soc/zte/zx2967_pm_domains.h b/drivers/soc/zte/zx2967_pm_domains.h
> new file mode 100644
> index 0000000..81ad4d6
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.h
> @@ -0,0 +1,46 @@
> +/*
> + * Header for ZTE's Power Domain Driver support
> + *
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef __ZTE_ZX2967_PM_DOMAIN_H
> +#define __ZTE_ZX2967_PM_DOMAIN_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +
> +enum {
> +	REG_CLKEN,
> +	REG_ISOEN,
> +	REG_RSTEN,
> +	REG_PWREN,
> +	REG_PWRDN,
> +	REG_ACK_SYNC,
> +
> +	/* The size of the array - must be last */
> +	REG_ARRAY_SIZE,
> +};
> +
> +enum zx2967_power_polarity {
> +	PWREN,
> +	PWRDN,
> +};
> +
> +struct zx2967_pm_domain {
> +	struct generic_pm_domain dm;
> +	const u16 bit;
> +	const enum zx2967_power_polarity polarity;
> +	const u16 *reg_offset;
> +};
> +
> +extern int zx2967_power_on(struct generic_pm_domain *domain);
> +extern int zx2967_power_off(struct generic_pm_domain *domain);
> +extern int zx2967_pd_probe(struct platform_device *pdev,
> +			   struct generic_pm_domain **zx_pm_domains,
> +			   int domain_num);

The 'extern' is not really necessary here.

Shawn

> +
> +#endif /* __ZTE_ZX2967_PM_DOMAIN_H */
> -- 
> 2.7.4
> 

^ permalink raw reply

* Re: [PATCH v5 2/4] soc: zte: pm_domains: Prepare for supporting ARMv8 zx2967 family
From: Shawn Guo @ 2017-01-04  5:44 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie-QSEj5FYQhm4dnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, linux-0h96xk9xTtrk1uMJSBkQmQ,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
	pankaj.dubey-Sze3O3UU22JBDgjK7y7TUQ, yangbo.lu-3arQi8VN3Tc,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w, claudiu.manoil-3arQi8VN3Tc,
	laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
	chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
	wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A
In-Reply-To: <1483489157-10782-2-git-send-email-baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On Wed, Jan 04, 2017 at 08:19:15AM +0800, Baoyou Xie wrote:
> The ARMv8 zx2967 family (296718, 296716 etc) uses different value
> for controlling the power domain on/off registers, Choose the
> value depending on the compatible.
> 
> Multiple domains are prepared for the family, this patch prepares
> the common functions.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Reviewed-by: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Reviewed-by: Jun Nie <jun.nie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Same here.  Neither myself nor Jun gave Reviewed-by tag on this patch.

> ---
>  drivers/soc/Kconfig                 |   1 +
>  drivers/soc/Makefile                |   1 +
>  drivers/soc/zte/Kconfig             |  13 ++++
>  drivers/soc/zte/Makefile            |   4 ++
>  drivers/soc/zte/zx2967_pm_domains.c | 139 ++++++++++++++++++++++++++++++++++++
>  drivers/soc/zte/zx2967_pm_domains.h |  46 ++++++++++++
>  6 files changed, 204 insertions(+)
>  create mode 100644 drivers/soc/zte/Kconfig
>  create mode 100644 drivers/soc/zte/Makefile
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.c
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.h
> 
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index f31bceb..f09023f 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -11,5 +11,6 @@ source "drivers/soc/tegra/Kconfig"
>  source "drivers/soc/ti/Kconfig"
>  source "drivers/soc/ux500/Kconfig"
>  source "drivers/soc/versatile/Kconfig"
> +source "drivers/soc/zte/Kconfig"
>  
>  endmenu
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 50c23d0..05eae52 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
>  obj-$(CONFIG_SOC_TI)		+= ti/
>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>  obj-$(CONFIG_PLAT_VERSATILE)	+= versatile/
> +obj-$(CONFIG_ARCH_ZX)		+= zte/
> diff --git a/drivers/soc/zte/Kconfig b/drivers/soc/zte/Kconfig
> new file mode 100644
> index 0000000..20bde38
> --- /dev/null
> +++ b/drivers/soc/zte/Kconfig
> @@ -0,0 +1,13 @@
> +#
> +# ZTE SoC drivers
> +#
> +menuconfig SOC_ZTE
> +	bool "ZTE SoC driver support"
> +
> +if SOC_ZTE
> +
> +config ZX2967_PM_DOMAINS
> +	bool "ZX2967 PM domains"
> +	depends on PM_GENERIC_DOMAINS
> +
> +endif
> diff --git a/drivers/soc/zte/Makefile b/drivers/soc/zte/Makefile
> new file mode 100644
> index 0000000..8a37f2f
> --- /dev/null
> +++ b/drivers/soc/zte/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# ZTE SOC drivers
> +#
> +obj-$(CONFIG_ZX2967_PM_DOMAINS) += zx2967_pm_domains.o
> diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c
> new file mode 100644
> index 0000000..a215875
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.c
> @@ -0,0 +1,139 @@
> +/*
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +
> +#include "zx2967_pm_domains.h"
> +
> +#define PCU_DM_CLKEN(zpd)	((zpd)->reg_offset[REG_CLKEN])
> +#define PCU_DM_ISOEN(zpd)	((zpd)->reg_offset[REG_ISOEN])
> +#define PCU_DM_RSTEN(zpd)	((zpd)->reg_offset[REG_RSTEN])
> +#define PCU_DM_PWREN(zpd)	((zpd)->reg_offset[REG_PWREN])
> +#define PCU_DM_PWRDN(zpd)	((zpd)->reg_offset[REG_PWRDN])

PCU_DM_PWRDN doesn't seem to be used anywhere in this driver.

> +#define PCU_DM_ACK_SYNC(zpd)	((zpd)->reg_offset[REG_ACK_SYNC])
> +
> +static void __iomem *pcubase;
> +
> +int zx2967_power_on(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val |= BIT(zpd->bit);
> +	else
> +		val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && !val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	pr_debug("normal poweron %s\n", domain->name);

Does "normal" in the debug message mean anything here?

> +
> +	return 0;
> +}
> +
> +int zx2967_power_off(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val &= ~BIT(zpd->bit);
> +	else
> +		val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	pr_debug("normal poweroff %s\n", domain->name);

Ditto

> +
> +	return 0;
> +}
> +
> +int zx2967_pd_probe(struct platform_device *pdev,
> +		    struct generic_pm_domain **zx_pm_domains,
> +		    int domain_num)
> +{
> +	struct genpd_onecell_data *genpd_data;
> +	struct resource *res;
> +	int i;
> +
> +	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
> +	if (!genpd_data)
> +		return -ENOMEM;
> +
> +	genpd_data->domains = zx_pm_domains;
> +	genpd_data->num_domains = domain_num;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pcubase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(pcubase)) {
> +		dev_err(&pdev->dev, "ioremap fail.\n");
> +		return PTR_ERR(pcubase);
> +	}
> +
> +	for (i = 0; i < domain_num; ++i)
> +		pm_genpd_init(zx_pm_domains[i], NULL, false);
> +
> +	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
> +	dev_info(&pdev->dev, "powerdomain init ok\n");
> +	return 0;
> +}
> diff --git a/drivers/soc/zte/zx2967_pm_domains.h b/drivers/soc/zte/zx2967_pm_domains.h
> new file mode 100644
> index 0000000..81ad4d6
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.h
> @@ -0,0 +1,46 @@
> +/*
> + * Header for ZTE's Power Domain Driver support
> + *
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef __ZTE_ZX2967_PM_DOMAIN_H
> +#define __ZTE_ZX2967_PM_DOMAIN_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +
> +enum {
> +	REG_CLKEN,
> +	REG_ISOEN,
> +	REG_RSTEN,
> +	REG_PWREN,
> +	REG_PWRDN,
> +	REG_ACK_SYNC,
> +
> +	/* The size of the array - must be last */
> +	REG_ARRAY_SIZE,
> +};
> +
> +enum zx2967_power_polarity {
> +	PWREN,
> +	PWRDN,
> +};
> +
> +struct zx2967_pm_domain {
> +	struct generic_pm_domain dm;
> +	const u16 bit;
> +	const enum zx2967_power_polarity polarity;
> +	const u16 *reg_offset;
> +};
> +
> +extern int zx2967_power_on(struct generic_pm_domain *domain);
> +extern int zx2967_power_off(struct generic_pm_domain *domain);
> +extern int zx2967_pd_probe(struct platform_device *pdev,
> +			   struct generic_pm_domain **zx_pm_domains,
> +			   int domain_num);

The 'extern' is not really necessary here.

Shawn

> +
> +#endif /* __ZTE_ZX2967_PM_DOMAIN_H */
> -- 
> 2.7.4
> 
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [RFC PATCH] ACPI/PCI: Fix bus range comparation in pci_mcfg_lookup
From: Zhou Wang @ 2017-01-04  5:44 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Tomasz Nowicki, Jayachandran C,
	jorn Helgaas, liudongdong3, gabriele.paoloni, linux-acpi,
	linux-pci, linux-kernel
In-Reply-To: <20170103120021.GC7145@red-moon>

On 2017/1/3 20:00, Lorenzo Pieralisi wrote:
> On Thu, Dec 22, 2016 at 05:07:43PM +0800, Zhou Wang wrote:
>> Multiple PCIe host bridges may exists in one PCIe segment. So bus range for each
>> host bridge should be in the coverage of bus range of related PCIe segment.
>>
>> This patch will support this kind of scenario:
>>
>> MCFG:
>> 	bus range: 0x00~0xff.
>> 	segment: 0.
>> DSDT:
>> 	host bridge 1:
>> 		bus range: 0x00~0x1f.
>> 		segment: 0.
>> 	host bridge 2:
>> 		bus range: 0x20~0x4f.
>> 		segment: 0.
> 
> "The configuration data provided by an MCFG region (ie PCI segment and
> bus range) may span multiple host bridges.
> 
> Current code in pci_mcfg_lookup() carries out an exact match of host
> bridge bus range start value against the MCFG region(s) bus range start
> value which would cause configurations like the following:
> 
> MCFG region:
> 	bus range: 0x00~0xff.
> 	segment: 0.
> 
> PCI host bridges configuration (segment numbers and bus ranges):
> 	host bridge 1:
> 		bus range: 0x00~0x1f.
> 		segment: 0.
> 	host bridge 2:
> 		bus range: 0x20~0x4f.
> 		segment: 0.
> 
> to fail, in that the bus range start value for host bridge 2 does
> not match the bus range start value of the respective MCFG region.
> 
> Relax the bus range check in pci_mcfg_lookup() to cater for
> PCI configurations with multiple host bridges sharing the same
> MCFG region."
> 
> Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>

Hi Lorenzo,

Thanks for your view.

I will modify the commit message and post a new version patch.

Regards,
Zhou

> 
>> Signed-off-by: Zhou Wang <wangzhou1@hisilicon.com>
>> ---
>>  drivers/acpi/pci_mcfg.c | 5 ++---
>>  1 file changed, 2 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
>> index b5b376e..46a3e32 100644
>> --- a/drivers/acpi/pci_mcfg.c
>> +++ b/drivers/acpi/pci_mcfg.c
>> @@ -40,11 +40,10 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
>>  	struct mcfg_entry *e;
>>  
>>  	/*
>> -	 * We expect exact match, unless MCFG entry end bus covers more than
>> -	 * specified by caller.
>> +	 * We expect the range in bus_res in the coverage of MCFG bus range.
>>  	 */
>>  	list_for_each_entry(e, &pci_mcfg_list, list) {
>> -		if (e->segment == seg && e->bus_start == bus_res->start &&
>> +		if (e->segment == seg && e->bus_start <= bus_res->start &&
>>  		    e->bus_end >= bus_res->end)
>>  			return e->addr;
>>  	}
>> -- 
>> 1.9.1
>>
> 
> .
> 

^ permalink raw reply

* Re: [RFC PATCH] ACPI/PCI: Fix bus range comparation in pci_mcfg_lookup
From: Zhou Wang @ 2017-01-04  5:44 UTC (permalink / raw)
  To: Lorenzo Pieralisi
  Cc: Rafael J. Wysocki, Len Brown, Tomasz Nowicki, Jayachandran C,
	jorn Helgaas, liudongdong3, gabriele.paoloni, linux-acpi,
	linux-pci, linux-kernel
In-Reply-To: <20170103120021.GC7145@red-moon>

On 2017/1/3 20:00, Lorenzo Pieralisi wrote:
> On Thu, Dec 22, 2016 at 05:07:43PM +0800, Zhou Wang wrote:
>> Multiple PCIe host bridges may exists in one PCIe segment. So bus range for each
>> host bridge should be in the coverage of bus range of related PCIe segment.
>>
>> This patch will support this kind of scenario:
>>
>> MCFG:
>> 	bus range: 0x00~0xff.
>> 	segment: 0.
>> DSDT:
>> 	host bridge 1:
>> 		bus range: 0x00~0x1f.
>> 		segment: 0.
>> 	host bridge 2:
>> 		bus range: 0x20~0x4f.
>> 		segment: 0.
> 
> "The configuration data provided by an MCFG region (ie PCI segment and
> bus range) may span multiple host bridges.
> 
> Current code in pci_mcfg_lookup() carries out an exact match of host
> bridge bus range start value against the MCFG region(s) bus range start
> value which would cause configurations like the following:
> 
> MCFG region:
> 	bus range: 0x00~0xff.
> 	segment: 0.
> 
> PCI host bridges configuration (segment numbers and bus ranges):
> 	host bridge 1:
> 		bus range: 0x00~0x1f.
> 		segment: 0.
> 	host bridge 2:
> 		bus range: 0x20~0x4f.
> 		segment: 0.
> 
> to fail, in that the bus range start value for host bridge 2 does
> not match the bus range start value of the respective MCFG region.
> 
> Relax the bus range check in pci_mcfg_lookup() to cater for
> PCI configurations with multiple host bridges sharing the same
> MCFG region."
> 
> Acked-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>

Hi Lorenzo,

Thanks for your view.

I will modify the commit message and post a new version patch.

Regards,
Zhou

> 
>> Signed-off-by: Zhou Wang <wangzhou1@hisilicon.com>
>> ---
>>  drivers/acpi/pci_mcfg.c | 5 ++---
>>  1 file changed, 2 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/acpi/pci_mcfg.c b/drivers/acpi/pci_mcfg.c
>> index b5b376e..46a3e32 100644
>> --- a/drivers/acpi/pci_mcfg.c
>> +++ b/drivers/acpi/pci_mcfg.c
>> @@ -40,11 +40,10 @@ phys_addr_t pci_mcfg_lookup(u16 seg, struct resource *bus_res)
>>  	struct mcfg_entry *e;
>>  
>>  	/*
>> -	 * We expect exact match, unless MCFG entry end bus covers more than
>> -	 * specified by caller.
>> +	 * We expect the range in bus_res in the coverage of MCFG bus range.
>>  	 */
>>  	list_for_each_entry(e, &pci_mcfg_list, list) {
>> -		if (e->segment == seg && e->bus_start == bus_res->start &&
>> +		if (e->segment == seg && e->bus_start <= bus_res->start &&
>>  		    e->bus_end >= bus_res->end)
>>  			return e->addr;
>>  	}
>> -- 
>> 1.9.1
>>
> 
> .
> 


^ permalink raw reply

* Re: [PATCH v5 2/4] soc: zte: pm_domains: Prepare for supporting ARMv8 zx2967 family
From: Shawn Guo @ 2017-01-04  5:44 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie, robh+dt, mark.rutland, gregkh, davem, geert+renesas,
	akpm, mchehab, linux, krzk, arnd, linus.walleij, pankaj.dubey,
	yangbo.lu, f.fainelli, claudiu.manoil, laurent.pinchart+renesas,
	linux-kernel, linux-arm-kernel, devicetree, xie.baoyou,
	chen.chaokai, wang.qiang01
In-Reply-To: <1483489157-10782-2-git-send-email-baoyou.xie@linaro.org>

On Wed, Jan 04, 2017 at 08:19:15AM +0800, Baoyou Xie wrote:
> The ARMv8 zx2967 family (296718, 296716 etc) uses different value
> for controlling the power domain on/off registers, Choose the
> value depending on the compatible.
> 
> Multiple domains are prepared for the family, this patch prepares
> the common functions.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawnguo@kernel.org>
> Reviewed-by: Jun Nie <jun.nie@linaro.org>

Same here.  Neither myself nor Jun gave Reviewed-by tag on this patch.

> ---
>  drivers/soc/Kconfig                 |   1 +
>  drivers/soc/Makefile                |   1 +
>  drivers/soc/zte/Kconfig             |  13 ++++
>  drivers/soc/zte/Makefile            |   4 ++
>  drivers/soc/zte/zx2967_pm_domains.c | 139 ++++++++++++++++++++++++++++++++++++
>  drivers/soc/zte/zx2967_pm_domains.h |  46 ++++++++++++
>  6 files changed, 204 insertions(+)
>  create mode 100644 drivers/soc/zte/Kconfig
>  create mode 100644 drivers/soc/zte/Makefile
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.c
>  create mode 100644 drivers/soc/zte/zx2967_pm_domains.h
> 
> diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
> index f31bceb..f09023f 100644
> --- a/drivers/soc/Kconfig
> +++ b/drivers/soc/Kconfig
> @@ -11,5 +11,6 @@ source "drivers/soc/tegra/Kconfig"
>  source "drivers/soc/ti/Kconfig"
>  source "drivers/soc/ux500/Kconfig"
>  source "drivers/soc/versatile/Kconfig"
> +source "drivers/soc/zte/Kconfig"
>  
>  endmenu
> diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
> index 50c23d0..05eae52 100644
> --- a/drivers/soc/Makefile
> +++ b/drivers/soc/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
>  obj-$(CONFIG_SOC_TI)		+= ti/
>  obj-$(CONFIG_ARCH_U8500)	+= ux500/
>  obj-$(CONFIG_PLAT_VERSATILE)	+= versatile/
> +obj-$(CONFIG_ARCH_ZX)		+= zte/
> diff --git a/drivers/soc/zte/Kconfig b/drivers/soc/zte/Kconfig
> new file mode 100644
> index 0000000..20bde38
> --- /dev/null
> +++ b/drivers/soc/zte/Kconfig
> @@ -0,0 +1,13 @@
> +#
> +# ZTE SoC drivers
> +#
> +menuconfig SOC_ZTE
> +	bool "ZTE SoC driver support"
> +
> +if SOC_ZTE
> +
> +config ZX2967_PM_DOMAINS
> +	bool "ZX2967 PM domains"
> +	depends on PM_GENERIC_DOMAINS
> +
> +endif
> diff --git a/drivers/soc/zte/Makefile b/drivers/soc/zte/Makefile
> new file mode 100644
> index 0000000..8a37f2f
> --- /dev/null
> +++ b/drivers/soc/zte/Makefile
> @@ -0,0 +1,4 @@
> +#
> +# ZTE SOC drivers
> +#
> +obj-$(CONFIG_ZX2967_PM_DOMAINS) += zx2967_pm_domains.o
> diff --git a/drivers/soc/zte/zx2967_pm_domains.c b/drivers/soc/zte/zx2967_pm_domains.c
> new file mode 100644
> index 0000000..a215875
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.c
> @@ -0,0 +1,139 @@
> +/*
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +#include <linux/delay.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of.h>
> +
> +#include "zx2967_pm_domains.h"
> +
> +#define PCU_DM_CLKEN(zpd)	((zpd)->reg_offset[REG_CLKEN])
> +#define PCU_DM_ISOEN(zpd)	((zpd)->reg_offset[REG_ISOEN])
> +#define PCU_DM_RSTEN(zpd)	((zpd)->reg_offset[REG_RSTEN])
> +#define PCU_DM_PWREN(zpd)	((zpd)->reg_offset[REG_PWREN])
> +#define PCU_DM_PWRDN(zpd)	((zpd)->reg_offset[REG_PWRDN])

PCU_DM_PWRDN doesn't seem to be used anywhere in this driver.

> +#define PCU_DM_ACK_SYNC(zpd)	((zpd)->reg_offset[REG_ACK_SYNC])
> +
> +static void __iomem *pcubase;
> +
> +int zx2967_power_on(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val |= BIT(zpd->bit);
> +	else
> +		val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && !val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	pr_debug("normal poweron %s\n", domain->name);

Does "normal" in the debug message mean anything here?

> +
> +	return 0;
> +}
> +
> +int zx2967_power_off(struct generic_pm_domain *domain)
> +{
> +	struct zx2967_pm_domain *zpd = (struct zx2967_pm_domain *)domain;
> +	unsigned long loop = 1000;
> +	u32 val;
> +
> +	val = readl_relaxed(pcubase + PCU_DM_CLKEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_CLKEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_ISOEN(zpd));
> +	val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_ISOEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_RSTEN(zpd));
> +	val &= ~BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_RSTEN(zpd));
> +	udelay(5);
> +
> +	val = readl_relaxed(pcubase + PCU_DM_PWREN(zpd));
> +	if (zpd->polarity == PWREN)
> +		val &= ~BIT(zpd->bit);
> +	else
> +		val |= BIT(zpd->bit);
> +	writel_relaxed(val, pcubase + PCU_DM_PWREN(zpd));
> +
> +	do {
> +		udelay(1);
> +		val = readl_relaxed(pcubase + PCU_DM_ACK_SYNC(zpd))
> +				   & BIT(zpd->bit);
> +	} while (--loop && val);
> +
> +	if (!loop) {
> +		pr_err("Error: %s %s fail\n", __func__, domain->name);
> +		return -EIO;
> +	}
> +
> +	pr_debug("normal poweroff %s\n", domain->name);

Ditto

> +
> +	return 0;
> +}
> +
> +int zx2967_pd_probe(struct platform_device *pdev,
> +		    struct generic_pm_domain **zx_pm_domains,
> +		    int domain_num)
> +{
> +	struct genpd_onecell_data *genpd_data;
> +	struct resource *res;
> +	int i;
> +
> +	genpd_data = devm_kzalloc(&pdev->dev, sizeof(*genpd_data), GFP_KERNEL);
> +	if (!genpd_data)
> +		return -ENOMEM;
> +
> +	genpd_data->domains = zx_pm_domains;
> +	genpd_data->num_domains = domain_num;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	pcubase = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(pcubase)) {
> +		dev_err(&pdev->dev, "ioremap fail.\n");
> +		return PTR_ERR(pcubase);
> +	}
> +
> +	for (i = 0; i < domain_num; ++i)
> +		pm_genpd_init(zx_pm_domains[i], NULL, false);
> +
> +	of_genpd_add_provider_onecell(pdev->dev.of_node, genpd_data);
> +	dev_info(&pdev->dev, "powerdomain init ok\n");
> +	return 0;
> +}
> diff --git a/drivers/soc/zte/zx2967_pm_domains.h b/drivers/soc/zte/zx2967_pm_domains.h
> new file mode 100644
> index 0000000..81ad4d6
> --- /dev/null
> +++ b/drivers/soc/zte/zx2967_pm_domains.h
> @@ -0,0 +1,46 @@
> +/*
> + * Header for ZTE's Power Domain Driver support
> + *
> + * Copyright (C) 2017 ZTE Ltd.
> + *
> + * Author: Baoyou Xie <baoyou.xie@linaro.org>
> + * License terms: GNU General Public License (GPL) version 2
> + */
> +
> +#ifndef __ZTE_ZX2967_PM_DOMAIN_H
> +#define __ZTE_ZX2967_PM_DOMAIN_H
> +
> +#include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> +
> +enum {
> +	REG_CLKEN,
> +	REG_ISOEN,
> +	REG_RSTEN,
> +	REG_PWREN,
> +	REG_PWRDN,
> +	REG_ACK_SYNC,
> +
> +	/* The size of the array - must be last */
> +	REG_ARRAY_SIZE,
> +};
> +
> +enum zx2967_power_polarity {
> +	PWREN,
> +	PWRDN,
> +};
> +
> +struct zx2967_pm_domain {
> +	struct generic_pm_domain dm;
> +	const u16 bit;
> +	const enum zx2967_power_polarity polarity;
> +	const u16 *reg_offset;
> +};
> +
> +extern int zx2967_power_on(struct generic_pm_domain *domain);
> +extern int zx2967_power_off(struct generic_pm_domain *domain);
> +extern int zx2967_pd_probe(struct platform_device *pdev,
> +			   struct generic_pm_domain **zx_pm_domains,
> +			   int domain_num);

The 'extern' is not really necessary here.

Shawn

> +
> +#endif /* __ZTE_ZX2967_PM_DOMAIN_H */
> -- 
> 2.7.4
> 

^ permalink raw reply

* Re: [PATCH v3 2/7] net/virtio_user: fix not properly reset device
From: Yuanhan Liu @ 2017-01-04  5:46 UTC (permalink / raw)
  To: Jianfeng Tan; +Cc: dev, ferruh.yigit, cunming.liang, stable
In-Reply-To: <1483502366-140154-3-git-send-email-jianfeng.tan@intel.com>

On Wed, Jan 04, 2017 at 03:59:21AM +0000, Jianfeng Tan wrote:
> virtio_user is not properly reset when users call vtpci_reset(),
> as it ignores VIRTIO_CONFIG_STATUS_RESET status in
> virtio_user_set_status().
> 
> This might lead to initialization failure as it starts to re-init
> the device before sending RESET messege to backend. Besides, previous
> callfds and kickfds are not closed.
> 
> To fix it, we add support to disable virtqueues when it's set to
> DRIVER OK status, and re-init fields in struct virtio_user_dev.
> 
> Fixes: e9efa4d93821 ("net/virtio-user: add new virtual PCI driver")
> Fixes: 37a7eb2ae816 ("net/virtio-user: add device emulation layer")
> 
> CC: stable@dpdk.org
> 
> Signed-off-by: Jianfeng Tan <jianfeng.tan@intel.com>

Note that, typically, there should be no empty line between 'Cc' and SoB.

> ---
>  drivers/net/virtio/virtio_user/virtio_user_dev.c | 26 ++++++++++++++++--------
>  drivers/net/virtio/virtio_user_ethdev.c          | 15 ++++++++------
>  2 files changed, 27 insertions(+), 14 deletions(-)
> 
> diff --git a/drivers/net/virtio/virtio_user/virtio_user_dev.c b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> index 0d7e17b..a38398b 100644
> --- a/drivers/net/virtio/virtio_user/virtio_user_dev.c
> +++ b/drivers/net/virtio/virtio_user/virtio_user_dev.c
> @@ -182,7 +182,17 @@ virtio_user_start_device(struct virtio_user_dev *dev)
>  
>  int virtio_user_stop_device(struct virtio_user_dev *dev)

The name doesn't seem to be well named: "dev_stop" comes to my firstly
when I saw that :/

Rename it to "xxx_reset_device"?

	--yliu

^ permalink raw reply

* Re: [tpmdd-devel] [PATCH RFC 0/4] RFC: in-kernel resource manager
From: Andy Lutomirski @ 2017-01-04  5:47 UTC (permalink / raw)
  To: James Bottomley, Jarkko Sakkinen
  Cc: linux-security-module, tpmdd-devel, open list
In-Reply-To: <1483421218.19261.4.camel@linux.vnet.ibm.com>

On 01/02/2017 09:26 PM, James Bottomley wrote:
> On Mon, 2017-01-02 at 13:40 -0800, James Bottomley wrote:
>> On Mon, 2017-01-02 at 21:33 +0200, Jarkko Sakkinen wrote:
>>> On Mon, Jan 02, 2017 at 08:36:20AM -0800, James Bottomley wrote:
>>>> On Mon, 2017-01-02 at 15:22 +0200, Jarkko Sakkinen wrote:
>>>>> This patch set adds support for TPM spaces that provide a
>>>>> context for isolating and swapping transient objects. This
>>>>> patch set does not yet include support for isolating policy and
>>>>> HMAC sessions but it is trivial to add once the basic approach
>>>>> is settled (and that's why I created an RFC patch set).
>>>>
>>>> The approach looks fine to me.  The only basic query I have is
>>>> about the default: shouldn't it be with resource manager on
>>>> rather than off?  I can't really think of a use case that wants
>>>> the RM off (even if you're running your own, having another
>>>> doesn't hurt anything, and it's still required to share with in
>>>> -kernel uses).
>>>
>>> This is a valid question and here's a longish explanation.
>>>
>>> In TPM2_GetCapability and maybe couple of other commands you can
>>> get handles in the response body. I do not want to have special
>>> cases in the kernel for response bodies because there is no a
>>> generic way to do the substitution. What's worse, new commands in
>>> the standard future revisions could have such commands requiring
>>> special cases. In addition, vendor specific commans could have
>>> handles in the response bodies.
>>
>> OK, in general I buy this ... what you're effectively saying is that
>> we need a non-RM interface for certain management type commands.
>>
>> However, let me expand a bit on why I'm fretting about the non-RM use
>> case.  Right at the moment, we have a single TPM device which you use
>> for access to the kernel TPM.  The current tss2 just makes direct use
>> of this, meaning it has to have 0666 permissions.  This means that
>> any local user can simply DoS the TPM by running us out of transient
>> resources if they don't activate the RM.  If they get a connection
>> always via the RM, this isn't a worry.  Perhaps the best way of
>> fixing this is to expose two separate device nodes: one raw to the
>> TPM which we could keep at 0600 and one with an always RM connection
>> which we can set to 0666.  That would mean that access to the non-RM
>> connection is either root only or governed by a system set ACL.
>
> OK, so I put a patch together that does this (see below). It all works
> nicely (with a udev script that sets the resource manager device to
> 0666):
>
> jejb@jarvis:~> ls -l /dev/tpm*
> crw------- 1 root root  10,   224 Jan  2 20:54 /dev/tpm0
> crw-rw-rw- 1 root root 246, 65536 Jan  2 20:54 /dev/tpm0rm
>
> I've modified the tss to connect to /dev/tpm0rm by default and it all
> seems to work.
>
> The patch applies on top of your tabrm branch, by the way.

Conceptually I like this a *lot* better.  I believe that this 
effectively solves my major gripe with the TPM 1.2 ecosystem.

However, can this be taken just a little farther?  IMO the tpm0rm (or 
tpms0 or whatever) node should also restrict commands that can be sent 
(perhaps by in-kernel whitelist?) to those that shouldn't be restricted 
to the owner (by which I probably mean the Owner, the Platform, etc)? 
For example, someone with tpm0rm open should not be able to change key 
hierarchy passwords, write to NV memory, clear hierarchies, etc.

Hmm.  Maybe there should be a way to allocate NV slots to users. 
/dev/tpm/nv0?  I don't really like that idea, though.

^ permalink raw reply

* [ANNOUNCE] xfs-linux: xfs-4.10-misc-fixes-5 updated to ff97f23
From: Darrick J. Wong @ 2017-01-04  5:47 UTC (permalink / raw)
  To: xfs

Hi folks,

The xfs-4.10-misc-fixes-5 branch of the xfs-linux repository at:

	git://git.kernel.org/pub/scm/fs/xfs/xfs-linux.git

has just been updated.

Patches often get missed, so please check if your outstanding
patches were in this update. If they have not been in this update,
please resubmit them to linux-xfs@vger.kernel.org so they can be
picked up in the next update.

I intend to send these four bugfixes to Linus later this week, but
thought I'd run it by the list first to see if anyone spots anything
obviously broken.

The new head of the xfs-4.10-misc-fixes-5 branch is commit:

ff97f23 xfs: fix max_retries _show and _store functions

New Commits:

Carlos Maiolino (1):
      [ff97f23] xfs: fix max_retries _show and _store functions

Christoph Hellwig (1):
      [a1b7a4d] xfs: fix crash and data corruption due to removal of busy COW extents

Darrick J. Wong (3):
      [7a21272] xfs: fix double-cleanup when CUI recovery fails
      [20e73b0] xfs: use the actual AG length when reserving blocks
      [721a0ed] xfs: update MAINTAINERS


Code Diffstat:

 MAINTAINERS                        |  4 ++--
 fs/xfs/libxfs/xfs_ag_resv.c        |  3 +++
 fs/xfs/libxfs/xfs_refcount_btree.c |  9 ++++++---
 fs/xfs/libxfs/xfs_refcount_btree.h |  3 ++-
 fs/xfs/libxfs/xfs_rmap_btree.c     | 14 +++++++-------
 fs/xfs/libxfs/xfs_rmap_btree.h     |  3 ++-
 fs/xfs/xfs_fsops.c                 | 14 ++++++++++++++
 fs/xfs/xfs_icache.c                |  3 ++-
 fs/xfs/xfs_refcount_item.c         |  3 ++-
 fs/xfs/xfs_sysfs.c                 |  4 ++--
 10 files changed, 42 insertions(+), 18 deletions(-)

^ permalink raw reply

* arm64: virt_to_page() does not return right page for a kernel image address
From: Pratyush Anand @ 2017-01-04  5:49 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

I noticed that on arm64 kmap_atomic() does not return correct address
corresponding to a page located in data section. It causes crash in
kdump kernel with v29 kdump patches. crash happens in a newly
implemented crypto test [1], and the same test fails(even though it
does not crash) in 1st kernel as well.

Further debugging showed that the physical address returned by
virt_to_phys(kaddr)  and virt_to_phys(kmap_atomic(virt_to_page(kaddr))
+ offset_in_page(kaddr)) are not same.

Mark Rutland thinks(IRC :#armlinux) that _virt_to_pgoff *only* handles
linear addresses, and not kernel image addresses. However, we have to
ask if it should?

Meanwhile, I reverted commit [2] and then everything worked fine
*atleast* in my case. But, I am not sure if that could be the right
and best solution.

Opinion?

~Pratyush

[1]
commit d7db7a882debaffc78f91aabedee973aa1f73390
Author: Giovanni Cabiddu <giovanni.cabiddu@intel.com>
Date:   Fri Oct 21 13:19:54 2016 +0100

    crypto: acomp - update testmgr with support for acomp

[2]commit 9f2875912dac35d9272a82ea9eec9e5884b42cd2
Author: Ard Biesheuvel <ard.biesheuvel@linaro.org>
Date:   Wed Mar 30 16:46:01 2016 +0200

    arm64: mm: restrict virt_to_page() to the linear mapping

^ permalink raw reply

* [PATCH v5 3/4] soc: zte: pm_domains: Add support for zx296718 board
From: Shawn Guo @ 2017-01-04  5:49 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1483489157-10782-3-git-send-email-baoyou.xie@linaro.org>

On Wed, Jan 04, 2017 at 08:19:16AM +0800, Baoyou Xie wrote:
> This patch introduces the power domain driver of zx296718
> which belongs to zte's zx2967 family.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawnguo@kernel.org>
> Reviewed-by: Jun Nie <jun.nie@linaro.org>

Jun did give his Reviewed-by tag on v2 of this patch, but I did not.

Once again, I put quite a few comments on v3 of this patch [1].  But
neither you responded to nor address any of them in reposting.

Shawn

[1] http://www.spinics.net/lists/arm-kernel/msg547691.html

^ permalink raw reply

* Re: [PATCH v5 3/4] soc: zte: pm_domains: Add support for zx296718 board
From: Shawn Guo @ 2017-01-04  5:49 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie-QSEj5FYQhm4dnm+yROfE0A, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	mark.rutland-5wv7dgnIgG8, gregkh-hQyY1W1yCW8ekmWlsbkhG0B+6BGkLq7r,
	davem-fT/PcQaiUtIeIZ0/mPfg9Q,
	geert+renesas-gXvu3+zWzMSzQB+pC5nmwQ,
	akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b,
	mchehab-DgEjT+Ai2ygdnm+yROfE0A, linux-0h96xk9xTtrk1uMJSBkQmQ,
	krzk-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	linus.walleij-QSEj5FYQhm4dnm+yROfE0A,
	pankaj.dubey-Sze3O3UU22JBDgjK7y7TUQ, yangbo.lu-3arQi8VN3Tc,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w, claudiu.manoil-3arQi8VN3Tc,
	laurent.pinchart+renesas-ryLnwIuWjnjg/C1BVhZhaw,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	xie.baoyou-Th6q7B73Y6EnDS1+zs4M5A,
	chen.chaokai-Th6q7B73Y6EnDS1+zs4M5A,
	wang.qiang01-Th6q7B73Y6EnDS1+zs4M5A
In-Reply-To: <1483489157-10782-3-git-send-email-baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

On Wed, Jan 04, 2017 at 08:19:16AM +0800, Baoyou Xie wrote:
> This patch introduces the power domain driver of zx296718
> which belongs to zte's zx2967 family.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Reviewed-by: Shawn Guo <shawnguo-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
> Reviewed-by: Jun Nie <jun.nie-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

Jun did give his Reviewed-by tag on v2 of this patch, but I did not.

Once again, I put quite a few comments on v3 of this patch [1].  But
neither you responded to nor address any of them in reposting.

Shawn

[1] http://www.spinics.net/lists/arm-kernel/msg547691.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* Re: [PATCH v5 3/4] soc: zte: pm_domains: Add support for zx296718 board
From: Shawn Guo @ 2017-01-04  5:49 UTC (permalink / raw)
  To: Baoyou Xie
  Cc: jun.nie, robh+dt, mark.rutland, gregkh, davem, geert+renesas,
	akpm, mchehab, linux, krzk, arnd, linus.walleij, pankaj.dubey,
	yangbo.lu, f.fainelli, claudiu.manoil, laurent.pinchart+renesas,
	linux-kernel, linux-arm-kernel, devicetree, xie.baoyou,
	chen.chaokai, wang.qiang01
In-Reply-To: <1483489157-10782-3-git-send-email-baoyou.xie@linaro.org>

On Wed, Jan 04, 2017 at 08:19:16AM +0800, Baoyou Xie wrote:
> This patch introduces the power domain driver of zx296718
> which belongs to zte's zx2967 family.
> 
> Signed-off-by: Baoyou Xie <baoyou.xie@linaro.org>
> Reviewed-by: Shawn Guo <shawnguo@kernel.org>
> Reviewed-by: Jun Nie <jun.nie@linaro.org>

Jun did give his Reviewed-by tag on v2 of this patch, but I did not.

Once again, I put quite a few comments on v3 of this patch [1].  But
neither you responded to nor address any of them in reposting.

Shawn

[1] http://www.spinics.net/lists/arm-kernel/msg547691.html

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.