Hi Sagar,

On Sat, Apr 18, 2020 at 4:05 PM Sagar Kadam <sagar.ka...@sifive.com> wrote:
>
> Hello Jagan,
>
> > -----Original Message-----
> > From: Jagan Teki <ja...@amarulasolutions.com>
> > Sent: Saturday, April 18, 2020 1:11 AM
> > To: Sagar Kadam <sagar.ka...@sifive.com>
> > Cc: Bin Meng <bmeng...@gmail.com>; U-Boot Mailing List <u-
> > b...@lists.denx.de>; linux-amarula <linux-
> > amar...@amarulasolutions.com>; pal...@dabbelt.com
> > Subject: Re: [U-Boot] [PATCH v2 5/5] sifive: fu540: Enable spi-nor flash
> > support
> >
> > [External Email] Do not click links or attachments unless you recognize the
> > sender and know the content is safe
> >
> > Hi Sagar,
> >
> > On Fri, Apr 17, 2020 at 11:50 PM Sagar Kadam <sagar.ka...@sifive.com>
> > wrote:
> > >
> > > Hello Jagan,
> > >
> > > > -----Original Message-----
> > > > From: Jagan Teki <ja...@amarulasolutions.com>
> > > > Sent: Friday, April 17, 2020 1:30 PM
> > > > To: Sagar Kadam <sagar.ka...@sifive.com>
> > > > Cc: Bin Meng <bmeng...@gmail.com>; U-Boot Mailing List <u-
> > > > b...@lists.denx.de>; linux-amarula <linux-
> > > > amar...@amarulasolutions.com>; pal...@dabbelt.com
> > > > Subject: Re: [U-Boot] [PATCH v2 5/5] sifive: fu540: Enable spi-nor flash
> > > > support
> > > >
> > > > [External Email] Do not click links or attachments unless you recognize
> > the
> > > > sender and know the content is safe
> > > >
> > > > Hi Sagar,
> > > >
> > > > On Tue, Apr 7, 2020 at 9:48 PM Sagar Kadam <sagar.ka...@sifive.com>
> > > > wrote:
> > > > >
> > > > > Hello Jagan,
> > > > >
> > > > > > -----Original Message-----
> > > > > > From: Jagan Teki <ja...@amarulasolutions.com>
> > > > > > Sent: Monday, April 6, 2020 9:30 PM
> > > > > > To: Sagar Kadam <sagar.ka...@sifive.com>
> > > > > > Cc: Bin Meng <bmeng...@gmail.com>; Palmer Dabbelt
> > > > > > <pal...@sifive.com>; U-Boot Mailing List <u-boot@lists.denx.de>;
> > > > > > linux- amarula <linux-amar...@amarulasolutions.com>
> > > > > > Subject: Re: [U-Boot] [PATCH v2 5/5] sifive: fu540: Enable spi-nor
> > > > > > flash support
> > > > > >
> > > > > > [External Email] Do not click links or attachments unless you
> > > > > > recognize the sender and know the content is safe
> > > > > >
> > > > > > Hi Sagar,
> > > > > >
> > > > > > On Sun, Apr 5, 2020 at 12:29 AM Sagar Kadam
> > > > <sagar.ka...@sifive.com>
> > > > > > wrote:
> > > > > > >
> > > > > > > Hello Jagan,
> > > > > > >
> > > > > > > > -----Original Message-----
> > > > > > > > From: Jagan Teki <ja...@amarulasolutions.com>
> > > > > > > > Sent: Saturday, April 4, 2020 11:45 PM
> > > > > > > > To: Sagar Kadam <sagar.ka...@sifive.com>
> > > > > > > > Cc: Bin Meng <bmeng...@gmail.com>; Palmer Dabbelt
> > > > > > > > <pal...@sifive.com>; U-Boot Mailing List <u-
> > b...@lists.denx.de>;
> > > > > > linux-
> > > > > > > > amarula <linux-amar...@amarulasolutions.com>
> > > > > > > > Subject: Re: [U-Boot] [PATCH v2 5/5] sifive: fu540: Enable
> > > > > > > > spi-nor flash support
> > > > > > > >
> > > > > > > > [External Email] Do not click links or attachments unless you
> > > > > > > > recognize
> > > > > > the
> > > > > > > > sender and know the content is safe
> > > > > > > >
> > > > > > > > Hi Sagar,
> > > > > > > >
> > > > > > > > On Mon, Nov 18, 2019 at 2:29 AM Sagar Kadam
> > > > > > <sagar.ka...@sifive.com>
> > > > > > > > wrote:
> > > > > > > > >
> > > > > > > > >
> > > > > > > > > Hello Jagan/Bin,
> > > > > > > > >
> > > > > > > > > > -----Original Message-----
> > > > > > > > > > From: U-Boot <u-boot-boun...@lists.denx.de> On Behalf Of
> > Bin
> > > > > > Meng
> > > > > > > > > > Sent: Monday, November 11, 2019 8:02 PM
> > > > > > > > > > To: Jagan Teki <ja...@amarulasolutions.com>
> > > > > > > > > > Cc: Palmer Dabbelt ( Sifive) <pal...@sifive.com>; U-Boot
> > > > > > > > > > Mailing
> > > > > > List
> > > > > > > > <u-
> > > > > > > > > > b...@lists.denx.de>; linux-amarula <linux-
> > > > > > > > amar...@amarulasolutions.com>
> > > > > > > > > > Subject: Re: [U-Boot] [PATCH v2 5/5] sifive: fu540: Enable
> > > > > > > > > > spi-nor
> > > > > > flash
> > > > > > > > > > support
> > > > > > > > > >
> > > > > > > > > > Hi Jagan,
> > > > > > > > > >
> > > > > > > > > > On Sat, Nov 9, 2019 at 7:57 PM Jagan Teki
> > > > > > > > <ja...@amarulasolutions.com>
> > > > > > > > > > wrote:
> > > > > > > > > > >
> > > > > > > > > > > Hi Bin,
> > > > > > > > > > >
> > > > > > > > > > > On Tue, Oct 29, 2019 at 3:50 PM Bin Meng
> > > > > > > > > > > <bmeng...@gmail.com>
> > > > > > > > wrote:
> > > > > > > > > > > >
> > > > > > > > > > > > Hi Jagan,
> > > > > > > > > > > >
> > > > > > > > > > > > On Tue, Oct 29, 2019 at 5:38 PM Bin Meng
> > > > > > <bmeng...@gmail.com>
> > > > > > > > > > wrote:
> > > > > > > > > > > > >
> > > > > > > > > > > > > Hi Jagan,
> > > > > > > > > > > > >
> > > > > > > > > > > > > On Wed, Oct 16, 2019 at 10:58 PM Jagan Teki
> > > > > > > > > > <ja...@amarulasolutions.com> wrote:
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > HiFive Unleashed A00 support is25wp256 spi-nor
> > > > > > > > > > > > > > flash, So enable the same and add test result log
> > > > > > > > > > > > > > for future reference.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Tested on SiFive FU540 board.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > Signed-off-by: Jagan Teki
> > > > > > > > > > > > > > <ja...@amarulasolutions.com>
> > > > > > > > > > > > > > Reviewed-by: Bin Meng <bmeng...@gmail.com>
> > > > > > > > > > > > > > Tested-by: Bin Meng <bmeng...@gmail.com>
> > > > > > > > > > > > > > ---
> > > > > > > > > > > > > >  .../dts/hifive-unleashed-a00-u-boot.dtsi      |  1 
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > >  board/sifive/fu540/Kconfig                    |  3 
> > > > > > > > > > > > > > +++
> > > > > > > > > > > > > >  doc/board/sifive/fu540.rst                    | 19
> > > > > > > > +++++++++++++++++++
> > > > > > > > > > > > > >  3 files changed, 23 insertions(+)
> > > > > > > > > > > > > >
> > > > > > > > > > > > > > diff --git
> > > > > > > > > > > > > > a/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
> > > > > > > > > > b/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
> > > > > > > > > > > > > > index 25ec8265a5..d7a64134db 100644
> > > > > > > > > > > > > > ---
> > > > > > > > > > > > > > a/arch/riscv/dts/hifive-unleashed-a00-u-boot.dtsi
> > > > > > > > > > > > > > +++ b/arch/riscv/dts/hifive-unleashed-a00-u-boot.dts
> > > > > > > > > > > > > > +++ i
> > > > > > > > > > > > > > @@ -5,6 +5,7 @@
> > > > > > > > > > > > > >
> > > > > > > > > > > > > >  / {
> > > > > > > > > > > > > >         aliases {
> > > > > > > > > > > > > > +               spi0 = &qspi0;
> > > > > > > > > > > > > >                 spi2 = &qspi2;
> > > > > > > > > > > > > >         };
> > > > > > > > > > > > > >  };
> > > > > > > > > > > > > > diff --git a/board/sifive/fu540/Kconfig
> > > > > > > > b/board/sifive/fu540/Kconfig
> > > > > > > > > > > > > > index 5d65080429..c5a1bca03c 100644
> > > > > > > > > > > > > > --- a/board/sifive/fu540/Kconfig
> > > > > > > > > > > > > > +++ b/board/sifive/fu540/Kconfig
> > > > > > > > > > > > > > @@ -26,6 +26,7 @@ config BOARD_SPECIFIC_OPTIONS
> > #
> > > > > > dummy
> > > > > > > > > > > > > >         imply CMD_FS_GENERIC
> > > > > > > > > > > > > >         imply CMD_NET
> > > > > > > > > > > > > >         imply CMD_PING
> > > > > > > > > > > > > > +       imply CMD_SF
> > > > > > > > > > > > > >         imply CLK_SIFIVE
> > > > > > > > > > > > > >         imply CLK_SIFIVE_FU540_PRCI
> > > > > > > > > > > > > >         imply DOS_PARTITION @@ -40,6 +41,8 @@ config
> > > > > > > > > > > > > > BOARD_SPECIFIC_OPTIONS #
> > > > > > dummy
> > > > > > > > > > > > > >         imply SIFIVE_SERIAL
> > > > > > > > > > > > > >         imply SPI
> > > > > > > > > > > > > >         imply SPI_SIFIVE
> > > > > > > > > > > > > > +       imply SPI_FLASH
> > > > > > > > > > > > > > +       imply SPI_FLASH_ISSI
> > > > > > > > > > > > > >         imply MMC
> > > > > > > > > > > > > >         imply MMC_SPI
> > > > > > > > > > > > > >         imply MMC_BROKEN_CD diff --git
> > > > > > > > > > > > > > a/doc/board/sifive/fu540.rst
> > > > > > > > b/doc/board/sifive/fu540.rst
> > > > > > > > > > > > > > index 91b94ee06f..2e70cad02e 100644
> > > > > > > > > > > > > > --- a/doc/board/sifive/fu540.rst
> > > > > > > > > > > > > > +++ b/doc/board/sifive/fu540.rst
> > > > > > > > > > > > > > @@ -366,3 +366,22 @@ load uImage.
> > > > > > > > > > > > > >
> > > > > > > > > > > > > >     Please press Enter to activate this console.
> > > > > > > > > > > > > >     / #
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > > +Sample spi nor flash test
> > > > > > > > > > > > > > +-------------------------
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > > +.. code-block:: none
> > > > > > > > > > > > > > +
> > > > > > > > > > > > > > +   => sf probe 0:2
> > > > > > > > > > > > >
> > > > > > > > > > > > > The cs number can't be 2. It should be zero.
> > > > > > > > > > > >
> > > > > > > > > > > > With this patch series, we got crazy duplicated flash
> > > > > > > > > > > > devices
> > > > > > created,
> > > > > > > > > > > > see below:
> > > > > > > > > > > >
> > > > > > > > > > > > => sf probe
> > > > > > > > > > > > unrecognized JEDEC id bytes: ff, ff, ff Failed to
> > > > > > > > > > > > initialize SPI flash at 0:0 (error -2) => sf probe 0:2
> > > > > > > > > > > > SF: Detected is25wp256 with page size 256 Bytes, erase
> > > > > > > > > > > > size 4 KiB,
> > > > > > > > total 32
> > > > > > > > > > MiB
> > > > > > > > > > > > => sf probe 0:4
> > > > > > > > > > > > SF: Detected is25wp256 with page size 256 Bytes, erase
> > > > > > > > > > > > size 4 KiB,
> > > > > > > > total 32
> > > > > > > > > > MiB
> > > > > > > > > > > > => sf probe 0:6
> > > > > > > > > > > > SF: Detected is25wp256 with page size 256 Bytes, erase
> > > > > > > > > > > > size 4 KiB,
> > > > > > > > total 32
> > > > > > > > > > MiB
> > > > > > > > > > > > => dm tree
> > > > > > > > > > > >  Class     Index  Probed  Driver                Name
> > > > > > > > > > > > -----------------------------------------------------------
> > > > > > > > > > > >  root          0  [ + ]   root_driver           
> > > > > > > > > > > > root_driver
> > > > > > > > > > > >  simple_bus    0  [ + ]   generic_simple_bus    |-- soc
> > > > > > > > > > > >  clk           0  [ + ]   sifive-fu540-prci     |   |--
> > > > > > > > > > > > clock-controller@10000000
> > > > > > > > > > > >  serial        0  [ + ]   serial_sifive         |   |--
> > serial@10010000
> > > > > > > > > > > >  serial        1  [   ]   serial_sifive         |   |--
> > serial@10011000
> > > > > > > > > > > >  spi           0  [ + ]   sifive_spi            |   |-- 
> > > > > > > > > > > > spi@10040000
> > > > > > > > > > > >  spi_flash     0  [   ]   spi_flash_std         |   |   
> > > > > > > > > > > > |-- flash@0
> > > > > > > > > > > >  spi_flash     1  [ + ]   spi_flash_std         |   |   
> > > > > > > > > > > > |--
> > spi_flash@0:2
> > > > > > > > > > > >  spi_flash     2  [ + ]   spi_flash_std         |   |   
> > > > > > > > > > > > |--
> > spi_flash@0:4
> > > > > > > > > > > >  spi_flash     3  [ + ]   spi_flash_std         |   |   
> > > > > > > > > > > > `--
> > spi_flash@0:6
> > > > > > > > > > > >
> > > > > > > > > > > > With my patch series below
> > > > > > > > > > > > http://patchwork.ozlabs.org/project/uboot/list/?series=1
> > > > > > > > > > > > 29736
> > > > > > > > > > > >
> > > > > > > > > > > > the CS number check was added to the SPI flash hence we
> > > > got:
> > > > > > > > > > > >
> > > > > > > > > > > > => sf probe
> > > > > > > > > > > > unrecognized JEDEC id bytes: ff, ff, ff Failed to
> > > > > > > > > > > > initialize SPI flash at 0:0 (error -2) => sf probe 2
> > > > > > > > > > > > Invalid cs 2 (err=-22) Invalid cs 2 (err=-22) Invalid
> > > > > > > > > > > > chip select 0:2 (err=-22) Failed to initialize SPI flash
> > > > > > > > > > > > at 0:2 (error -22) => sf probe 4 Invalid cs 4 (err=-22)
> > > > > > > > > > > > Invalid cs 4 (err=-22) Invalid chip select 0:4 (err=-22)
> > > > > > > > > > > > Failed to initialize SPI flash at 0:4 (error -22)
> > > > > > > > > > > >
> > > > > > > > > > > > So we still need figure out why CS number 0 cannot work
> > > > > > > > > > > > on
> > > > > > SiFive.
> > > > > > > > > > >
> > > > > > > > > > > The existing quad wire that driver pushing for flash seems
> > > > > > > > > > > not detecting flash at CS 0 so making cr to single wire
> > > > > > > > > > > detecting flash at
> > > > > > > > > >
> > > > > > > > > > I vaguely remember someone from SiFive posted a patch that
> > > > > > > > > > forced
> > > > > > the
> > > > > > > > > > SPI controller to work under single wire mode. Maybe
> > someone
> > > > > > > > > > from SiFive could help explain the weird behavior as you
> > > > mentioned.
> > > > > > > > > >
> > > > > > > > > Sorry, I had not been closely looking into this thread.
> > > > > > > > > Yes, the initial patch posted was to add support for is25wp256
> > > > device.
> > > > > > > > > https://patchwork.ozlabs.org/patch/1146523/
> > > > > > > > > but it seems it was bricking the board, and later couldn't
> > > > > > > > > follow-up on
> > > > > > > > this.
> > > > > > > > > IIUC, spi_nor_scan has default reg_proto set to 1_1_1, and
> > > > > > > > > further
> > > > > > parses
> > > > > > > > > slave mode and accordingly sets the hwcaps fields. Now since
> > > > > > > > > the
> > > > > > device
> > > > > > > > tree
> > > > > > > > > set's the slave mode to 4 bit mode based on the tx-bus-width,
> > > > > > > > > this
> > > > > > creates
> > > > > > > > > a conflict due to which reg_read fails and so the sf probe 
> > > > > > > > > fails
> > too.
> > > > > > > > > Now forcing the SPI-controller in single wire mode helped the
> > > > > > > > > sf probe
> > > > > > to
> > > > > > > > > detect the flash. Alternatively I think if we use bitlen
> > > > > > > > > passed by
> > > > > > > > spi_mem_exec_op
> > > > > > > > > and prepare spi transfer's in controller based on max of
> > > > > > > > >t->rx_nbits, t- tx_nbits as  done in linux driver this should
> > > > > > > > >work. Any thoughts on this?
> > > > > > > >
> > > > > > > > Thanks for the explanation. Infact I found how to handle this
> > > > > > > > via set_mode where I can preserve the mode into driver private
> > > > > > > > structure so-that the same can be used while setting cr. One of
> > > > > > > > your patch doing the same [1] but with every transfer
> > bitlen(which
> > > > seems reasonable).
> > > > > > > >
> > > > > > > Good to know that it is working in case of cs0 as well.
> > > > > > >
> > > > > > > > With this I can detect the flash at cs 0 but still flash still
> > > > > > > > requires bfpt fixes to make it work. I would like to poke your
> > > > > > > > series and do the changes accordingly (by keeping your
> > > > > > > > respective authorship), will that be fine?
> > > > > > > >
> > > > > > > Yes, please feel free to do so and also let me know if there is
> > > > > > > something which I can be of help here.
> > > > > >
> > > > > > Here are my observations.
> > > > > >
> > > > > Thanks for sharing your input/observations
> > > > >
> > > > > > 1. flash probe:
> > > > > > Probe happened to cs0 only when we enabled cs format propto
> > value to
> > > > > > single (SIFIVE_SPI_FMT_PROTO_SINGLE). I did check it on the Linux
> > > > > > probe as well, same behaviour.
> > > > > >
> > > > >
> > > > > Yes, this matches with our understanding above, since reg read's
> > > > > during spi_nor_scan is 1-bit mode, and if cs proto value is set to
> > > > SIFIVE_SPI_FMT_PROTO_SINGLE It works.
> > > > >
> > > > > > 2. flash operations:
> > > > > > Driver always assign single to format proto (say if we enable cr
> > > > > > format proto value based on bitlen) and it can't assign it ot dual
> > > > > > and quad even though the tx/rx-bus-width is 4 or 1
> > > > > >
> > > > > > flash operations are not working with quad, I did check the quad
> > > > > > enable bit, it is already enabled (RDSR is 0x40 BIT[6] is 1 that
> > > > > > means quad enabled by default). Is Linux flash operations working
> > > > > > with quad or does it revert back to default commands like fast read
> > > > > > and page program? could you please check?
> > > > > >
> > > > > > "flash operations are working only for fast read, that means 
> > > > > > 2-wire."
> > > > > >
> > > > >
> > > > > I checked in linux-5.6.0-rc7 flash operations for issi devices:
> > > > > - quad enable bit is set via issi_set_default_init.
> > > > > - post spi_nor_init, the read protocol is set to FAST_READ_QUAD I/O
> > > > mode, I do see
> > > > >    corresponding opcode during Read operations (opcode observed
> > here
> > > > > is 0xec)
> > > > > - The page program is set and uses the SPI mode (opcode 12h).
> > > >
> > > > I did manage to get the tx or rx bits logic related to u-boot via 
> > > > buswidth.
> > I
> > > > can see it is working with quad read and quad write is not working.
> > Seems
> > > > like Quad write is also not working even for Linux any idea, why [2]?
> > > >
> > > > [2] https://paste.ubuntu.com/p/nhZzQkkG33/
> > > >
> > >
> > > Yes, It looks that in linux the write protocol is set to SPI mode and it 
> > > uses
> > 0x12 for
> > > PAGE PROGRAM operation.
> > > For is25wp256 flash device the SFDP table has NPH Field as 0x01 (number
> > of parameter headers) i.e
> > > just  has two parameter table's and the optional 4-byte address function
> > specific table is not populated.
> > > Page Program capabilities of hardware are present in this table along with
> > other information. Now since
> > > the spi_nor_parse_sfdp function doesn't get the 4BAIT table it setup's
> > spi_nor with single Page
> > > program setting (i.e the page program opcode used for writing flash is
> > 0x12)
> > >
> > > So in case of linux adding the following code into xxxx_post_bfpt_fixup
> > hooks updates the flash hwcaps and
> > > enable's flash write in QUAD mode
> > >
> > > static int
> > >  is25lp256_post_bfpt_fixups(struct spi_nor *nor,
> > >                            const struct sfdp_parameter_header 
> > > *bfpt_header,
> > > @@ -2256,6 +2267,15 @@ is25lp256_post_bfpt_fixups(struct spi_nor
> > *nor,
> > >         if ((bfpt->dwords[BFPT_DWORD(1)] &
> > BFPT_DWORD1_ADDRESS_BYTES_MASK) ==
> > >                 BFPT_DWORD1_ADDRESS_BYTES_3_ONLY)
> > >                 nor->addr_width = 4;
> > > +
> > > +       if (params->hwcaps.mask != SNOR_HWCAPS_PP_1_1_4) {
> > > +               struct spi_nor_pp_command *params_pp = params-
> > >page_programs;
> > > +
> > > +               params->hwcaps.mask |= SNOR_HWCAPS_PP_1_1_4;
> > > +               spi_nor_set_pp_settings(&params-
> > >page_programs[SNOR_CMD_PP_1_1_4],
> > > +                                         SPINOR_OP_PP_1_1_4,
> > > +                                         SNOR_PROTO_1_1_4);
> > > +       }
> > >
> > > I tried these changes in linux, and did observe that flash write's 
> > > happened
> > in quad mode.
> >
> > Thanks for the update, what about the SPI direction field incase of TX
> > QUAD, is the direction needed to set for all TX transactions or only
> > for QUAD?
> >
> For our case i.e QUAD TX we need to set the TX direction bit and clear it 
> during
> read.

Yes, I'm trying to do exactly the same. Can you try to check the
attached file (sorry it has prints, hope you can comment). It worked
with QPP and I can see quad read exception after reading 8K.
Unhandled exception: Load access fault
EPC: 00000000fffac14a TVAL: 0000000000002000

Let me know if you find any? by the way are you online in #u-boot?

Jagan.
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2018 SiFive, Inc.
 * Copyright 2019 Bhargav Shah <bhargavshah1...@gmail.com>
 *
 * SiFive SPI controller driver (master mode only)
 */

#include <common.h>
#include <dm.h>
#include <malloc.h>
#include <spi-mem.h>
#include <asm/io.h>
#include <linux/log2.h>
#include <clk.h>

#define SIFIVE_SPI_MAX_CS		32

#define SIFIVE_SPI_DEFAULT_DEPTH	8
#define SIFIVE_SPI_DEFAULT_BITS		8

/* register offsets */
#define SIFIVE_SPI_REG_SCKDIV            0x00 /* Serial clock divisor */
#define SIFIVE_SPI_REG_SCKMODE           0x04 /* Serial clock mode */
#define SIFIVE_SPI_REG_CSID              0x10 /* Chip select ID */
#define SIFIVE_SPI_REG_CSDEF             0x14 /* Chip select default */
#define SIFIVE_SPI_REG_CSMODE            0x18 /* Chip select mode */
#define SIFIVE_SPI_REG_DELAY0            0x28 /* Delay control 0 */
#define SIFIVE_SPI_REG_DELAY1            0x2c /* Delay control 1 */
#define SIFIVE_SPI_REG_FMT               0x40 /* Frame format */
#define SIFIVE_SPI_REG_TXDATA            0x48 /* Tx FIFO data */
#define SIFIVE_SPI_REG_RXDATA            0x4c /* Rx FIFO data */
#define SIFIVE_SPI_REG_TXMARK            0x50 /* Tx FIFO watermark */
#define SIFIVE_SPI_REG_RXMARK            0x54 /* Rx FIFO watermark */
#define SIFIVE_SPI_REG_FCTRL             0x60 /* SPI flash interface control */
#define SIFIVE_SPI_REG_FFMT              0x64 /* SPI flash instruction format */
#define SIFIVE_SPI_REG_IE                0x70 /* Interrupt Enable Register */
#define SIFIVE_SPI_REG_IP                0x74 /* Interrupt Pendings Register */

/* sckdiv bits */
#define SIFIVE_SPI_SCKDIV_DIV_MASK       0xfffU

/* sckmode bits */
#define SIFIVE_SPI_SCKMODE_PHA           BIT(0)
#define SIFIVE_SPI_SCKMODE_POL           BIT(1)
#define SIFIVE_SPI_SCKMODE_MODE_MASK     (SIFIVE_SPI_SCKMODE_PHA | \
					  SIFIVE_SPI_SCKMODE_POL)

/* csmode bits */
#define SIFIVE_SPI_CSMODE_MODE_AUTO      0U
#define SIFIVE_SPI_CSMODE_MODE_HOLD      2U
#define SIFIVE_SPI_CSMODE_MODE_OFF       3U

/* delay0 bits */
#define SIFIVE_SPI_DELAY0_CSSCK(x)       ((u32)(x))
#define SIFIVE_SPI_DELAY0_CSSCK_MASK     0xffU
#define SIFIVE_SPI_DELAY0_SCKCS(x)       ((u32)(x) << 16)
#define SIFIVE_SPI_DELAY0_SCKCS_MASK     (0xffU << 16)

/* delay1 bits */
#define SIFIVE_SPI_DELAY1_INTERCS(x)     ((u32)(x))
#define SIFIVE_SPI_DELAY1_INTERCS_MASK   0xffU
#define SIFIVE_SPI_DELAY1_INTERXFR(x)    ((u32)(x) << 16)
#define SIFIVE_SPI_DELAY1_INTERXFR_MASK  (0xffU << 16)

/* fmt bits */
#define SIFIVE_SPI_FMT_PROTO_SINGLE      0U
#define SIFIVE_SPI_FMT_PROTO_DUAL        1U
#define SIFIVE_SPI_FMT_PROTO_QUAD        2U
#define SIFIVE_SPI_FMT_PROTO_MASK        3U
#define SIFIVE_SPI_FMT_ENDIAN            BIT(2)
#define SIFIVE_SPI_FMT_DIR               BIT(3)
#define SIFIVE_SPI_FMT_LEN(x)            ((u32)(x) << 16)
#define SIFIVE_SPI_FMT_LEN_MASK          (0xfU << 16)

/* txdata bits */
#define SIFIVE_SPI_TXDATA_DATA_MASK      0xffU
#define SIFIVE_SPI_TXDATA_FULL           BIT(31)

/* rxdata bits */
#define SIFIVE_SPI_RXDATA_DATA_MASK      0xffU
#define SIFIVE_SPI_RXDATA_EMPTY          BIT(31)

/* ie and ip bits */
#define SIFIVE_SPI_IP_TXWM               BIT(0)
#define SIFIVE_SPI_IP_RXWM               BIT(1)

#define SPI_NBITS_QUAD			0x4
#define SPI_NBITS_DUAL			0x2
#define SPI_NBITS_SINGLE		0x1

struct sifive_spi {
	void		*regs;		/* base address of the registers */
	u32		fifo_depth;
	u32		bits_per_word;
	u32		cs_inactive;	/* Level of the CS pins when inactive*/
	u32		freq;
	u32		num_cs;
	u32		fmt_proto;
	bool		fmt_dir;	/* for quad/dual, tx is true and rx is false */
};

static void sifive_spi_prep_device(struct sifive_spi *spi,
				   struct dm_spi_slave_platdata *slave)
{
	/* Update the chip select polarity */
	if (slave->mode & SPI_CS_HIGH)
		spi->cs_inactive &= ~BIT(slave->cs);
	else
		spi->cs_inactive |= BIT(slave->cs);
	writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);

	/* Select the correct device */
	writel(slave->cs, spi->regs + SIFIVE_SPI_REG_CSID);
}

static int sifive_spi_set_cs(struct sifive_spi *spi,
			     struct dm_spi_slave_platdata *slave)
{
	u32 cs_mode = SIFIVE_SPI_CSMODE_MODE_HOLD;

	if (slave->mode & SPI_CS_HIGH)
		cs_mode = SIFIVE_SPI_CSMODE_MODE_AUTO;

	writel(cs_mode, spi->regs + SIFIVE_SPI_REG_CSMODE);

	return 0;
}

static void sifive_spi_clear_cs(struct sifive_spi *spi)
{
	writel(SIFIVE_SPI_CSMODE_MODE_AUTO, spi->regs + SIFIVE_SPI_REG_CSMODE);
}

static void sifive_spi_prep_transfer(struct sifive_spi *spi,
				     struct dm_spi_slave_platdata *slave)
{
	u32 cr;

	/* Modify the SPI protocol mode */
	cr = readl(spi->regs + SIFIVE_SPI_REG_FMT);

	/* Bits per word ? */
	cr &= ~SIFIVE_SPI_FMT_LEN_MASK;
	cr |= SIFIVE_SPI_FMT_LEN(spi->bits_per_word);

	/* LSB first? */
	cr &= ~SIFIVE_SPI_FMT_ENDIAN;
	if (slave->mode & SPI_LSB_FIRST)
		cr |= SIFIVE_SPI_FMT_ENDIAN;

	/* Number of wires ? */
	cr &= ~SIFIVE_SPI_FMT_PROTO_MASK;
	switch (spi->fmt_proto) {
	case SPI_NBITS_QUAD:
		cr |= SIFIVE_SPI_FMT_PROTO_QUAD;
		break;
	case SPI_NBITS_DUAL:
		cr |= SIFIVE_SPI_FMT_PROTO_DUAL;
		break;
	default:
		cr |= SIFIVE_SPI_FMT_PROTO_SINGLE;
		break;
	}

	/* SPI direction in/out ? */
	cr &= ~SIFIVE_SPI_FMT_DIR;
	if (spi->fmt_dir)
		cr |= SIFIVE_SPI_FMT_DIR;

	writel(cr, spi->regs + SIFIVE_SPI_REG_FMT);
}

#if 1
static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
{
	WARN_ON_ONCE((readl(spi->regs + SIFIVE_SPI_REG_TXDATA) &
		     SIFIVE_SPI_TXDATA_FULL) != 0);
	writel(*tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK,
	       spi->regs + SIFIVE_SPI_REG_TXDATA);
}

static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
{
	u32 data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);

	WARN_ON_ONCE((data & SIFIVE_SPI_RXDATA_EMPTY) != 0);
	*rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
}
#else
static void sifive_spi_rx(struct sifive_spi *spi, u8 *rx_ptr)
{
	u32 data;

	do {
		data = readl(spi->regs + SIFIVE_SPI_REG_RXDATA);
	//	printf("loop 0x%x, ISR 0x%x\n", data, readl(spi->regs + SIFIVE_SPI_REG_IP));
	} while (data & SIFIVE_SPI_RXDATA_EMPTY);

	if (rx_ptr)
		*rx_ptr = data & SIFIVE_SPI_RXDATA_DATA_MASK;
}

static void sifive_spi_tx(struct sifive_spi *spi, const u8 *tx_ptr)
{
	u32 data;
	u8 tx_data = (tx_ptr) ? *tx_ptr & SIFIVE_SPI_TXDATA_DATA_MASK :
				SIFIVE_SPI_TXDATA_DATA_MASK;

	do {
		data = readl(spi->regs + SIFIVE_SPI_REG_TXDATA);
//		printf("txdata.......\n");
	} while (data & SIFIVE_SPI_TXDATA_FULL);

	writel(tx_data, spi->regs + SIFIVE_SPI_REG_TXDATA);
}
#endif

static void sifive_spi_wait(struct sifive_spi *spi, u32 bit)
{
	u32 cr;

	do {
		cr = readl(spi->regs + SIFIVE_SPI_REG_IP);
	} while (!(cr & bit));
}

static void dump(struct sifive_spi *spi)
{
	printf("SCKDIV=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_SCKDIV));
	printf("SCKMODE=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_SCKMODE));
	printf("CSID=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_CSID));
	printf("CSDEF=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_CSDEF));
	printf("CSMODE=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_CSMODE));
	printf("DE0=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_DELAY0));
	printf("DE1=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_DELAY1));
	printf("FMT=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_FMT));
//	printf("TX=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_TXDATA));
//	printf("RX=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_RXDATA));
	printf("TXM=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_TXMARK));
	printf("RXM=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_RXMARK));
	printf("FC=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_FCTRL));
	printf("FF=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_FFMT));
	printf("IE=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_IE));
	printf("IP=0x%x\n",readl(spi->regs + SIFIVE_SPI_REG_IP));
}

static int sifive_spi_xfer(struct udevice *dev, unsigned int bitlen,
			   const void *dout, void *din, unsigned long flags)
{
	struct udevice *bus = dev->parent;
	struct sifive_spi *spi = dev_get_priv(bus);
	struct dm_spi_slave_platdata *slave = dev_get_parent_platdata(dev);
	const unsigned char *tx_ptr = dout;
	u8 *rx_ptr = din;
	u32 remaining_len;
	int ret;

	if (flags & SPI_XFER_BEGIN) {
		sifive_spi_prep_device(spi, slave);

		ret = sifive_spi_set_cs(spi, slave);
		if (ret)
			return ret;
	}

	sifive_spi_prep_transfer(spi, slave);

	remaining_len = bitlen / 8;

#if 1
	while (remaining_len) {
		unsigned int n_words = min(remaining_len, spi->fifo_depth);
		unsigned int i;

		printf("remaining %d, n_words %d\n", remaining_len, n_words);
		printf("**************dump********************\n");
		dump(spi);

		/* Enqueue n_words for transmission */
		for (i = 0; i < n_words; i++)
			sifive_spi_tx(spi, tx_ptr++);

		if (rx_ptr) {
			/* Wait for transmission + reception to complete */
			writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_RXMARK);
			sifive_spi_wait(spi, SIFIVE_SPI_IP_RXWM);

			/* Read out all the data from the RX FIFO */
			for (i = 0; i < n_words; i++)
				sifive_spi_rx(spi, rx_ptr++);
			printf("***********dump after rx*****************\n");
			dump(spi);
		} else {
			/* Wait for transmission to complete */
//			writel(n_words - 1, spi->regs + SIFIVE_SPI_REG_TXMARK);
			sifive_spi_wait(spi, SIFIVE_SPI_IP_TXWM);
		}

		remaining_len -= n_words;
	}
#else
	while (remaining_len) {
		int n_words, tx_words, rx_words;

		n_words = min(remaining_len, spi->fifo_depth);
		printf("remaining %d, n_words %d\n", remaining_len, n_words);

		/* Enqueue n_words for transmission */
		if (tx_ptr) {
			for (tx_words = 0; tx_words < n_words; ++tx_words) {
				sifive_spi_tx(spi, tx_ptr);
				sifive_spi_rx(spi, NULL);
				tx_ptr++;
			}
		}

		/* Read out all the data from the RX FIFO */
		if (rx_ptr) {
			for (rx_words = 0; rx_words < n_words; ++rx_words) {
				sifive_spi_tx(spi, NULL);
				sifive_spi_rx(spi, rx_ptr);
				rx_ptr++;
			}
		}

		remaining_len -= n_words;
	}
#endif

	if (flags & SPI_XFER_END)
		sifive_spi_clear_cs(spi);

//	printf("++FMT DIR = %d\n", spi->fmt_dir);
	return 0;
}

static int sifive_spi_exec_op(struct spi_slave *slave,
			      const struct spi_mem_op *op)
{
	struct sifive_spi *spi = dev_get_priv(slave->dev->parent);
	unsigned long flags = SPI_XFER_BEGIN;
	const void *tx;
	void *rx;
	u8 opcode, *buf;
	u8 *addr;
	int i, temp, ret;

	if (op->cmd.buswidth != 1) {
		printf("buswidth must be 1\n");
		return -ENOTSUPP;
	}

	spi->fmt_proto = SPI_NBITS_SINGLE;
	spi->fmt_dir = true;

	/* send cmd */
	i = 0;
	opcode = op->cmd.opcode;
	printf("OPCODE 0x%x\n", opcode);

	if (!op->data.nbytes && !op->addr.nbytes && !op->dummy.nbytes)
		flags |= SPI_XFER_END;

	if (op->cmd.buswidth == 2)
		spi->fmt_proto = SPI_NBITS_DUAL;

	if (op->cmd.buswidth == 4)
		spi->fmt_proto = SPI_NBITS_QUAD;

//	printf("cmd: buswidth %d\n", op->cmd.buswidth);
	ret = sifive_spi_xfer(slave->dev, 8, (void *)&opcode, NULL, flags);
	if (ret < 0) {
		printf("%s: failed to xfer opcode\n", __func__);
		return ret;
	}

	/* send address and dummy */
	if (op->addr.nbytes) {
		/* alloc buffer for address + dummy */
		buf = (u8 *)calloc(1, op->addr.nbytes + op->dummy.nbytes);
		if (!buf) {
			printf("%s Out of memory\n", __func__);
			return -ENOMEM;
		}

		addr = (u8 *)&op->addr.val;

		for (temp = 0; temp < op->addr.nbytes; temp++)
			buf[i++] = *(u8 *)(addr + op->addr.nbytes - 1 - temp);

		for (temp = 0; temp < op->dummy.nbytes; temp++)
			buf[i++] = 0xff;

		if (op->addr.buswidth == 2)
			spi->fmt_proto = SPI_NBITS_DUAL;

		if (op->addr.buswidth == 4)
			spi->fmt_proto = SPI_NBITS_QUAD;

		if (!op->data.nbytes)
			flags |= SPI_XFER_END;

//		printf("addr: buswidth %d\n", op->addr.buswidth);
		ret = sifive_spi_xfer(slave->dev, i * 8, (void *)buf, NULL, flags);
		if (ret < 0) {
			printf("%s: failed to xfer address\n", __func__);
			return ret;
		}

		free(buf);
	}

	if (!op->data.nbytes)
		return 0;

	/* send/receive data */
	flags |= SPI_XFER_END;

	if (op->data.buswidth == 2)
		spi->fmt_proto = SPI_NBITS_DUAL;

	if (op->data.buswidth == 4)
		spi->fmt_proto = SPI_NBITS_QUAD;

	rx = (op->data.dir == SPI_MEM_DATA_IN) ? op->data.buf.in : NULL;
	tx = (op->data.dir == SPI_MEM_DATA_OUT) ? op->data.buf.out : NULL;

//	if (tx && ((spi->fmt_proto == SPI_NBITS_QUAD) || (spi->fmt_proto == SPI_NBITS_DUAL)))
	if (rx)
		spi->fmt_dir = false;

//	printf("data: buswidth %d\n", op->data.buswidth);
	ret = sifive_spi_xfer(slave->dev, (op->data.nbytes * 8), tx, rx, flags);
	if (ret < 0) {
		printf("%s: failed to xfer data\n", __func__);
		return ret;
	}

	return 0;
}

static int sifive_spi_set_speed(struct udevice *bus, uint speed)
{
	struct sifive_spi *spi = dev_get_priv(bus);
	u32 scale;

	if (speed > spi->freq)
		speed = spi->freq;

	/* Cofigure max speed */
	scale = (DIV_ROUND_UP(spi->freq >> 1, speed) - 1)
					& SIFIVE_SPI_SCKDIV_DIV_MASK;
	writel(scale, spi->regs + SIFIVE_SPI_REG_SCKDIV);

	return 0;
}

static int sifive_spi_set_mode(struct udevice *bus, uint mode)
{
	struct sifive_spi *spi = dev_get_priv(bus);
	u32 cr;

	/* Switch clock mode bits */
	cr = readl(spi->regs + SIFIVE_SPI_REG_SCKMODE) &
				~SIFIVE_SPI_SCKMODE_MODE_MASK;
	if (mode & SPI_CPHA)
		cr |= SIFIVE_SPI_SCKMODE_PHA;
	if (mode & SPI_CPOL)
		cr |= SIFIVE_SPI_SCKMODE_POL;

	writel(cr, spi->regs + SIFIVE_SPI_REG_SCKMODE);

	return 0;
}

static int sifive_spi_cs_info(struct udevice *bus, uint cs,
			      struct spi_cs_info *info)
{
	struct sifive_spi *spi = dev_get_priv(bus);

	if (cs >= spi->num_cs)
		return -EINVAL;

	return 0;
}

static void sifive_spi_init_hw(struct sifive_spi *spi)
{
	u32 cs_bits;

	/* probe the number of CS lines */
	spi->cs_inactive = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
	writel(0xffffffffU, spi->regs + SIFIVE_SPI_REG_CSDEF);
	cs_bits = readl(spi->regs + SIFIVE_SPI_REG_CSDEF);
	writel(spi->cs_inactive, spi->regs + SIFIVE_SPI_REG_CSDEF);
	if (!cs_bits) {
		printf("Could not auto probe CS lines\n");
		return;
	}

	spi->num_cs = ilog2(cs_bits) + 1;
	if (spi->num_cs > SIFIVE_SPI_MAX_CS) {
		printf("Invalid number of spi slaves\n");
		return;
	}

	/* Watermark interrupts are disabled by default */
	writel(0, spi->regs + SIFIVE_SPI_REG_IE);

	/* Default watermark FIFO threshold values */
	writel(1, spi->regs + SIFIVE_SPI_REG_TXMARK);
	writel(0, spi->regs + SIFIVE_SPI_REG_RXMARK);

	/* Set CS/SCK Delays and Inactive Time to defaults */
	writel(SIFIVE_SPI_DELAY0_CSSCK(1) | SIFIVE_SPI_DELAY0_SCKCS(1),
	       spi->regs + SIFIVE_SPI_REG_DELAY0);
	writel(SIFIVE_SPI_DELAY1_INTERCS(1) | SIFIVE_SPI_DELAY1_INTERXFR(0),
	       spi->regs + SIFIVE_SPI_REG_DELAY1);

	/* Exit specialized memory-mapped SPI flash mode */
	writel(0, spi->regs + SIFIVE_SPI_REG_FCTRL);
}

static int sifive_spi_probe(struct udevice *bus)
{
	struct sifive_spi *spi = dev_get_priv(bus);
	struct clk clkdev;
	int ret;

	spi->regs = (void *)(ulong)dev_remap_addr(bus);
	if (!spi->regs)
		return -ENODEV;

	spi->fifo_depth = dev_read_u32_default(bus,
					       "sifive,fifo-depth",
					       SIFIVE_SPI_DEFAULT_DEPTH);

	spi->bits_per_word = dev_read_u32_default(bus,
						  "sifive,max-bits-per-word",
						  SIFIVE_SPI_DEFAULT_BITS);

	ret = clk_get_by_index(bus, 0, &clkdev);
	if (ret)
		return ret;
	spi->freq = clk_get_rate(&clkdev);

	/* init the sifive spi hw */
	sifive_spi_init_hw(spi);

	return 0;
}

static const struct spi_controller_mem_ops sifive_spi_mem_ops = {
	.exec_op = sifive_spi_exec_op,
};

static const struct dm_spi_ops sifive_spi_ops = {
	.set_speed	= sifive_spi_set_speed,
	.set_mode	= sifive_spi_set_mode,
	.cs_info        = sifive_spi_cs_info,
	.mem_ops	= &sifive_spi_mem_ops,
};

static const struct udevice_id sifive_spi_ids[] = {
	{ .compatible = "sifive,spi0" },
	{ }
};

U_BOOT_DRIVER(sifive_spi) = {
	.name	= "sifive_spi",
	.id	= UCLASS_SPI,
	.of_match = sifive_spi_ids,
	.ops	= &sifive_spi_ops,
	.priv_auto_alloc_size = sizeof(struct sifive_spi),
	.probe	= sifive_spi_probe,
};

Reply via email to