Re: [PATCH v4 1/2] spi: Add Renesas R-Car Gen3 RPC SPI controller driver

2019-01-04 Thread Sergei Shtylyov
Hello!

On 01/03/2019 09:35 AM, masonccy...@mxic.com.tw wrote:

[...]
>> >> > +#define RPC_CMNCR_MOIIO3(val)   (((val) & 0x3) << 22)
>> >> > +#define RPC_CMNCR_MOIIO2(val)   (((val) & 0x3) << 20)
>> >> > +#define RPC_CMNCR_MOIIO1(val)   (((val) & 0x3) << 18)
>> >> > +#define RPC_CMNCR_MOIIO0(val)   (((val) & 0x3) << 16)
>> >> > +#define RPC_CMNCR_MOIIO_HIZ   (RPC_CMNCR_MOIIO0(3) |
>> >> RPC_CMNCR_MOIIO1(3) | \
>> >> > + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
>> >> > +#define RPC_CMNCR_IO3FV(val)   (((val) & 0x3) << 14)
>> >> > +#define RPC_CMNCR_IO2FV(val)   (((val) & 0x3) << 12)
>> >>
>> >>Like I said, the above 2 aren't documented in the manual v1.00...
>> >
>> > okay, add a description as:
>> > /* RPC_CMNCR_IO3FV/IO2FV are undocumented bit, but must be set */
>> > #define RPC_CMNCR_IO3FV(val)(((val) & 0x3) << 14)
>> > #define RPC_CMNCR_IO2FV(val)(((val) & 0x3) << 12)
>> > #define RPC_CMNCR_IO0FV(val)(((val) & 0x3) << 8)
>> > #define RPC_CMNCR_IOFV_HIZ  (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | 
>> > \
>> >   RPC_CMNCR_IO3FV(3))
>> >
>> > is it ok?
>>
>>Yes. But would have been enough if you just commented with // on
>> the same line --
>> it seems these are legal now...
> 
> on the same line is over 80 char,
> #define RPC_CMNCR_IO3FV(val)(((val) & 0x3) << 14) // undocumented bit, 
> but must be set
> #define RPC_CMNCR_IO2FV(val)(((val) & 0x3) << 12) // undocumented bit, 
> but must be set
> 
> or just
> #define RPC_CMNCR_IO3FV(val)(((val) & 0x3) << 14) // undocumented bit
> #define RPC_CMNCR_IO2FV(val)(((val) & 0x3) << 12) // undocumented bit
> is it ok ?

   The second variant would be enough.

[...]

>> >> > +   ret = rpc_spi_set_freq(rpc, desc->mem->spi->max_speed_hz);
>> >> > +   if (ret)
>> >> > +  return ret;
>> >> > +
>> >> > +   rpc_spi_mem_set_prep_op_cfg(desc->mem->spi,
>> >> > +>info.op_tmpl, , );
>> >> > +
>> >> > +   regmap_write(rpc->regmap, RPC_CMNCR, RPC_CMNCR_SFDE |
>> >> > +   RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
>> >> > +   RPC_CMNCR_BSZ(0));
>> >>
>> >>Why not set this in the probing time and only set/clear the MD bit?
>> >>
>> >
>> > same above!
>> > Make sure the value in these register are setting correctly
>> > before RPC starting a SPI transfer.
>>
>>You can set it once and only change the bits you need to change 
>> afterwards.
>> What's wrong with it?
>>
> 
> if so, it will patch to:
> --
> regmap_read(rpc->regmap, RPC_CMNCR, );
> data &= ~RPC_CMNCR_MD;
> regmap_write(rpc->regmap, RPC_CMNCR, data);
> --
> Do you think this way is better ?

No, this one is better:

regmap_update_bits(rpc->regmap, RPC_CMNCR, RPC_CMNCR_MD, 0);

> maybe this is better,
> write(read(rpc->regs + RPC_CMNCR) & ~RPC_CMNCR_MD,
> rpc->regs + RPC_CMNCR);

   It's effectively the same code as your 1st variant...
 
[...]
>> >> > +static void rpc_spi_transfer_setup(struct rpc_spi *rpc,
>> >> > +   struct spi_message *msg)
>> >> > +{
>> >> [...]
>> >> > +   for (i = xfercnt - 1; i < xfercnt && xfercnt > 1; i++) {
>> >> > +  if (xfer[i].rx_buf) {
>> >> > + rpc->smenr |=
>> >> > +RPC_SMENR_SPIDE(rpc_bits_set(xfer[i].len)) |
>> >> > +RPC_SMENR_SPIDB
>> >> > +(ilog2((unsigned int)xfer[i].rx_nbits));
>> >>
>> >>Mhm, I would indent this contination line by 1 extra tab...
>> >>
>> >> > +  } else if (xfer[i].tx_buf) {
>> >> > + rpc->smenr |=
>> >> > +RPC_SMENR_SPIDE(rpc_bits_set(xfer[i].len)) |
>> >> > +RPC_SMENR_SPIDB
>> >> > +(ilog2((unsigned int)xfer[i].tx_nbits));
>> >>
>> >>And this one...
>> >
>> > like this ?
>> > 
>> >  for (i = xfercnt - 1; i < xfercnt && xfercnt > 1; i++) {
>> >  if (xfer[i].rx_buf) {
>> >  rpc->smenr |=
>> >  RPC_SMENR_SPIDE(rpc_bits_set(xfer
>> [i].len)) |
>> >  RPC_SMENR_SPIDB(
>> >  ilog2((unsigned int)xfer
>> [i].rx_nbits));
>> >  } else if (xfer[i].tx_buf) {
>> >  rpc->smenr |=
>> >  RPC_SMENR_SPIDE(rpc_bits_set(xfer
>> [i].len)) |
>> >  RPC_SMENR_SPIDB(
>> >  ilog2((unsigned int)xfer
>> [i].tx_nbits));
>>
>>I didn't mean you need to leave ( on the first line, can be left
>> on the  new
>> line, as before.
>>
> 
> how about this style ?
> -
>  for (i = xfercnt - 1; i < xfercnt && xfercnt > 1; i++) {
>  if (xfer[i].rx_buf) {
> 

Re: [PATCH v4 1/2] spi: Add Renesas R-Car Gen3 RPC SPI controller driver

2018-12-26 Thread Sergei Shtylyov
Hello!

On 12/26/2018 07:24 AM, masonccy...@mxic.com.tw wrote:

>> [...]
>> > diff --git a/drivers/spi/spi-renesas-rpc.c b/drivers/spi/spi-renesas-rpc.c
>> > new file mode 100644
>> > index 000..6dd739a
>> > --- /dev/null
>> > +++ b/drivers/spi/spi-renesas-rpc.c
>> > @@ -0,0 +1,788 @@
[...]
>> > +#define RPC_CMNCR  0x   /* R/W */
>> > +#define RPC_CMNCR_MD  BIT(31)
>> > +#define RPC_CMNCR_SFDE  BIT(24) /* undocumented bit but must be set */
>> > +#define RPC_CMNCR_MOIIO3(val)   (((val) & 0x3) << 22)
>> > +#define RPC_CMNCR_MOIIO2(val)   (((val) & 0x3) << 20)
>> > +#define RPC_CMNCR_MOIIO1(val)   (((val) & 0x3) << 18)
>> > +#define RPC_CMNCR_MOIIO0(val)   (((val) & 0x3) << 16)
>> > +#define RPC_CMNCR_MOIIO_HIZ   (RPC_CMNCR_MOIIO0(3) |
>> RPC_CMNCR_MOIIO1(3) | \
>> > + RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
>> > +#define RPC_CMNCR_IO3FV(val)   (((val) & 0x3) << 14)
>> > +#define RPC_CMNCR_IO2FV(val)   (((val) & 0x3) << 12)
>>
>>Like I said, the above 2 aren't documented in the manual v1.00...
> 
> okay, add a description as:
> /* RPC_CMNCR_IO3FV/IO2FV are undocumented bit, but must be set */
> #define RPC_CMNCR_IO3FV(val)(((val) & 0x3) << 14)
> #define RPC_CMNCR_IO2FV(val)(((val) & 0x3) << 12)
> #define RPC_CMNCR_IO0FV(val)(((val) & 0x3) << 8)
> #define RPC_CMNCR_IOFV_HIZ  (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \
>   RPC_CMNCR_IO3FV(3))
> 
> is it ok?

   Yes. But would have been enough if you just commented with // on the same 
line --
it seems these are legal now... 

>> [...]
>> > +static void rpc_spi_hw_init(struct rpc_spi *rpc)
>> > +{
>> > +   /*
>> > +* NOTE: The 0x260 are undocumented bits, but they must be set.
>> > +*   RPC_PHYCNT_STRTIM is strobe timing adjustment bit,
>> > +*   0x0 : the delay is biggest,
>> > +*   0x1 : the delay is 2nd biggest,
>> > +*   On H3 ES1.x, the value should be 0, while on others,
>> > +*   the value should be 6.
>> > +*/
>> > +   regmap_write(rpc->regmap, RPC_PHYCNT, RPC_PHYCNT_CAL |
>> > +  RPC_PHYCNT_STRTIM(6) | 0x260);
>> > +
>> > +   /*
>> > +* NOTE: The 0x31511144 are undocumented bits, but they must be set
>> > +*   for RPC_PHYOFFSET1.
>> > +*The 0x31 are undocumented bits, but they must be set
>> > +*for RPC_PHYOFFSET2.
>> > +*/
>> > +   regmap_write(rpc->regmap, RPC_PHYOFFSET1, 0x31511144);
>>
>>   0x3000 is documented, missed that last time...
>>
> 
> okay,patch it to:
> 
> #define RPC_PHYOFFSET1_DDRTMG(v) (((v) & 0x3) << 28)
> 
> regmap_write(rpc->regmap, RPC_PHYOFFSET1,
>   RPC_PHYOFFSET1_DDRTMG(3) | 0x1511144);
> 

   OK, thanx.

>> [...]
>> > +static int rpc_spi_do_reset(struct rpc_spi *rpc)
>> > +{
>> > +   int ret;
>> > +
>> > +   ret = reset_control_reset(rpc->rstc);
>> > +   if (ret)
>> > +  return ret;
>> > +
>> > +   return 0;
>> > +}
>>
>>Like I said, this function folds to a mere reset_control_reset() call...
>>
> 
> Do you mean just drop this rpc_spi_do_reset( )

   I mean we don't need this wrapper, we can call reset_contreol_reset() 
directly.

> because driver is never
> come here from an error path ?

   You are mixing things up -- of course we call it from the error path.

>> [...]
>> > +
>> > +  while (pos < rpc->xferlen) {
>> > + u32 nbytes = rpc->xferlen - pos;
>> > +
>> > + if (nbytes > 4)
>> > +nbytes = 4;
>> > +
>> > + smcr = rpc->smcr | RPC_SMCR_SPIE;
>> > +
>> > + if (rpc->xferlen > 4 && rpc->xferlen < 8 && pos == 0)
>> > +smcr |= RPC_SMCR_SSLKP;
>> > +
>> > + regmap_write(rpc->regmap, RPC_SMENR, smenr);
>> > + regmap_write(rpc->regmap, RPC_SMCR, smcr);
>> > + ret = wait_msg_xfer_end(rpc);
>> > + if (ret)
>> > +goto out;
>> > +
>> > + regmap_read(rpc->regmap, RPC_SMRDR0, );
>> > + memcpy(rx_buf + pos, , nbytes);
>> > + pos += nbytes;
>> > +
>> > + if (rpc->xferlen > 4 && rpc->xferlen < 8 && pos == 4) {
>>
>>This looks hackish. What I think matters is whether the address
>> bits are set or not.
>> Anyway, maybe it works OK for you but not for me (on V3H), the 4th
>> byte of the JEDEC ID
>> is clobbered from 0 to 3... I've been working on a better workaround
>> using Marek's
>> approach (reading in extended memory mode) -- should port to v4 of
>> your patch yet...
> 
> Do you mean I also patch external address space read mode
> for RPC to read ID and data ?

   No, I meant using the dirmap read mode for RDID and company. I have the patch
almost ready now and I hope you'll merge it with yours... either that or add it 
to
your series atop of this patch.

[...]
> I also think this is kind of RPC HW bug in manual I/O mode.

   Yes.

> Renesas FAE@Taiwan has replied me that their the last bare-metal code,
> mini-monitor v5.x still use one command to read 4 bytes data each time
> and I think RPC HW designer 

Re: [PATCH v4 1/2] spi: Add Renesas R-Car Gen3 RPC SPI controller driver

2018-12-25 Thread Sergei Shtylyov
On 12/24/2018 09:52 AM, Mason Yang wrote:

> Add a driver for Renesas R-Car Gen3 RPC SPI controller.
> 
> Signed-off-by: Mason Yang 
> ---
>  drivers/spi/Kconfig   |   6 +
>  drivers/spi/Makefile  |   1 +
>  drivers/spi/spi-renesas-rpc.c | 788 
> ++
>  3 files changed, 795 insertions(+)
>  create mode 100644 drivers/spi/spi-renesas-rpc.c
> 
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 7d3a5c9..54b40f8 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -528,6 +528,12 @@ config SPI_RSPI
>   help
> SPI driver for Renesas RSPI and QSPI blocks.
>  
> +config SPI_RENESAS_RPC
> + tristate "Renesas R-Car Gen3 RPC SPI controller"

   Well, technically it's called RPC-IF in the manual, not just RPC...

> + depends on ARCH_RENESAS || COMPILE_TEST
> + help
> +   SPI driver for Renesas R-Car Gen3 RPC.
> +
>  config SPI_QCOM_QSPI
>   tristate "QTI QSPI controller"
>   depends on ARCH_QCOM
[...]
> diff --git a/drivers/spi/spi-renesas-rpc.c b/drivers/spi/spi-renesas-rpc.c
> new file mode 100644
> index 000..6dd739a
> --- /dev/null
> +++ b/drivers/spi/spi-renesas-rpc.c
> @@ -0,0 +1,788 @@
> +// SPDX-License-Identifier: GPL-2.0
> +//
> +// Copyright (C) 2018 ~ 2019 Renesas Solutions Corp.
> +// Copyright (C) 2018 Macronix International Co., Ltd.
> +//
> +// R-Car Gen3 RPC SPI/QSPI/Octa driver
> +//
> +// Authors:
> +//   Mason Yang 
> +//
> +
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +#include 
> +
> +#include 
> +
> +#define RPC_CMNCR0x  /* R/W */
> +#define RPC_CMNCR_MD BIT(31)
> +#define RPC_CMNCR_SFDE   BIT(24) /* undocumented bit but must be 
> set */
> +#define RPC_CMNCR_MOIIO3(val)(((val) & 0x3) << 22)
> +#define RPC_CMNCR_MOIIO2(val)(((val) & 0x3) << 20)
> +#define RPC_CMNCR_MOIIO1(val)(((val) & 0x3) << 18)
> +#define RPC_CMNCR_MOIIO0(val)(((val) & 0x3) << 16)
> +#define RPC_CMNCR_MOIIO_HIZ  (RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \
> +  RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
> +#define RPC_CMNCR_IO3FV(val) (((val) & 0x3) << 14)
> +#define RPC_CMNCR_IO2FV(val) (((val) & 0x3) << 12)

   Like I said, the above 2 aren't documented in the manual v1.00...

> +#define RPC_CMNCR_IO0FV(val) (((val) & 0x3) << 8)

   Only this one is...

[...]
> +static void rpc_spi_hw_init(struct rpc_spi *rpc)
> +{
> + /*
> +  * NOTE: The 0x260 are undocumented bits, but they must be set.
> +  *  RPC_PHYCNT_STRTIM is strobe timing adjustment bit,
> +  *  0x0 : the delay is biggest,
> +  *  0x1 : the delay is 2nd biggest,
> +  *  On H3 ES1.x, the value should be 0, while on others,
> +  *  the value should be 6.
> +  */
> + regmap_write(rpc->regmap, RPC_PHYCNT, RPC_PHYCNT_CAL |
> +   RPC_PHYCNT_STRTIM(6) | 0x260);
> +
> + /*
> +  * NOTE: The 0x31511144 are undocumented bits, but they must be set
> +  *   for RPC_PHYOFFSET1.
> +  *   The 0x31 are undocumented bits, but they must be set
> +  *   for RPC_PHYOFFSET2.
> +  */
> + regmap_write(rpc->regmap, RPC_PHYOFFSET1, 0x31511144);

  0x3000 is documented, missed that last time...

[...]
> +static int rpc_spi_do_reset(struct rpc_spi *rpc)
> +{
> + int ret;
> +
> + ret = reset_control_reset(rpc->rstc);
> + if (ret)
> + return ret;
> +
> + return 0;
> +}

   Like I said, this function folds to a mere reset_control_reset() call...

[...]
> +static int rpc_spi_io_xfer(struct rpc_spi *rpc,
> +const void *tx_buf, void *rx_buf)
> +{
> + u32 smenr, smcr, data, pos = 0;
> + int ret = 0;

   Looks like we don't need this variable...

> +
> + regmap_write(rpc->regmap, RPC_CMNCR, RPC_CMNCR_MD | RPC_CMNCR_SFDE |
> +   RPC_CMNCR_MOIIO_HIZ | RPC_CMNCR_IOFV_HIZ |
> +   RPC_CMNCR_BSZ(0));
> + regmap_write(rpc->regmap, RPC_SMDRENR, 0);
> + regmap_write(rpc->regmap, RPC_SMCMR, rpc->cmd);
> + regmap_write(rpc->regmap, RPC_SMDMCR, rpc->dummy);
> + regmap_write(rpc->regmap, RPC_SMADR, rpc->addr);
[...]
> + } else if (rx_buf) {
> + smenr = rpc->smenr;

   Could be done before the *if*...

> +
> + while (pos < rpc->xferlen) {
> + u32 nbytes = rpc->xferlen - pos;
> +
> + if (nbytes > 4)
> + nbytes = 4;
> +
> + smcr = rpc->smcr | RPC_SMCR_SPIE;
> +
> + if (rpc->xferlen > 4 && rpc->xferlen < 8 && pos == 0)
> + smcr |= RPC_SMCR_SSLKP;
> +
> + regmap_write(rpc->regmap, RPC_SMENR, smenr);
> + regmap_write(rpc->regmap, RPC_SMCR, 

[PATCH v4 1/2] spi: Add Renesas R-Car Gen3 RPC SPI controller driver

2018-12-23 Thread Mason Yang
Add a driver for Renesas R-Car Gen3 RPC SPI controller.

Signed-off-by: Mason Yang 
---
 drivers/spi/Kconfig   |   6 +
 drivers/spi/Makefile  |   1 +
 drivers/spi/spi-renesas-rpc.c | 788 ++
 3 files changed, 795 insertions(+)
 create mode 100644 drivers/spi/spi-renesas-rpc.c

diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 7d3a5c9..54b40f8 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -528,6 +528,12 @@ config SPI_RSPI
help
  SPI driver for Renesas RSPI and QSPI blocks.
 
+config SPI_RENESAS_RPC
+   tristate "Renesas R-Car Gen3 RPC SPI controller"
+   depends on ARCH_RENESAS || COMPILE_TEST
+   help
+ SPI driver for Renesas R-Car Gen3 RPC.
+
 config SPI_QCOM_QSPI
tristate "QTI QSPI controller"
depends on ARCH_QCOM
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3575205..5d5c523 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -81,6 +81,7 @@ obj-$(CONFIG_SPI_QUP) += spi-qup.o
 obj-$(CONFIG_SPI_ROCKCHIP) += spi-rockchip.o
 obj-$(CONFIG_SPI_RB4XX)+= spi-rb4xx.o
 obj-$(CONFIG_SPI_RSPI) += spi-rspi.o
+obj-$(CONFIG_SPI_RENESAS_RPC)  += spi-renesas-rpc.o
 obj-$(CONFIG_SPI_S3C24XX)  += spi-s3c24xx-hw.o
 spi-s3c24xx-hw-y   := spi-s3c24xx.o
 spi-s3c24xx-hw-$(CONFIG_SPI_S3C24XX_FIQ) += spi-s3c24xx-fiq.o
diff --git a/drivers/spi/spi-renesas-rpc.c b/drivers/spi/spi-renesas-rpc.c
new file mode 100644
index 000..6dd739a
--- /dev/null
+++ b/drivers/spi/spi-renesas-rpc.c
@@ -0,0 +1,788 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (C) 2018 ~ 2019 Renesas Solutions Corp.
+// Copyright (C) 2018 Macronix International Co., Ltd.
+//
+// R-Car Gen3 RPC SPI/QSPI/Octa driver
+//
+// Authors:
+// Mason Yang 
+//
+
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#define RPC_CMNCR  0x  /* R/W */
+#define RPC_CMNCR_MD   BIT(31)
+#define RPC_CMNCR_SFDE BIT(24) /* undocumented bit but must be set */
+#define RPC_CMNCR_MOIIO3(val)  (((val) & 0x3) << 22)
+#define RPC_CMNCR_MOIIO2(val)  (((val) & 0x3) << 20)
+#define RPC_CMNCR_MOIIO1(val)  (((val) & 0x3) << 18)
+#define RPC_CMNCR_MOIIO0(val)  (((val) & 0x3) << 16)
+#define RPC_CMNCR_MOIIO_HIZ(RPC_CMNCR_MOIIO0(3) | RPC_CMNCR_MOIIO1(3) | \
+RPC_CMNCR_MOIIO2(3) | RPC_CMNCR_MOIIO3(3))
+#define RPC_CMNCR_IO3FV(val)   (((val) & 0x3) << 14)
+#define RPC_CMNCR_IO2FV(val)   (((val) & 0x3) << 12)
+#define RPC_CMNCR_IO0FV(val)   (((val) & 0x3) << 8)
+#define RPC_CMNCR_IOFV_HIZ (RPC_CMNCR_IO0FV(3) | RPC_CMNCR_IO2FV(3) | \
+RPC_CMNCR_IO3FV(3))
+#define RPC_CMNCR_BSZ(val) (((val) & 0x3) << 0)
+
+#define RPC_SSLDR  0x0004  /* R/W */
+#define RPC_SSLDR_SPNDL(d) (((d) & 0x7) << 16)
+#define RPC_SSLDR_SLNDL(d) (((d) & 0x7) << 8)
+#define RPC_SSLDR_SCKDL(d) (((d) & 0x7) << 0)
+
+#define RPC_DRCR   0x000C  /* R/W */
+#define RPC_DRCR_SSLN  BIT(24)
+#define RPC_DRCR_RBURST(v) v) - 1) & 0x1F) << 16)
+#define RPC_DRCR_RCF   BIT(9)
+#define RPC_DRCR_RBE   BIT(8)
+#define RPC_DRCR_SSLE  BIT(0)
+
+#define RPC_DRCMR  0x0010  /* R/W */
+#define RPC_DRCMR_CMD(c)   (((c) & 0xFF) << 16)
+#define RPC_DRCMR_OCMD(c)  (((c) & 0xFF) << 0)
+
+#define RPC_DREAR  0x0014  /* R/W */
+#define RPC_DREAR_EAC(c)   (((c) & 0x7) << 0)
+
+#define RPC_DROPR  0x0018  /* R/W */
+
+#define RPC_DRENR  0x001C  /* R/W */
+#define RPC_DRENR_CDB(o)   (u32)o) & 0x3) << 30))
+#define RPC_DRENR_OCDB(o)  (((o) & 0x3) << 28)
+#define RPC_DRENR_ADB(o)   (((o) & 0x3) << 24)
+#define RPC_DRENR_OPDB(o)  (((o) & 0x3) << 20)
+#define RPC_DRENR_SPIDB(o) (((o) & 0x3) << 16)
+#define RPC_DRENR_DME  BIT(15)
+#define RPC_DRENR_CDE  BIT(14)
+#define RPC_DRENR_OCDE BIT(12)
+#define RPC_DRENR_ADE(v)   (((v) & 0xF) << 8)
+#define RPC_DRENR_OPDE(v)  (((v) & 0xF) << 4)
+
+#define RPC_SMCR   0x0020  /* R/W */
+#define RPC_SMCR_SSLKP BIT(8)
+#define RPC_SMCR_SPIRE BIT(2)
+#define RPC_SMCR_SPIWE BIT(1)
+#define RPC_SMCR_SPIE  BIT(0)
+
+#define RPC_SMCMR  0x0024  /* R/W */
+#define RPC_SMCMR_CMD(c)   (((c) & 0xFF) << 16)
+#define RPC_SMCMR_OCMD(c)  (((c) & 0xFF) << 0)
+
+#define RPC_SMADR  0x0028  /* R/W */
+#define RPC_SMOPR  0x002C  /* R/W */
+#define RPC_SMOPR_OPD3(o)  (((o) & 0xFF) << 24)
+#define RPC_SMOPR_OPD2(o)  (((o) & 0xFF) << 16)
+#define RPC_SMOPR_OPD1(o)  (((o) & 0xFF) << 8)
+#define RPC_SMOPR_OPD0(o)  (((o) & 0xFF) << 0)
+
+#define RPC_SMENR  0x0030  /* R/W */