[PATCH linux dev-4.13 2/2] fsi/gpio: wip: Use relative-addressing commands
Jeremy Kerr
jk at ozlabs.org
Thu May 10 18:59:38 AEST 2018
FSI CFAMs support shorter commands that use a relative (or same) address
as the last. This change introduces a last_addr to the master state, and
uses it for subsequent reads/writes, and performs relative addressing
when a subsequent read/write is in range.
Signed-off-by: Jeremy Kerr <jk at ozlabs.org>
---
drivers/fsi/fsi-master-gpio.c | 109 +++++++++++++++++++++++++++++++++++++-----
1 file changed, 98 insertions(+), 11 deletions(-)
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index d8895dffe754..50b6ebdb8b77 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -28,6 +28,8 @@
#define FSI_GPIO_CMD_DPOLL 0x2
#define FSI_GPIO_CMD_TERM 0x3f
#define FSI_GPIO_CMD_ABS_AR 0x4
+#define FSI_GPIO_CMD_REL_AR 0x5
+#define FSI_GPIO_CMD_SAME_AR 0x3 /* but only a 2-bit opcode... */
#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to hang */
@@ -51,6 +53,8 @@
#define FSI_GPIO_MSG_RESPID_SIZE 2
#define FSI_GPIO_PRIME_SLAVE_CLOCKS 100
+#define LAST_ADDR_INVALID 0x1
+
struct fsi_master_gpio {
struct fsi_master master;
struct device *dev;
@@ -62,6 +66,8 @@ struct fsi_master_gpio {
struct gpio_desc *gpio_enable; /* FSI enable */
struct gpio_desc *gpio_mux; /* Mux control */
bool external_mode;
+
+ uint32_t last_addr;
};
#define CREATE_TRACE_POINTS
@@ -192,22 +198,95 @@ static void msg_push_crc(struct fsi_gpio_msg *msg)
msg_push_bits(msg, crc, 4);
}
+static bool check_same_address(struct fsi_master_gpio *master, int id,
+ uint32_t addr)
+{
+ /* this will also handle LAST_ADDR_INVALID */
+ return master->last_addr == ((id & 0x3) << 21) && (addr & ~0x3);
+}
+
+static bool check_relative_address(struct fsi_master_gpio *master, int id,
+ uint32_t addr, uint32_t *rel_addrp)
+{
+ uint32_t word_addr, last_addr = master->last_addr;
+ int16_t rel_addr;
+
+ if (last_addr == LAST_ADDR_INVALID)
+ return false;
+
+ /* We may be in 23-bit addressing mode, which uses the id as the
+ * top two address bits. So, if we're referencing a different ID,
+ * use absolute addresses
+ */
+ if (((last_addr >> 21) & 0x3) != id)
+ return false;
+
+ word_addr = addr & ~0x3;
+
+ /* calculate offset, including sign bit (at 0x100) */
+ if (word_addr > last_addr) {
+ if (word_addr - last_addr > 255)
+ return false;
+ rel_addr = word_addr - last_addr;
+ } else {
+ if (last_addr - word_addr > 255)
+ return false;
+ rel_addr = (word_addr - last_addr) & 0xff;
+ rel_addr |= 0x100;
+ }
+
+ rel_addr |= addr & 0x3;
+ *rel_addrp = rel_addr;
+
+ return true;
+}
+
+static void last_address_update(struct fsi_master_gpio *master,
+ int id, bool valid, uint32_t addr)
+{
+ if (!valid)
+ master->last_addr = LAST_ADDR_INVALID;
+ else
+ master->last_addr = ((id & 0x3) << 21) | (addr & ~0x3);
+}
+
/*
- * Encode an Absolute Address command
+ * Encode an Absolute/Relative/Same Address command
*/
-static void build_abs_ar_command(struct fsi_gpio_msg *cmd,
- uint8_t id, uint32_t addr, size_t size, const void *data)
+static void build_ar_command(struct fsi_master_gpio *master,
+ struct fsi_gpio_msg *cmd, uint8_t id,
+ uint32_t addr, size_t size, const void *data)
{
+ int i, addr_bits, opcode_bits;
bool write = !!data;
- uint8_t ds;
- int i;
+ uint8_t ds, opcode;
+ uint32_t rel_addr;
cmd->bits = 0;
cmd->msg = 0;
- msg_push_bits(cmd, id, 2);
- msg_push_bits(cmd, FSI_GPIO_CMD_ABS_AR, 3);
- msg_push_bits(cmd, write ? 0 : 1, 1);
+ /* we have 21 bits of address max */
+ addr &= ((1 << 21) - 1);
+
+ /* cmd opcodes are variable length - SAME_AR is only two bits */
+ opcode_bits = 3;
+
+ if (check_same_address(master, id, addr)) {
+ /* we still address the byte offset within the word */
+ addr_bits = 2;
+ opcode_bits = 2;
+ opcode = FSI_GPIO_CMD_SAME_AR;
+
+ } else if (check_relative_address(master, id, addr, &rel_addr)) {
+ /* 8 bits plus sign */
+ addr_bits = 9;
+ addr = rel_addr;
+ opcode = FSI_GPIO_CMD_REL_AR;
+
+ } else {
+ addr_bits = 21;
+ opcode = FSI_GPIO_CMD_ABS_AR;
+ }
/*
* The read/write size is encoded in the lower bits of the address
@@ -224,7 +303,10 @@ static void build_abs_ar_command(struct fsi_gpio_msg *cmd,
if (size == 4)
addr |= 1;
- msg_push_bits(cmd, addr & ((1 << 21) - 1), 21);
+ msg_push_bits(cmd, id, 2);
+ msg_push_bits(cmd, opcode, opcode_bits);
+ msg_push_bits(cmd, write ? 0 : 1, 1);
+ msg_push_bits(cmd, addr, addr_bits);
msg_push_bits(cmd, ds, 1);
for (i = 0; write && i < size; i++)
msg_push_bits(cmd, ((uint8_t *)data)[i], 8);
@@ -468,8 +550,9 @@ static int fsi_master_gpio_read(struct fsi_master *_master, int link,
return -ENODEV;
mutex_lock(&master->cmd_lock);
- build_abs_ar_command(&cmd, id, addr, size, NULL);
+ build_ar_command(master, &cmd, id, addr, size, NULL);
rc = fsi_master_gpio_xfer(master, id, &cmd, size, val);
+ last_address_update(master, id, rc == 0, addr);
mutex_unlock(&master->cmd_lock);
return rc;
@@ -486,8 +569,9 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
return -ENODEV;
mutex_lock(&master->cmd_lock);
- build_abs_ar_command(&cmd, id, addr, size, val);
+ build_ar_command(master, &cmd, id, addr, size, val);
rc = fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+ last_address_update(master, id, rc == 0, addr);
mutex_unlock(&master->cmd_lock);
return rc;
@@ -506,6 +590,7 @@ static int fsi_master_gpio_term(struct fsi_master *_master,
mutex_lock(&master->cmd_lock);
build_term_command(&cmd, id);
rc = fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+ last_address_update(master, id, false, 0);
mutex_unlock(&master->cmd_lock);
return rc;
@@ -539,6 +624,7 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
clock_toggle(master, FSI_POST_BREAK_CLOCKS);
spin_unlock_irqrestore(&master->bit_lock, flags);
+ last_address_update(master, 0, false, 0);
mutex_unlock(&master->cmd_lock);
/* Wait for logic reset to take effect */
@@ -649,6 +735,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
master->dev = &pdev->dev;
master->master.dev.parent = master->dev;
master->master.dev.of_node = of_node_get(dev_of_node(master->dev));
+ master->last_addr = LAST_ADDR_INVALID;
gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
if (IS_ERR(gpio)) {
--
2.14.1
More information about the openbmc
mailing list