With the introduction of the new spi-mem model operations changed
slightly, which broke the ich-spi driver. The new spi-mem operations
make things a bit easier to handle for ich flash interface. This patch
makes ich-spi work again by using spi-mem operations.

Signed-off-by: Bernhard Messerklinger <bernhard.messerklin...@br-automation.com>
---
Unfortunately I only have apollo lake HW so I can't test
with the other boards. Please give feedback if anybody is interested in
this patch or any errors appear.

 drivers/spi/ich.c | 250 +++++++++++++++++-----------------------------
 drivers/spi/ich.h |   9 +-
 2 files changed, 96 insertions(+), 163 deletions(-)

diff --git a/drivers/spi/ich.c b/drivers/spi/ich.c
index 03531a8c0c..6e6e606da9 100644
--- a/drivers/spi/ich.c
+++ b/drivers/spi/ich.c
@@ -14,6 +14,8 @@
 #include <pci_ids.h>
 #include <spi.h>
 #include <asm/io.h>
+#include <spi-mem.h>
+#include <div64.h>
 
 #include "ich.h"
 
@@ -171,18 +173,6 @@ static int ich_init_controller(struct udevice *dev,
        return 0;
 }
 
-static inline void spi_use_out(struct spi_trans *trans, unsigned bytes)
-{
-       trans->out += bytes;
-       trans->bytesout -= bytes;
-}
-
-static inline void spi_use_in(struct spi_trans *trans, unsigned bytes)
-{
-       trans->in += bytes;
-       trans->bytesin -= bytes;
-}
-
 static void spi_lock_down(struct ich_spi_platdata *plat, void *sbase)
 {
        if (plat->ich_version == ICHV_7) {
@@ -213,47 +203,12 @@ static bool spi_lock_status(struct ich_spi_platdata 
*plat, void *sbase)
        return lock != 0;
 }
 
-static void spi_setup_type(struct spi_trans *trans, int data_bytes)
-{
-       trans->type = 0xFF;
-
-       /* Try to guess spi type from read/write sizes */
-       if (trans->bytesin == 0) {
-               if (trans->bytesout + data_bytes > 4)
-                       /*
-                        * If bytesin = 0 and bytesout > 4, we presume this is
-                        * a write data operation, which is accompanied by an
-                        * address.
-                        */
-                       trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS;
-               else
-                       trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
-               return;
-       }
-
-       if (trans->bytesout == 1) {     /* and bytesin is > 0 */
-               trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
-               return;
-       }
-
-       if (trans->bytesout == 4)       /* and bytesin is > 0 */
-               trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
-
-       /* Fast read command is called with 5 bytes instead of 4 */
-       if (trans->out[0] == SPI_OPCODE_FAST_READ && trans->bytesout == 5) {
-               trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
-               --trans->bytesout;
-       }
-}
-
 static int spi_setup_opcode(struct ich_spi_priv *ctlr, struct spi_trans *trans,
                            bool lock)
 {
        uint16_t optypes;
        uint8_t opmenu[ctlr->menubytes];
 
-       trans->opcode = trans->out[0];
-       spi_use_out(trans, 1);
        if (!lock) {
                /* The lock is off, so just use index 0. */
                ich_writeb(ctlr, trans->opcode, ctlr->opmenu);
@@ -300,26 +255,6 @@ static int spi_setup_opcode(struct ich_spi_priv *ctlr, 
struct spi_trans *trans,
        }
 }
 
-static int spi_setup_offset(struct spi_trans *trans)
-{
-       /* Separate the SPI address and data */
-       switch (trans->type) {
-       case SPI_OPCODE_TYPE_READ_NO_ADDRESS:
-       case SPI_OPCODE_TYPE_WRITE_NO_ADDRESS:
-               return 0;
-       case SPI_OPCODE_TYPE_READ_WITH_ADDRESS:
-       case SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS:
-               trans->offset = ((uint32_t)trans->out[0] << 16) |
-                               ((uint32_t)trans->out[1] << 8) |
-                               ((uint32_t)trans->out[2] << 0);
-               spi_use_out(trans, 3);
-               return 1;
-       default:
-               printf("Unrecognized SPI transaction type %#x\n", trans->type);
-               return -EPROTO;
-       }
-}
-
 /*
  * Wait for up to 6s til status register bit(s) turn 1 (in case wait_til_set
  * below is true) or 0. In case the wait was for the bit(s) to set - write
@@ -365,73 +300,35 @@ void ich_spi_config_opcode(struct udevice *dev)
        ich_writel(ctlr, SPI_OPMENU_UPPER, ctlr->opmenu + sizeof(u32));
 }
 
-static int ich_spi_xfer(struct udevice *dev, unsigned int bitlen,
-                       const void *dout, void *din, unsigned long flags)
+int ich_spi_exec_op(struct spi_slave *slave, const struct spi_mem_op *op)
 {
-       struct udevice *bus = dev_get_parent(dev);
+       struct udevice *bus = dev_get_parent(slave->dev);
        struct ich_spi_platdata *plat = dev_get_platdata(bus);
        struct ich_spi_priv *ctlr = dev_get_priv(bus);
-       uint16_t control;
-       int16_t opcode_index;
-       int with_address;
-       int status;
-       int bytes = bitlen / 8;
        struct spi_trans *trans = &ctlr->trans;
-       unsigned type = flags & (SPI_XFER_BEGIN | SPI_XFER_END);
-       int using_cmd = 0;
+       u16 control;
+       u16 opcode_index;
+       int ret = 0;
+       int status;
        bool lock = spi_lock_status(plat, ctlr->base);
-       int ret;
+       bool with_address = 0;
 
-       /* We don't support writing partial bytes */
-       if (bitlen % 8) {
-               debug("ICH SPI: Accessing partial bytes not supported\n");
-               return -EPROTONOSUPPORT;
-       }
-
-       /* An empty end transaction can be ignored */
-       if (type == SPI_XFER_END && !dout && !din)
-               return 0;
-
-       if (type & SPI_XFER_BEGIN)
-               memset(trans, '\0', sizeof(*trans));
+       trans->in = NULL;
+       trans->out = NULL;
+       trans->type = 0xFF;
 
-       /* Dp we need to come back later to finish it? */
-       if (dout && type == SPI_XFER_BEGIN) {
-               if (bytes > ICH_MAX_CMD_LEN) {
-                       debug("ICH SPI: Command length limit exceeded\n");
-                       return -ENOSPC;
+       if (op->data.nbytes) {
+               if (op->data.dir == SPI_MEM_DATA_IN) {
+                       trans->in = op->data.buf.in;
+                       trans->bytesin = op->data.nbytes;
+               } else {
+                       trans->out = op->data.buf.out;
+                       trans->bytesout = op->data.nbytes;
                }
-               memcpy(trans->cmd, dout, bytes);
-               trans->cmd_len = bytes;
-               debug_trace("ICH SPI: Saved %d bytes\n", bytes);
-               return 0;
-       }
-
-       /*
-        * We process a 'middle' spi_xfer() call, which has no
-        * SPI_XFER_BEGIN/END, as an independent transaction as if it had
-        * an end. We therefore repeat the command. This is because ICH
-        * seems to have no support for this, or because interest (in digging
-        * out the details and creating a special case in the code) is low.
-        */
-       if (trans->cmd_len) {
-               trans->out = trans->cmd;
-               trans->bytesout = trans->cmd_len;
-               using_cmd = 1;
-               debug_trace("ICH SPI: Using %d bytes\n", trans->cmd_len);
-       } else {
-               trans->out = dout;
-               trans->bytesout = dout ? bytes : 0;
        }
 
-       trans->in = din;
-       trans->bytesin = din ? bytes : 0;
-
-       /* There has to always at least be an opcode */
-       if (!trans->bytesout) {
-               debug("ICH SPI: No opcode for transfer\n");
-               return -EPROTO;
-       }
+       if (trans->opcode != op->cmd.opcode)
+               trans->opcode = op->cmd.opcode;
 
        ret = ich_status_poll(ctlr, SPIS_SCIP, 0);
        if (ret < 0)
@@ -442,13 +339,38 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
        else
                ich_writeb(ctlr, SPIS_CDS | SPIS_FCERR, ctlr->status);
 
-       spi_setup_type(trans, using_cmd ? bytes : 0);
+       /*
+        * Try to guess spi type, special handling of erase case if data size
+        * is 3 and no address is set
+        */
+       if (op->data.dir == SPI_MEM_DATA_OUT) {
+               if (op->addr.nbytes ||
+                   (!op->addr.nbytes && op->data.nbytes == 3))
+                       trans->type = SPI_OPCODE_TYPE_WRITE_WITH_ADDRESS;
+               else
+                       trans->type = SPI_OPCODE_TYPE_WRITE_NO_ADDRESS;
+       } else {
+               if (op->addr.nbytes)
+                       trans->type = SPI_OPCODE_TYPE_READ_WITH_ADDRESS;
+               else
+                       trans->type = SPI_OPCODE_TYPE_READ_NO_ADDRESS;
+       }
+
        opcode_index = spi_setup_opcode(ctlr, trans, lock);
        if (opcode_index < 0)
                return -EINVAL;
-       with_address = spi_setup_offset(trans);
-       if (with_address < 0)
-               return -EINVAL;
+
+       /* Handle special case for erase where address is stored in data */
+       if (op->addr.nbytes) {
+               trans->offset = op->addr.val;
+               with_address = 1;
+       } else if (op->data.nbytes == 3 && trans->bytesout) {
+               trans->offset = ((uint32_t)trans->out[0] << 16) |
+                               ((uint32_t)trans->out[1] << 8) |
+                               ((uint32_t)trans->out[2] << 0);
+               with_address = 1;
+               trans->bytesout = 0;
+       }
 
        if (trans->opcode == SPI_OPCODE_WREN) {
                /*
@@ -472,13 +394,6 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
                ich_writeb(ctlr, byte, ctlr->speed);
        }
 
-       /* See if we have used up the command data */
-       if (using_cmd && dout && bytes) {
-               trans->out = dout;
-               trans->bytesout = bytes;
-               debug_trace("ICH SPI: Moving to data, %d bytes\n", bytes);
-       }
-
        /* Preset control fields */
        control = SPIC_SCGO | ((opcode_index & 0x07) << 4);
 
@@ -513,22 +428,6 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
                return 0;
        }
 
-       /*
-        * Check if this is a write command atempting to transfer more bytes
-        * than the controller can handle. Iterations for writes are not
-        * supported here because each SPI write command needs to be preceded
-        * and followed by other SPI commands, and this sequence is controlled
-        * by the SPI chip driver.
-        */
-       if (trans->bytesout > ctlr->databytes) {
-               debug("ICH SPI: Too much to write. This should be prevented by 
the driver's max_write_size?\n");
-               return -EPROTO;
-       }
-
-       /*
-        * Read or write up to databytes bytes at a time until everything has
-        * been sent.
-        */
        while (trans->bytesout || trans->bytesin) {
                uint32_t data_length;
 
@@ -543,9 +442,7 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
                /* Program data into FDATA0 to N */
                if (trans->bytesout) {
                        write_reg(ctlr, trans->out, ctlr->data, data_length);
-                       spi_use_out(trans, data_length);
-                       if (with_address)
-                               trans->offset += data_length;
+                       trans->bytesout -= data_length;
                }
 
                /* Add proper control fields' values */
@@ -568,9 +465,7 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
 
                if (trans->bytesin) {
                        read_reg(ctlr, ctlr->data, trans->in, data_length);
-                       spi_use_in(trans, data_length);
-                       if (with_address)
-                               trans->offset += data_length;
+                       trans->bytesin -= data_length;
                }
        }
 
@@ -581,6 +476,40 @@ static int ich_spi_xfer(struct udevice *dev, unsigned int 
bitlen,
        return 0;
 }
 
+static int ich_spi_adjust_size(struct spi_slave *slave, struct spi_mem_op *op)
+{
+       size_t page_offset;
+       int addr = op->addr.val;
+       unsigned int byte_count = op->data.nbytes;
+
+       if (hweight32(ICH_BOUNDARY) == 1) {
+               page_offset = addr & (ICH_BOUNDARY - 1);
+       } else {
+               u64 aux = addr;
+
+               page_offset = do_div(aux, ICH_BOUNDARY);
+       }
+
+       if (op->data.dir == SPI_MEM_DATA_IN && slave->max_read_size) {
+               op->data.nbytes = min(ICH_BOUNDARY - page_offset,
+                                     slave->max_read_size);
+       } else if (slave->max_write_size) {
+               op->data.nbytes = min(ICH_BOUNDARY - page_offset,
+                                     slave->max_write_size);
+       }
+
+       op->data.nbytes = min(op->data.nbytes, byte_count);
+
+       return 0;
+}
+
+static int ich_spi_xfer(struct udevice *dev, unsigned int bitlen,
+                       const void *dout, void *din, unsigned long flags)
+{
+       printf("ICH SPI: Only supports memory operations\n");
+       return -1;
+}
+
 static int ich_spi_probe(struct udevice *dev)
 {
        struct ich_spi_platdata *plat = dev_get_platdata(dev);
@@ -686,10 +615,17 @@ static int ich_spi_ofdata_to_platdata(struct udevice *dev)
        return ret;
 }
 
+static const struct spi_controller_mem_ops ich_controller_mem_ops = {
+       .adjust_op_size = ich_spi_adjust_size,
+       .supports_op    = NULL,
+       .exec_op        = ich_spi_exec_op,
+};
+
 static const struct dm_spi_ops ich_spi_ops = {
        .xfer           = ich_spi_xfer,
        .set_speed      = ich_spi_set_speed,
        .set_mode       = ich_spi_set_mode,
+       .mem_ops        = &ich_controller_mem_ops,
        /*
         * cs_info is not needed, since we require all chip selects to be
         * in the device tree explicitly
diff --git a/drivers/spi/ich.h b/drivers/spi/ich.h
index a974241f98..3dfb2aaff1 100644
--- a/drivers/spi/ich.h
+++ b/drivers/spi/ich.h
@@ -100,13 +100,8 @@ enum {
        HSFC_FSMIE =            0x8000
 };
 
-enum {
-       ICH_MAX_CMD_LEN         = 5,
-};
-
 struct spi_trans {
-       uint8_t cmd[ICH_MAX_CMD_LEN];
-       int cmd_len;
+       uint8_t cmd;
        const uint8_t *out;
        uint32_t bytesout;
        uint8_t *in;
@@ -166,6 +161,8 @@ struct spi_trans {
 #define SPI_OPMENU_LOWER ((SPI_OPMENU_3 << 24) | (SPI_OPMENU_2 << 16) | \
                          (SPI_OPMENU_1 <<  8) | (SPI_OPMENU_0 <<  0))
 
+#define ICH_BOUNDARY   0x1000
+
 enum ich_version {
        ICHV_7,
        ICHV_9,
-- 
2.21.0


_______________________________________________
U-Boot mailing list
U-Boot@lists.denx.de
https://lists.denx.de/listinfo/u-boot

Reply via email to