Skip to contentSkip to conversation

FSI device driver introduction

PATCH Superseded [email protected] 2 participants 29 messages Oct 11, 2016 – Oct 13, 2016

Subsystems (2)

v3 of 6 Superseded Latest is v6 →

lore
Share & tools
lore
#0 lore raw

v3 cover FSI device driver introduction

From: Chris Bostic <[email protected]>

Patch Descriptions:

Patches 01-13 have been provided by Jeremy Kerr as an example of how FSI function should be phased in. This includes the basic framework to add FSI extensions to the Linux bus and device models. Patch 13 is a beginning definition of a GPIO controlled master controlled in firmware.

Patch 14 contains minimal text formatting changes to deal with checkpatch and git am warnings.

Patch 15 covers minor recommended changes to Jeremy's patches 01-13.

Patch 16 adds function to set up read/write access to a given CFAM.

Patch 17 covers implementation of the FSI GPIO master.

Patch 18 defines the SCOM FSI device driver client.

General Information:

Introduction of the IBM 'Flexible Support Interface' (FSI) bus device driver. FSI is a high fan out serial bus consisting of a clock and a serial data line capable of running at speeds up to 166 MHz.

This set provides the basic framework to add FSI extensions to the Linux bus and device models. Master specific implementations are defined to utilize the core FSI function.

This patch set does not include extended FSI function such as:

    *  Hub master support
    *  Cascaded master support
    *  Application layer hot plug notification
    *  Application layer FSI bus status interface

Common FSI terminology:

* Master
Controller of the FSI bus. Only the master is allowed to control the clock line and is the initiator of all transactions on a bus.

* Slave
The receiver or target of a master initiated transaction. The slave cannot initiate communications on a bus and must respond to any master requests for data.

* CFAM
Stands for Common Field replaceable unit Access Macro. A CFAM is an ASIC residing in any device requiring FSI communications. CFAMs consist of an array of hardware 'engines' used for various purposes. I2C masters, UARTs, General Purpose IO hardware are common types of these engines.

* Configuration Space / Table
A table contained at the beginning of each CFAM address space. This table lists information such as the CFAM's ID, which engine types and versions it has available, as well as its addressing range.

* FSI Client driver
A device driver that registers with the FSI core so that it can access devices it owns on an FSI bus.

Chris Bostic (5):
drivers/fsi: Fix some text formatting
drivers/fsi: Revisions to existing patch set
drivers/fsi: Set up CFAMs for communication
drivers/fsi: Add GPIO master functionality
drivers/fsi: Add SCOM FSI client device driver

Jeremy Kerr (13):
fsi: Add empty fsi bus definitions
fsi: Add device & driver definitions
fsi: add driver to device matches
fsi: Add fsi master definition
fsi: Add fake master driver
fsi: enable debug
fsi: Add slave defintion
fsi: Add empty master scan
fsi: Add crc4 helpers
fsi: Implement slave initialisation
fsi: scan slaves & register devices
fsi: Add device read/write/peek functions
fsi: Add GPIO master driver

 .../devicetree/bindings/fsi/fsi-master-gpio.txt    |   8 +
 drivers/Kconfig                                    |   2 +
 drivers/Makefile                                   |   1 +
 drivers/fsi/Kconfig                                |  36 ++
 drivers/fsi/Makefile                               |   5 +
 drivers/fsi/fsi-cfam.h                             |  63 +++
 drivers/fsi/fsi-core.c                             | 432 +++++++++++++++++++
 drivers/fsi/fsi-master-fake.c                      |  97 +++++
 drivers/fsi/fsi-master-gpio.c                      | 457 +++++++++++++++++++++
 drivers/fsi/fsi-master.h                           |  39 ++
 drivers/fsi/fsi-scom.c                             |  62 +++
 drivers/fsi/fsi-slave.h                            |  62 +++
 include/linux/fsi.h                                |  63 +++
 13 files changed, 1327 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
 create mode 100644 drivers/fsi/Kconfig
 create mode 100644 drivers/fsi/Makefile
 create mode 100644 drivers/fsi/fsi-cfam.h
 create mode 100644 drivers/fsi/fsi-core.c
 create mode 100644 drivers/fsi/fsi-master-fake.c
 create mode 100644 drivers/fsi/fsi-master-gpio.c
 create mode 100644 drivers/fsi/fsi-master.h
 create mode 100644 drivers/fsi/fsi-scom.c
 create mode 100644 drivers/fsi/fsi-slave.h
 create mode 100644 include/linux/fsi.h
Signature · 1 line
1.8.2.2
#1 lore raw

PATCH v3 1/18 fsi: Add empty fsi bus definitions

From: Jeremy Kerr <[email protected]>

This change adds the initial (empty) fsi bus definition, and introduces drivers/fsi/.

---
 drivers/Kconfig        |  2 ++
 drivers/Makefile       |  1 +
 drivers/fsi/Kconfig    | 12 ++++++++++++
 drivers/fsi/Makefile   |  2 ++
 drivers/fsi/fsi-core.c | 38 ++++++++++++++++++++++++++++++++++++++
 include/linux/fsi.h    | 23 +++++++++++++++++++++++
 6 files changed, 78 insertions(+)
 create mode 100644 drivers/fsi/Kconfig
 create mode 100644 drivers/fsi/Makefile
 create mode 100644 drivers/fsi/fsi-core.c
 create mode 100644 include/linux/fsi.h
drivers/Kconfig +2 -0
index e1e2066..117ca14c 100644
@@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
 
 source "drivers/fpga/Kconfig"
 
+source "drivers/fsi/Kconfig"
+
 endmenu
drivers/Makefile +1 -0
index 0b6f3d6..7a1c96f 100644
@@ -173,3 +173,4 @@ obj-$(CONFIG_STM)		+= hwtracing/stm/
 obj-$(CONFIG_ANDROID)		+= android/
 obj-$(CONFIG_NVMEM)		+= nvmem/
 obj-$(CONFIG_FPGA)		+= fpga/
+obj-$(CONFIG_FSI)		+= fsi/
drivers/fsi/Kconfig new file +12 -0
new file mode 100644
index 0000000..04c1a0e
@@ -0,0 +1,12 @@
+#
+# FSI subsystem
+#
+
+menu "FSI support"
+
+config FSI
+	tristate "FSI support"
+	---help---
+	  FSI - the FRU Support Interface - is a simple bus for low-level
+	  access to POWER-based hardware.
+endmenu
drivers/fsi/Makefile new file +2 -0
new file mode 100644
index 0000000..db0e5e7
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_FSI) += fsi-core.o
drivers/fsi/fsi-core.c new file +38 -0
new file mode 100644
index 0000000..3e45306
@@ -0,0 +1,38 @@
+/*
+ * FSI core driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+
+/* FSI core & Linux bus type definitions */
+
+struct bus_type fsi_bus_type = {
+	.name		= "fsi",
+};
+EXPORT_SYMBOL_GPL(fsi_bus_type);
+
+static int fsi_init(void)
+{
+	return bus_register(&fsi_bus_type);
+}
+
+static void fsi_exit(void)
+{
+	bus_unregister(&fsi_bus_type);
+}
+
+module_init(fsi_init);
+module_exit(fsi_exit);
include/linux/fsi.h new file +23 -0
new file mode 100644
index 0000000..8e8bdea
@@ -0,0 +1,23 @@
+/*
+ * FSI device & driver interfaces
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef LINUX_FSI_H
+#define LINUX_FSI_H
+
+#include <linux/device.h>
+
+extern struct bus_type fsi_bus_type;
+
+#endif /* LINUX_FSI_H */
Signature · 1 line
1.8.2.2
#2 lore raw

PATCH v3 2/18 fsi: Add device & driver definitions

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Add structs for fsi devices & drivers, and struct device conversion functions.

---
 include/linux/fsi.h | 11 +++++++++++
 1 file changed, 11 insertions(+)
include/linux/fsi.h +11 -0
index 8e8bdea..66dcf25 100644
@@ -18,6 +18,17 @@
 
 #include <linux/device.h>
 
+struct fsi_device {
+	struct device dev;
+};
+
+struct fsi_driver {
+	struct device_driver drv;
+};
+
+#define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
+#define to_fsi_drv(drvp) container_of(drvp, struct fsi_driver, drv)
+
 extern struct bus_type fsi_bus_type;
 
 #endif /* LINUX_FSI_H */
Signature · 1 line
1.8.2.2
#3 lore raw

PATCH v3 3/18 fsi: add driver to device matches

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Driver bind to devices based on the engine types & (optional) versions.

---
 drivers/fsi/fsi-core.c | 21 +++++++++++++++++++++
 include/linux/fsi.h    | 21 +++++++++++++++++++--
 2 files changed, 40 insertions(+), 2 deletions(-)
drivers/fsi/fsi-core.c +21 -0
index 3e45306..3d55bd5 100644
@@ -19,8 +19,29 @@
 
 /* FSI core & Linux bus type definitions */
 
+static int fsi_bus_match(struct device *dev, struct device_driver *drv)
+{
+	struct fsi_device *fsi_dev = to_fsi_dev(dev);
+	struct fsi_driver *fsi_drv = to_fsi_drv(drv);
+	const struct fsi_device_id *id;
+
+	if (!fsi_drv->id_table)
+		return 0;
+
+	for (id = fsi_drv->id_table; id->engine_type; id++) {
+		if (id->engine_type != fsi_dev->engine_type)
+			continue;
+		if (id->version == FSI_VERSION_ANY ||
+				id->version == fsi_dev->version)
+			return 1;
+	}
+
+	return 0;
+}
+
 struct bus_type fsi_bus_type = {
 	.name		= "fsi",
+	.match		= fsi_bus_match,
 };
 EXPORT_SYMBOL_GPL(fsi_bus_type);
 
include/linux/fsi.h +19 -2
index 66dcf25..6d843d4 100644
@@ -19,11 +19,28 @@
 #include <linux/device.h>
 
 struct fsi_device {
-	struct device dev;
+	struct device		dev;
+	u8			engine_type;
+	u8			version;
 };
 
+struct fsi_device_id {
+	u8	engine_type;
+	u8	version;
+};
+
+#define FSI_VERSION_ANY		0
+
+#define FSI_DEVICE(t) \
+	.engine_type = (t), .version = FSI_VERSION_ANY,
+
+#define FSI_DEVICE_VERSIONED(t, v) \
+	.engine_type = (t), .version = (v),
+
+
 struct fsi_driver {
-	struct device_driver drv;
+	struct device_driver		drv;
+	const struct fsi_device_id	*id_table;
 };
 
 #define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
Signature · 1 line
1.8.2.2
#4 lore raw

PATCH v3 4/18 fsi: Add fsi master definition

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

---
 drivers/fsi/fsi-core.c   | 20 ++++++++++++++++++++
 drivers/fsi/fsi-master.h | 37 +++++++++++++++++++++++++++++++++++++
 2 files changed, 57 insertions(+)
 create mode 100644 drivers/fsi/fsi-master.h
drivers/fsi/fsi-core.c +20 -0
index 3d55bd5..ce9428d 100644
@@ -17,6 +17,26 @@
 #include <linux/fsi.h>
 #include <linux/module.h>
 
+#include "fsi-master.h"
+
+static atomic_t master_idx = ATOMIC_INIT(-1);
+
+/* FSI master support */
+
+int fsi_master_register(struct fsi_master *master)
+{
+	master->idx = atomic_inc_return(&master_idx);
+	get_device(master->dev);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_master_register);
+
+void fsi_master_unregister(struct fsi_master *master)
+{
+	put_device(master->dev);
+}
+EXPORT_SYMBOL_GPL(fsi_master_unregister);
+
 /* FSI core & Linux bus type definitions */
 
 static int fsi_bus_match(struct device *dev, struct device_driver *drv)
drivers/fsi/fsi-master.h new file +37 -0
new file mode 100644
index 0000000..e75a810
@@ -0,0 +1,37 @@
+/*
+ * FSI master definitions. These comprise the core <--> master interface,
+ * to allow the core to interact with the (hardware-specific) masters.
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DRIVERS_FSI_MASTER_H
+#define DRIVERS_FSI_MASTER_H
+
+#include <linux/device.h>
+
+struct fsi_master {
+	struct device	*dev;
+	int		idx;
+	int		n_links;
+	int		(*read)(struct fsi_master *, int link,
+				uint8_t slave, uint32_t addr,
+				void *val, size_t size);
+	int		(*write)(struct fsi_master *, int link,
+				uint8_t slave, uint32_t addr,
+				const void *val, size_t size);
+};
+
+extern int fsi_master_register(struct fsi_master *master);
+extern void fsi_master_unregister(struct fsi_master *master);
+
+#endif /* DRIVERS_FSI_MASTER_H */
Signature · 1 line
1.8.2.2
#5 lore raw

PATCH v3 5/18 fsi: Add fake master driver

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

For debugging, add a fake master driver, that only supports reads, returning a fixed set of data.

---
 drivers/fsi/Kconfig           | 10 +++++
 drivers/fsi/Makefile          |  1 +
 drivers/fsi/fsi-master-fake.c | 96 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 107 insertions(+)
 create mode 100644 drivers/fsi/fsi-master-fake.c
drivers/fsi/Kconfig +10 -0
index 04c1a0e..f065dbe 100644
@@ -9,4 +9,14 @@ config FSI
 	---help---
 	  FSI - the FRU Support Interface - is a simple bus for low-level
 	  access to POWER-based hardware.
+
+if FSI
+
+config FSI_MASTER_FAKE
+	tristate "Fake FSI master"
+	depends on FSI
+	---help---
+	This option enables a fake FSI master driver for debugging.
+endif
+
 endmenu
drivers/fsi/Makefile +1 -0
index db0e5e7..847c00c 100644
@@ -1,2 +1,3 @@
 
 obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
drivers/fsi/fsi-master-fake.c new file +96 -0

96 lines not on this page · raw message

Signature · 1 line
1.8.2.2
#6 lore raw

PATCH v3 6/18 fsi: enable debug

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Enable debug for the fsi core during development. Remove before submission.

---
 drivers/fsi/fsi-core.c | 2 ++
 1 file changed, 2 insertions(+)
drivers/fsi/fsi-core.c +2 -0
index ce9428d..db1a1ce 100644
@@ -13,6 +13,8 @@
  * GNU General Public License for more details.
  */
 
+#define DEBUG
+
 #include <linux/device.h>
 #include <linux/fsi.h>
 #include <linux/module.h>
Signature · 1 line
1.8.2.2
#7 lore raw

PATCH v3 7/18 fsi: Add slave defintion

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Add the initial fsi slave device, which is private to the core code. This will be a child of the master, and parent to endpoint devices.

---
 drivers/fsi/fsi-core.c | 9 +++++++++
 1 file changed, 9 insertions(+)
drivers/fsi/fsi-core.c +9 -0
index db1a1ce..7df4291 100644
@@ -23,6 +23,15 @@
 
 static atomic_t master_idx = ATOMIC_INIT(-1);
 
+struct fsi_slave {
+	struct device		dev;
+	struct fsi_master	*master;
+	int			link;
+	uint8_t			id;
+};
+
+#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+
 /* FSI master support */
 
 int fsi_master_register(struct fsi_master *master)
Signature · 1 line
1.8.2.2
#8 lore raw

PATCH v3 8/18 fsi: Add empty master scan

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

When a new fsi master is added, we will need to scan its links, and slaves attached to those links. This change introduces a little shell to iterate the links, which we will populate with the actual slave scan in a later change.

---
 drivers/fsi/fsi-core.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)
drivers/fsi/fsi-core.c +24 -0
index 7df4291..9744a55 100644
@@ -21,6 +21,8 @@
 
 #include "fsi-master.h"
 
+#define FSI_N_SLAVES	4
+
 static atomic_t master_idx = ATOMIC_INIT(-1);
 
 struct fsi_slave {
@@ -32,12 +34,34 @@ struct fsi_slave {
 
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+/* FSI slave support */
+static int fsi_slave_init(struct fsi_master *master,
+		int link, uint8_t slave_id)
+{
+	/* todo: initialise slave device, perform engine scan */
+
+	return -ENODEV;
+}
+
 /* FSI master support */
 
+static int fsi_master_scan(struct fsi_master *master)
+{
+	int link, slave_id;
+
+	for (link = 0; link < master->n_links; link++)
+		for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
+			fsi_slave_init(master, link, slave_id);
+
+	return 0;
+
+}
+
 int fsi_master_register(struct fsi_master *master)
 {
 	master->idx = atomic_inc_return(&master_idx);
 	get_device(master->dev);
+	fsi_master_scan(master);
 	return 0;
 }
 EXPORT_SYMBOL_GPL(fsi_master_register);
Signature · 1 line
1.8.2.2
#9 lore raw

PATCH v3 9/18 fsi: Add crc4 helpers

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Add some helpers for the crc checks for the slave configuration table. This works 4-bits-at-a-time, using a simple table approach.

---
 drivers/fsi/fsi-core.c | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)
drivers/fsi/fsi-core.c +23 -0
index 9744a55..24538ae 100644
@@ -34,6 +34,29 @@ struct fsi_slave {
 
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+/* crc helpers */
+static const uint8_t crc4_tab[] = {
+	0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
+	0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
+};
+
+static uint8_t crc4(uint32_t x)
+{
+	uint8_t c = 0;
+	int i;
+
+	/* Calculate crc4 over four-bit nibbles, starting at the MSbit */
+	for (i = 28; i >= 0; i -= 4)
+		c = crc4_tab[c ^ ((x >> i) & 0xf)];
+
+	return c;
+}
+
+static bool check_crc4(uint32_t x)
+{
+	return crc4(x) == 0;
+}
+
 /* FSI slave support */
 static int fsi_slave_init(struct fsi_master *master,
 		int link, uint8_t slave_id)
Signature · 1 line
1.8.2.2
#10 lore raw

PATCH v3 10/18 fsi: Implement slave initialisation

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Create fsi_slave devices during the master scan.

---
 drivers/fsi/fsi-core.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 48 insertions(+), 2 deletions(-)
drivers/fsi/fsi-core.c +48 -2
index 24538ae..49a8e14 100644
@@ -18,6 +18,7 @@
 #include <linux/device.h>
 #include <linux/fsi.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 #include "fsi-master.h"
 
@@ -58,12 +59,57 @@ static bool check_crc4(uint32_t x)
 }
 
 /* FSI slave support */
+
+static void fsi_slave_release(struct device *dev)
+{
+	struct fsi_slave *slave = to_fsi_slave(dev);
+	kfree(slave);
+}
+
 static int fsi_slave_init(struct fsi_master *master,
 		int link, uint8_t slave_id)
 {
-	/* todo: initialise slave device, perform engine scan */
+	struct fsi_slave *slave;
+	uint32_t chip_id;
+	int rc;
+
+	rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
+	if (rc) {
+		dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
+				link, slave_id, rc);
+		return -ENODEV;
+	}
+
+	chip_id = be32_to_cpu(chip_id);
+	if (!check_crc4(chip_id)) {
+		dev_warn(master->dev, "slave %02x:%02x: invalid chip id CRC!\n",
+				link, slave_id);
+		return -EIO;
+	}
+
+	pr_debug("fsi: found chip %08x at %02x:%02x:%02x\n",
+			master->idx, chip_id, link, slave_id);
+
+	/* we can communicate with a slave; create devices and scan */
+	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+	if (!slave)
+		return -ENOMEM;
+
+	slave->master = master;
+	slave->id = slave_id;
+	slave->dev.parent = master->dev;
+	slave->dev.release = fsi_slave_release;
+
+	dev_set_name(&slave->dev, "slave@%02x:%02x", link, slave_id);
+	rc = device_register(&slave->dev);
+	if (rc < 0) {
+		dev_warn(master->dev, "failed to create slave device: %d\n",
+				rc);
+		put_device(&slave->dev);
+		return rc;
+	}
 
-	return -ENODEV;
+	return rc;
 }
 
 /* FSI master support */
Signature · 1 line
1.8.2.2
#11 lore raw

PATCH v3 11/18 fsi: scan slaves & register devices

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

Now that we have fsi_slave devices, scan each for endpoints, and register them on the fsi bus.

---
 drivers/fsi/fsi-core.c | 125 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/fsi.h    |   4 ++
 2 files changed, 128 insertions(+), 1 deletion(-)
drivers/fsi/fsi-core.c +124 -1
index 49a8e14..63d26b5 100644
@@ -24,6 +24,16 @@
 
 #define FSI_N_SLAVES	4
 
+#define FSI_SLAVE_CONF_NEXT_MASK	0x80000000
+#define FSI_SLAVE_CONF_SLOTS_MASK	0x00ff0000
+#define FSI_SLAVE_CONF_SLOTS_SHIFT	16
+#define FSI_SLAVE_CONF_VERSION_MASK	0x0000f000
+#define FSI_SLAVE_CONF_VERSION_SHIFT	12
+#define FSI_SLAVE_CONF_TYPE_MASK	0x00000ff0
+#define FSI_SLAVE_CONF_TYPE_SHIFT	4
+
+static const int engine_page_size = 0x400;
+
 static atomic_t master_idx = ATOMIC_INIT(-1);
 
 struct fsi_slave {
@@ -35,6 +45,29 @@ struct fsi_slave {
 
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+/* FSI endpoint-device support */
+
+static void fsi_device_release(struct device *_device)
+{
+	struct fsi_device *device = to_fsi_dev(_device);
+	kfree(device);
+}
+
+static struct fsi_device *fsi_create_device(struct fsi_slave *slave)
+{
+	struct fsi_device *dev;
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev)
+		return NULL;
+
+	dev->dev.parent = &slave->dev;
+	dev->dev.bus = &fsi_bus_type;
+	dev->dev.release = fsi_device_release;
+
+	return dev;
+}
+
 /* crc helpers */
 static const uint8_t crc4_tab[] = {
 	0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
@@ -59,6 +92,95 @@ static bool check_crc4(uint32_t x)
 }
 
 /* FSI slave support */
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+		void *val, size_t size)
+{
+	return slave->master->read(slave->master, slave->link,
+			slave->id, addr, val, size);
+}
+
+static int fsi_slave_scan(struct fsi_slave *slave)
+{
+	uint32_t engine_addr;
+	uint32_t conf;
+	int rc, i;
+
+	/*
+	 * scan engines
+	 *
+	 * We keep the peek mode and slave engines for the core; so start
+	 * at the third slot in the configuration table. We also need to
+	 * skip the chip ID entry at the start of the address space.
+	 */
+	engine_addr = engine_page_size * 3;
+	for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) {
+		uint8_t slots, version, type;
+		struct fsi_device *dev;
+
+		rc = fsi_slave_read(slave, (i + 1) * sizeof(conf),
+				&conf, sizeof(conf));
+		if (rc) {
+			dev_warn(&slave->dev,
+					"error reading slave registers\n");
+			return -1;
+		}
+
+		conf = be32_to_cpu(conf);
+
+		if (!check_crc4(conf)) {
+			dev_warn(&slave->dev,
+				"crc error in slave register at 0x%04x\n",
+					i);
+			return -1;
+		}
+
+		slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
+			>> FSI_SLAVE_CONF_SLOTS_SHIFT;
+		version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
+			>> FSI_SLAVE_CONF_VERSION_SHIFT;
+		type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
+			>> FSI_SLAVE_CONF_TYPE_SHIFT;
+
+		/* Unused address areas are marked by a zero type value; this
+		 * skips the defined address areas */
+		if (type != 0) {
+
+			/* create device */
+			dev = fsi_create_device(slave);
+			if (!dev)
+				return -ENOMEM;
+
+			dev->engine_type = type;
+			dev->version = version;
+			dev->unit = i;
+			dev->addr = engine_addr;
+			dev->size = slots * engine_page_size;
+
+			dev_info(&slave->dev,
+			"engine[%i]: type %x, version %x, addr %x size %x\n",
+					dev->unit, dev->engine_type, version,
+					dev->addr, dev->size);
+
+			device_initialize(&dev->dev);
+			dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
+					slave->master->idx, slave->link,
+					slave->id, i - 2);
+
+			rc = device_add(&dev->dev);
+			if (rc) {
+				dev_warn(&slave->dev, "add failed: %d\n", rc);
+				put_device(&dev->dev);
+			}
+		}
+
+		engine_addr += slots * engine_page_size;
+
+		if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
+			break;
+	}
+
+	return 0;
+}
 
 static void fsi_slave_release(struct device *dev)
 {
@@ -109,7 +231,8 @@ static int fsi_slave_init(struct fsi_master *master,
 		return rc;
 	}
 
-	return rc;
+	fsi_slave_scan(slave);
+	return 0;
 }
 
 /* FSI master support */
include/linux/fsi.h +4 -0
index 6d843d4..dfd3513 100644
@@ -22,6 +22,10 @@ struct fsi_device {
 	struct device		dev;
 	u8			engine_type;
 	u8			version;
+	u8			unit;
+	struct fsi_slave	*slave;
+	uint32_t		addr;
+	uint32_t		size;
 };
 
 struct fsi_device_id {
Signature · 1 line
1.8.2.2
#12 lore raw

PATCH v3 12/18 fsi: Add device read/write/peek functions

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

This change introduces the fsi device API: simple read, write and peek accessors for the devices' address spaces.

---
 drivers/fsi/fsi-core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fsi.h    |  7 ++++++-
 2 files changed, 50 insertions(+), 1 deletion(-)
drivers/fsi/fsi-core.c +44 -0
index 63d26b5..10bf817 100644
@@ -32,6 +32,8 @@
 #define FSI_SLAVE_CONF_TYPE_MASK	0x00000ff0
 #define FSI_SLAVE_CONF_TYPE_SHIFT	4
 
+#define FSI_PEEK_BASE			0x410
+
 static const int engine_page_size = 0x400;
 
 static atomic_t master_idx = ATOMIC_INIT(-1);
@@ -45,7 +47,42 @@ struct fsi_slave {
 
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+		void *val, size_t size);
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+		const void *val, size_t size);
+
 /* FSI endpoint-device support */
+int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
+		size_t size)
+{
+	if (addr > dev->size)
+		return -EINVAL;
+
+	if (addr + size > dev->size)
+		return -EINVAL;
+
+	return fsi_slave_read(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
+		size_t size)
+{
+	if (addr > dev->size)
+		return -EINVAL;
+
+	if (addr + size > dev->size)
+		return -EINVAL;
+
+	return fsi_slave_write(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_peek(struct fsi_device *dev, void *val)
+{
+	uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t));
+
+	return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
+}
 
 static void fsi_device_release(struct device *_device)
 {
@@ -99,6 +136,13 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
 			slave->id, addr, val, size);
 }
 
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+		const void *val, size_t size)
+{
+	return slave->master->write(slave->master, slave->link,
+			slave->id, addr, val, size);
+}
+
 static int fsi_slave_scan(struct fsi_slave *slave)
 {
 	uint32_t engine_addr;
include/linux/fsi.h +6 -1
index dfd3513..47af0af 100644
@@ -28,6 +28,12 @@ struct fsi_device {
 	uint32_t		size;
 };
 
+extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
+		void *val, size_t size);
+extern int fsi_device_write(struct fsi_device *dev, uint32_t addr,
+		const void *val, size_t size);
+extern int fsi_device_peek(struct fsi_device *dev, void *val);
+
 struct fsi_device_id {
 	u8	engine_type;
 	u8	version;
@@ -41,7 +47,6 @@ struct fsi_device_id {
 #define FSI_DEVICE_VERSIONED(t, v) \
 	.engine_type = (t), .version = (v),
 
-
 struct fsi_driver {
 	struct device_driver		drv;
 	const struct fsi_device_id	*id_table;
Signature · 1 line
1.8.2.2
#13 lore raw

PATCH v3 13/18 fsi: Add GPIO master driver

In reply to [email protected]

From: Jeremy Kerr <[email protected]>

---
 .../devicetree/bindings/fsi/fsi-master-gpio.txt    |  8 ++
 drivers/fsi/Kconfig                                |  7 ++
 drivers/fsi/Makefile                               |  1 +
 drivers/fsi/fsi-master-gpio.c                      | 92 ++++++++++++++++++++++
 4 files changed, 108 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
 create mode 100644 drivers/fsi/fsi-master-gpio.c
Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt new file +8 -0
new file mode 100644
index 0000000..44762a3
@@ -0,0 +1,8 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+fsi-master {
+	compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+	clk-gpio = <&gpio 0>;
+	data-gpio = <&gpio 1>;
+}
drivers/fsi/Kconfig +7 -0
index f065dbe..69e7ee8 100644
@@ -17,6 +17,13 @@ config FSI_MASTER_FAKE
 	depends on FSI
 	---help---
 	This option enables a fake FSI master driver for debugging.
+
+config FSI_MASTER_GPIO
+	tristate "GPIO-based FSI master"
+	depends on FSI
+	select GPIO_DEVRES
+	---help---
+	This option enables a FSI master driver, using GPIO lines directly.
 endif
 
 endmenu
drivers/fsi/Makefile +1 -0
index 847c00c..2021ce5 100644
@@ -1,3 +1,4 @@
 
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
drivers/fsi/fsi-master-gpio.c new file +92 -0

92 lines not on this page · raw message

Signature · 1 line
1.8.2.2
#14 lore raw

PATCH v3 14/18 drivers/fsi: Fix some text formatting

In reply to [email protected]

From: Chris Bostic <[email protected]>

Cleaned up a few formatting warnings like blank lines at EOF and block comment style.

---
 drivers/fsi/fsi-core.c        | 8 ++++++--
 drivers/fsi/fsi-master-fake.c | 1 -
 drivers/fsi/fsi-master-gpio.c | 1 -
 3 files changed, 6 insertions(+), 4 deletions(-)
drivers/fsi/fsi-core.c +6 -2
index 10bf817..a3a860a 100644
@@ -87,6 +87,7 @@ int fsi_device_peek(struct fsi_device *dev, void *val)
 static void fsi_device_release(struct device *_device)
 {
 	struct fsi_device *device = to_fsi_dev(_device);
+
 	kfree(device);
 }
 
@@ -185,8 +186,10 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 		type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
 			>> FSI_SLAVE_CONF_TYPE_SHIFT;
 
-		/* Unused address areas are marked by a zero type value; this
-		 * skips the defined address areas */
+		/*
+		 * Unused address areas are marked by a zero type value; this
+		 * skips the defined address areas
+		 */
 		if (type != 0) {
 
 			/* create device */
@@ -229,6 +232,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 static void fsi_slave_release(struct device *dev)
 {
 	struct fsi_slave *slave = to_fsi_slave(dev);
+
 	kfree(slave);
 }
 
drivers/fsi/fsi-master-fake.c +0 -1
index 50aac0b..ec1ed5e 100644
@@ -93,4 +93,3 @@ static int __init fsi_master_fake_init(void)
 }
 
 module_init(fsi_master_fake_init);
-
drivers/fsi/fsi-master-gpio.c +0 -1
index 3855829..5c3d5cd 100644
@@ -89,4 +89,3 @@ static struct platform_driver fsi_master_gpio_driver = {
 };
 
 module_platform_driver(fsi_master_gpio_driver);
-
Signature · 1 line
1.8.2.2
#15 lore raw

PATCH v3 15/18 drivers/fsi: Revisions to existing patch set

In reply to [email protected]

From: Chris Bostic <[email protected]>

Changes recommended to the patch set so far as supplied by
Jeremy Kerr. Moved some CFAM specific details to a new cfam header

Changes in V2:

- Removed the renaming of slave to cfam

---
 drivers/fsi/fsi-cfam.h | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++
 drivers/fsi/fsi-core.c | 22 ++++--------------
 2 files changed, 68 insertions(+), 17 deletions(-)
 create mode 100644 drivers/fsi/fsi-cfam.h
drivers/fsi/fsi-cfam.h new file +63 -0
new file mode 100644
index 0000000..ddb03cd
@@ -0,0 +1,63 @@
+/*
+ * FSI CFAM definitions
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DRIVERS_FSI_CFAM_H
+#define DRIVERS_FSI_CFAM_H
+
+#include <linux/device.h>
+
+#include "fsi-master.h"
+
+#define FSI_SLAVE_ENG_ID	2
+
+#define FSI_MAX_CFAMS_PER_LINK	4
+#define FSI_CFAM_SIZE		(FSI_LINK_SIZE / FSI_MAX_CFAMS_PER_LINK)
+#define FSI_PEEK_BASE		0x410
+#define FSI_SLAVE_BASE		0x800
+
+/* Config space decoding */
+#define FSI_CFG_NEXT_MASK	0x80000000
+#define FSI_CFG_SLOT_MASK	0x00ff0000
+#define FSI_CFG_SLOT_SHIFT	16
+#define FSI_CFG_VERS_MASK	0x0000f000
+#define FSI_CFG_VERS_SHIFT	12
+#define FSI_CFG_TYPE_MASK	0x00000ff0
+#define FSI_CFG_TYPE_SHIFT	4
+
+/*
+ * Return number of slots in the configuration word
+ */
+static inline uint8_t fsi_cfg_slot(uint32_t x)
+{
+	return (x & FSI_CFG_SLOT_MASK) >> FSI_CFG_SLOT_SHIFT;
+}
+
+/*
+ * Return version in the configuration word
+ */
+static inline uint8_t fsi_cfg_version(uint32_t x)
+{
+	return (x & FSI_CFG_VERS_MASK) >> FSI_CFG_VERS_SHIFT;
+}
+
+/*
+ * Return type field in the configuration word
+ */
+static inline uint8_t fsi_cfg_type(uint32_t x)
+{
+	return (x & FSI_CFG_TYPE_MASK) >> FSI_CFG_TYPE_SHIFT;
+}
+
+#endif /* DRIVERS_FSI_CFAM_H */
drivers/fsi/fsi-core.c +5 -17
index a3a860a..0da8cf0 100644
@@ -21,19 +21,10 @@
 #include <linux/slab.h>
 
 #include "fsi-master.h"
+#include "fsi-cfam.h"
 
 #define FSI_N_SLAVES	4
 
-#define FSI_SLAVE_CONF_NEXT_MASK	0x80000000
-#define FSI_SLAVE_CONF_SLOTS_MASK	0x00ff0000
-#define FSI_SLAVE_CONF_SLOTS_SHIFT	16
-#define FSI_SLAVE_CONF_VERSION_MASK	0x0000f000
-#define FSI_SLAVE_CONF_VERSION_SHIFT	12
-#define FSI_SLAVE_CONF_TYPE_MASK	0x00000ff0
-#define FSI_SLAVE_CONF_TYPE_SHIFT	4
-
-#define FSI_PEEK_BASE			0x410
-
 static const int engine_page_size = 0x400;
 
 static atomic_t master_idx = ATOMIC_INIT(-1);
@@ -179,12 +170,9 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 			return -1;
 		}
 
-		slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
-			>> FSI_SLAVE_CONF_SLOTS_SHIFT;
-		version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
-			>> FSI_SLAVE_CONF_VERSION_SHIFT;
-		type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
-			>> FSI_SLAVE_CONF_TYPE_SHIFT;
+		slots = fsi_cfg_slot(conf);
+		version = fsi_cfg_version(conf);
+		type = fsi_cfg_type(conf);
 
 		/*
 		 * Unused address areas are marked by a zero type value; this
@@ -222,7 +210,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 
 		engine_addr += slots * engine_page_size;
 
-		if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
+		if (!(conf & FSI_CFG_NEXT_MASK))
 			break;
 	}
 
Signature · 1 line
1.8.2.2
#16 lore raw

PATCH v3 16/18 drivers/fsi: Set up CFAMs for communication

In reply to [email protected]

From: Chris Bostic <[email protected]>

Issue break command to reset CFAM logic and set ID as appropriate for a particular master type. Begin definition of the slave engine register set and accessors.

V3 - Remove definition of BREAK command from fsi-master.h

- Remove definitions of fsi_master_fake for set_smode and break

- Remove master->set_smode master dependent function and moved to a generic base master set_smode.

- Add fsi_master_link_enable with master type dependencies

---
 drivers/fsi/fsi-core.c        | 75 +++++++++++++++++++++++++++++++++++++++++--
 drivers/fsi/fsi-master-fake.c |  2 ++
 drivers/fsi/fsi-master-gpio.c | 33 +++++++++++++++++++
 drivers/fsi/fsi-master.h      |  2 ++
 drivers/fsi/fsi-slave.h       | 62 +++++++++++++++++++++++++++++++++++
 5 files changed, 171 insertions(+), 3 deletions(-)
 create mode 100644 drivers/fsi/fsi-slave.h
drivers/fsi/fsi-core.c +72 -3
index 0da8cf0..c5e7441 100644
@@ -22,6 +22,7 @@
 
 #include "fsi-master.h"
 #include "fsi-cfam.h"
+#include "fsi-slave.h"
 
 #define FSI_N_SLAVES	4
 
@@ -224,13 +225,66 @@ static void fsi_slave_release(struct device *dev)
 	kfree(slave);
 }
 
+/*
+ * Issue a break commad on this link
+ */
+static int fsi_master_break(struct fsi_master *master, int link)
+{
+	int rc = 0;
+
+	if (master->send_break)
+		rc = master->send_break(master, link);
+
+	return rc;
+}
+
+static void set_smode_defaults(struct fsi_master *master, uint32_t *smode)
+{
+	*smode = FSI_SMODE_WSC | FSI_SMODE_ECRC
+		| fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+		| fsi_smode_lbcrr(1) | fsi_smode_sid(0);
+}
+
+static int fsi_master_set_smode(struct fsi_master *master, int link)
+{
+	uint32_t smode;
+	int rc;
+
+	set_smode_defaults(master, &smode);
+	rc = master->write(master, link, 0, FSI_SLAVE_BASE + FSI_SMODE,
+			&smode, sizeof(smode));
+
+	return rc;
+}
+
 static int fsi_slave_init(struct fsi_master *master,
 		int link, uint8_t slave_id)
 {
-	struct fsi_slave *slave;
+	struct fsi_slave *slave = NULL;
 	uint32_t chip_id;
 	int rc;
 
+	/*
+	 * todo: Due to CFAM hardware issues related to BREAK commands we're
+	 * limited to only one CFAM per link. Once hardware fixes are available
+	 * this restriction can be removed.
+	 */
+	if (slave_id > 0)
+		return 0;
+
+	rc = fsi_master_break(master, link);
+	if (rc) {
+		dev_warn(master->dev, "no slave detected at %02x:%02x\n",
+				link, slave_id);
+		return -ENODEV;
+	}
+
+	rc = fsi_master_set_smode(master, link);
+	if (rc) {
+		dev_warn(master->dev, "failed to set smode id, rc:%d\n", rc);
+		return -ENODEV;
+	}
+
 	rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
 	if (rc) {
 		dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
@@ -273,13 +327,28 @@ static int fsi_slave_init(struct fsi_master *master,
 
 /* FSI master support */
 
+static int fsi_master_link_enable(struct fsi_master *master, int link)
+{
+	int rc;
+
+	if (master->link_enable)
+		rc = master->link_enable(master, link);
+
+	return rc;
+}
+
 static int fsi_master_scan(struct fsi_master *master)
 {
-	int link, slave_id;
+	int link, slave_id, rc;
+
+	for (link = 0; link < master->n_links; link++) {
+		rc = fsi_master_link_enable(master, link);
+		if (rc)
+			continue;
 
-	for (link = 0; link < master->n_links; link++)
 		for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
 			fsi_slave_init(master, link, slave_id);
+	}
 
 	return 0;
 
drivers/fsi/fsi-master-fake.c +2 -0
index ec1ed5e..c7ad631 100644
@@ -63,6 +63,8 @@ static int fsi_master_fake_probe(struct platform_device *pdev)
 	master->n_links = 1;
 	master->read = fsi_master_fake_read;
 	master->write = fsi_master_fake_write;
+	master->send_break = NULL;
+	master->link_enable = NULL;
 
 	return fsi_master_register(master);
 }
drivers/fsi/fsi-master-gpio.c +33 -0
index 5c3d5cd..166370a 100644
@@ -7,6 +7,8 @@
 #include <linux/module.h>
 
 #include "fsi-master.h"
+#include "fsi-cfam.h"
+#include "fsi-slave.h"
 
 struct fsi_master_gpio {
 	struct fsi_master	master;
@@ -50,6 +52,35 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 	return 0;
 }
 
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+	if (link != 0)
+		return -ENODEV;
+
+	/* todo: send the break pattern over gpio */
+	(void)master;
+
+	return 0;
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+	if (link != 0)
+		return -ENODEV;
+
+	/* todo: set the enable pin */
+	(void)master;
+
+	return 0;
+}
+
 static int fsi_master_gpio_probe(struct platform_device *pdev)
 {
 	struct fsi_master_gpio *master;
@@ -71,6 +102,8 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 
 	master->master.read = fsi_master_gpio_read;
 	master->master.write = fsi_master_gpio_write;
+	master->master.send_break = fsi_master_gpio_break;
+	master->master.link_enable = fsi_master_gpio_link_enable;
 
 	return fsi_master_register(&master->master);
 }
drivers/fsi/fsi-master.h +2 -0
index e75a810..94a0671 100644
@@ -29,6 +29,8 @@ struct fsi_master {
 	int		(*write)(struct fsi_master *, int link,
 				uint8_t slave, uint32_t addr,
 				const void *val, size_t size);
+	int		(*send_break)(struct fsi_master *, int link);
+	int		(*link_enable)(struct fsi_master *, int link);
 };
 
 extern int fsi_master_register(struct fsi_master *master);
drivers/fsi/fsi-slave.h new file +62 -0

62 lines not on this page · raw message

Signature · 1 line
1.8.2.2
#17 lore raw

Re: [PATCH linux v3 16/18] drivers/fsi: Set up CFAMs for communication

Hi Chris,

Quoted 9 lines

Issue break command to reset CFAM logic and set ID as appropriate for a particular master type. Begin definition of the slave engine register set and accessors.

V3 - Remove definition of BREAK command from fsi-master.h

- Remove definitions of fsi_master_fake for set_smode and break

Quoted 6 lines

- Remove master->set_smode master dependent function and moved to a generic base master set_smode.

- Add fsi_master_link_enable with master type dependencies

Looking better, I still have a few comments though:

The most significant part of this patch is to add the send_break and link_enable callbacks, but we're also adding the smode setup. Because of that, I'd suggest splitting this into two:

- one patch to update the fsi_master API to add the new callbacks, and their implementations; and

- another patch to do the cfam setup

Quoted 2 lines

+/*
+ * Issue a break commad on this link

*command

Quoted 7 lines

+ */

drivers/fsi/fsi-core.c +6 -0
+static int fsi_master_break(struct fsi_master *master, int link)
+{
+	int rc = 0;
+
+	if (master->send_break)
+		rc = master->send_break(master, link);

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 246

Quoted 10 lines
drivers/fsi/fsi-core.c +10 -0
+
+	return rc;
+}
+
+static void set_smode_defaults(struct fsi_master *master, uint32_t *smode)
+{
+	*smode = FSI_SMODE_WSC | FSI_SMODE_ECRC
+		| fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+		| fsi_smode_lbcrr(1) | fsi_smode_sid(0);
+}

I assume that the smode value will depend on the master later, hence the need for a function for this (otherwise we'd just want a const uint32_t). However, can you just return the smode rather than setting a pointer and returning void?

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 259

Quoted 12 lines
drivers/fsi/fsi-core.c +12 -0
+static int fsi_master_set_smode(struct fsi_master *master, int link)
+{
+	uint32_t smode;
+	int rc;
+
+	set_smode_defaults(master, &smode);
+	rc = master->write(master, link, 0, FSI_SLAVE_BASE + FSI_SMODE,
+			&smode, sizeof(smode));
+
+	return rc;
+}
+

On reading this a little further, I'm a little confused about whether the smode register is part of the master or part of a slave. I've assumed the latter (as there are no registers on a GPIO master), but wouldn't that need to know the slave ID or base address?

[Or is the absence of that a factor of only supporting one slave per link at the moment? If that's the case, can you rename this to fsi_slave_set_smode, and add the slave ID parameter that we'll need later? That will clarify things a bit]

Quoted 12 lines

static int fsi_slave_init(struct fsi_master *master,

drivers/fsi/fsi-core.c +5 -1
 		int link, uint8_t slave_id)
 {
-	struct fsi_slave *slave;
+	struct fsi_slave *slave = NULL;
 	uint32_t chip_id;
 	int rc;
 
+	/*
+	 * todo: Due to CFAM hardware issues related to BREAK commands we're
+	 * limited to only one CFAM per link. Once hardware fixes are available
+	 * this restriction can be removed.

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 280

Quoted 10 lines
drivers/fsi/fsi-core.c +10 -0
+	 */
+	if (slave_id > 0)
+		return 0;
+
+	rc = fsi_master_break(master, link);
+	if (rc) {
+		dev_warn(master->dev, "no slave detected at %02x:%02x\n",
+				link, slave_id);
+		return -ENODEV;
+	}

Shoudln't we do the break as part of a master init (and then build slave IDs)? Seeing as it will reset the slave IDs, this operation can't be confined to only a single slave (as the name slave_init would imply.

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 287

Quoted 7 lines
drivers/fsi/fsi-core.c +7 -0
+
+	rc = fsi_master_set_smode(master, link);
+	if (rc) {
+		dev_warn(master->dev, "failed to set smode id, rc:%d\n", rc);
+		return -ENODEV;
+	}
+

You'll probably be better returning rc for both of these cases, no need to squash it to ENODEV (and if you do, EIO might be more appropriate).

drivers/fsi/fsi-core.c static int fsi_slave_init(struct fsi_master *master, · line 339

Quoted 12 lines

/* FSI master support */

drivers/fsi/fsi-core.c +10 -0
+static int fsi_master_link_enable(struct fsi_master *master, int link)
+{
+	int rc;
+
+	if (master->link_enable)
+		rc = master->link_enable(master, link);
+
+	return rc;
+}
+

OK, looks good for a link_enable function of the masters. However, can you add the new callbacks (link_enable & send_break) and their wrappers in a separate earlier patch?

drivers/fsi/fsi-core.c static int fsi_slave_init(struct fsi_master *master, · line 347

Quoted 9 lines

static int fsi_master_scan(struct fsi_master *master) {

drivers/fsi/fsi-core.c +6 -1
-	int link, slave_id;
+	int link, slave_id, rc;
+
+	for (link = 0; link < master->n_links; link++) {
+		rc = fsi_master_link_enable(master, link);
+		if (rc)
+			continue;

Shouldn't we fail on that?

Quoted 13 lines
drivers/fsi/fsi-core.c +1 -1
-	for (link = 0; link < master->n_links; link++)
 		for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
 			fsi_slave_init(master, link, slave_id);
+	}
 
 	return 0;
drivers/fsi/fsi-master-fake.c +0 -0
index ec1ed5e..c7ad631 100644

drivers/fsi/fsi-master-fake.c static int fsi_master_fake_probe(struct platform_device *pdev) · line 67

Quoted 5 lines
drivers/fsi/fsi-master-fake.c +2 -0
@@ -63,6 +63,8 @@ static int fsi_master_fake_probe(struct platform_device *pdev)
 	master->n_links = 1;
 	master->read = fsi_master_fake_read;
 	master->write = fsi_master_fake_write;
+	master->send_break = NULL;
+	master->link_enable = NULL;

master is kzalloc()ed, no need to set to NULL.

Quoted 19 lines
drivers/fsi/fsi-master-fake.c +19 -0
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+	if (link != 0)
+		return -ENODEV;
+
+	/* todo: send the break pattern over gpio */
+	(void)master;
+
+	return 0;
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);

drivers/fsi/fsi-master-fake.c static int fsi_master_fake_probe(struct platform_device *pdev)

Quoted 10 lines
drivers/fsi/fsi-master-fake.c +10 -0
+
+	if (link != 0)
+		return -ENODEV;
+
+	/* todo: set the enable pin */
+	(void)master;
+
+	return 0;
+}
+

Looks good.

drivers/fsi/fsi-slave.h line 3

Quoted 9 lines
drivers/fsi/fsi-slave.h new file +3 -0
new file mode 100644
index 0000000..ea7a760
@@ -0,0 +1,62 @@
+/*
+ * FSI slave engine definitions.
+ *

Why does this need to be in a header file? Isn't it only used by the FSI core?

Cheers,

Jeremy

#18 lore raw

Re: [PATCH linux v3 16/18] drivers/fsi: Set up CFAMs for communication

Quoted 20 lines · Jeremy Kerr

Hi Chris,

Quoted 9 lines

Issue break command to reset CFAM logic and set ID as appropriate for a particular master type. Begin definition of the slave engine register set and accessors.

V3 - Remove definition of BREAK command from fsi-master.h

- Remove definitions of fsi_master_fake for set_smode and break

Quoted 6 lines

- Remove master->set_smode master dependent function and moved to a generic base master set_smode.

- Add fsi_master_link_enable with master type dependencies

Looking better, I still have a few comments though:

Quoted 9 lines

The most significant part of this patch is to add the send_break and link_enable callbacks, but we're also adding the smode setup. Because of that, I'd suggest splitting this into two:

- one patch to update the fsi_master API to add the new callbacks, and their implementations; and

- another patch to do the cfam setup

OK will do that.

Quoted 5 lines
Quoted 2 lines

+/*
+ * Issue a break commad on this link

*command

Oops, will fix.

Quoted 14 lines
Quoted 14 lines

+ */

drivers/fsi/fsi-core.c +13 -0
+static int fsi_master_break(struct fsi_master *master, int link)
+{
+     int rc = 0;
+
+     if (master->send_break)
+             rc = master->send_break(master, link);
+
+     return rc;
+}
+
+static void set_smode_defaults(struct fsi_master *master, uint32_t *smode)
+{
+     *smode = FSI_SMODE_WSC | FSI_SMODE_ECRC
Quoted 8 lines

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 246

Quoted 3 lines
drivers/fsi/fsi-core.c +3 -0
+             | fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+             | fsi_smode_lbcrr(1) | fsi_smode_sid(0);
+}

I assume that the smode value will depend on the master later, hence the need for a function for this (otherwise we'd just want a const uint32_t). However, can you just return the smode rather than setting a pointer and returning void?

OK will fix.

Quoted 19 lines

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 259

Quoted 13 lines
drivers/fsi/fsi-core.c +12 -0
+static int fsi_master_set_smode(struct fsi_master *master, int link)
+{
+     uint32_t smode;
+     int rc;
+
+     set_smode_defaults(master, &smode);
+     rc = master->write(master, link, 0, FSI_SLAVE_BASE + FSI_SMODE,
+                     &smode, sizeof(smode));
+
+     return rc;
+}
+

On reading this a little further, I'm a little confused about whether the smode register is part of the master or part of a slave. I've assumed the latter (as there are no registers on a GPIO master), but wouldn't that need to know the slave ID or base address?

Quoted 5 lines

[Or is the absence of that a factor of only supporting one slave per link at the moment? If that's the case, can you rename this to fsi_slave_set_smode, and add the slave ID parameter that we'll need later? That will clarify things a bit]

Its the latter, due to having only one slave per link at the moment. Will rename and add slave ID as a parameter.

Quoted 27 lines
Quoted 12 lines

static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
{

drivers/fsi/fsi-core.c +5 -1
-     struct fsi_slave *slave;
+     struct fsi_slave *slave = NULL;
      uint32_t chip_id;
      int rc;
 
+     /*
+      * todo: Due to CFAM hardware issues related to BREAK commands we're
+      * limited to only one CFAM per link. Once hardware fixes are available
+      * this restriction can be removed.

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 280

Quoted 10 lines
drivers/fsi/fsi-core.c +10 -0
+      */
+     if (slave_id > 0)
+             return 0;
+
+     rc = fsi_master_break(master, link);
+     if (rc) {
+             dev_warn(master->dev, "no slave detected at %02x:%02x\n",
+                             link, slave_id);
+             return -ENODEV;
+     }

Shoudln't we do the break as part of a master init (and then build slave IDs)? Seeing as it will reset the slave IDs, this operation can't be confined to only a single slave (as the name slave_init would imply.

Quoted 8 lines

drivers/fsi/fsi-core.c static void fsi_slave_release(struct device *dev) · line 287

Quoted 7 lines
drivers/fsi/fsi-core.c +7 -0
+
+     rc = fsi_master_set_smode(master, link);
+     if (rc) {
+             dev_warn(master->dev, "failed to set smode id, rc:%d\n", rc);
+             return -ENODEV;
+     }
+

Good point, will do that.

Quoted 3 lines

You'll probably be better returning rc for both of these cases, no need to squash it to ENODEV (and if you do, EIO might be more appropriate).

The main way to detect if there is a real and functional slave on the other end of the link, at least in the past, was to see if the break command failed or not. If break failed then there was nothing present to respond to the read of the slave smode register. rc is just the result of the slave smode read. That was the reasoning for returning ENODEV. Maybe it makes more sense to place that value in the break function itself. Let me know.

Quoted 16 lines

drivers/fsi/fsi-core.c static int fsi_slave_init(struct fsi_master *master, · line 339

Quoted 12 lines

/* FSI master support */

drivers/fsi/fsi-core.c +10 -0
+static int fsi_master_link_enable(struct fsi_master *master, int link)
+{
+     int rc;
+
+     if (master->link_enable)
+             rc = master->link_enable(master, link);
+
+     return rc;
+}
+

OK, looks good for a link_enable function of the masters. However, can you add the new callbacks (link_enable & send_break) and their wrappers in a separate earlier patch?

Is this the same change you discuss above where you'd like to see link_enable and send_break code in a separate patch from the set_smode code? Or is this a third patch to spell out sub details on link_enable and send_break?

Quoted 11 lines

drivers/fsi/fsi-core.c static int fsi_slave_init(struct fsi_master *master, · line 347

Quoted 10 lines

static int fsi_master_scan(struct fsi_master *master) {

drivers/fsi/fsi-core.c +6 -1
-     int link, slave_id;
+     int link, slave_id, rc;
+
+     for (link = 0; link < master->n_links; link++) {
+             rc = fsi_master_link_enable(master, link);
+             if (rc)
+                     continue;
Quoted 2 lines

Shouldn't we fail on that?

Yes, but what that means right now is uncertain. I can put in a dev_dbg( ) to track the fail but I don't want to exit immediately on that error since there may still be links to deal with in the for loop.

Quoted 8 lines
Quoted 8 lines
drivers/fsi/fsi-core.c +1 -1
-     for (link = 0; link < master->n_links; link++)
              for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
                      fsi_slave_init(master, link, slave_id);
+     }
 
      return 0;
Quoted 13 lines

drivers/fsi/fsi-master-fake.c static int fsi_master_fake_probe(struct platform_device *pdev) · line 67

Quoted 10 lines
drivers/fsi/fsi-master-fake.c +2 -0
index ec1ed5e..c7ad631 100644
@@ -63,6 +63,8 @@ static int fsi_master_fake_probe(struct platform_device *pdev)
      master->n_links = 1;
      master->read = fsi_master_fake_read;
      master->write = fsi_master_fake_write;
+     master->send_break = NULL;
+     master->link_enable = NULL;

master is kzalloc()ed, no need to set to NULL.

Will remove.

Quoted 32 lines
Quoted 19 lines
drivers/fsi/fsi-master-fake.c +19 -0
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+     struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+     if (link != 0)
+             return -ENODEV;
+
+     /* todo: send the break pattern over gpio */
+     (void)master;
+
+     return 0;
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+     struct fsi_master_gpio *master = to_fsi_master_gpio(_master);

drivers/fsi/fsi-master-fake.c static int fsi_master_fake_probe(struct platform_device *pdev)

Quoted 10 lines
drivers/fsi/fsi-master-fake.c +10 -0
+
+     if (link != 0)
+             return -ENODEV;
+
+     /* todo: set the enable pin */
+     (void)master;
+
+     return 0;
+}
+

Looks good.

Quoted 13 lines

drivers/fsi/fsi-slave.h line 3

Quoted 9 lines
drivers/fsi/fsi-slave.h new file +3 -0
new file mode 100644
index 0000000..ea7a760
@@ -0,0 +1,62 @@
+/*
+ * FSI slave engine definitions.
+ *

Why does this need to be in a header file? Isn't it only used by the FSI core?

I suppose this is me not being familiar with the common coding practices of the Linux community. I would typically put constants and structs in an associated header file even if there was no intent to have them be used
by anything outside the core. Is it a requirement that this all be moved
into fsi-core.c as a means of limiting accessibility?

Thanks,
Chris

Quoted 4 lines

Cheers,

Jeremy

#19 lore raw

Re: [PATCH linux v3 16/18] drivers/fsi: Set up CFAMs for communication

Hi Chris,

Quoted 7 lines
Quoted 3 lines

You'll probably be better returning rc for both of these cases, no need to squash it to ENODEV (and if you do, EIO might be more appropriate).

The main way to detect if there is a real and functional slave on the other end of the link, at least in the past, was to see if the break command failed or not.

But the break has no response, so any failure there would only indicate a problem with the master, right? Wouldn't a slave error only be detected by the smode write?

But yes, in that case, -ENODEV is appropriate for the smode write.

Quoted 8 lines
Quoted 3 lines

OK, looks good for a link_enable function of the masters. However, can you add the new callbacks (link_enable & send_break) and their wrappers in a separate earlier patch?

Is this the same change you discuss above where you'd like to see link_enable and send_break code in a separate patch from the set_smode code? Or is this a third patch to spell out sub details on link_enable and send_break?

No, same as above. Sorry for the confusion!

Quoted 10 lines
Quoted 10 lines
Quoted 9 lines

static int fsi_master_scan(struct fsi_master *master) {

drivers/fsi/fsi-core.c +6 -1
-     int link, slave_id;
+     int link, slave_id, rc;
+
+     for (link = 0; link < master->n_links; link++) {
+             rc = fsi_master_link_enable(master, link);
+             if (rc)
+                     continue;
Quoted 7 lines
Quoted 2 lines

Shouldn't we fail on that?

Yes, but what that means right now is uncertain. I can put in a dev_dbg( ) to track the fail but I don't want to exit immediately on that error since there may still be links to deal with in the for loop.

OK, makes sense.

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/fsi/fsi-slave.h new file +0 -1
new file mode 100644
index 0000000..ea7a760
--- /dev/null
Quoted 8 lines

drivers/fsi/fsi-slave.h line 3

Quoted 5 lines

+++ b/drivers/fsi/fsi-slave.h

drivers/fsi/fsi-slave.h new file +3 -0
@@ -0,0 +1,62 @@
+/*
+ * FSI slave engine definitions.
+ *

Why does this need to be in a header file? Isn't it only used by the FSI core?

Quoted 5 lines

I suppose this is me not being familiar with the common coding practices of the Linux community. I would typically put constants and structs in an associated header file even if there was no intent to have them be used
by anything outside the core. Is it a requirement that this all be moved
into fsi-core.c as a means of limiting accessibility?

It's not a requirement, but a strong convention. Only use headers for stuff that you need to share across other files; this gives future developers a definite indication of the scope of these macros, consts and structs.

The less that is globally accessible, the better.

Regards,

Jeremy

#20 lore raw

Re: [PATCH linux v3 16/18] drivers/fsi: Set up CFAMs for communication

Quoted 22 lines · Jeremy Kerr

Hi Chris,

Quoted 13 lines
Quoted 4 lines
Quoted 4 lines
drivers/fsi/fsi-slave.h new file +0 -1
new file mode 100644
index 0000000..ea7a760
--- /dev/null
Quoted 8 lines

drivers/fsi/fsi-slave.h line 3

Quoted 5 lines

+++ b/drivers/fsi/fsi-slave.h

drivers/fsi/fsi-slave.h new file +3 -0
@@ -0,0 +1,62 @@
+/*
+ * FSI slave engine definitions.
+ *

Why does this need to be in a header file? Isn't it only used by the FSI core?

Quoted 5 lines

I suppose this is me not being familiar with the common coding practices of the Linux community. I would typically put constants and structs in an associated header file even if there was no intent to have them be used
by anything outside the core. Is it a requirement that this all be moved
into fsi-core.c as a means of limiting accessibility?

Quoted 7 lines

It's not a requirement, but a strong convention. Only use headers for stuff that you need to share across other files; this gives future developers a definite indication of the scope of these macros, consts and structs.

The less that is globally accessible, the better.

OK will move that all into fsi-core.c

Thanks,

Chris

Quoted 4 lines

Regards,

Jeremy

#21 lore raw

PATCH v3 17/18 drivers/fsi: Add GPIO master functionality

In reply to [email protected]

From: Chris Bostic <[email protected]>

Add setup of the GPIO pins for FSI master function. Setup I/O directions, define all pins and setup their initial directions. Define serial out operation.

V3 - Added remainder of base I/O ops (excluding dev tree and crc calcs).
- Add encoding of all read/write commands and send data over sda
- Check for and decode response from slave.
- Add set enable pin in master->link_enable() master specific function.

---
 drivers/fsi/fsi-master-gpio.c | 355 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 344 insertions(+), 11 deletions(-)
drivers/fsi/fsi-master-gpio.c +344 -11
index 166370a..fe757cc 100644
@@ -5,15 +5,52 @@
 #include <linux/platform_device.h>
 #include <linux/gpio/consumer.h>
 #include <linux/module.h>
+#include <linux/delay.h>
 
 #include "fsi-master.h"
 #include "fsi-cfam.h"
 #include "fsi-slave.h"
 
+#define FSI_ECHO_DELAY_CLOCKS	16	/* Number clocks for echo delay */
+#define FSI_PRE_BREAK_CLOCKS	50	/* Number clocks to prep for break */
+#define FSI_BREAK_CLOCKS	256	/* Number of clocks to issue break */
+#define FSI_POST_BREAK_CLOCKS	16000	/* Number clocks to set up cfam */
+#define FSI_INIT_CLOCKS		5000	/* Clear out any old data and states */
+
+#define FSI_GPIO_STD_DELAY	10	/* Standard GPIO delay in nS */
+					/* todo: adjust down as low as */
+					/* possible or eliminate */
+
+#define FSI_GPIO_CMD_DPOLL	0x0000808A
+#define FSI_GPIO_CMD_DPOLL_SIZE	10
+#define FSI_GPIO_DPOLL_CLOCKS	24	/* < 21 will cause slave to hang */
+#define FSI_GPIO_CMD_DEFAULT	0x2000000000000000ULL
+#define FSI_GPIO_CMD_WRITE	0
+#define FSI_GPIO_CMD_READ	0x0400000000000000ULL
+#define FSI_GPIO_CMD_SLAVE_MASK	0xC000000000000000ULL
+#define FSI_GPIO_CMD_ADDR_SHIFT	3
+#define FSI_GPIO_CMD_SIZE_16	0x0000001000000000ULL
+#define FSI_GPIO_CMD_SIZE_32	0x0000003000000000ULL
+#define FSI_GPIO_CMD_DATA_SHIFT	28
+#define FSI_GPIO_CMD_DFLT_LEN	32
+#define FSI_GPIO_RESP_BUSY	1
+#define FSI_GPIO_RESP_ERRA	2
+#define FSI_GPIO_RESP_ERRC	3
+#define FSI_GPIO_RESP_ACK	0
+#define FSI_GPIO_RESP_ACKD	4
+#define FSI_GPIO_MAX_BUSY	100
+#define FSI_GPIO_MTOE_COUNT	1000
+#define FSI_GPIO_MTOE		1
+#define FSI_GPIO_DRAIN_BITS	20
+#define FSI_GPIO_CRC_SIZE	4
+
 struct fsi_master_gpio {
 	struct fsi_master	master;
 	struct gpio_desc	*gpio_clk;
 	struct gpio_desc	*gpio_data;
+	struct gpio_desc	*gpio_trans;		/* Voltage translator */
+	struct gpio_desc	*gpio_enable;		/* FSI enable */
+	struct gpio_desc	*gpio_mux;		/* Mux control */
 };
 
 #define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
@@ -23,33 +60,287 @@ struct fsi_gpio_msg {
 	uint8_t		bits;
 };
 
+static void add_crc(struct fsi_gpio_msg *cmd)
+{
+
+}
+
+static void set_clock(struct fsi_master_gpio *master)
+{
+	/* This delay is required - do not remove */
+	ndelay(FSI_GPIO_STD_DELAY);
+	gpiod_set_value(master->gpio_clk, 1);
+}
+
+static void clear_clock(struct fsi_master_gpio *master)
+{
+	/* This delay is required - do not remove */
+	ndelay(FSI_GPIO_STD_DELAY);
+	gpiod_set_value(master->gpio_clk, 0);
+}
+
+static void clock_toggle(struct fsi_master_gpio *master, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		clear_clock(master);
+		set_clock(master);
+	}
+}
+
+static int sda_in(struct fsi_master_gpio *master)
+{
+	return gpiod_get_value(master->gpio_data);
+}
+
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+	gpiod_set_value(master->gpio_data, value);
+
+	/* Required -do not remove */
+	ndelay(FSI_GPIO_STD_DELAY);
+}
+
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+	gpiod_direction_input(master->gpio_data);
+	gpiod_direction_output(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master,
+			   int value)
+{
+	gpiod_direction_output(master->gpio_data, value);
+	gpiod_direction_output(master->gpio_trans, 1);
+}
+
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)
+{
+	uint8_t bit;
+	uint64_t msg = 0;
+	uint8_t in_bit = 0;
+
+	set_sda_input(master);
+
+	for (bit = 0; bit < cmd->bits; bit++) {
+		clock_toggle(master, 1);
+		in_bit = sda_in(master);
+		msg |= in_bit;
+		msg <<= 1;
+	}
+	cmd->msg = ~msg;		/*  Data is negative active */
+}
+
+static void serial_out(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)
+{
+	uint8_t bit;
+	uint64_t msg = ~cmd->msg;	/* Data is negative active */
+	uint64_t sda_mask = 1;
+	uint64_t last_bit = ~0;
+
+	set_sda_output(master, 0);
+
+	/* Send the start bit */
+	sda_out(master, 1);
+	clock_toggle(master, 1);
+
+	/* Send the message */
+	for (bit = 0; bit < cmd->bits; bit++) {
+		if (last_bit ^ (msg & sda_mask)) {
+			sda_out(master, msg & sda_mask);
+			last_bit = msg & sda_mask;
+		}
+		clock_toggle(master, 1);
+		msg >>= 1;
+	}
+}
+
+/*
+ * Clock out some 0's after every message to ride out line reflections
+ */
+static void echo_delay(struct fsi_master_gpio *master)
+{
+	set_sda_output(master, 0);
+	clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+/*
+ * Used in bus error cases only.  Clears out any remaining data the slave
+ * is attempting to send
+ */
+static void drain_response(struct fsi_master_gpio *master)
+{
+	struct fsi_gpio_msg msg;
+
+	msg.bits = FSI_GPIO_DRAIN_BITS;
+	serial_in(master, &msg);
+}
+
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}
+
+static int poll_for_response(struct fsi_master_gpio *master, uint8_t expected,
+			uint8_t size, void *data)
+{
+	int busy_count = 0, i;
+	struct fsi_gpio_msg response, cmd;
+	int bits_remaining = 0;
+
+	do {
+		response.msg = 0;
+		response.bits = 1;
+		for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+			serial_in(master, &response);
+			if (response.msg)
+				break;
+		}
+		if (unlikely(i >= FSI_GPIO_MTOE_COUNT)) {
+			dev_info(master->master.dev,
+				"Master time out waiting for response\n");
+			drain_response(master);
+			fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+			return FSI_GPIO_MTOE;
+		}
+
+		/* Response received */
+		response.msg = 0;
+		response.bits = 2;
+		serial_in(master, &response);
+		dev_info(master->master.dev, "cfam id:%d\n", (int)response.msg);
+
+		response.msg = 0;
+		response.bits = 2;
+		serial_in(master, &response);
+		dev_info(master->master.dev, "response id:%d\n",
+							(int)response.msg);
+
+		switch (response.msg) {
+		case FSI_GPIO_RESP_ACK:
+			if (expected == FSI_GPIO_RESP_ACKD)
+				bits_remaining = 8 * sizeof(size);
+			break;
+
+		case FSI_GPIO_RESP_BUSY:
+			/*
+			 * Its necessary to clock slave before issuing
+			 * d-poll, not indicated in the hardware protocol
+			 * spec. < 20 clocks causes slave to hang, 21 ok.
+			 */
+			set_sda_output(master, 0);
+			clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
+			cmd.msg = FSI_GPIO_CMD_DPOLL;
+			cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
+			serial_out(master, &cmd);
+			continue;
+
+		case FSI_GPIO_RESP_ERRA:
+		case FSI_GPIO_RESP_ERRC:
+			dev_info(master->master.dev, "ERR received: %d\n",
+				(int)response.msg);
+			drain_response(master);
+			fsi_master_gpio_error(master, response.msg);
+			return response.msg;
+		}
+
+		/* Read in the data field if applicable */
+		if (bits_remaining) {
+			response.msg = 0;
+			response.bits = bits_remaining;
+			serial_in(master, &response);
+		}
+
+		/* Read in the crc and check it */
+		response.msg = 0;
+		response.bits = FSI_GPIO_CRC_SIZE;
+		serial_in(master, &response);
+
+		/* todo: verify crc is correct, flag error if not */
+
+		return 0;
+
+	} while (busy_count++ < FSI_GPIO_MAX_BUSY);
+
+	return busy_count;
+}
+
+static void build_command(struct fsi_gpio_msg *cmd, uint64_t mode,
+			uint8_t slave, uint32_t addr, size_t size,
+			const void *data)
+{
+	cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
+	cmd->msg = FSI_GPIO_CMD_DEFAULT;
+	cmd->msg |= mode;
+
+	/* todo: handle more than just slave id 0 */
+	cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
+
+	cmd->msg |= (addr << FSI_GPIO_CMD_ADDR_SHIFT);
+	if (size == sizeof(uint8_t)) {
+		if (data)
+			cmd->msg |=
+				*((uint8_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	} else if (size == sizeof(uint16_t)) {
+		cmd->msg |= FSI_GPIO_CMD_SIZE_16;
+		if (data)
+			cmd->msg |=
+				*((uint16_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	} else {
+		cmd->msg |= FSI_GPIO_CMD_SIZE_32;
+		if (data)
+			cmd->msg |=
+				*((uint32_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	}
+	if (mode == FSI_GPIO_CMD_WRITE)
+		cmd->bits += (8 * size);
+
+	add_crc(cmd);
+}
+
 static int fsi_master_gpio_read(struct fsi_master *_master, int link,
 		uint8_t slave, uint32_t addr, void *val, size_t size)
 {
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
+	int rc;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: format read into a message, send, poll for response */
-	(void)master;
+	/* Send the command */
+	build_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size, NULL);
+	serial_out(master, &cmd);
+	echo_delay(master);
 
+	rc = poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
 
-	return 0;
+	return rc;
 }
 
 static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 		uint8_t slave, uint32_t addr, const void *val, size_t size)
 {
+	int rc;
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: format write into a message, send, poll for response */
-	(void)master;
+	/* Send the command */
+	build_command(&cmd, FSI_GPIO_CMD_WRITE, slave, addr, size, val);
+	serial_out(master, &cmd);
+	echo_delay(master);
 
-	return 0;
+	rc = poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
+
+	return rc;
 }
 
 /*
@@ -58,14 +349,40 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 static int fsi_master_gpio_break(struct fsi_master *_master, int link)
 {
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	uint32_t smode;
+	int rc;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: send the break pattern over gpio */
-	(void)master;
+	set_sda_output(master, 0);
+	clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
 
-	return 0;
+	sda_out(master, 1);
+	clock_toggle(master, FSI_BREAK_CLOCKS);
+
+	echo_delay(master);
+
+	sda_out(master, 0);
+	clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+	udelay(200);		/* wait for logic reset to take effect */
+	rc = _master->read(_master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+			&smode, sizeof(smode));
+	dev_info(_master->dev, "smode after break:%08x rc:%d\n", smode, rc);
+
+	return rc;
+}
+
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+	gpiod_direction_output(master->gpio_mux, 1);
+	gpiod_direction_output(master->gpio_clk, 1);
+	set_sda_output(master, 1);
+	gpiod_direction_output(master->gpio_enable, 0);
+
+	/* todo: evaluate if clocks can be reduced */
+	clock_toggle(master, FSI_INIT_CLOCKS);
 }
 
 static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
@@ -75,8 +392,7 @@ static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: set the enable pin */
-	(void)master;
+	gpiod_set_value(master->gpio_enable, 1);
 
 	return 0;
 }
@@ -100,11 +416,28 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 		return PTR_ERR(gpio);
 	master->gpio_data = gpio;
 
+	gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_trans = gpio;
+
+	gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_enable = gpio;
+
+	gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_mux = gpio;
+
 	master->master.read = fsi_master_gpio_read;
 	master->master.write = fsi_master_gpio_write;
 	master->master.send_break = fsi_master_gpio_break;
 	master->master.link_enable = fsi_master_gpio_link_enable;
 
+	fsi_master_gpio_init(master);
+
 	return fsi_master_register(&master->master);
 }
 
Signature · 1 line
1.8.2.2
#22 lore raw

Re: [PATCH linux v3 17/18] drivers/fsi: Add GPIO master functionality

Hi Chris,

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 66

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+static void add_crc(struct fsi_gpio_msg *cmd)
+{
+
+}

I'll send you some code to help with implementing this...

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 70

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+
+static void set_clock(struct fsi_master_gpio *master)
+{
+	/* This delay is required - do not remove */

We probably don't need these comments, as 'do not remove' applies to most of the code here :)

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 116

Quoted 12 lines
drivers/fsi/fsi-master-gpio.c +12 -0
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+	gpiod_direction_input(master->gpio_data);
+	gpiod_direction_output(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master,
+			   int value)
+{
+	gpiod_direction_output(master->gpio_data, value);
+	gpiod_direction_output(master->gpio_trans, 1);
+}

These should be conditional on gpio_trans != NULL.

Quoted 1 line

+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)

Can we have the number of bits to receive a separate parameter? This means we don't need a dual-use *cmd argument, and won't have subtle bugs arise where a caller has not initialised it.

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 131

Quoted 13 lines
drivers/fsi/fsi-master-gpio.c +13 -0
+{
+	uint8_t bit;
+	uint64_t msg = 0;
+	uint8_t in_bit = 0;
+
+	set_sda_input(master);
+
+	for (bit = 0; bit < cmd->bits; bit++) {
+		clock_toggle(master, 1);
+		in_bit = sda_in(master);
+		msg |= in_bit;
+		msg <<= 1;
+	}

I think you want the shift before the |=, otherwise your last bit won't be at the end of msg. More on that below though...

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 135

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+	cmd->msg = ~msg;		/*  Data is negative active */
+}
+
+static void serial_out(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)

Really minor: make *cmd a const struct fsi_gpio_msg *.

Quoted 12 lines
drivers/fsi/fsi-master-gpio.c +12 -0
+{
+	uint8_t bit;
+	uint64_t msg = ~cmd->msg;	/* Data is negative active */
+	uint64_t sda_mask = 1;
+	uint64_t last_bit = ~0;
+
+	set_sda_output(master, 0);
+
+	/* Send the start bit */
+	sda_out(master, 1);
+	clock_toggle(master, 1);
+

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 157

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +10 -0
+	/* Send the message */
+	for (bit = 0; bit < cmd->bits; bit++) {
+		if (last_bit ^ (msg & sda_mask)) {
+			sda_out(master, msg & sda_mask);
+			last_bit = msg & sda_mask;
+		}
+		clock_toggle(master, 1);
+		msg >>= 1;
+	}
+}

These functions seem incompatible.

serial_in clocks the least-significant-bit last, while serial_out clocks the least-significant-bit first.

The comment on struct fsi_message is:

/* Our structure for bit-wise message sending. We left-align this, so the
* last bit sent is at the 0x1 mask position.
*/
struct fsi_gpio_msg {
uint64_t msg;
uint8_t bits;
};

- which (I think) matches the message layout described by the docs. If we use that format, we'll need to clock the LSb *last*. We'll also need to start sending from the appropriate bit in the uint64_t, as the bits in msg are left-aligned (ie, messages shorter than 64 bits do not use the least-significant-bits).

I'm happy if you'd prefer to change that to right-aligned if that makes message construction easier, but we need to be consistent (and update the comment to suit).

Also, you do msg & sda_mask three times in three lines there.

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 187

Quoted 8 lines
drivers/fsi/fsi-master-gpio.c +8 -0
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}

I know this is a todo, but where are you planning to 'store' this information? Shouldn't errors just be returned immediately?

Quoted 108 lines
drivers/fsi/fsi-master-gpio.c +108 -0
+
+static int poll_for_response(struct fsi_master_gpio *master, uint8_t expected,
+			uint8_t size, void *data)
+{
+	int busy_count = 0, i;
+	struct fsi_gpio_msg response, cmd;
+	int bits_remaining = 0;
+
+	do {
+		response.msg = 0;
+		response.bits = 1;
+		for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+			serial_in(master, &response);
+			if (response.msg)
+				break;
+		}
+		if (unlikely(i >= FSI_GPIO_MTOE_COUNT)) {
+			dev_info(master->master.dev,
+				"Master time out waiting for response\n");
+			drain_response(master);
+			fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+			return FSI_GPIO_MTOE;
+		}
+
+		/* Response received */
+		response.msg = 0;
+		response.bits = 2;
+		serial_in(master, &response);
+		dev_info(master->master.dev, "cfam id:%d\n", (int)response.msg);
+
+		response.msg = 0;
+		response.bits = 2;
+		serial_in(master, &response);
+		dev_info(master->master.dev, "response id:%d\n",
+							(int)response.msg);
+
+		switch (response.msg) {
+		case FSI_GPIO_RESP_ACK:
+			if (expected == FSI_GPIO_RESP_ACKD)
+				bits_remaining = 8 * sizeof(size);
+			break;
+
+		case FSI_GPIO_RESP_BUSY:
+			/*
+			 * Its necessary to clock slave before issuing
+			 * d-poll, not indicated in the hardware protocol
+			 * spec. < 20 clocks causes slave to hang, 21 ok.
+			 */
+			set_sda_output(master, 0);
+			clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
+			cmd.msg = FSI_GPIO_CMD_DPOLL;
+			cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
+			serial_out(master, &cmd);
+			continue;
+
+		case FSI_GPIO_RESP_ERRA:
+		case FSI_GPIO_RESP_ERRC:
+			dev_info(master->master.dev, "ERR received: %d\n",
+				(int)response.msg);
+			drain_response(master);
+			fsi_master_gpio_error(master, response.msg);
+			return response.msg;
+		}
+
+		/* Read in the data field if applicable */
+		if (bits_remaining) {
+			response.msg = 0;
+			response.bits = bits_remaining;
+			serial_in(master, &response);
+		}
+
+		/* Read in the crc and check it */
+		response.msg = 0;
+		response.bits = FSI_GPIO_CRC_SIZE;
+		serial_in(master, &response);
+
+		/* todo: verify crc is correct, flag error if not */
+
+		return 0;
+
+	} while (busy_count++ < FSI_GPIO_MAX_BUSY);
+
+	return busy_count;
+}
+
+static void build_command(struct fsi_gpio_msg *cmd, uint64_t mode,
+			uint8_t slave, uint32_t addr, size_t size,
+			const void *data)
+{
+	cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
+	cmd->msg = FSI_GPIO_CMD_DEFAULT;
+	cmd->msg |= mode;
+
+	/* todo: handle more than just slave id 0 */
+	cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
+
+	cmd->msg |= (addr << FSI_GPIO_CMD_ADDR_SHIFT);
+	if (size == sizeof(uint8_t)) {
+		if (data)
+			cmd->msg |=
+				*((uint8_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	} else if (size == sizeof(uint16_t)) {
+		cmd->msg |= FSI_GPIO_CMD_SIZE_16;
+		if (data)
+			cmd->msg |=
+				*((uint16_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	} else {
+		cmd->msg |= FSI_GPIO_CMD_SIZE_32;

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 305

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +10 -0
+		if (data)
+			cmd->msg |=
+				*((uint32_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+	}
+	if (mode == FSI_GPIO_CMD_WRITE)
+		cmd->bits += (8 * size);
+
+	add_crc(cmd);
+}
+

The message construction will be dependent on the format of struct fsi_msg, so this may need to be updated if that changes from the above comments.

Quoted 64 lines

static int fsi_master_gpio_read(struct fsi_master *_master, int link,

drivers/fsi/fsi-master-gpio.c +22 -9
 		uint8_t slave, uint32_t addr, void *val, size_t size)
 {
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
+	int rc;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: format read into a message, send, poll for response */
-	(void)master;
+	/* Send the command */
+	build_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size, NULL);
+	serial_out(master, &cmd);
+	echo_delay(master);
 
+	rc = poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
 
-	return 0;
+	return rc;
 }
 
 static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 		uint8_t slave, uint32_t addr, const void *val, size_t size)
 {
+	int rc;
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: format write into a message, send, poll for response */
-	(void)master;
+	/* Send the command */
+	build_command(&cmd, FSI_GPIO_CMD_WRITE, slave, addr, size, val);
+	serial_out(master, &cmd);
+	echo_delay(master);
 
-	return 0;
+	rc = poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
+
+	return rc;
 }
 
 /*
@@ -58,14 +349,40 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 static int fsi_master_gpio_break(struct fsi_master *_master, int link)
 {
 	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	uint32_t smode;
+	int rc;
 
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: send the break pattern over gpio */
-	(void)master;
+	set_sda_output(master, 0);
+	clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
 
-	return 0;
+	sda_out(master, 1);

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_write(struct fsi_master *_master, int link, · line 371

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +10 -0
+	clock_toggle(master, FSI_BREAK_CLOCKS);
+
+	echo_delay(master);
+
+	sda_out(master, 0);
+	clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+	udelay(200);		/* wait for logic reset to take effect */
+	rc = _master->read(_master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+			&smode, sizeof(smode));

No need to use the callback, we know that this is a GPIO master.

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_write(struct fsi_master *_master, int link, · line 372

Quoted 1 line
drivers/fsi/fsi-master-gpio.c +1 -0
+	dev_info(_master->dev, "smode after break:%08x rc:%d\n", smode, rc);

Use dev_dbg() here. We don't want printk output for normal operations for non-debug.

Quoted 35 lines
drivers/fsi/fsi-master-gpio.c +19 -2
+
+	return rc;
+}
+
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+	gpiod_direction_output(master->gpio_mux, 1);
+	gpiod_direction_output(master->gpio_clk, 1);
+	set_sda_output(master, 1);
+	gpiod_direction_output(master->gpio_enable, 0);
+
+	/* todo: evaluate if clocks can be reduced */
+	clock_toggle(master, FSI_INIT_CLOCKS);
 }
 
 static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
@@ -75,8 +392,7 @@ static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
 	if (link != 0)
 		return -ENODEV;
 
-	/* todo: set the enable pin */
-	(void)master;
+	gpiod_set_value(master->gpio_enable, 1);
 
 	return 0;
 }
@@ -100,11 +416,28 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 		return PTR_ERR(gpio);
 	master->gpio_data = gpio;
 
+	gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_trans = gpio;
+

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_probe(struct platform_device *pdev) · line 433

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +10 -0
+	gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_enable = gpio;
+
+	gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+	if (IS_ERR(gpio))
+		return PTR_ERR(gpio);
+	master->gpio_mux = gpio;
+

trans, enable and mux should be optional, right?

Cheers,

Jeremy

#23 lore raw

Re: [PATCH linux v3 17/18] drivers/fsi: Add GPIO master functionality

Quoted 9 lines · Jeremy Kerr

Hi Chris,

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 66

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+static void add_crc(struct fsi_gpio_msg *cmd)
+{
+
+}

I'll send you some code to help with implementing this...

Quoted 8 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 70

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+
+static void set_clock(struct fsi_master_gpio *master)
+{
+     /* This delay is required - do not remove */

We probably don't need these comments, as 'do not remove' applies to most of the code here :)

OK, will remove those comments.

Quoted 6 lines
Quoted 6 lines
drivers/fsi/fsi-master-gpio.c +6 -0
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+     gpiod_direction_input(master->gpio_data);
+     gpiod_direction_output(master->gpio_trans, 0);
+}
+
Quoted 9 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 116

Quoted 6 lines
drivers/fsi/fsi-master-gpio.c +6 -0
+static void set_sda_output(struct fsi_master_gpio *master,
+                        int value)
+{
+     gpiod_direction_output(master->gpio_data, value);
+     gpiod_direction_output(master->gpio_trans, 1);
+}

These should be conditional on gpio_trans != NULL.

Will fix.

Quoted 6 lines
Quoted 1 line

+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)

Can we have the number of bits to receive a separate parameter? This means we don't need a dual-use *cmd argument, and won't have subtle bugs arise where a caller has not initialised it.

Would you suggest having a separate buffer passed in as well to store the serialized in data or use the struct fsi_gpio_msg msg field?

Quoted 8 lines
Quoted 8 lines
drivers/fsi/fsi-master-gpio.c +8 -0
+{
+     uint8_t bit;
+     uint64_t msg = 0;
+     uint8_t in_bit = 0;
+
+     set_sda_input(master);
+
+     for (bit = 0; bit < cmd->bits; bit++) {
Quoted 9 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 131

Quoted 5 lines
drivers/fsi/fsi-master-gpio.c +5 -0
+             clock_toggle(master, 1);
+             in_bit = sda_in(master);
+             msg |= in_bit;
+             msg <<= 1;
+     }

I think you want the shift before the |=, otherwise your last bit won't be at the end of msg. More on that below though...

Ah right, ok will fix.

Quoted 7 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 135

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+     cmd->msg = ~msg;                /*  Data is negative active */
+}
+
+static void serial_out(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)

Really minor: make *cmd a const struct fsi_gpio_msg *.

Will fix.

Quoted 44 lines
Quoted 12 lines
drivers/fsi/fsi-master-gpio.c +12 -0
+{
+     uint8_t bit;
+     uint64_t msg = ~cmd->msg;       /* Data is negative active */
+     uint64_t sda_mask = 1;
+     uint64_t last_bit = ~0;
+
+     set_sda_output(master, 0);
+
+     /* Send the start bit */
+     sda_out(master, 1);
+     clock_toggle(master, 1);
+

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 157

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +10 -0
+     /* Send the message */
+     for (bit = 0; bit < cmd->bits; bit++) {
+             if (last_bit ^ (msg & sda_mask)) {
+                     sda_out(master, msg & sda_mask);
+                     last_bit = msg & sda_mask;
+             }
+             clock_toggle(master, 1);
+             msg >>= 1;
+     }
+}

These functions seem incompatible.

serial_in clocks the least-significant-bit last, while serial_out clocks the least-significant-bit first.

The comment on struct fsi_message is:

/* Our structure for bit-wise message sending. We left-align this, so the
* last bit sent is at the 0x1 mask position.
*/

  struct fsi_gpio_msg {
        uint64_t        msg;
        uint8_t         bits;

};

- which (I think) matches the message layout described by the docs. If we use that format, we'll need to clock the LSb *last*. We'll also need to start sending from the appropriate bit in the uint64_t, as the bits in msg are left-aligned (ie, messages shorter than 64 bits do not use the least-significant-bits).

Quoted 4 lines

I'm happy if you'd prefer to change that to right-aligned if that makes message construction easier, but we need to be consistent (and update the comment to suit).

Right, my serial out was mirror imaged. Will fix. I modified my existing prototype serial_out code for this patch and introduced this bug in the process. No, I agree we should keep it all left aligned. I'll keep in mind we need to start on the appropriate bit in the uint64_t.

Quoted 2 lines

Also, you do msg & sda_mask three times in three lines there.

Will fix.

Quoted 11 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 187

Quoted 8 lines
drivers/fsi/fsi-master-gpio.c +8 -0
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}

I know this is a todo, but where are you planning to 'store' this information? Shouldn't errors just be returned immediately?

This is intended to be the entry point for the bus error handling code. I could return the failing rc to the caller right there but ultimately the error handler needs to be invoked somewhere in the call chain anyway. As it is here the error condition is cleaned up as early as possible.

Even if the error is of a master type - such as master
time out errors (MTOE) there is still a possibility that the connected slave is in some latched failed state which requires recovery to resume normal bus communications (issue a terminate command to slave, etc...)

Quoted 115 lines
Quoted 115 lines
drivers/fsi/fsi-master-gpio.c +114 -0
+
+static int poll_for_response(struct fsi_master_gpio *master, uint8_t expected,
+                     uint8_t size, void *data)
+{
+     int busy_count = 0, i;
+     struct fsi_gpio_msg response, cmd;
+     int bits_remaining = 0;
+
+     do {
+             response.msg = 0;
+             response.bits = 1;
+             for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+                     serial_in(master, &response);
+                     if (response.msg)
+                             break;
+             }
+             if (unlikely(i >= FSI_GPIO_MTOE_COUNT)) {
+                     dev_info(master->master.dev,
+                             "Master time out waiting for response\n");
+                     drain_response(master);
+                     fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+                     return FSI_GPIO_MTOE;
+             }
+
+             /* Response received */
+             response.msg = 0;
+             response.bits = 2;
+             serial_in(master, &response);
+             dev_info(master->master.dev, "cfam id:%d\n", (int)response.msg);
+
+             response.msg = 0;
+             response.bits = 2;
+             serial_in(master, &response);
+             dev_info(master->master.dev, "response id:%d\n",
+                                                     (int)response.msg);
+
+             switch (response.msg) {
+             case FSI_GPIO_RESP_ACK:
+                     if (expected == FSI_GPIO_RESP_ACKD)
+                             bits_remaining = 8 * sizeof(size);
+                     break;
+
+             case FSI_GPIO_RESP_BUSY:
+                     /*
+                      * Its necessary to clock slave before issuing
+                      * d-poll, not indicated in the hardware protocol
+                      * spec. < 20 clocks causes slave to hang, 21 ok.
+                      */
+                     set_sda_output(master, 0);
+                     clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
+                     cmd.msg = FSI_GPIO_CMD_DPOLL;
+                     cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
+                     serial_out(master, &cmd);
+                     continue;
+
+             case FSI_GPIO_RESP_ERRA:
+             case FSI_GPIO_RESP_ERRC:
+                     dev_info(master->master.dev, "ERR received: %d\n",
+                             (int)response.msg);
+                     drain_response(master);
+                     fsi_master_gpio_error(master, response.msg);
+                     return response.msg;
+             }
+
+             /* Read in the data field if applicable */
+             if (bits_remaining) {
+                     response.msg = 0;
+                     response.bits = bits_remaining;
+                     serial_in(master, &response);
+             }
+
+             /* Read in the crc and check it */
+             response.msg = 0;
+             response.bits = FSI_GPIO_CRC_SIZE;
+             serial_in(master, &response);
+
+             /* todo: verify crc is correct, flag error if not */
+
+             return 0;
+
+     } while (busy_count++ < FSI_GPIO_MAX_BUSY);
+
+     return busy_count;
+}
+
+static void build_command(struct fsi_gpio_msg *cmd, uint64_t mode,
+                     uint8_t slave, uint32_t addr, size_t size,
+                     const void *data)
+{
+     cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
+     cmd->msg = FSI_GPIO_CMD_DEFAULT;
+     cmd->msg |= mode;
+
+     /* todo: handle more than just slave id 0 */
+     cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
+
+     cmd->msg |= (addr << FSI_GPIO_CMD_ADDR_SHIFT);
+     if (size == sizeof(uint8_t)) {
+             if (data)
+                     cmd->msg |=
+                             *((uint8_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+     } else if (size == sizeof(uint16_t)) {
+             cmd->msg |= FSI_GPIO_CMD_SIZE_16;
+             if (data)
+                     cmd->msg |=
+                             *((uint16_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+     } else {
+             cmd->msg |= FSI_GPIO_CMD_SIZE_32;
+             if (data)
+                     cmd->msg |=
+                             *((uint32_t *)data) >> FSI_GPIO_CMD_DATA_SHIFT;
+     }
+     if (mode == FSI_GPIO_CMD_WRITE)
+             cmd->bits += (8 * size);
Quoted 9 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 305

Quoted 4 lines
drivers/fsi/fsi-master-gpio.c +4 -0
+
+     add_crc(cmd);
+}
+

The message construction will be dependent on the format of struct fsi_msg, so this may need to be updated if that changes from the above comments.

OK will keep that in mind. Would like to hear your thoughts on my above questions before I make any changes here.

Quoted 62 lines
Quoted 62 lines

static int fsi_master_gpio_read(struct fsi_master *_master, int link,
uint8_t slave, uint32_t addr, void *val, size_t size)

 {
      struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
drivers/fsi/fsi-master-gpio.c +21 -8
+     struct fsi_gpio_msg cmd;
+     int rc;
 
      if (link != 0)
              return -ENODEV;
 
-     /* todo: format read into a message, send, poll for response */
-     (void)master;
+     /* Send the command */
+     build_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size, NULL);
+     serial_out(master, &cmd);
+     echo_delay(master);
 
+     rc = poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
 
-     return 0;
+     return rc;
 }
 
 static int fsi_master_gpio_write(struct fsi_master *_master, int link,
              uint8_t slave, uint32_t addr, const void *val, size_t size)
 {
+     int rc;
      struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+     struct fsi_gpio_msg cmd;
 
      if (link != 0)
              return -ENODEV;
 
-     /* todo: format write into a message, send, poll for response */
-     (void)master;
+     /* Send the command */
+     build_command(&cmd, FSI_GPIO_CMD_WRITE, slave, addr, size, val);
+     serial_out(master, &cmd);
+     echo_delay(master);
 
-     return 0;
+     rc = poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
+
+     return rc;
 }
 
 /*
@@ -58,14 +349,40 @@ static int fsi_master_gpio_write(struct fsi_master *_master, int link,
 static int fsi_master_gpio_break(struct fsi_master *_master, int link)
 {
      struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+     uint32_t smode;
+     int rc;
 
      if (link != 0)
              return -ENODEV;
 
-     /* todo: send the break pattern over gpio */
-     (void)master;
+     set_sda_output(master, 0);
+     clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
 
Quoted 15 lines

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_write(struct fsi_master *_master, int link, · line 371

Quoted 12 lines
drivers/fsi/fsi-master-gpio.c +11 -1
-     return 0;
+     sda_out(master, 1);
+     clock_toggle(master, FSI_BREAK_CLOCKS);
+
+     echo_delay(master);
+
+     sda_out(master, 0);
+     clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+     udelay(200);            /* wait for logic reset to take effect */
+     rc = _master->read(_master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+                     &smode, sizeof(smode));

No need to use the callback, we know that this is a GPIO master.

OK will fix.

Quoted 5 lines

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_write(struct fsi_master *_master, int link, · line 372

Quoted 1 line
drivers/fsi/fsi-master-gpio.c +1 -0
+     dev_info(_master->dev, "smode after break:%08x rc:%d\n", smode, rc);

Use dev_dbg() here. We don't want printk output for normal operations for non-debug.

Will change.

Quoted 39 lines
Quoted 39 lines
drivers/fsi/fsi-master-gpio.c +23 -2
+
+     return rc;
+}
+
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+     gpiod_direction_output(master->gpio_mux, 1);
+     gpiod_direction_output(master->gpio_clk, 1);
+     set_sda_output(master, 1);
+     gpiod_direction_output(master->gpio_enable, 0);
+
+     /* todo: evaluate if clocks can be reduced */
+     clock_toggle(master, FSI_INIT_CLOCKS);
 }
 
 static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
@@ -75,8 +392,7 @@ static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
      if (link != 0)
              return -ENODEV;
 
-     /* todo: set the enable pin */
-     (void)master;
+     gpiod_set_value(master->gpio_enable, 1);
 
      return 0;
 }
@@ -100,11 +416,28 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
              return PTR_ERR(gpio);
      master->gpio_data = gpio;
 
+     gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_trans = gpio;
+
+     gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_enable = gpio;
Quoted 9 lines

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_probe(struct platform_device *pdev) · line 433

Quoted 6 lines
drivers/fsi/fsi-master-gpio.c +6 -0
+
+     gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_mux = gpio;
+

trans, enable and mux should be optional, right?

Right, I had sent a question via slack to you regarding how to code this up. Should the mandatory and optional specifics be placed in the dev tree code in arch/arm/boot/*dts ? Also any basic examples you could point me to would be appreciated. Which of the platforms would you recommend I adjust this for? I suppose just Witherspoon for now.

Thanks for your feedback.
Chris

Quoted 4 lines

Cheers,

Jeremy

#24 lore raw

Re: [PATCH linux v3 17/18] drivers/fsi: Add GPIO master functionality

Hi Chris,

Quoted 9 lines
Quoted 6 lines
Quoted 1 line

+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)

Can we have the number of bits to receive a separate parameter? This means we don't need a dual-use *cmd argument, and won't have subtle bugs arise where a caller has not initialised it.

Would you suggest having a separate buffer passed in as well to store the serialized in data or use the struct fsi_gpio_msg msg field?

Yeah, store the data in the existing struct fsi_gpio_msg (otherwise there'd be no need for that struct). Something like:

static void serial_in(struct fsi_master_gpio *master, int bits,
struct fsi_gpio_msg *msg)

where, on return of this function, msg->bits == bits, and msd->data is the clocked-in data.

Quoted 18 lines
Quoted 11 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 187

Quoted 8 lines
drivers/fsi/fsi-master-gpio.c +8 -0
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}

I know this is a todo, but where are you planning to 'store' this information? Shouldn't errors just be returned immediately?

This is intended to be the entry point for the bus error handling code. I could return the failing rc to the caller right there but ultimately the error handler needs to be invoked somewhere in the call chain anyway. As it is here the error condition is cleaned up as early as possible.

Quoted 5 lines

Even if the error is of a master type - such as master
time out errors (MTOE) there is still a possibility that the connected slave is in some latched failed state which requires recovery to resume normal bus communications (issue a terminate command to slave, etc...)

OK, makes sense. Would this logic be repeated for all masters? If so, we may want the core to manage common error recovery that involves the slaves.

Quoted 23 lines
Quoted 13 lines
Quoted 13 lines
drivers/fsi/fsi-master-gpio.c +9 -0
@@ -100,11 +416,28 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
              return PTR_ERR(gpio);
      master->gpio_data = gpio;
 
+     gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_trans = gpio;
+
+     gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_enable = gpio;
Quoted 9 lines

drivers/fsi/fsi-master-gpio.c static int fsi_master_gpio_probe(struct platform_device *pdev) · line 433

Quoted 6 lines
drivers/fsi/fsi-master-gpio.c +6 -0
+
+     gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+     if (IS_ERR(gpio))
+             return PTR_ERR(gpio);
+     master->gpio_mux = gpio;
+

trans, enable and mux should be optional, right?

Quoted 1 line

Right, I had sent a question via slack to you regarding how to code this up.

I'd suggest:

	gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
	if (!IS_ERR(gpio))
		master->gpio_trans = gpio;

Then, since these are optional, we would make any interactions on this gpio conditional on

if (master->gpio_trans) {

Quoted 2 lines

Should the mandatory and optional specifics be placed in the dev tree code in arch/arm/boot/*dts ?

They'd be described in the device tree binding spec, in Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt . That only contains an example at the moment, we would need to expand on that before submission. Check out the i2c-gpio spec for something similar, at Documentation/devicetree/bindings/i2c/i2c-gpio.txt . Something like:

Device-tree bindings for gpio-based FSI master driver
-----------------------------------------------------

Required properties:
- compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
- clk-gpio: GPIO descriptor for FSI clock
- data-gpio: GPIO descriptor for FSI data

Optional properties:
- enable-gpio: GPIO descriptor for enable line
- trans-gpio: ...
- mux-gpio: ...

  fsi-master {
  	compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
  	clk-gpio = <&gpio 0>;
  	data-gpio = <&gpio 1>;
  }

Again though, we'd need to review which of those really make sense to use in the gpio node, and which are just gpios that need to be set in a static configuration for specific hardware.

Quoted 3 lines

Also any basic examples you could point me to
would be appreciated. Which of the platforms would you recommend I adjust this for? I suppose just Witherspoon for now.

Whatever platforms have this capability (and would be useful to enable it). But yes, I'd suggest enabling it on whatever you're testing with.

Cheers,

Jeremy

#25 lore raw

Re: [PATCH linux v3 17/18] drivers/fsi: Add GPIO master functionality

Quoted 28 lines · Jeremy Kerr

Hi Chris,

Quoted 20 lines
Quoted 13 lines

drivers/fsi/fsi-master-gpio.c struct fsi_gpio_msg { · line 187

Quoted 10 lines
drivers/fsi/fsi-master-gpio.c +9 -0
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd)
 
+/*
+ * Store information on master errors so handler can detect and clean
+ * up the bus
+ */
+static void fsi_master_gpio_error(struct fsi_master_gpio *master, int error)
+{
+
+}

I know this is a todo, but where are you planning to 'store' this information? Shouldn't errors just be returned immediately?

This is intended to be the entry point for the bus error handling code. I could return the failing rc to the caller right there but ultimately the error handler needs to be invoked somewhere in the call chain anyway. As it is here the error condition is cleaned up as early as possible.

Quoted 5 lines

Even if the error is of a master type - such as master
time out errors (MTOE) there is still a possibility that the connected slave is in some latched failed state which requires recovery to resume normal bus communications (issue a terminate command to slave, etc...)

Quoted 3 lines

OK, makes sense. Would this logic be repeated for all masters? If so, we may want the core to manage common error recovery that involves the slaves.

Hi Jeremy,

There would be some fsi-master-gpio specifics here to deal with but there is also a portion of error handling that is standard to all masters so that would be spelled out in fsi-core.c. For now, I'm leaving bus error handling out of the first set.

Thanks
Chris

#26 lore raw

PATCH v3 18/18 drivers/fsi: Add SCOM FSI client device driver

In reply to [email protected]

From: Chris Bostic <[email protected]>

Create a bare bones FSI client device driver that registers with the FSI core.

---
 drivers/fsi/Kconfig    |  7 ++++++
 drivers/fsi/Makefile   |  1 +
 drivers/fsi/fsi-core.c | 21 +++++++++++++++++
 drivers/fsi/fsi-scom.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++
 include/linux/fsi.h    |  3 +++
 5 files changed, 94 insertions(+)
 create mode 100644 drivers/fsi/fsi-scom.c
drivers/fsi/Kconfig +7 -0
index 69e7ee8..35a8e12 100644
@@ -24,6 +24,13 @@ config FSI_MASTER_GPIO
 	select GPIO_DEVRES
 	---help---
 	This option enables a FSI master driver, using GPIO lines directly.
+
+config FSI_SCOM
+	tristate "SCOM FSI client"
+	depends on FSI
+	---help---
+	This option enables the SCOM FSI client device driver.
+
 endif
 
 endmenu
drivers/fsi/Makefile +1 -0
index 2021ce5..3e31d9a 100644
@@ -2,3 +2,4 @@
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
drivers/fsi/fsi-core.c +21 -0
index c5e7441..15d8635 100644
@@ -391,6 +391,27 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
 	return 0;
 }
 
+int fsidrv_register(struct fsi_driver *fsi_drv)
+{
+	int rc = 0;
+
+	if (!fsi_drv)
+		return -EINVAL;
+	if (!fsi_drv->id_table)
+		return -EINVAL;
+
+	rc = driver_register(&fsi_drv->drv);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsidrv_register);
+
+void fsidrv_unregister(struct fsi_driver *fsi_drv)
+{
+	driver_unregister(&fsi_drv->drv);
+}
+EXPORT_SYMBOL_GPL(fsidrv_unregister);
+
 struct bus_type fsi_bus_type = {
 	.name		= "fsi",
 	.match		= fsi_bus_match,
drivers/fsi/fsi-scom.c new file +62 -0

62 lines not on this page · raw message

include/linux/fsi.h +3 -0
index 47af0af..09670b7 100644
@@ -55,6 +55,9 @@ struct fsi_driver {
 #define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
 #define to_fsi_drv(drvp) container_of(drvp, struct fsi_driver, drv)
 
+extern int fsidrv_register(struct fsi_driver *);
+extern void fsidrv_unregister(struct fsi_driver *);
+
 extern struct bus_type fsi_bus_type;
 
 #endif /* LINUX_FSI_H */
Signature · 1 line
1.8.2.2
#27 lore raw

Re: [PATCH linux v3 18/18] drivers/fsi: Add SCOM FSI client device driver

Hi Chris,

Quoted 2 lines

Create a bare bones FSI client device driver that registers with the FSI core.

Cool!

Quoted 28 lines
drivers/fsi/Makefile +1 -0
index 2021ce5..3e31d9a 100644
@@ -2,3 +2,4 @@
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
drivers/fsi/fsi-core.c +11 -0
index c5e7441..15d8635 100644
@@ -391,6 +391,27 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
 	return 0;
 }
 
+int fsidrv_register(struct fsi_driver *fsi_drv)
+{
+	int rc = 0;
+
+	if (!fsi_drv)
+		return -EINVAL;
+	if (!fsi_drv->id_table)
+		return -EINVAL;
+
+	rc = driver_register(&fsi_drv->drv);
+

drivers/fsi/fsi-core.c static int fsi_bus_match(struct device *dev, struct device_driver *drv) · line 414

Quoted 10 lines
drivers/fsi/fsi-core.c +10 -0
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsidrv_register);
+
+void fsidrv_unregister(struct fsi_driver *fsi_drv)
+{
+	driver_unregister(&fsi_drv->drv);
+}
+EXPORT_SYMBOL_GPL(fsidrv_unregister);
+

These aren't part of the SCOM engine device driver, they're changes to the core. Can you do them as a separate patch?

Quoted 29 lines
drivers/fsi/fsi-core.c +0 -0
 	.name		= "fsi",
 	.match		= fsi_bus_match,
 
drivers/fsi/fsi-scom.c new file +20 -0
new file mode 100644
index 0000000..1600f3b
@@ -0,0 +1,62 @@
+/*
+ * SCOM FSI Client device driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+
+#define FSI_ENGID_SCOM		5
+

drivers/fsi/fsi-scom.c line 30

Quoted 10 lines
drivers/fsi/fsi-scom.c new file +10 -0
+static int scom_probe(struct device *);
+
+struct fsi_device_id scom_ids[] = {
+	{
+		.engine_type = FSI_ENGID_SCOM,
+		.version = FSI_VERSION_ANY,
+	},
+	{
+		.engine_type = 0,
+	}

You can just use { 0 } for the end sentinel:

  struct fsi_device_id scom_ids[] = {
  	{
  		.engine_type = FSI_ENGID_SCOM,
  		.version = FSI_VERSION_ANY,
  	},
  	{ 0 }
  }

- which makes it a *tiny* bit more obvious that you're only matching one fsi_device_id.

drivers/fsi/fsi-scom.c line 54

Quoted 8 lines
drivers/fsi/fsi-scom.c new file +8 -0
+static int scom_init(void)
+{
+	int rc;
+
+	rc = fsidrv_register(&scom_drv);
+
+	return rc;
+}

You have this pattern a few times in your series. It's pretty minor, but a little neater if you just return the value directly in cases like that. For example:

  static int scom_init(void)
  {
  	return fsidrv_register(&scom_drv);
  }

Cheers,

Jeremy

#28 lore raw

Re: [PATCH linux v3 18/18] drivers/fsi: Add SCOM FSI client device driver

Quoted 40 lines · Jeremy Kerr

Hi Chris,

Quoted 2 lines

Create a bare bones FSI client device driver that registers with the FSI core.

Cool!

Quoted 33 lines
drivers/fsi/Makefile +1 -0
index 2021ce5..3e31d9a 100644
@@ -2,3 +2,4 @@
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
drivers/fsi/fsi-core.c +16 -0
index c5e7441..15d8635 100644
@@ -391,6 +391,27 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
      return 0;
 }
 
+int fsidrv_register(struct fsi_driver *fsi_drv)
+{
+     int rc = 0;
+
+     if (!fsi_drv)
+             return -EINVAL;
+     if (!fsi_drv->id_table)
+             return -EINVAL;
+
+     rc = driver_register(&fsi_drv->drv);
+
+     return rc;
+}
+EXPORT_SYMBOL_GPL(fsidrv_register);
+
+void fsidrv_unregister(struct fsi_driver *fsi_drv)
Quoted 9 lines

drivers/fsi/fsi-core.c static int fsi_bus_match(struct device *dev, struct device_driver *drv) · line 414

Quoted 5 lines
drivers/fsi/fsi-core.c +5 -0
+{
+     driver_unregister(&fsi_drv->drv);
+}
+EXPORT_SYMBOL_GPL(fsidrv_unregister);
+

These aren't part of the SCOM engine device driver, they're changes to the core. Can you do them as a separate patch?

OK, will break these up into separate patches.

Quoted 51 lines
Quoted 30 lines
drivers/fsi/fsi-core.c +0 -0
      .name           = "fsi",
      .match          = fsi_bus_match,
 
drivers/fsi/fsi-scom.c new file +20 -0
new file mode 100644
index 0000000..1600f3b
@@ -0,0 +1,62 @@
+/*
+ * SCOM FSI Client device driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+
+#define FSI_ENGID_SCOM               5
+

drivers/fsi/fsi-scom.c line 30

Quoted 10 lines
drivers/fsi/fsi-scom.c new file +10 -0
+static int scom_probe(struct device *);
+
+struct fsi_device_id scom_ids[] = {
+     {
+             .engine_type = FSI_ENGID_SCOM,
+             .version = FSI_VERSION_ANY,
+     },
+     {
+             .engine_type = 0,
+     }

You can just use { 0 } for the end sentinel:

  struct fsi_device_id scom_ids[] = {
        {
                .engine_type = FSI_ENGID_SCOM,
                .version = FSI_VERSION_ANY,
        },
        { 0 }
  }
Quoted 3 lines

- which makes it a *tiny* bit more obvious that you're only matching one fsi_device_id.

Will change.

Quoted 13 lines

drivers/fsi/fsi-scom.c line 54

Quoted 8 lines
drivers/fsi/fsi-scom.c new file +8 -0
+static int scom_init(void)
+{
+     int rc;
+
+     rc = fsidrv_register(&scom_drv);
+
+     return rc;
+}

You have this pattern a few times in your series. It's pretty minor, but a little neater if you just return the value directly in cases like that. For example:

Quoted 4 lines
  static int scom_init(void)
  {
        return fsidrv_register(&scom_drv);
  }

Ok will revise that.

Thanks,
Chris

Quoted 5 lines

Cheers,

Jeremy