linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v8 00/24] FSI device driver implementation
@ 2017-06-06 21:08 Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 01/24] drivers/fsi: Add fsi master definition Christopher Bostic
                   ` (23 more replies)
  0 siblings, 24 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Implementation 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.

In Linux, we have a core FSI "bus type", along with drivers for FSI
masters and engines.

The FSI master drivers expose a read/write interface to the bus address
space. The master drivers are under drivers/fsi/fsi-master-*.c.

The core handles probing and discovery of slaves and slave
engines, using those read/write interfaces. It is responsible for
creating the endpoint Linux devices corresponding to the discovered
engines on each slave.

Slave engines are identified by an 'engine' type, and an optional
version. Engine, a.k.a. client, drivers are matched and bound to these
engines during discovery.

This patch set does not include extended FSI function such as:
    *  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 Engine driver
    A device driver that registers with the FSI core so that it can access
    devices it owns on an FSI bus.

* Hub
    An FSI master that connects to an upstream 'primary' master allowing
    high fanout of target devices.

----
Changes in v8:
   - Export symbols fsi_slave_read, fsi_slave_write,
     fsi_slave_claim_range, fsi_release_range so that hub and gpio
     modules can use
   - Type cast to (int) to clean up build warnings in
     include/trace/events/fsi.h
   - Add MODULE_LICENSE() to fsi-core.c

Changes in v7:
   - Use GENMASK for bitmask definitions
   - Move instances of dev_info to dev_dbg
   - Add missing return code checks
   - Add API details for fsi_device_read/write/peek()
   - GPIO master: Unscan when unregistering master
   - Document ABI sysfs files raw, term, break, rescan
   - GPIO master: move global spinlock into struct fsi_master_gpio
   - GPIO master: remove redundant checks for valid pin descriptors

Changes in v6:
   - Remove redundant 'depends on' specifiers in Kconfig for SCOM
     GPIO master and Hub masters.

Changes in v5:
   - Remove explicit kfree of struct fsi_master in fsi_master_gpio.
   - Remove Palmetto and Romulus dts device tree file udates for FSI gpio
     master

Changes in v4:
   - endianness: the _read() and _write() APIs are now all *bus endian*,
     so will be the same on all platforms (the previous fsi patches
     exposed as (BMC/FSP) CPU endian, which is variable).
   - device tree: Remove the "ibm," prefix for the fsi core and GPIO
     master compatibility strings, as they're not describing
     IBM-specific
   - device model: Create separate struct devices for each FSI
     master, which fits better with the Linux device model, and allows
     addition of sysfs attributes that are implemented by the fsi core
   - sysfs: there are now sysfs facilities for break and term. Raw
     file supports reads and writes of arbitrary sizes.
   - GPIO master: split the xfer() logic out a little, so that the
     response handling & DPOLL retry mechanism is more obvious
   - GPIO master: simplifications for message construction
   - GPIO master: fixes for some CRC calculations
   - GPIO master: issue TERM in response to DPOLL busy-loops
   - Error handling: rather than handle errors on (potentially) an entire
     cascaded read or write, the error handling is now down on a per-slave
     basis, where we try to reestablish communication in a more "gradual"
     manner, rather than sending a break immediately. May need to add a
     hook to percolate error recovery up to a slave's master but no need
     seen for that at present.
   - Hub master: this is now implemented as a fsi engine driver, as the
     fsi_slave_{read,write}() functions are exported (and the port count
     is available in the hMFSI configuration register)
     This means we need fewer special-cases in the fsi core.
   - Tracepoints: Add tracepoints for FSI core read & write, and another
     set for low-level GPIO in/out operations.

Changes in v3:
    - Patch set contained an invalid 18/18 test patch not
      meant for community review, corrected.

Changes in v2:
   - Change from atomic global for master number to ida simple
      interface.
    - Add valid pointer checks on register and unregister utils.
    - Move CRC calculation utilities out of driver to lib path.
    - Clean up white space issues.
    - Remove added list management of master devices and use
      instead the device_for_each_child method available in the
      bus.
    - Add new patch to document FSI bus functionality.
    - Add new patch documenting FSI gpio master.
    - Rearrage patch set to have documentation earlier than code
      implementing it.
    - Document all compatible strings used in device tree bindings.
    - Elaborate documentation definition of FSI GPIO master.
    - Describe in more detail what each GPIO FSI master pin is for.
    - Re-order compatible strings in example binding so that most
      specific device comes first.
    - Indicate proper activation order of all FSI GPIO master pins.
    - Fix an unmatched '>' bracket in the example for binding.
    - Bracket each element of the example bindings individually.
    - Add new patch documenting sysfs-bus-fsi attributes.
    - Merge FSI GPIO master init into probe function.
    - Set pin initial values at time of pin request.
    - Assign value of master->master.dev at probe time.
    - Use get_optional interface for all optional GPIO pins.

Christopher Bostic (10):
  drivers/fsi: Set up links for slave communication
  drivers/fsi: Set slave SMODE to init communication
  drivers/fsi: Add master unscan
  drivers/fsi: Add documentation for GPIO bindings
  drivers/fsi: Add client driver register utilities
  drivers/fsi: Document FSI master sysfs files in ABI
  drivers/fsi: Add GPIO based FSI master
  drivers/fsi: Add SCOM FSI client device driver
  drivers/fsi: Add hub master support
  drivers/fsi: Add module license to core driver

Jeremy Kerr (14):
  drivers/fsi: Add fsi master definition
  drivers/fsi: Add slave definition
  drivers/fsi: Add empty master scan
  lib: Add crc4 module
  drivers/fsi: Add slave & master read/write APIs
  drivers/fsi: Implement slave initialisation
  drivers/fsi: scan slaves & register devices
  drivers/fsi: Add device read/write/peek API
  drivers/fsi: Add sysfs files for FSI master & slave accesses
  drivers/fsi: expose direct-access slave API
  drivers/fsi: Add tracepoints for low-level operations
  drivers/fsi: Add error handling for slave
  drivers/fsi/gpio: Add tracepoints for GPIO master
  drivers/fsi: Use asynchronous slave mode

 Documentation/ABI/testing/sysfs-bus-fsi            |  38 +
 .../devicetree/bindings/fsi/fsi-master-gpio.txt    |  24 +
 drivers/fsi/Kconfig                                |  26 +
 drivers/fsi/Makefile                               |   3 +
 drivers/fsi/fsi-core.c                             | 841 +++++++++++++++++++++
 drivers/fsi/fsi-master-gpio.c                      | 604 +++++++++++++++
 drivers/fsi/fsi-master-hub.c                       | 327 ++++++++
 drivers/fsi/fsi-master.h                           |  43 ++
 drivers/fsi/fsi-scom.c                             | 263 +++++++
 include/linux/crc4.h                               |   8 +
 include/linux/fsi.h                                |  35 +-
 include/trace/events/fsi.h                         | 127 ++++
 include/trace/events/fsi_master_gpio.h             |  68 ++
 lib/Kconfig                                        |   8 +
 lib/Makefile                                       |   1 +
 lib/crc4.c                                         |  46 ++
 16 files changed, 2461 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-fsi
 create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
 create mode 100644 drivers/fsi/fsi-master-gpio.c
 create mode 100644 drivers/fsi/fsi-master-hub.c
 create mode 100644 drivers/fsi/fsi-master.h
 create mode 100644 drivers/fsi/fsi-scom.c
 create mode 100644 include/linux/crc4.h
 create mode 100644 include/trace/events/fsi.h
 create mode 100644 include/trace/events/fsi_master_gpio.h
 create mode 100644 lib/crc4.c

-- 
1.8.2.2

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH v8 01/24] drivers/fsi: Add fsi master definition
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 02/24] drivers/fsi: Add slave definition Christopher Bostic
                   ` (22 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh, Chris Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Add a `struct fsi_master` to represent a FSI master controller.

FSI master drivers register one of these structs to provide
device-specific of the standard operations: read/write/term/break and
link control.

Includes changes from Edward A. James <eajames@us.ibm.com> & Jeremy Kerr
<jk@ozlabs.org>.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c   | 35 +++++++++++++++++++++++++++++++++++
 drivers/fsi/fsi-master.h | 41 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 drivers/fsi/fsi-master.h

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3d55bd5..ca02913 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -15,8 +15,43 @@
 
 #include <linux/device.h>
 #include <linux/fsi.h>
+#include <linux/idr.h>
 #include <linux/module.h>
 
+#include "fsi-master.h"
+
+static DEFINE_IDA(master_ida);
+
+/* FSI master support */
+int fsi_master_register(struct fsi_master *master)
+{
+	int rc;
+
+	if (!master)
+		return -EINVAL;
+
+	master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
+	dev_set_name(&master->dev, "fsi%d", master->idx);
+
+	rc = device_register(&master->dev);
+	if (rc)
+		ida_simple_remove(&master_ida, master->idx);
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(fsi_master_register);
+
+void fsi_master_unregister(struct fsi_master *master)
+{
+	if (master->idx >= 0) {
+		ida_simple_remove(&master_ida, master->idx);
+		master->idx = -1;
+	}
+
+	device_unregister(&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)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
new file mode 100644
index 0000000..7764b00
--- /dev/null
+++ b/drivers/fsi/fsi-master.h
@@ -0,0 +1,41 @@
+/*
+ * 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		flags;
+	int		(*read)(struct fsi_master *, int link, uint8_t id,
+				uint32_t addr, void *val, size_t size);
+	int		(*write)(struct fsi_master *, int link, uint8_t id,
+				uint32_t addr, const void *val, size_t size);
+	int		(*term)(struct fsi_master *, int link, uint8_t id);
+	int		(*send_break)(struct fsi_master *, int link);
+	int		(*link_enable)(struct fsi_master *, int link);
+};
+
+#define dev_to_fsi_master(d) container_of(d, struct fsi_master, dev)
+
+extern int fsi_master_register(struct fsi_master *master);
+extern void fsi_master_unregister(struct fsi_master *master);
+
+#endif /* DRIVERS_FSI_MASTER_H */
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 02/24] drivers/fsi: Add slave definition
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 01/24] drivers/fsi: Add fsi master definition Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 03/24] drivers/fsi: Add empty master scan Christopher Bostic
                   ` (21 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh, Chris Bostic

From: Jeremy Kerr <jk@ozlabs.org>

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.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index ca02913..2f19509 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -22,6 +22,16 @@
 
 static DEFINE_IDA(master_ida);
 
+struct fsi_slave {
+	struct device		dev;
+	struct fsi_master	*master;
+	int			id;
+	int			link;
+	uint32_t		size;	/* size of slave address space */
+};
+
+#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+
 /* FSI master support */
 int fsi_master_register(struct fsi_master *master)
 {
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 03/24] drivers/fsi: Add empty master scan
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 01/24] drivers/fsi: Add fsi master definition Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 02/24] drivers/fsi: Add slave definition Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 04/24] lib: Add crc4 module Christopher Bostic
                   ` (20 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh, Chris Bostic

From: Jeremy Kerr <jk@ozlabs.org>

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.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 25 +++++++++++++++++++++++--
 1 file changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 2f19509..e90d45d 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -32,7 +32,25 @@ 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 id)
+{
+	/* todo: initialise slave device, perform engine scan */
+
+	return -ENODEV;
+}
+
 /* FSI master support */
+static int fsi_master_scan(struct fsi_master *master)
+{
+	int link;
+
+	for (link = 0; link < master->n_links; link++)
+		fsi_slave_init(master, link, 0);
+
+	return 0;
+}
+
 int fsi_master_register(struct fsi_master *master)
 {
 	int rc;
@@ -44,10 +62,13 @@ int fsi_master_register(struct fsi_master *master)
 	dev_set_name(&master->dev, "fsi%d", master->idx);
 
 	rc = device_register(&master->dev);
-	if (rc)
+	if (rc) {
 		ida_simple_remove(&master_ida, master->idx);
+		return rc;
+	}
 
-	return rc;
+	fsi_master_scan(master);
+	return 0;
 }
 EXPORT_SYMBOL_GPL(fsi_master_register);
 
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 04/24] lib: Add crc4 module
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (2 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 03/24] drivers/fsi: Add empty master scan Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 05/24] drivers/fsi: Add slave & master read/write APIs Christopher Bostic
                   ` (19 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh, Chris Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Add a little helper for crc4 calculations. This works 4-bits-at-a-time,
using a simple table approach.

We will need this in the FSI core code, as well as any master
implementations that need to calculate CRCs in software.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 include/linux/crc4.h |  8 ++++++++
 lib/Kconfig          |  8 ++++++++
 lib/Makefile         |  1 +
 lib/crc4.c           | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 63 insertions(+)
 create mode 100644 include/linux/crc4.h
 create mode 100644 lib/crc4.c

diff --git a/include/linux/crc4.h b/include/linux/crc4.h
new file mode 100644
index 0000000..8f739f1
--- /dev/null
+++ b/include/linux/crc4.h
@@ -0,0 +1,8 @@
+#ifndef _LINUX_CRC4_H
+#define _LINUX_CRC4_H
+
+#include <linux/types.h>
+
+extern uint8_t crc4(uint8_t c, uint64_t x, int bits);
+
+#endif /* _LINUX_CRC4_H */
diff --git a/lib/Kconfig b/lib/Kconfig
index 0c8b78a..d2fd262 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -158,6 +158,14 @@ config CRC32_BIT
 
 endchoice
 
+config CRC4
+	tristate "CRC4 functions"
+	help
+	  This option is provided for the case where no in-kernel-tree
+	  modules require CRC4 functions, but a module built outside
+	  the kernel tree does. Such modules that use library CRC4
+	  functions require M here.
+
 config CRC7
 	tristate "CRC7 functions"
 	help
diff --git a/lib/Makefile b/lib/Makefile
index 0166fbc..90cf124 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_CRC_T10DIF)+= crc-t10dif.o
 obj-$(CONFIG_CRC_ITU_T)	+= crc-itu-t.o
 obj-$(CONFIG_CRC32)	+= crc32.o
 obj-$(CONFIG_CRC32_SELFTEST)	+= crc32test.o
+obj-$(CONFIG_CRC4)	+= crc4.o
 obj-$(CONFIG_CRC7)	+= crc7.o
 obj-$(CONFIG_LIBCRC32C)	+= libcrc32c.o
 obj-$(CONFIG_CRC8)	+= crc8.o
diff --git a/lib/crc4.c b/lib/crc4.c
new file mode 100644
index 0000000..cf6db46
--- /dev/null
+++ b/lib/crc4.c
@@ -0,0 +1,46 @@
+/*
+ * crc4.c - simple crc-4 calculations.
+ *
+ * This source code is licensed under the GNU General Public License, Version
+ * 2. See the file COPYING for more details.
+ */
+
+#include <linux/crc4.h>
+#include <linux/module.h>
+
+static const uint8_t crc4_tab[] = {
+	0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
+	0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
+};
+
+/**
+ * crc4 - calculate the 4-bit crc of a value.
+ * @crc:  starting crc4
+ * @x:    value to checksum
+ * @bits: number of bits in @x to checksum
+ *
+ * Returns the crc4 value of @x, using polynomial 0b10111.
+ *
+ * The @x value is treated as left-aligned, and bits above @bits are ignored
+ * in the crc calculations.
+ */
+uint8_t crc4(uint8_t c, uint64_t x, int bits)
+{
+	int i;
+
+	/* mask off anything above the top bit */
+	x &= (1ull << bits) - 1;
+
+	/* Align to 4-bits */
+	bits = (bits + 3) & ~0x3;
+
+	/* Calculate crc4 over four-bit nibbles, starting at the MSbit */
+	for (i = bits - 4; i >= 0; i -= 4)
+		c = crc4_tab[c ^ ((x >> i) & 0xf)];
+
+	return c;
+}
+EXPORT_SYMBOL_GPL(crc4);
+
+MODULE_DESCRIPTION("CRC4 calculations");
+MODULE_LICENSE("GPL");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 05/24] drivers/fsi: Add slave & master read/write APIs
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (3 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 04/24] lib: Add crc4 module Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 06/24] drivers/fsi: Set up links for slave communication Christopher Bostic
                   ` (18 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh, Chris Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Introduce functions to perform reads/writes on the slave address space;
these simply pass the request on the slave's master with the correct
link and slave ID.

We implement these on top of similar helpers for the master.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Chris Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 92 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index e90d45d..1ec9790 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -32,7 +32,64 @@ struct fsi_slave {
 
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+static int fsi_master_read(struct fsi_master *master, int link,
+		uint8_t slave_id, uint32_t addr, void *val, size_t size);
+static int fsi_master_write(struct fsi_master *master, int link,
+		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
+
 /* FSI slave support */
+static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
+		uint8_t *idp)
+{
+	uint32_t addr = *addrp;
+	uint8_t id = *idp;
+
+	if (addr > slave->size)
+		return -EINVAL;
+
+	/* For 23 bit addressing, we encode the extra two bits in the slave
+	 * id (and the slave's actual ID needs to be 0).
+	 */
+	if (addr > 0x1fffff) {
+		if (slave->id != 0)
+			return -EINVAL;
+		id = (addr >> 21) & 0x3;
+		addr &= 0x1fffff;
+	}
+
+	*addrp = addr;
+	*idp = id;
+	return 0;
+}
+
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+			void *val, size_t size)
+{
+	uint8_t id = slave->id;
+	int rc;
+
+	rc = fsi_slave_calc_addr(slave, &addr, &id);
+	if (rc)
+		return rc;
+
+	return fsi_master_read(slave->master, slave->link, id,
+			addr, val, size);
+}
+
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+			const void *val, size_t size)
+{
+	uint8_t id = slave->id;
+	int rc;
+
+	rc = fsi_slave_calc_addr(slave, &addr, &id);
+	if (rc)
+		return rc;
+
+	return fsi_master_write(slave->master, slave->link, id,
+			addr, val, size);
+}
+
 static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 {
 	/* todo: initialise slave device, perform engine scan */
@@ -41,6 +98,41 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 }
 
 /* FSI master support */
+static int fsi_check_access(uint32_t addr, size_t size)
+{
+	if (size != 1 && size != 2 && size != 4)
+		return -EINVAL;
+
+	if ((addr & 0x3) != (size & 0x3))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int fsi_master_read(struct fsi_master *master, int link,
+		uint8_t slave_id, uint32_t addr, void *val, size_t size)
+{
+	int rc;
+
+	rc = fsi_check_access(addr, size);
+	if (rc)
+		return rc;
+
+	return master->read(master, link, slave_id, addr, val, size);
+}
+
+static int fsi_master_write(struct fsi_master *master, int link,
+		uint8_t slave_id, uint32_t addr, const void *val, size_t size)
+{
+	int rc;
+
+	rc = fsi_check_access(addr, size);
+	if (rc)
+		return rc;
+
+	return master->write(master, link, slave_id, addr, val, size);
+}
+
 static int fsi_master_scan(struct fsi_master *master)
 {
 	int link;
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 06/24] drivers/fsi: Set up links for slave communication
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (4 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 05/24] drivers/fsi: Add slave & master read/write APIs Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 07/24] drivers/fsi: Implement slave initialisation Christopher Bostic
                   ` (17 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Enable each link and send a break command, and try to detect a slave by
reading from the SMODE register.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 37 +++++++++++++++++++++++++++++++++++--
 1 file changed, 35 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 1ec9790..235f17a 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -133,12 +133,45 @@ static int fsi_master_write(struct fsi_master *master, int link,
 	return master->write(master, link, slave_id, addr, val, size);
 }
 
+static int fsi_master_link_enable(struct fsi_master *master, int link)
+{
+	if (master->link_enable)
+		return master->link_enable(master, link);
+
+	return 0;
+}
+
+/*
+ * Issue a break command on this link
+ */
+static int fsi_master_break(struct fsi_master *master, int link)
+{
+	if (master->send_break)
+		return master->send_break(master, link);
+
+	return 0;
+}
+
 static int fsi_master_scan(struct fsi_master *master)
 {
-	int link;
+	int link, rc;
+
+	for (link = 0; link < master->n_links; link++) {
+		rc = fsi_master_link_enable(master, link);
+		if (rc) {
+			dev_dbg(&master->dev,
+				"enable link %d failed: %d\n", link, rc);
+			continue;
+		}
+		rc = fsi_master_break(master, link);
+		if (rc) {
+			dev_dbg(&master->dev,
+				"break to link %d failed: %d\n", link, rc);
+			continue;
+		}
 
-	for (link = 0; link < master->n_links; link++)
 		fsi_slave_init(master, link, 0);
+	}
 
 	return 0;
 }
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 07/24] drivers/fsi: Implement slave initialisation
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (5 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 06/24] drivers/fsi: Set up links for slave communication Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 08/24] drivers/fsi: Set slave SMODE to init communication Christopher Bostic
                   ` (16 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Implement fsi_slave_init: if we can read a chip ID, create fsi_slave
devices and register with the driver core.

Includes changes from Christopher Bostic <cbostic@linux.vnet.ibm.com>.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/Kconfig    |  1 +
 drivers/fsi/fsi-core.c | 67 ++++++++++++++++++++++++++++++++++++++++++++++++--
 2 files changed, 66 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 04c1a0e..e1006c6 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -6,6 +6,7 @@ menu "FSI support"
 
 config FSI
 	tristate "FSI support"
+	select CRC4
 	---help---
 	  FSI - the FRU Support Interface - is a simple bus for low-level
 	  access to POWER-based hardware.
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 235f17a..9d9adc1 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -13,13 +13,17 @@
  * GNU General Public License for more details.
  */
 
+#include <linux/crc4.h>
 #include <linux/device.h>
 #include <linux/fsi.h>
 #include <linux/idr.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 
 #include "fsi-master.h"
 
+#define FSI_SLAVE_SIZE_23b		0x800000
+
 static DEFINE_IDA(master_ida);
 
 struct fsi_slave {
@@ -90,11 +94,70 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 			addr, val, size);
 }
 
+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 id)
 {
-	/* todo: initialise slave device, perform engine scan */
+	struct fsi_slave *slave;
+	uint32_t chip_id;
+	uint8_t crc;
+	int rc;
+
+	/* Currently, we only support single slaves on a link, and use the
+	 * full 23-bit address range
+	 */
+	if (id != 0)
+		return -EINVAL;
+
+	rc = fsi_master_read(master, link, id, 0, &chip_id, sizeof(chip_id));
+	if (rc) {
+		dev_dbg(&master->dev, "can't read slave %02x:%02x %d\n",
+				link, id, rc);
+		return -ENODEV;
+	}
+	chip_id = be32_to_cpu(chip_id);
+
+	crc = crc4(0, chip_id, 32);
+	if (crc) {
+		dev_warn(&master->dev, "slave %02x:%02x invalid chip id CRC!\n",
+				link, id);
+		return -EIO;
+	}
+
+	dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
+			chip_id, master->idx, link, id);
+
+	/* We can communicate with a slave; create the slave device and
+	 * register.
+	 */
+	slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+	if (!slave)
+		return -ENOMEM;
+
+	slave->master = master;
+	slave->dev.parent = &master->dev;
+	slave->dev.release = fsi_slave_release;
+	slave->link = link;
+	slave->id = id;
+	slave->size = FSI_SLAVE_SIZE_23b;
+
+	dev_set_name(&slave->dev, "slave@%02x:%02x", link, 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;
+	}
+
+	/* todo: perform engine scan */
 
-	return -ENODEV;
+	return rc;
 }
 
 /* FSI master support */
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 08/24] drivers/fsi: Set slave SMODE to init communication
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (6 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 07/24] drivers/fsi: Implement slave initialisation Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 09/24] drivers/fsi: scan slaves & register devices Christopher Bostic
                   ` (15 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh,
	Jeremy Kerr

Set CFAM to appropriate ID so that the controlling master can manage
link memory ranges.  Add slave engine register definitions.

Includes changes from Jeremy Kerr <jk@ozlabs.org>.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 9d9adc1..04795f2 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -22,6 +22,27 @@
 
 #include "fsi-master.h"
 
+#define FSI_SLAVE_BASE			0x800
+
+/*
+ * FSI slave engine control register offsets
+ */
+#define FSI_SMODE			0x0	/* R/W: Mode register */
+
+/*
+ * SMODE fields
+ */
+#define FSI_SMODE_WSC		0x80000000	/* Warm start done */
+#define FSI_SMODE_ECRC		0x20000000	/* Hw CRC check */
+#define FSI_SMODE_SID_SHIFT	24		/* ID shift */
+#define FSI_SMODE_SID_MASK	3		/* ID Mask */
+#define FSI_SMODE_ED_SHIFT	20		/* Echo delay shift */
+#define FSI_SMODE_ED_MASK	0xf		/* Echo delay mask */
+#define FSI_SMODE_SD_SHIFT	16		/* Send delay shift */
+#define FSI_SMODE_SD_MASK	0xf		/* Send delay mask */
+#define FSI_SMODE_LBCRR_SHIFT	8		/* Clk ratio shift */
+#define FSI_SMODE_LBCRR_MASK	0xf		/* Clk ratio mask */
+
 #define FSI_SLAVE_SIZE_23b		0x800000
 
 static DEFINE_IDA(master_ida);
@@ -94,6 +115,52 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 			addr, val, size);
 }
 
+/* Encode slave local bus echo delay */
+static inline uint32_t fsi_smode_echodly(int x)
+{
+	return (x & FSI_SMODE_ED_MASK) << FSI_SMODE_ED_SHIFT;
+}
+
+/* Encode slave local bus send delay */
+static inline uint32_t fsi_smode_senddly(int x)
+{
+	return (x & FSI_SMODE_SD_MASK) << FSI_SMODE_SD_SHIFT;
+}
+
+/* Encode slave local bus clock rate ratio */
+static inline uint32_t fsi_smode_lbcrr(int x)
+{
+	return (x & FSI_SMODE_LBCRR_MASK) << FSI_SMODE_LBCRR_SHIFT;
+}
+
+/* Encode slave ID */
+static inline uint32_t fsi_smode_sid(int x)
+{
+	return (x & FSI_SMODE_SID_MASK) << FSI_SMODE_SID_SHIFT;
+}
+
+static const uint32_t fsi_slave_smode(int id)
+{
+	return FSI_SMODE_WSC | FSI_SMODE_ECRC
+		| fsi_smode_sid(id)
+		| fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+		| fsi_smode_lbcrr(0x8);
+}
+
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id)
+{
+	uint32_t smode;
+
+	/* set our smode register with the slave ID field to 0; this enables
+	 * extended slave addressing
+	 */
+	smode = fsi_slave_smode(id);
+	smode = cpu_to_be32(smode);
+
+	return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SMODE,
+			&smode, sizeof(smode));
+}
+
 static void fsi_slave_release(struct device *dev)
 {
 	struct fsi_slave *slave = to_fsi_slave(dev);
@@ -132,6 +199,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 	dev_info(&master->dev, "fsi: found chip %08x at %02x:%02x:%02x\n",
 			chip_id, master->idx, link, id);
 
+	rc = fsi_slave_set_smode(master, link, id);
+	if (rc) {
+		dev_warn(&master->dev,
+				"can't set smode on slave:%02x:%02x %d\n",
+				link, id, rc);
+		return -ENODEV;
+	}
+
 	/* We can communicate with a slave; create the slave device and
 	 * register.
 	 */
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 09/24] drivers/fsi: scan slaves & register devices
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (7 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 08/24] drivers/fsi: Set slave SMODE to init communication Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 10/24] drivers/fsi: Add device read/write/peek API Christopher Bostic
                   ` (14 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

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

Includes contributions from Christopher Bostic
<cbostic@linux.vnet.ibm.com>.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 128 ++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/fsi.h    |   4 ++
 2 files changed, 131 insertions(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 04795f2..eac0bc4 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -19,9 +19,23 @@
 #include <linux/idr.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/bitops.h>
 
 #include "fsi-master.h"
 
+#define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
+#define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
+#define FSI_SLAVE_CONF_SLOTS_SHIFT	16
+#define FSI_SLAVE_CONF_VERSION_MASK	GENMASK(15, 12)
+#define FSI_SLAVE_CONF_VERSION_SHIFT	12
+#define FSI_SLAVE_CONF_TYPE_MASK	GENMASK(11, 4)
+#define FSI_SLAVE_CONF_TYPE_SHIFT	4
+#define FSI_SLAVE_CONF_CRC_SHIFT	4
+#define FSI_SLAVE_CONF_CRC_MASK		GENMASK(3, 0)
+#define FSI_SLAVE_CONF_DATA_BITS	28
+
+static const int engine_page_size = 0x400;
+
 #define FSI_SLAVE_BASE			0x800
 
 /*
@@ -62,6 +76,30 @@ static int fsi_master_read(struct fsi_master *master, int link,
 static int fsi_master_write(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
 
+/* 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;
+}
+
 /* FSI slave support */
 static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
 		uint8_t *idp)
@@ -115,6 +153,91 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 			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, crc;
+		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);
+
+		crc = crc4(0, conf, 32);
+		if (crc) {
+			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 && slots != 0) {
+
+			/* create device */
+			dev = fsi_create_device(slave);
+			if (!dev)
+				return -ENOMEM;
+
+			dev->slave = slave;
+			dev->engine_type = type;
+			dev->version = version;
+			dev->unit = i;
+			dev->addr = engine_addr;
+			dev->size = slots * engine_page_size;
+
+			dev_dbg(&slave->dev,
+			"engine[%i]: type %x, version %x, addr %x size %x\n",
+					dev->unit, dev->engine_type, version,
+					dev->addr, dev->size);
+
+			dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
+					slave->master->idx, slave->link,
+					slave->id, i - 2);
+
+			rc = device_register(&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;
+}
+
 /* Encode slave local bus echo delay */
 static inline uint32_t fsi_smode_echodly(int x)
 {
@@ -230,7 +353,10 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 		return rc;
 	}
 
-	/* todo: perform engine scan */
+	rc = fsi_slave_scan(slave);
+	if (rc)
+		dev_dbg(&master->dev, "failed during slave scan with: %d\n",
+				rc);
 
 	return rc;
 }
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 273cbf6..efa55ba 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -21,6 +21,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 {
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 10/24] drivers/fsi: Add device read/write/peek API
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (8 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 09/24] drivers/fsi: scan slaves & register devices Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 11/24] drivers/fsi: Add master unscan Christopher Bostic
                   ` (13 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Edward A . James, Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

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

Includes contributions from Christopher Bostic
<cbostic@linux.vnet.ibm.com> and Edward A. James <eajames@us.ibm.com>.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 include/linux/fsi.h    |  7 ++++++-
 2 files changed, 55 insertions(+), 2 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index eac0bc4..d7a6e76 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -34,6 +34,8 @@
 #define FSI_SLAVE_CONF_CRC_MASK		GENMASK(3, 0)
 #define FSI_SLAVE_CONF_DATA_BITS	28
 
+#define FSI_PEEK_BASE			0x410
+
 static const int engine_page_size = 0x400;
 
 #define FSI_SLAVE_BASE			0x800
@@ -75,8 +77,54 @@ static int fsi_master_read(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, void *val, size_t size);
 static int fsi_master_write(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
+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_device_read() / fsi_device_write() / fsi_device_peek()
+ *
+ * FSI endpoint-device support
+ *
+ * Read / write / peek accessors for a client
+ *
+ * Parameters:
+ * dev:  Structure passed to FSI client device drivers on probe().
+ * addr: FSI address of given device.  Client should pass in its base address
+ *       plus desired offset to access its register space.
+ * val:  For read/peek this is the value read at the specified address. For
+ *       write this is value to write to the specified address.
+ *       The data in val must be FSI bus endian (big endian).
+ * size: Size in bytes of the operation.  Sizes supported are 1, 2 and 4 bytes.
+ *       Addresses must be aligned on size boundaries or an error will result.
+ */
+int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
+		size_t size)
+{
+	if (addr > dev->size || size > dev->size || addr > dev->size - size)
+		return -EINVAL;
 
-/* FSI endpoint-device support */
+	return fsi_slave_read(dev->slave, dev->addr + addr, val, size);
+}
+EXPORT_SYMBOL_GPL(fsi_device_read);
+
+int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
+		size_t size)
+{
+	if (addr > dev->size || size > dev->size || addr > dev->size - size)
+		return -EINVAL;
+
+	return fsi_slave_write(dev->slave, dev->addr + addr, val, size);
+}
+EXPORT_SYMBOL_GPL(fsi_device_write);
+
+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)
 {
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index efa55ba..66bce48 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -27,6 +27,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;
@@ -40,7 +46,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;
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 11/24] drivers/fsi: Add master unscan
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (9 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 10/24] drivers/fsi: Add device read/write/peek API Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 12/24] drivers/fsi: Add documentation for GPIO bindings Christopher Bostic
                   ` (12 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Allow a master to undo a previous scan.  Should a master scan a bus
twice it will need to ensure it doesn't double register any
previously detected device.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
----
v7 - Unscan when unregistering master
   - Remove leading '__'s from function names
   - Return fail state for sysfs rescan file
---
 drivers/fsi/fsi-core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 44 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index d7a6e76..fcb0c81 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -71,6 +71,7 @@ struct fsi_slave {
 	uint32_t		size;	/* size of slave address space */
 };
 
+#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
 static int fsi_master_read(struct fsi_master *master, int link,
@@ -488,6 +489,40 @@ static int fsi_master_scan(struct fsi_master *master)
 	return 0;
 }
 
+static int fsi_slave_remove_device(struct device *dev, void *arg)
+{
+	device_unregister(dev);
+	return 0;
+}
+
+static int fsi_master_remove_slave(struct device *dev, void *arg)
+{
+	device_for_each_child(dev, NULL, fsi_slave_remove_device);
+	device_unregister(dev);
+	return 0;
+}
+
+static void fsi_master_unscan(struct fsi_master *master)
+{
+	device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
+}
+
+static ssize_t master_rescan_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct fsi_master *master = to_fsi_master(dev);
+	int rc;
+
+	fsi_master_unscan(master);
+	rc = fsi_master_scan(master);
+	if (rc < 0)
+		return rc;
+
+	return count;
+}
+
+static DEVICE_ATTR(rescan, 0200, NULL, master_rescan_store);
+
 int fsi_master_register(struct fsi_master *master)
 {
 	int rc;
@@ -504,7 +539,15 @@ int fsi_master_register(struct fsi_master *master)
 		return rc;
 	}
 
+	rc = device_create_file(&master->dev, &dev_attr_rescan);
+	if (rc) {
+		device_unregister(&master->dev);
+		ida_simple_remove(&master_ida, master->idx);
+		return rc;
+	}
+
 	fsi_master_scan(master);
+
 	return 0;
 }
 EXPORT_SYMBOL_GPL(fsi_master_register);
@@ -516,6 +559,7 @@ void fsi_master_unregister(struct fsi_master *master)
 		master->idx = -1;
 	}
 
+	fsi_master_unscan(master);
 	device_unregister(&master->dev);
 }
 EXPORT_SYMBOL_GPL(fsi_master_unregister);
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 12/24] drivers/fsi: Add documentation for GPIO bindings
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (10 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 11/24] drivers/fsi: Add master unscan Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 13/24] drivers/fsi: Add client driver register utilities Christopher Bostic
                   ` (11 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh,
	Jeremy Kerr

Add fsi master gpio device tree binding documentation.

Includes changes from Jeremy Kerr <jk@ozlabs.org>.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Acked-by: Rob Herring <robh@kernel.org>
---
 .../devicetree/bindings/fsi/fsi-master-gpio.txt    | 24 ++++++++++++++++++++++
 1 file changed, 24 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt

diff --git a/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
new file mode 100644
index 0000000..a767259
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
@@ -0,0 +1,24 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+Required properties:
+ - compatible = "fsi-master-gpio";
+ - clock-gpios = <gpio-descriptor>;	: GPIO for FSI clock
+ - data-gpios = <gpio-descriptor>;	: GPIO for FSI data signal
+
+Optional properties:
+ - enable-gpios = <gpio-descriptor>;	: GPIO for enable signal
+ - trans-gpios = <gpio-descriptor>;	: GPIO for voltage translator enable
+ - mux-gpios = <gpio-descriptor>;	: GPIO for pin multiplexing with other
+                                          functions (eg, external FSI masters)
+
+Examples:
+
+    fsi-master {
+        compatible = "fsi-master-gpio", "fsi-master";
+        clock-gpios = <&gpio 0>;
+        data-gpios = <&gpio 1>;
+        enable-gpios = <&gpio 2>;
+        trans-gpios = <&gpio 3>;
+        mux-gpios = <&gpio 4>;
+    }
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 13/24] drivers/fsi: Add client driver register utilities
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (11 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 12/24] drivers/fsi: Add documentation for GPIO bindings Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 14/24] drivers/fsi: Add sysfs files for FSI master & slave accesses Christopher Bostic
                   ` (10 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Add driver_register and driver_unregister wrappers for FSI.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/fsi-core.c | 17 +++++++++++++++++
 include/linux/fsi.h    | 12 ++++++++++++
 2 files changed, 29 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index fcb0c81..e9fbd9f 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -586,6 +586,23 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
 	return 0;
 }
 
+int fsi_driver_register(struct fsi_driver *fsi_drv)
+{
+	if (!fsi_drv)
+		return -EINVAL;
+	if (!fsi_drv->id_table)
+		return -EINVAL;
+
+	return driver_register(&fsi_drv->drv);
+}
+EXPORT_SYMBOL_GPL(fsi_driver_register);
+
+void fsi_driver_unregister(struct fsi_driver *fsi_drv)
+{
+	driver_unregister(&fsi_drv->drv);
+}
+EXPORT_SYMBOL_GPL(fsi_driver_unregister);
+
 struct bus_type fsi_bus_type = {
 	.name		= "fsi",
 	.match		= fsi_bus_match,
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 66bce48..34f1e9a 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -54,6 +54,18 @@ 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 fsi_driver_register(struct fsi_driver *fsi_drv);
+extern void fsi_driver_unregister(struct fsi_driver *fsi_drv);
+
+/* module_fsi_driver() - Helper macro for drivers that don't do
+ * anything special in module init/exit.  This eliminates a lot of
+ * boilerplate.  Each module may only use this macro once, and
+ * calling it replaces module_init() and module_exit()
+ */
+#define module_fsi_driver(__fsi_driver) \
+		module_driver(__fsi_driver, fsi_driver_register, \
+				fsi_driver_unregister)
+
 extern struct bus_type fsi_bus_type;
 
 #endif /* LINUX_FSI_H */
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 14/24] drivers/fsi: Add sysfs files for FSI master & slave accesses
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (12 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 13/24] drivers/fsi: Add client driver register utilities Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 15/24] drivers/fsi: expose direct-access slave API Christopher Bostic
                   ` (9 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

This change adds a 'raw' file for reads & writes, and a 'term' file for
the TERM command, and a 'break' file for issuing a BREAK.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c | 116 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index e9fbd9f..626cc06 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -287,6 +287,95 @@ static int fsi_slave_scan(struct fsi_slave *slave)
 	return 0;
 }
 
+static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
+		struct kobject *kobj, struct bin_attribute *attr, char *buf,
+		loff_t off, size_t count)
+{
+	struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+	size_t total_len, read_len;
+	int rc;
+
+	if (off < 0)
+		return -EINVAL;
+
+	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
+		return -EINVAL;
+
+	for (total_len = 0; total_len < count; total_len += read_len) {
+		read_len = min_t(size_t, count, 4);
+		read_len -= off & 0x3;
+
+		rc = fsi_slave_read(slave, off, buf + total_len, read_len);
+		if (rc)
+			return rc;
+
+		off += read_len;
+	}
+
+	return count;
+}
+
+static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
+		struct kobject *kobj, struct bin_attribute *attr,
+		char *buf, loff_t off, size_t count)
+{
+	struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+	size_t total_len, write_len;
+	int rc;
+
+	if (off < 0)
+		return -EINVAL;
+
+	if (off > 0xffffffff || count > 0xffffffff || off + count > 0xffffffff)
+		return -EINVAL;
+
+	for (total_len = 0; total_len < count; total_len += write_len) {
+		write_len = min_t(size_t, count, 4);
+		write_len -= off & 0x3;
+
+		rc = fsi_slave_write(slave, off, buf + total_len, write_len);
+		if (rc)
+			return rc;
+
+		off += write_len;
+	}
+
+	return count;
+}
+
+static struct bin_attribute fsi_slave_raw_attr = {
+	.attr = {
+		.name = "raw",
+		.mode = 0600,
+	},
+	.size = 0,
+	.read = fsi_slave_sysfs_raw_read,
+	.write = fsi_slave_sysfs_raw_write,
+};
+
+static ssize_t fsi_slave_sysfs_term_write(struct file *file,
+		struct kobject *kobj, struct bin_attribute *attr,
+		char *buf, loff_t off, size_t count)
+{
+	struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
+	struct fsi_master *master = slave->master;
+
+	if (!master->term)
+		return -ENODEV;
+
+	master->term(master, slave->link, slave->id);
+	return count;
+}
+
+static struct bin_attribute fsi_slave_term_attr = {
+	.attr = {
+		.name = "term",
+		.mode = 0200,
+	},
+	.size = 0,
+	.write = fsi_slave_sysfs_term_write,
+};
+
 /* Encode slave local bus echo delay */
 static inline uint32_t fsi_smode_echodly(int x)
 {
@@ -402,6 +491,14 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 		return rc;
 	}
 
+	rc = device_create_bin_file(&slave->dev, &fsi_slave_raw_attr);
+	if (rc)
+		dev_warn(&slave->dev, "failed to create raw attr: %d\n", rc);
+
+	rc = device_create_bin_file(&slave->dev, &fsi_slave_term_attr);
+	if (rc)
+		dev_warn(&slave->dev, "failed to create term attr: %d\n", rc);
+
 	rc = fsi_slave_scan(slave);
 	if (rc)
 		dev_dbg(&master->dev, "failed during slave scan with: %d\n",
@@ -523,6 +620,18 @@ static ssize_t master_rescan_store(struct device *dev,
 
 static DEVICE_ATTR(rescan, 0200, NULL, master_rescan_store);
 
+static ssize_t master_break_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct fsi_master *master = to_fsi_master(dev);
+
+	fsi_master_break(master, 0);
+
+	return count;
+}
+
+static DEVICE_ATTR(break, 0200, NULL, master_break_store);
+
 int fsi_master_register(struct fsi_master *master)
 {
 	int rc;
@@ -546,6 +655,13 @@ int fsi_master_register(struct fsi_master *master)
 		return rc;
 	}
 
+	rc = device_create_file(&master->dev, &dev_attr_break);
+	if (rc) {
+		device_unregister(&master->dev);
+		ida_simple_remove(&master_ida, master->idx);
+		return rc;
+	}
+
 	fsi_master_scan(master);
 
 	return 0;
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 15/24] drivers/fsi: expose direct-access slave API
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (13 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 14/24] drivers/fsi: Add sysfs files for FSI master & slave accesses Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations Christopher Bostic
                   ` (8 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Allow drivers to access the slave address ranges.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
v8 - export symbols fsi_slave_read, fsi_slave_write,
     fsi_slave_claim_range, fsi_slave_release_range
---
 drivers/fsi/fsi-core.c | 30 ++++++++++++++++++++++++------
 include/linux/fsi.h    | 12 ++++++++++++
 2 files changed, 36 insertions(+), 6 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 626cc06..3681365 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -78,10 +78,6 @@ static int fsi_master_read(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, void *val, size_t size);
 static int fsi_master_write(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
-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_device_read() / fsi_device_write() / fsi_device_peek()
@@ -174,7 +170,7 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
 	return 0;
 }
 
-static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
 			void *val, size_t size)
 {
 	uint8_t id = slave->id;
@@ -187,8 +183,9 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
 	return fsi_master_read(slave->master, slave->link, id,
 			addr, val, size);
 }
+EXPORT_SYMBOL_GPL(fsi_slave_read);
 
-static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 			const void *val, size_t size)
 {
 	uint8_t id = slave->id;
@@ -201,6 +198,27 @@ static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 	return fsi_master_write(slave->master, slave->link, id,
 			addr, val, size);
 }
+EXPORT_SYMBOL_GPL(fsi_slave_write);
+
+extern int fsi_slave_claim_range(struct fsi_slave *slave,
+		uint32_t addr, uint32_t size)
+{
+	if (addr + size < addr)
+		return -EINVAL;
+
+	if (addr + size > slave->size)
+		return -EINVAL;
+
+	/* todo: check for overlapping claims */
+	return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_slave_claim_range);
+
+extern void fsi_slave_release_range(struct fsi_slave *slave,
+		uint32_t addr, uint32_t size)
+{
+}
+EXPORT_SYMBOL_GPL(fsi_slave_release_range);
 
 static int fsi_slave_scan(struct fsi_slave *slave)
 {
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 34f1e9a..141fd38 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -66,6 +66,18 @@ struct fsi_driver {
 		module_driver(__fsi_driver, fsi_driver_register, \
 				fsi_driver_unregister)
 
+/* direct slave API */
+extern int fsi_slave_claim_range(struct fsi_slave *slave,
+		uint32_t addr, uint32_t size);
+extern void fsi_slave_release_range(struct fsi_slave *slave,
+		uint32_t addr, uint32_t size);
+extern int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+		void *val, size_t size);
+extern int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+		const void *val, size_t size);
+
+
+
 extern struct bus_type fsi_bus_type;
 
 #endif /* LINUX_FSI_H */
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (14 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 15/24] drivers/fsi: expose direct-access slave API Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-07 19:16   ` Steven Rostedt
  2017-06-06 21:08 ` [PATCH v8 17/24] drivers/fsi: Add error handling for slave Christopher Bostic
                   ` (7 subsequent siblings)
  23 siblings, 1 reply; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Trace low level read and write FSI bus operations.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
v8 - type cast to 'int' to clean up build warnings
---
 drivers/fsi/fsi-core.c     |  27 +++++++---
 include/trace/events/fsi.h | 127 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 148 insertions(+), 6 deletions(-)
 create mode 100644 include/trace/events/fsi.h

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3681365..db54561 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -23,6 +23,9 @@
 
 #include "fsi-master.h"
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi.h>
+
 #define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
 #define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
 #define FSI_SLAVE_CONF_SLOTS_SHIFT	16
@@ -542,11 +545,16 @@ static int fsi_master_read(struct fsi_master *master, int link,
 {
 	int rc;
 
+	trace_fsi_master_read(master, link, slave_id, addr, size);
+
 	rc = fsi_check_access(addr, size);
-	if (rc)
-		return rc;
+	if (!rc)
+		rc = master->read(master, link, slave_id, addr, val, size);
+
+	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
+			false, val, rc);
 
-	return master->read(master, link, slave_id, addr, val, size);
+	return rc;
 }
 
 static int fsi_master_write(struct fsi_master *master, int link,
@@ -554,11 +562,16 @@ static int fsi_master_write(struct fsi_master *master, int link,
 {
 	int rc;
 
+	trace_fsi_master_write(master, link, slave_id, addr, size, val);
+
 	rc = fsi_check_access(addr, size);
-	if (rc)
-		return rc;
+	if (!rc)
+		rc = master->write(master, link, slave_id, addr, val, size);
 
-	return master->write(master, link, slave_id, addr, val, size);
+	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
+			true, val, rc);
+
+	return rc;
 }
 
 static int fsi_master_link_enable(struct fsi_master *master, int link)
@@ -574,6 +587,8 @@ static int fsi_master_link_enable(struct fsi_master *master, int link)
  */
 static int fsi_master_break(struct fsi_master *master, int link)
 {
+	trace_fsi_master_break(master, link);
+
 	if (master->send_break)
 		return master->send_break(master, link);
 
diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
new file mode 100644
index 0000000..697ee66
--- /dev/null
+++ b/include/trace/events/fsi.h
@@ -0,0 +1,127 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi
+
+#if !defined(_TRACE_FSI_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fsi_master_read,
+	TP_PROTO(const struct fsi_master *master, int link, int id,
+			uint32_t addr, size_t size),
+	TP_ARGS(master, link, id, addr, size),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	link)
+		__field(int,	id)
+		__field(__u32,	addr)
+		__field(size_t,	size)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->idx;
+		__entry->link = link;
+		__entry->id = id;
+		__entry->addr = addr;
+		__entry->size = size;
+	),
+	TP_printk("fsi%d:%02d:%02d %08x[%zd]",
+		__entry->master_idx,
+		__entry->link,
+		__entry->id,
+		__entry->addr,
+		__entry->size
+	)
+);
+
+TRACE_EVENT(fsi_master_write,
+	TP_PROTO(const struct fsi_master *master, int link, int id,
+			uint32_t addr, size_t size, const void *data),
+	TP_ARGS(master, link, id, addr, size, data),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	link)
+		__field(int,	id)
+		__field(__u32,	addr)
+		__field(size_t,	size)
+		__field(__u32,	data)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->idx;
+		__entry->link = link;
+		__entry->id = id;
+		__entry->addr = addr;
+		__entry->size = size;
+		__entry->data = 0;
+		memcpy(&__entry->data, data, size);
+	),
+	TP_printk("fsi%d:%02d:%02d %08x[%zd] <= {%*ph}",
+		__entry->master_idx,
+		__entry->link,
+		__entry->id,
+		__entry->addr,
+		__entry->size,
+		(int)__entry->size, &__entry->data
+	)
+);
+
+TRACE_EVENT(fsi_master_rw_result,
+	TP_PROTO(const struct fsi_master *master, int link, int id,
+			uint32_t addr, size_t size,
+			bool write, const void *data, int ret),
+	TP_ARGS(master, link, id, addr, size, write, data, ret),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	link)
+		__field(int,	id)
+		__field(__u32,	addr)
+		__field(size_t,	size)
+		__field(bool,	write)
+		__field(__u32,	data)
+		__field(int,	ret)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->idx;
+		__entry->link = link;
+		__entry->id = id;
+		__entry->addr = addr;
+		__entry->size = size;
+		__entry->write = write;
+		__entry->data = 0;
+		__entry->ret = ret;
+		if (__entry->write || !__entry->ret)
+			memcpy(&__entry->data, data, size);
+	),
+	TP_printk("fsi%d:%02d:%02d %08x[%zd] %s {%*ph} ret %d",
+		__entry->master_idx,
+		__entry->link,
+		__entry->id,
+		__entry->addr,
+		__entry->size,
+		__entry->write ? "<=" : "=>",
+		(int)__entry->size, &__entry->data,
+		__entry->ret
+	)
+);
+
+TRACE_EVENT(fsi_master_break,
+	TP_PROTO(const struct fsi_master *master, int link),
+	TP_ARGS(master, link),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	link)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->idx;
+		__entry->link = link;
+	),
+	TP_printk("fsi%d:%d",
+		__entry->master_idx,
+		__entry->link
+	)
+);
+
+
+#endif /* _TRACE_FSI_H */
+
+#include <trace/define_trace.h>
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 17/24] drivers/fsi: Add error handling for slave
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (15 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 18/24] drivers/fsi: Document FSI master sysfs files in ABI Christopher Bostic
                   ` (6 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

This change implements error handling in the FSI core, by cleaining up
and retrying failed operations, using the SISC, TERM and BREAK
facilities.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c | 121 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 114 insertions(+), 7 deletions(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index db54561..c9ff8d3 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -46,7 +46,9 @@
 /*
  * FSI slave engine control register offsets
  */
-#define FSI_SMODE			0x0	/* R/W: Mode register */
+#define FSI_SMODE		0x0	/* R/W: Mode register */
+#define FSI_SISC		0x8	/* R/W: Interrupt condition */
+#define FSI_SSTAT		0x14	/* R  : Slave status */
 
 /*
  * SMODE fields
@@ -77,10 +79,14 @@ struct fsi_slave {
 #define to_fsi_master(d) container_of(d, struct fsi_master, dev)
 #define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
 
+static const int slave_retries = 2;
+static int discard_errors;
+
 static int fsi_master_read(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, void *val, size_t size);
 static int fsi_master_write(struct fsi_master *master, int link,
 		uint8_t slave_id, uint32_t addr, const void *val, size_t size);
+static int fsi_master_break(struct fsi_master *master, int link);
 
 /*
  * fsi_device_read() / fsi_device_write() / fsi_device_peek()
@@ -173,18 +179,107 @@ static int fsi_slave_calc_addr(struct fsi_slave *slave, uint32_t *addrp,
 	return 0;
 }
 
+int fsi_slave_report_and_clear_errors(struct fsi_slave *slave)
+{
+	struct fsi_master *master = slave->master;
+	uint32_t irq, stat;
+	int rc, link;
+	uint8_t id;
+
+	link = slave->link;
+	id = slave->id;
+
+	rc = fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
+			&irq, sizeof(irq));
+	if (rc)
+		return rc;
+
+	rc =  fsi_master_read(master, link, id, FSI_SLAVE_BASE + FSI_SSTAT,
+			&stat, sizeof(stat));
+	if (rc)
+		return rc;
+
+	dev_info(&slave->dev, "status: 0x%08x, sisc: 0x%08x\n",
+			be32_to_cpu(stat), be32_to_cpu(irq));
+
+	/* clear interrupts */
+	return fsi_master_write(master, link, id, FSI_SLAVE_BASE + FSI_SISC,
+			&irq, sizeof(irq));
+}
+
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id);
+
+int fsi_slave_handle_error(struct fsi_slave *slave, bool write, uint32_t addr,
+		size_t size)
+{
+	struct fsi_master *master = slave->master;
+	int rc, link;
+	uint32_t reg;
+	uint8_t id;
+
+	if (discard_errors)
+		return -1;
+
+	link = slave->link;
+	id = slave->id;
+
+	dev_dbg(&slave->dev, "handling error on %s to 0x%08x[%zd]",
+			write ? "write" : "read", addr, size);
+
+	/* try a simple clear of error conditions, which may fail if we've lost
+	 * communication with the slave
+	 */
+	rc = fsi_slave_report_and_clear_errors(slave);
+	if (!rc)
+		return 0;
+
+	/* send a TERM and retry */
+	if (master->term) {
+		rc = master->term(master, link, id);
+		if (!rc) {
+			rc = fsi_master_read(master, link, id, 0,
+					&reg, sizeof(reg));
+			if (!rc)
+				rc = fsi_slave_report_and_clear_errors(slave);
+			if (!rc)
+				return 0;
+		}
+	}
+
+	/* getting serious, reset the slave via BREAK */
+	rc = fsi_master_break(master, link);
+	if (rc)
+		return rc;
+
+	rc = fsi_slave_set_smode(master, link, id);
+	if (rc)
+		return rc;
+
+	return fsi_slave_report_and_clear_errors(slave);
+}
+
 int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
 			void *val, size_t size)
 {
 	uint8_t id = slave->id;
-	int rc;
+	int rc, err_rc, i;
 
 	rc = fsi_slave_calc_addr(slave, &addr, &id);
 	if (rc)
 		return rc;
 
-	return fsi_master_read(slave->master, slave->link, id,
-			addr, val, size);
+	for (i = 0; i < slave_retries; i++) {
+		rc = fsi_master_read(slave->master, slave->link,
+				id, addr, val, size);
+		if (!rc)
+			break;
+
+		err_rc = fsi_slave_handle_error(slave, false, addr, size);
+		if (err_rc)
+			break;
+	}
+
+	return rc;
 }
 EXPORT_SYMBOL_GPL(fsi_slave_read);
 
@@ -192,14 +287,24 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
 			const void *val, size_t size)
 {
 	uint8_t id = slave->id;
-	int rc;
+	int rc, err_rc, i;
 
 	rc = fsi_slave_calc_addr(slave, &addr, &id);
 	if (rc)
 		return rc;
 
-	return fsi_master_write(slave->master, slave->link, id,
-			addr, val, size);
+	for (i = 0; i < slave_retries; i++) {
+		rc = fsi_master_write(slave->master, slave->link,
+				id, addr, val, size);
+		if (!rc)
+			break;
+
+		err_rc = fsi_slave_handle_error(slave, true, addr, size);
+		if (err_rc)
+			break;
+	}
+
+	return rc;
 }
 EXPORT_SYMBOL_GPL(fsi_slave_write);
 
@@ -770,3 +875,5 @@ static void fsi_exit(void)
 
 module_init(fsi_init);
 module_exit(fsi_exit);
+module_param(discard_errors, int, 0664);
+MODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 18/24] drivers/fsi: Document FSI master sysfs files in ABI
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (16 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 17/24] drivers/fsi: Add error handling for slave Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 19/24] drivers/fsi: Add GPIO based FSI master Christopher Bostic
                   ` (5 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Add info for sysfs scan file in Documentaiton ABI/testing

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
v7 - Rename scan file to 'rescan'
   - Add 'raw' file details
   - Add 'term' file details
   - Add 'break' file details
   - Update kernel version for each file to 4.12
---
 Documentation/ABI/testing/sysfs-bus-fsi | 38 +++++++++++++++++++++++++++++++++
 1 file changed, 38 insertions(+)
 create mode 100644 Documentation/ABI/testing/sysfs-bus-fsi

diff --git a/Documentation/ABI/testing/sysfs-bus-fsi b/Documentation/ABI/testing/sysfs-bus-fsi
new file mode 100644
index 0000000..57c8063
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-fsi
@@ -0,0 +1,38 @@
+What:           /sys/bus/platform/devices/fsi-master/rescan
+Date:		May 2017
+KernelVersion:  4.12
+Contact:        cbostic@linux.vnet.ibm.com
+Description:
+                Initiates a FSI master scan for all connected slave devices
+		on its links.
+
+What:           /sys/bus/platform/devices/fsi-master/break
+Date:		May 2017
+KernelVersion:  4.12
+Contact:        cbostic@linux.vnet.ibm.com
+Description:
+		Sends an FSI BREAK command on a master's communication
+		link to any connnected slaves.  A BREAK resets connected
+		device's logic and preps it to receive further commands
+		from the master.
+
+What:           /sys/bus/platform/devices/fsi-master/slave@00:00/term
+Date:		May 2017
+KernelVersion:  4.12
+Contact:        cbostic@linux.vnet.ibm.com
+Description:
+		Sends an FSI terminate command from the master to its
+		connected slave. A terminate resets the slave's state machines
+		that control access to the internally connected engines.  In
+		addition the slave freezes its internal error register for
+		debugging purposes.  This command is also needed to abort any
+		ongoing operation in case of an expired 'Master Time Out'
+		timer.
+
+What:           /sys/bus/platform/devices/fsi-master/slave@00:00/raw
+Date:		May 2017
+KernelVersion:  4.12
+Contact:        cbostic@linux.vnet.ibm.com
+Description:
+		Provides a means of reading/writing a 32 bit value from/to a
+		specified FSI bus address.
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 19/24] drivers/fsi: Add GPIO based FSI master
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (17 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 18/24] drivers/fsi: Document FSI master sysfs files in ABI Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 20/24] drivers/fsi/gpio: Add tracepoints for GPIO master Christopher Bostic
                   ` (4 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh,
	Edward A . James, Jeremy Kerr

Implement a FSI master using GPIO.  Will generate FSI protocol for
read and write commands to particular addresses.  Sends master command
and waits for and decodes a slave response.

Includes changes from Edward A. James <eajames@us.ibm.com> and Jeremy
Kerr <jk@ozlabs.org>.

Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
v7 - Move global spinlock to struct fsi_master_gpio
   - Remove redundant checks for valid gpio descriptors
   - Clarify 'active low' comments
   - More descriptive comments for build_abs_ar_command()
   - Test MTOE count with == instead of >=
   - Clean up multi line and redundant comments
   - Check for negative return codes in issue_term()
   - Simplify input data processing in poll_for_response()
   - Remove empty function fsi_master_gpio_release()
   - Check return code for fsi_master_register()

v6 - Kconfig: remove 'depends on FSI' for GPIO master
   - Remove explicit kfree of devm_kzalloc master struct
---
 drivers/fsi/Kconfig           |  12 +
 drivers/fsi/Makefile          |   1 +
 drivers/fsi/fsi-master-gpio.c | 594 ++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 607 insertions(+)
 create mode 100644 drivers/fsi/fsi-master-gpio.c

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index e1006c6..ba16637 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -10,4 +10,16 @@ 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_GPIO
+	tristate "GPIO-based FSI master"
+	depends on GPIOLIB
+	select CRC4
+	---help---
+	This option enables a FSI master driver using GPIO lines.
+
+endif
+
 endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index db0e5e7..ed28ac0 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,2 +1,3 @@
 
 obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
new file mode 100644
index 0000000..d467e61
--- /dev/null
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -0,0 +1,594 @@
+/*
+ * A FSI master controller, using a simple GPIO bit-banging interface
+ */
+
+#include <linux/crc4.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fsi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "fsi-master.h"
+
+#define	FSI_GPIO_STD_DLY	1	/* Standard pin delay in nS */
+#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	/* Clock out any old data */
+#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      0x2
+#define	FSI_GPIO_CMD_TERM	0x3f
+#define FSI_GPIO_CMD_ABS_AR	0x4
+
+#define	FSI_GPIO_DPOLL_CLOCKS	100      /* < 21 will cause slave to hang */
+
+/* Bus errors */
+#define	FSI_GPIO_ERR_BUSY	1	/* Slave stuck in busy state */
+#define	FSI_GPIO_RESP_ERRA	2	/* Any (misc) Error */
+#define	FSI_GPIO_RESP_ERRC	3	/* Slave reports master CRC error */
+#define	FSI_GPIO_MTOE		4	/* Master time out error */
+#define	FSI_GPIO_CRC_INVAL	5	/* Master reports slave CRC error */
+
+/* Normal slave responses */
+#define	FSI_GPIO_RESP_BUSY	1
+#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_DRAIN_BITS	20
+#define	FSI_GPIO_CRC_SIZE	4
+#define	FSI_GPIO_MSG_ID_SIZE		2
+#define	FSI_GPIO_MSG_RESPID_SIZE	2
+#define	FSI_GPIO_PRIME_SLAVE_CLOCKS	100
+
+struct fsi_master_gpio {
+	struct fsi_master	master;
+	struct device		*dev;
+	spinlock_t		cmd_lock;	/* Lock for commands */
+	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)
+
+struct fsi_gpio_msg {
+	uint64_t	msg;
+	uint8_t		bits;
+};
+
+static void clock_toggle(struct fsi_master_gpio *master, int count)
+{
+	int i;
+
+	for (i = 0; i < count; i++) {
+		ndelay(FSI_GPIO_STD_DLY);
+		gpiod_set_value(master->gpio_clk, 0);
+		ndelay(FSI_GPIO_STD_DLY);
+		gpiod_set_value(master->gpio_clk, 1);
+	}
+}
+
+static int sda_in(struct fsi_master_gpio *master)
+{
+	int in;
+
+	ndelay(FSI_GPIO_STD_DLY);
+	in = gpiod_get_value(master->gpio_data);
+	return in ? 1 : 0;
+}
+
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+	gpiod_set_value(master->gpio_data, value);
+}
+
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+	gpiod_direction_input(master->gpio_data);
+	gpiod_set_value(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master, int value)
+{
+	gpiod_set_value(master->gpio_trans, 1);
+	gpiod_direction_output(master->gpio_data, value);
+}
+
+static void clock_zeros(struct fsi_master_gpio *master, int count)
+{
+	set_sda_output(master, 1);
+	clock_toggle(master, count);
+}
+
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
+			uint8_t num_bits)
+{
+	uint8_t bit, in_bit;
+
+	set_sda_input(master);
+
+	for (bit = 0; bit < num_bits; bit++) {
+		clock_toggle(master, 1);
+		in_bit = sda_in(master);
+		msg->msg <<= 1;
+		msg->msg |= ~in_bit & 0x1;	/* Data is active low */
+	}
+	msg->bits += num_bits;
+}
+
+static void serial_out(struct fsi_master_gpio *master,
+			const struct fsi_gpio_msg *cmd)
+{
+	uint8_t bit;
+	uint64_t msg = ~cmd->msg;	/* Data is active low */
+	uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
+	uint64_t last_bit = ~0;
+	int next_bit;
+
+	if (!cmd->bits) {
+		dev_warn(master->dev, "trying to output 0 bits\n");
+		return;
+	}
+	set_sda_output(master, 0);
+
+	/* Send the start bit */
+	sda_out(master, 0);
+	clock_toggle(master, 1);
+
+	/* Send the message */
+	for (bit = 0; bit < cmd->bits; bit++) {
+		next_bit = (msg & sda_mask) >> (cmd->bits - 1);
+		if (last_bit ^ next_bit) {
+			sda_out(master, next_bit);
+			last_bit = next_bit;
+		}
+		clock_toggle(master, 1);
+		msg <<= 1;
+	}
+}
+
+static void msg_push_bits(struct fsi_gpio_msg *msg, uint64_t data, int bits)
+{
+	msg->msg <<= bits;
+	msg->msg |= data & ((1ull << bits) - 1);
+	msg->bits += bits;
+}
+
+static void msg_push_crc(struct fsi_gpio_msg *msg)
+{
+	uint8_t crc;
+	int top;
+
+	top = msg->bits & 0x3;
+
+	/* start bit, and any non-aligned top bits */
+	crc = crc4(0, 1 << top | msg->msg >> (msg->bits - top), top + 1);
+
+	/* aligned bits */
+	crc = crc4(crc, msg->msg, msg->bits - top);
+
+	msg_push_bits(msg, crc, 4);
+}
+
+/*
+ * Encode an Absolute Address command
+ */
+static void build_abs_ar_command(struct fsi_gpio_msg *cmd,
+		uint8_t id, uint32_t addr, size_t size, const void *data)
+{
+	bool write = !!data;
+	uint8_t ds;
+	int i;
+
+	cmd->bits = 0;
+	cmd->msg = 0;
+
+	msg_push_bits(cmd, id, 2);
+	msg_push_bits(cmd, FSI_GPIO_CMD_ABS_AR, 3);
+	msg_push_bits(cmd, write ? 0 : 1, 1);
+
+	/*
+	 * The read/write size is encoded in the lower bits of the address
+	 * (as it must be naturally-aligned), and the following ds bit.
+	 *
+	 *	size	addr:1	addr:0	ds
+	 *	1	x	x	0
+	 *	2	x	0	1
+	 *	4	0	1	1
+	 *
+	 */
+	ds = size > 1 ? 1 : 0;
+	addr &= ~(size - 1);
+	if (size == 4)
+		addr |= 1;
+
+	msg_push_bits(cmd, addr & ((1 << 21) - 1), 21);
+	msg_push_bits(cmd, ds, 1);
+	for (i = 0; write && i < size; i++)
+		msg_push_bits(cmd, ((uint8_t *)data)[i], 8);
+
+	msg_push_crc(cmd);
+}
+
+static void build_dpoll_command(struct fsi_gpio_msg *cmd, uint8_t slave_id)
+{
+	cmd->bits = 0;
+	cmd->msg = 0;
+
+	msg_push_bits(cmd, slave_id, 2);
+	msg_push_bits(cmd, FSI_GPIO_CMD_DPOLL, 3);
+	msg_push_crc(cmd);
+}
+
+static void echo_delay(struct fsi_master_gpio *master)
+{
+	set_sda_output(master, 1);
+	clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+static void build_term_command(struct fsi_gpio_msg *cmd, uint8_t slave_id)
+{
+	cmd->bits = 0;
+	cmd->msg = 0;
+
+	msg_push_bits(cmd, slave_id, 2);
+	msg_push_bits(cmd, FSI_GPIO_CMD_TERM, 6);
+	msg_push_crc(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)
+{
+
+}
+
+static int read_one_response(struct fsi_master_gpio *master,
+		uint8_t data_size, struct fsi_gpio_msg *msgp, uint8_t *tagp)
+{
+	struct fsi_gpio_msg msg;
+	uint8_t id, tag;
+	uint32_t crc;
+	int i;
+
+	/* wait for the start bit */
+	for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+		msg.bits = 0;
+		msg.msg = 0;
+		serial_in(master, &msg, 1);
+		if (msg.msg)
+			break;
+	}
+	if (i == FSI_GPIO_MTOE_COUNT) {
+		dev_dbg(master->dev,
+			"Master time out waiting for response\n");
+		fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+		return -EIO;
+	}
+
+	msg.bits = 0;
+	msg.msg = 0;
+
+	/* Read slave ID & response tag */
+	serial_in(master, &msg, 4);
+
+	id = (msg.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
+	tag = msg.msg & 0x3;
+
+	/* If we have an ACK and we're expecting data, clock the data in too */
+	if (tag == FSI_GPIO_RESP_ACK && data_size)
+		serial_in(master, &msg, data_size * 8);
+
+	/* read CRC */
+	serial_in(master, &msg, FSI_GPIO_CRC_SIZE);
+
+	/* we have a whole message now; check CRC */
+	crc = crc4(0, 1, 1);
+	crc = crc4(crc, msg.msg, msg.bits);
+	if (crc) {
+		dev_dbg(master->dev, "ERR response CRC\n");
+		fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
+		return -EIO;
+	}
+
+	if (msgp)
+		*msgp = msg;
+	if (tagp)
+		*tagp = tag;
+
+	return 0;
+}
+
+static int issue_term(struct fsi_master_gpio *master, uint8_t slave)
+{
+	struct fsi_gpio_msg cmd;
+	uint8_t tag;
+	int rc;
+
+	build_term_command(&cmd, slave);
+	serial_out(master, &cmd);
+	echo_delay(master);
+
+	rc = read_one_response(master, 0, NULL, &tag);
+	if (rc < 0) {
+		dev_err(master->dev,
+				"TERM failed; lost communication with slave\n");
+		return -EIO;
+	} else if (tag != FSI_GPIO_RESP_ACK) {
+		dev_err(master->dev, "TERM failed; response %d\n", tag);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int poll_for_response(struct fsi_master_gpio *master,
+		uint8_t slave, uint8_t size, void *data)
+{
+	struct fsi_gpio_msg response, cmd;
+	int busy_count = 0, rc, i;
+	uint8_t tag;
+	uint8_t *data_byte = data;
+
+retry:
+	rc = read_one_response(master, size, &response, &tag);
+	if (rc)
+		return rc;
+
+	switch (tag) {
+	case FSI_GPIO_RESP_ACK:
+		if (size && data) {
+			uint64_t val = response.msg;
+			/* clear crc & mask */
+			val >>= 4;
+			val &= (1ull << (size * 8)) - 1;
+
+			for (i = 0; i < size; i++) {
+				data_byte[size-i-1] = val;
+				val >>= 8;
+			}
+		}
+		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.
+		 */
+		clock_zeros(master, FSI_GPIO_DPOLL_CLOCKS);
+		if (busy_count++ < FSI_GPIO_MAX_BUSY) {
+			build_dpoll_command(&cmd, slave);
+			serial_out(master, &cmd);
+			echo_delay(master);
+			goto retry;
+		}
+		dev_warn(master->dev,
+			"ERR slave is stuck in busy state, issuing TERM\n");
+		issue_term(master, slave);
+		rc = -EIO;
+		break;
+
+	case FSI_GPIO_RESP_ERRA:
+	case FSI_GPIO_RESP_ERRC:
+		dev_dbg(master->dev, "ERR%c received: 0x%x\n",
+			tag == FSI_GPIO_RESP_ERRA ? 'A' : 'C',
+			(int)response.msg);
+		fsi_master_gpio_error(master, response.msg);
+		rc = -EIO;
+		break;
+	}
+
+	/* Clock the slave enough to be ready for next operation */
+	clock_zeros(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
+	return rc;
+}
+
+static int fsi_master_gpio_xfer(struct fsi_master_gpio *master, uint8_t slave,
+		struct fsi_gpio_msg *cmd, size_t resp_len, void *resp)
+{
+	unsigned long flags;
+	int rc;
+
+	spin_lock_irqsave(&master->cmd_lock, flags);
+	serial_out(master, cmd);
+	echo_delay(master);
+	rc = poll_for_response(master, slave, resp_len, resp);
+	spin_unlock_irqrestore(&master->cmd_lock, flags);
+
+	return rc;
+}
+
+static int fsi_master_gpio_read(struct fsi_master *_master, int link,
+		uint8_t id, uint32_t addr, void *val, size_t size)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
+
+	if (link != 0)
+		return -ENODEV;
+
+	build_abs_ar_command(&cmd, id, addr, size, NULL);
+	return fsi_master_gpio_xfer(master, id, &cmd, size, val);
+}
+
+static int fsi_master_gpio_write(struct fsi_master *_master, int link,
+		uint8_t id, uint32_t addr, const void *val, size_t size)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
+
+	if (link != 0)
+		return -ENODEV;
+
+	build_abs_ar_command(&cmd, id, addr, size, val);
+	return fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+}
+
+static int fsi_master_gpio_term(struct fsi_master *_master,
+		int link, uint8_t id)
+{
+	struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+	struct fsi_gpio_msg cmd;
+
+	if (link != 0)
+		return -ENODEV;
+
+	build_term_command(&cmd, id);
+	return fsi_master_gpio_xfer(master, id, &cmd, 0, NULL);
+}
+
+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;
+
+	set_sda_output(master, 1);
+	sda_out(master, 1);
+	clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
+	sda_out(master, 0);
+	clock_toggle(master, FSI_BREAK_CLOCKS);
+	echo_delay(master);
+	sda_out(master, 1);
+	clock_toggle(master, FSI_POST_BREAK_CLOCKS);
+
+	/* Wait for logic reset to take effect */
+	udelay(200);
+
+	return 0;
+}
+
+static void fsi_master_gpio_init(struct fsi_master_gpio *master)
+{
+	gpiod_direction_output(master->gpio_mux, 1);
+	gpiod_direction_output(master->gpio_trans, 1);
+	gpiod_direction_output(master->gpio_enable, 1);
+	gpiod_direction_output(master->gpio_clk, 1);
+	gpiod_direction_output(master->gpio_data, 1);
+
+	/* todo: evaluate if clocks can be reduced */
+	clock_zeros(master, FSI_INIT_CLOCKS);
+}
+
+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;
+	gpiod_set_value(master->gpio_enable, 1);
+
+	return 0;
+}
+
+static int fsi_master_gpio_probe(struct platform_device *pdev)
+{
+	struct fsi_master_gpio *master;
+	struct gpio_desc *gpio;
+
+	master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+	if (!master)
+		return -ENOMEM;
+
+	master->dev = &pdev->dev;
+	master->master.dev.parent = master->dev;
+
+	gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
+	if (IS_ERR(gpio)) {
+		dev_err(&pdev->dev, "failed to get clock gpio\n");
+		return PTR_ERR(gpio);
+	}
+	master->gpio_clk = gpio;
+
+	gpio = devm_gpiod_get(&pdev->dev, "data", 0);
+	if (IS_ERR(gpio)) {
+		dev_err(&pdev->dev, "failed to get data gpio\n");
+		return PTR_ERR(gpio);
+	}
+	master->gpio_data = gpio;
+
+	/* Optional GPIOs */
+	gpio = devm_gpiod_get_optional(&pdev->dev, "trans", 0);
+	if (IS_ERR(gpio)) {
+		dev_err(&pdev->dev, "failed to get trans gpio\n");
+		return PTR_ERR(gpio);
+	}
+	master->gpio_trans = gpio;
+
+	gpio = devm_gpiod_get_optional(&pdev->dev, "enable", 0);
+	if (IS_ERR(gpio)) {
+		dev_err(&pdev->dev, "failed to get enable gpio\n");
+		return PTR_ERR(gpio);
+	}
+	master->gpio_enable = gpio;
+
+	gpio = devm_gpiod_get_optional(&pdev->dev, "mux", 0);
+	if (IS_ERR(gpio)) {
+		dev_err(&pdev->dev, "failed to get mux gpio\n");
+		return PTR_ERR(gpio);
+	}
+	master->gpio_mux = gpio;
+
+	master->master.n_links = 1;
+	master->master.read = fsi_master_gpio_read;
+	master->master.write = fsi_master_gpio_write;
+	master->master.term = fsi_master_gpio_term;
+	master->master.send_break = fsi_master_gpio_break;
+	master->master.link_enable = fsi_master_gpio_link_enable;
+	platform_set_drvdata(pdev, master);
+	spin_lock_init(&master->cmd_lock);
+
+	fsi_master_gpio_init(master);
+
+	return fsi_master_register(&master->master);
+}
+
+
+static int fsi_master_gpio_remove(struct platform_device *pdev)
+{
+	struct fsi_master_gpio *master = platform_get_drvdata(pdev);
+
+	devm_gpiod_put(&pdev->dev, master->gpio_clk);
+	devm_gpiod_put(&pdev->dev, master->gpio_data);
+	if (master->gpio_trans)
+		devm_gpiod_put(&pdev->dev, master->gpio_trans);
+	if (master->gpio_enable)
+		devm_gpiod_put(&pdev->dev, master->gpio_enable);
+	if (master->gpio_mux)
+		devm_gpiod_put(&pdev->dev, master->gpio_mux);
+	fsi_master_unregister(&master->master);
+
+	return 0;
+}
+
+static const struct of_device_id fsi_master_gpio_match[] = {
+	{ .compatible = "fsi-master-gpio" },
+	{ },
+};
+
+static struct platform_driver fsi_master_gpio_driver = {
+	.driver = {
+		.name		= "fsi-master-gpio",
+		.of_match_table	= fsi_master_gpio_match,
+	},
+	.probe	= fsi_master_gpio_probe,
+	.remove = fsi_master_gpio_remove,
+};
+
+module_platform_driver(fsi_master_gpio_driver);
+MODULE_LICENSE("GPL");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 20/24] drivers/fsi/gpio: Add tracepoints for GPIO master
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (18 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 19/24] drivers/fsi: Add GPIO based FSI master Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 21/24] drivers/fsi: Add SCOM FSI client device driver Christopher Bostic
                   ` (3 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

Trace low level input/output GPIO operations.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-master-gpio.c          |  9 +++++
 include/trace/events/fsi_master_gpio.h | 68 ++++++++++++++++++++++++++++++++++
 2 files changed, 77 insertions(+)
 create mode 100644 include/trace/events/fsi_master_gpio.h

diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index d467e61..a5d6e70 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -61,6 +61,9 @@ struct fsi_master_gpio {
 	struct gpio_desc	*gpio_mux;	/* Mux control */
 };
 
+#define CREATE_TRACE_POINTS
+#include <trace/events/fsi_master_gpio.h>
+
 #define to_fsi_master_gpio(m) container_of(m, struct fsi_master_gpio, master)
 
 struct fsi_gpio_msg {
@@ -126,6 +129,8 @@ static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *msg,
 		msg->msg |= ~in_bit & 0x1;	/* Data is active low */
 	}
 	msg->bits += num_bits;
+
+	trace_fsi_master_gpio_in(master, num_bits, msg->msg);
 }
 
 static void serial_out(struct fsi_master_gpio *master,
@@ -137,6 +142,8 @@ static void serial_out(struct fsi_master_gpio *master,
 	uint64_t last_bit = ~0;
 	int next_bit;
 
+	trace_fsi_master_gpio_out(master, cmd->bits, cmd->msg);
+
 	if (!cmd->bits) {
 		dev_warn(master->dev, "trying to output 0 bits\n");
 		return;
@@ -458,6 +465,8 @@ static int fsi_master_gpio_break(struct fsi_master *_master, int link)
 	if (link != 0)
 		return -ENODEV;
 
+	trace_fsi_master_gpio_break(master);
+
 	set_sda_output(master, 1);
 	sda_out(master, 1);
 	clock_toggle(master, FSI_PRE_BREAK_CLOCKS);
diff --git a/include/trace/events/fsi_master_gpio.h b/include/trace/events/fsi_master_gpio.h
new file mode 100644
index 0000000..11b36c1
--- /dev/null
+++ b/include/trace/events/fsi_master_gpio.h
@@ -0,0 +1,68 @@
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM fsi_master_gpio
+
+#if !defined(_TRACE_FSI_MASTER_GPIO_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_FSI_MASTER_GPIO_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(fsi_master_gpio_in,
+	TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+	TP_ARGS(master, bits, msg),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	bits)
+		__field(uint64_t, msg)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->master.idx;
+		__entry->bits = bits;
+		__entry->msg  = msg & ((1ull<<bits) - 1);
+	),
+	TP_printk("fsi-gpio%d => %0*llx[%d]",
+		__entry->master_idx,
+		(__entry->bits + 3) / 4,
+		__entry->msg,
+		__entry->bits
+	)
+);
+
+TRACE_EVENT(fsi_master_gpio_out,
+	TP_PROTO(const struct fsi_master_gpio *master, int bits, uint64_t msg),
+	TP_ARGS(master, bits, msg),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+		__field(int,	bits)
+		__field(uint64_t, msg)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->master.idx;
+		__entry->bits = bits;
+		__entry->msg  = msg & ((1ull<<bits) - 1);
+	),
+	TP_printk("fsi-gpio%d <= %0*llx[%d]",
+		__entry->master_idx,
+		(__entry->bits + 3) / 4,
+		__entry->msg,
+		__entry->bits
+	)
+);
+
+TRACE_EVENT(fsi_master_gpio_break,
+	TP_PROTO(const struct fsi_master_gpio *master),
+	TP_ARGS(master),
+	TP_STRUCT__entry(
+		__field(int,	master_idx)
+	),
+	TP_fast_assign(
+		__entry->master_idx = master->master.idx;
+	),
+	TP_printk("fsi-gpio%d ----break---",
+		__entry->master_idx
+	)
+);
+
+#endif /* _TRACE_FSI_MASTER_GPIO_H */
+
+#include <trace/define_trace.h>
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 21/24] drivers/fsi: Add SCOM FSI client device driver
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (19 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 20/24] drivers/fsi/gpio: Add tracepoints for GPIO master Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 22/24] drivers/fsi: Add hub master support Christopher Bostic
                   ` (2 subsequent siblings)
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh,
	Edward A . James, Jeremy Kerr

Create a simple SCOM engine device driver that reads and writes
its control registers via an FSI bus.

Includes changes from Edward A. James <eajames@us.ibm.com>.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Joel Stanley <joel@jms.id.au>
Signed-off-by: Edward A. James <eajames@us.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
 drivers/fsi/Kconfig    |   5 +
 drivers/fsi/Makefile   |   1 +
 drivers/fsi/fsi-scom.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 269 insertions(+)
 create mode 100644 drivers/fsi/fsi-scom.c

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index ba16637..5582523 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -20,6 +20,11 @@ config FSI_MASTER_GPIO
 	---help---
 	This option enables a FSI master driver using GPIO lines.
 
+config FSI_SCOM
+	tristate "SCOM FSI client device driver"
+	---help---
+	This option enables an FSI based SCOM device driver.
+
 endif
 
 endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index ed28ac0..3466f08 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,3 +1,4 @@
 
 obj-$(CONFIG_FSI) += fsi-core.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
new file mode 100644
index 0000000..98d062f
--- /dev/null
+++ b/drivers/fsi/fsi-scom.c
@@ -0,0 +1,263 @@
+/*
+ * 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
+ * MERGCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/cdev.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/miscdevice.h>
+#include <linux/list.h>
+#include <linux/idr.h>
+
+#define FSI_ENGID_SCOM		0x5
+
+#define SCOM_FSI2PIB_DELAY	50
+
+/* SCOM engine register set */
+#define SCOM_DATA0_REG		0x00
+#define SCOM_DATA1_REG		0x04
+#define SCOM_CMD_REG		0x08
+#define SCOM_RESET_REG		0x1C
+
+#define SCOM_RESET_CMD		0x80000000
+#define SCOM_WRITE_CMD		0x80000000
+
+struct scom_device {
+	struct list_head link;
+	struct fsi_device *fsi_dev;
+	struct miscdevice mdev;
+	char	name[32];
+	int idx;
+};
+
+#define to_scom_dev(x)		container_of((x), struct scom_device, mdev)
+
+static struct list_head scom_devices;
+
+static DEFINE_IDA(scom_ida);
+
+static int put_scom(struct scom_device *scom_dev, uint64_t value,
+			uint32_t addr)
+{
+	int rc;
+	uint32_t data;
+
+	data = cpu_to_be32(SCOM_RESET_CMD);
+	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	data = cpu_to_be32((value >> 32) & 0xffffffff);
+	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	data = cpu_to_be32(value & 0xffffffff);
+	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	data = cpu_to_be32(SCOM_WRITE_CMD | addr);
+	return fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+				sizeof(uint32_t));
+}
+
+static int get_scom(struct scom_device *scom_dev, uint64_t *value,
+			uint32_t addr)
+{
+	uint32_t result, data;
+	int rc;
+
+	*value = 0ULL;
+	data = cpu_to_be32(addr);
+	rc = fsi_device_write(scom_dev->fsi_dev, SCOM_CMD_REG, &data,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA0_REG, &result,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	*value |= (uint64_t)cpu_to_be32(result) << 32;
+	rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
+				sizeof(uint32_t));
+	if (rc)
+		return rc;
+
+	*value |= cpu_to_be32(result);
+
+	return 0;
+}
+
+static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
+			loff_t *offset)
+{
+	int rc;
+	struct miscdevice *mdev =
+				(struct miscdevice *)filep->private_data;
+	struct scom_device *scom = to_scom_dev(mdev);
+	struct device *dev = &scom->fsi_dev->dev;
+	uint64_t val;
+
+	if (len != sizeof(uint64_t))
+		return -EINVAL;
+
+	rc = get_scom(scom, &val, *offset);
+	if (rc) {
+		dev_dbg(dev, "get_scom fail:%d\n", rc);
+		return rc;
+	}
+
+	rc = copy_to_user(buf, &val, len);
+	if (rc)
+		dev_dbg(dev, "copy to user failed:%d\n", rc);
+
+	return rc ? rc : len;
+}
+
+static ssize_t scom_write(struct file *filep, const char __user *buf,
+			size_t len, loff_t *offset)
+{
+	int rc;
+	struct miscdevice *mdev = filep->private_data;
+	struct scom_device *scom = to_scom_dev(mdev);
+	struct device *dev = &scom->fsi_dev->dev;
+	uint64_t val;
+
+	if (len != sizeof(uint64_t))
+		return -EINVAL;
+
+	rc = copy_from_user(&val, buf, len);
+	if (rc) {
+		dev_dbg(dev, "copy from user failed:%d\n", rc);
+		return -EINVAL;
+	}
+
+	rc = put_scom(scom, val, *offset);
+	if (rc) {
+		dev_dbg(dev, "put_scom failed with:%d\n", rc);
+		return rc;
+	}
+
+	return len;
+}
+
+static loff_t scom_llseek(struct file *file, loff_t offset, int whence)
+{
+	switch (whence) {
+	case SEEK_CUR:
+		break;
+	case SEEK_SET:
+		file->f_pos = offset;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return offset;
+}
+
+static const struct file_operations scom_fops = {
+	.owner	= THIS_MODULE,
+	.llseek	= scom_llseek,
+	.read	= scom_read,
+	.write	= scom_write,
+};
+
+static int scom_probe(struct device *dev)
+{
+	struct fsi_device *fsi_dev = to_fsi_dev(dev);
+	struct scom_device *scom;
+
+	scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+	if (!scom)
+		return -ENOMEM;
+
+	scom->idx = ida_simple_get(&scom_ida, 1, INT_MAX, GFP_KERNEL);
+	snprintf(scom->name, sizeof(scom->name), "scom%d", scom->idx);
+	scom->fsi_dev = fsi_dev;
+	scom->mdev.minor = MISC_DYNAMIC_MINOR;
+	scom->mdev.fops = &scom_fops;
+	scom->mdev.name = scom->name;
+	scom->mdev.parent = dev;
+	list_add(&scom->link, &scom_devices);
+
+	return misc_register(&scom->mdev);
+}
+
+static int scom_remove(struct device *dev)
+{
+	struct scom_device *scom, *scom_tmp;
+	struct fsi_device *fsi_dev = to_fsi_dev(dev);
+
+	list_for_each_entry_safe(scom, scom_tmp, &scom_devices, link) {
+		if (scom->fsi_dev == fsi_dev) {
+			list_del(&scom->link);
+			ida_simple_remove(&scom_ida, scom->idx);
+			misc_deregister(&scom->mdev);
+		}
+	}
+
+	return 0;
+}
+
+static struct fsi_device_id scom_ids[] = {
+	{
+		.engine_type = FSI_ENGID_SCOM,
+		.version = FSI_VERSION_ANY,
+	},
+	{ 0 }
+};
+
+static struct fsi_driver scom_drv = {
+	.id_table = scom_ids,
+	.drv = {
+		.name = "scom",
+		.bus = &fsi_bus_type,
+		.probe = scom_probe,
+		.remove = scom_remove,
+	}
+};
+
+static int scom_init(void)
+{
+	INIT_LIST_HEAD(&scom_devices);
+	return fsi_driver_register(&scom_drv);
+}
+
+static void scom_exit(void)
+{
+	struct list_head *pos;
+	struct scom_device *scom;
+
+	list_for_each(pos, &scom_devices) {
+		scom = list_entry(pos, struct scom_device, link);
+		misc_deregister(&scom->mdev);
+		devm_kfree(&scom->fsi_dev->dev, scom);
+	}
+	fsi_driver_unregister(&scom_drv);
+}
+
+module_init(scom_init);
+module_exit(scom_exit);
+MODULE_LICENSE("GPL");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 22/24] drivers/fsi: Add hub master support
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (20 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 21/24] drivers/fsi: Add SCOM FSI client device driver Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 23/24] drivers/fsi: Use asynchronous slave mode Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 24/24] drivers/fsi: Add module license to core driver Christopher Bostic
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh,
	Jeremy Kerr

Add an engine driver to expose a "hub" FSI master - which has a set of
control registers in the engine address space, and uses a chunk of the
slave address space for actual FSI communication.

Additional changes from Jeremy Kerr <jk@ozlabs.org>.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Joel Stanley <joel@jms.id.au>
---
 drivers/fsi/Kconfig          |   8 ++
 drivers/fsi/Makefile         |   1 +
 drivers/fsi/fsi-master-hub.c | 327 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 336 insertions(+)
 create mode 100644 drivers/fsi/fsi-master-hub.c

diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 5582523..6821ed0 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -20,6 +20,14 @@ config FSI_MASTER_GPIO
 	---help---
 	This option enables a FSI master driver using GPIO lines.
 
+config FSI_MASTER_HUB
+	tristate "FSI hub master"
+	---help---
+	This option enables a FSI hub master driver.  Hub is a type of FSI
+	master that is connected to the upstream master via a slave.  Hubs
+	allow chaining of FSI links to an arbitrary depth.  This allows for
+	a high target device fanout.
+
 config FSI_SCOM
 	tristate "SCOM FSI client device driver"
 	---help---
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 3466f08..65eb99d 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,4 +1,5 @@
 
 obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_HUB) += fsi-master-hub.o
 obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
 obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
new file mode 100644
index 0000000..133b9bf
--- /dev/null
+++ b/drivers/fsi/fsi-master-hub.c
@@ -0,0 +1,327 @@
+/*
+ * FSI hub master 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/delay.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "fsi-master.h"
+
+/* Control Registers */
+#define FSI_MMODE		0x0		/* R/W: mode */
+#define FSI_MDLYR		0x4		/* R/W: delay */
+#define FSI_MCRSP		0x8		/* R/W: clock rate */
+#define FSI_MENP0		0x10		/* R/W: enable */
+#define FSI_MLEVP0		0x18		/* R: plug detect */
+#define FSI_MSENP0		0x18		/* S: Set enable */
+#define FSI_MCENP0		0x20		/* C: Clear enable */
+#define FSI_MAEB		0x70		/* R: Error address */
+#define FSI_MVER		0x74		/* R: master version/type */
+#define FSI_MRESP0		0xd0		/* W: Port reset */
+#define FSI_MESRB0		0x1d0		/* R: Master error status */
+#define FSI_MRESB0		0x1d0		/* W: Reset bridge */
+#define FSI_MECTRL		0x2e0		/* W: Error control */
+
+/* MMODE: Mode control */
+#define FSI_MMODE_EIP		0x80000000	/* Enable interrupt polling */
+#define FSI_MMODE_ECRC		0x40000000	/* Enable error recovery */
+#define FSI_MMODE_EPC		0x10000000	/* Enable parity checking */
+#define FSI_MMODE_P8_TO_LSB	0x00000010	/* Timeout value LSB */
+						/*   MSB=1, LSB=0 is 0.8 ms */
+						/*   MSB=0, LSB=1 is 0.9 ms */
+#define FSI_MMODE_CRS0SHFT	18		/* Clk rate selection 0 shift */
+#define FSI_MMODE_CRS0MASK	0x3ff		/* Clk rate selection 0 mask */
+#define FSI_MMODE_CRS1SHFT	8		/* Clk rate selection 1 shift */
+#define FSI_MMODE_CRS1MASK	0x3ff		/* Clk rate selection 1 mask */
+
+/* MRESB: Reset brindge */
+#define FSI_MRESB_RST_GEN	0x80000000	/* General reset */
+#define FSI_MRESB_RST_ERR	0x40000000	/* Error Reset */
+
+/* MRESB: Reset port */
+#define FSI_MRESP_RST_ALL_MASTER 0x20000000	/* Reset all FSI masters */
+#define FSI_MRESP_RST_ALL_LINK	0x10000000	/* Reset all FSI port contr. */
+#define FSI_MRESP_RST_MCR	0x08000000	/* Reset FSI master reg. */
+#define FSI_MRESP_RST_PYE	0x04000000	/* Reset FSI parity error */
+#define FSI_MRESP_RST_ALL	0xfc000000	/* Reset any error */
+
+/* MECTRL: Error control */
+#define FSI_MECTRL_EOAE		0x8000		/* Enable machine check when */
+						/* master 0 in error */
+#define FSI_MECTRL_P8_AUTO_TERM	0x4000		/* Auto terminate */
+
+#define FSI_ENGID_HUB_MASTER		0x1c
+#define FSI_HUB_LINK_OFFSET		0x80000
+#define FSI_HUB_LINK_SIZE		0x80000
+#define FSI_HUB_MASTER_MAX_LINKS	8
+
+#define FSI_LINK_ENABLE_SETUP_TIME	10	/* in mS */
+
+/*
+ * FSI hub master support
+ *
+ * A hub master increases the number of potential target devices that the
+ * primary FSI master can access. For each link a primary master supports,
+ * each of those links can in turn be chained to a hub master with multiple
+ * links of its own.
+ *
+ * The hub is controlled by a set of control registers exposed as a regular fsi
+ * device (the hub->upstream device), and provides access to the downstream FSI
+ * bus as through an address range on the slave itself (->addr and ->size).
+ *
+ * [This differs from "cascaded" masters, which expose the entire downstream
+ * bus entirely through the fsi device address range, and so have a smaller
+ * accessible address space.]
+ */
+struct fsi_master_hub {
+	struct fsi_master	master;
+	struct fsi_device	*upstream;
+	uint32_t		addr, size;	/* slave-relative addr of */
+						/* master address space */
+};
+
+#define to_fsi_master_hub(m) container_of(m, struct fsi_master_hub, master)
+
+static int hub_master_read(struct fsi_master *master, int link,
+			uint8_t id, uint32_t addr, void *val, size_t size)
+{
+	struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+	if (id != 0)
+		return -EINVAL;
+
+	addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+	return fsi_slave_read(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_write(struct fsi_master *master, int link,
+			uint8_t id, uint32_t addr, const void *val, size_t size)
+{
+	struct fsi_master_hub *hub = to_fsi_master_hub(master);
+
+	if (id != 0)
+		return -EINVAL;
+
+	addr += hub->addr + (link * FSI_HUB_LINK_SIZE);
+	return fsi_slave_write(hub->upstream->slave, addr, val, size);
+}
+
+static int hub_master_break(struct fsi_master *master, int link)
+{
+	uint32_t addr, cmd;
+
+	addr = 0x4;
+	cmd = cpu_to_be32(0xc0de0000);
+
+	return hub_master_write(master, link, 0, addr, &cmd, sizeof(cmd));
+}
+
+static int hub_master_link_enable(struct fsi_master *master, int link)
+{
+	struct fsi_master_hub *hub = to_fsi_master_hub(master);
+	int idx, bit;
+	__be32 reg;
+	int rc;
+
+	idx = link / 32;
+	bit = link % 32;
+
+	reg = cpu_to_be32(0x80000000 >> bit);
+
+	rc = fsi_device_write(hub->upstream, FSI_MSENP0 + (4 * idx), &reg, 4);
+
+	mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+	fsi_device_read(hub->upstream, FSI_MENP0 + (4 * idx), &reg, 4);
+
+	return rc;
+}
+
+static void hub_master_release(struct device *dev)
+{
+	struct fsi_master_hub *hub = to_fsi_master_hub(dev_to_fsi_master(dev));
+
+	kfree(hub);
+}
+
+/* mmode encoders */
+static inline u32 fsi_mmode_crs0(u32 x)
+{
+	return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
+}
+
+static inline u32 fsi_mmode_crs1(u32 x)
+{
+	return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+}
+
+static int hub_master_init(struct fsi_master_hub *hub)
+{
+	struct fsi_device *dev = hub->upstream;
+	__be32 reg;
+	int rc;
+
+	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+			| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	/* Initialize the MFSI (hub master) engine */
+	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+			| FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE);
+	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = cpu_to_be32(FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM);
+	rc = fsi_device_write(dev, FSI_MECTRL, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = cpu_to_be32(FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
+			| fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
+			| FSI_MMODE_P8_TO_LSB);
+	rc = fsi_device_write(dev, FSI_MMODE, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = cpu_to_be32(0xffff0000);
+	rc = fsi_device_write(dev, FSI_MDLYR, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = ~0;
+	rc = fsi_device_write(dev, FSI_MSENP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	/* Leave enabled long enough for master logic to set up */
+	mdelay(FSI_LINK_ENABLE_SETUP_TIME);
+
+	rc = fsi_device_write(dev, FSI_MCENP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	rc = fsi_device_read(dev, FSI_MAEB, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = cpu_to_be32(FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK);
+	rc = fsi_device_write(dev, FSI_MRESP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	rc = fsi_device_read(dev, FSI_MLEVP0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	/* Reset the master bridge */
+	reg = cpu_to_be32(FSI_MRESB_RST_GEN);
+	rc = fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
+	if (rc)
+		return rc;
+
+	reg = cpu_to_be32(FSI_MRESB_RST_ERR);
+	return fsi_device_write(dev, FSI_MRESB0, &reg, sizeof(reg));
+}
+
+static int hub_master_probe(struct device *dev)
+{
+	struct fsi_device *fsi_dev = to_fsi_dev(dev);
+	struct fsi_master_hub *hub;
+	uint32_t reg, links;
+	__be32 __reg;
+	int rc;
+
+	rc = fsi_device_read(fsi_dev, FSI_MVER, &__reg, sizeof(__reg));
+	if (rc)
+		return rc;
+
+	reg = be32_to_cpu(__reg);
+	links = (reg >> 8) & 0xff;
+	dev_info(dev, "hub version %08x (%d links)\n", reg, links);
+
+	rc = fsi_slave_claim_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+			FSI_HUB_LINK_SIZE * links);
+	if (rc) {
+		dev_err(dev, "can't claim slave address range for links");
+		return rc;
+	}
+
+	hub = kzalloc(sizeof(*hub), GFP_KERNEL);
+	if (!hub) {
+		rc = -ENOMEM;
+		goto err_release;
+	}
+
+	hub->addr = FSI_HUB_LINK_OFFSET;
+	hub->size = FSI_HUB_LINK_SIZE * links;
+	hub->upstream = fsi_dev;
+
+	hub->master.dev.parent = dev;
+	hub->master.dev.release = hub_master_release;
+
+	hub->master.n_links = links;
+	hub->master.read = hub_master_read;
+	hub->master.write = hub_master_write;
+	hub->master.send_break = hub_master_break;
+	hub->master.link_enable = hub_master_link_enable;
+
+	dev_set_drvdata(dev, hub);
+
+	hub_master_init(hub);
+
+	rc = fsi_master_register(&hub->master);
+	if (!rc)
+		return 0;
+
+	kfree(hub);
+err_release:
+	fsi_slave_release_range(fsi_dev->slave, FSI_HUB_LINK_OFFSET,
+			FSI_HUB_LINK_SIZE * links);
+	return rc;
+}
+
+static int hub_master_remove(struct device *dev)
+{
+	struct fsi_master_hub *hub = dev_get_drvdata(dev);
+
+	fsi_master_unregister(&hub->master);
+	fsi_slave_release_range(hub->upstream->slave, hub->addr, hub->size);
+	return 0;
+}
+
+static struct fsi_device_id hub_master_ids[] = {
+	{
+		.engine_type = FSI_ENGID_HUB_MASTER,
+		.version = FSI_VERSION_ANY,
+	},
+	{ 0 }
+};
+
+static struct fsi_driver hub_master_driver = {
+	.id_table = hub_master_ids,
+	.drv = {
+		.name = "fsi-master-hub",
+		.bus = &fsi_bus_type,
+		.probe = hub_master_probe,
+		.remove = hub_master_remove,
+	}
+};
+
+module_fsi_driver(hub_master_driver);
+MODULE_LICENSE("GPL");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 23/24] drivers/fsi: Use asynchronous slave mode
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (21 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 22/24] drivers/fsi: Add hub master support Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  2017-06-06 21:08 ` [PATCH v8 24/24] drivers/fsi: Add module license to core driver Christopher Bostic
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Jeremy Kerr, joel, linux-kernel, andrew, alistair, benh,
	Christopher Bostic

From: Jeremy Kerr <jk@ozlabs.org>

For slaves that are behind a software-clocked master, we want FSI CFAMs
to run asynchronously to the FSI clock, so set up our slaves to be in
async mode.

Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c        | 22 +++++++++++++++++++++-
 drivers/fsi/fsi-master-gpio.c |  1 +
 drivers/fsi/fsi-master.h      |  2 ++
 3 files changed, 24 insertions(+), 1 deletion(-)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index c9ff8d3..b56f4ed 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -49,6 +49,7 @@
 #define FSI_SMODE		0x0	/* R/W: Mode register */
 #define FSI_SISC		0x8	/* R/W: Interrupt condition */
 #define FSI_SSTAT		0x14	/* R  : Slave status */
+#define FSI_LLMODE		0x100	/* R/W: Link layer mode register */
 
 /*
  * SMODE fields
@@ -64,6 +65,11 @@
 #define FSI_SMODE_LBCRR_SHIFT	8		/* Clk ratio shift */
 #define FSI_SMODE_LBCRR_MASK	0xf		/* Clk ratio mask */
 
+/*
+ * LLMODE fields
+ */
+#define FSI_LLMODE_ASYNC	0x1
+
 #define FSI_SLAVE_SIZE_23b		0x800000
 
 static DEFINE_IDA(master_ida);
@@ -557,8 +563,8 @@ static void fsi_slave_release(struct device *dev)
 
 static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 {
+	uint32_t chip_id, llmode;
 	struct fsi_slave *slave;
-	uint32_t chip_id;
 	uint8_t crc;
 	int rc;
 
@@ -594,6 +600,20 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
 		return -ENODEV;
 	}
 
+	/* If we're behind a master that doesn't provide a self-running bus
+	 * clock, put the slave into async mode
+	 */
+	if (master->flags & FSI_MASTER_FLAG_SWCLOCK) {
+		llmode = cpu_to_be32(FSI_LLMODE_ASYNC);
+		rc = fsi_master_write(master, link, id,
+				FSI_SLAVE_BASE + FSI_LLMODE,
+				&llmode, sizeof(llmode));
+		if (rc)
+			dev_warn(&master->dev,
+				"can't set llmode on slave:%02x:%02x %d\n",
+				link, id, rc);
+	}
+
 	/* We can communicate with a slave; create the slave device and
 	 * register.
 	 */
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index a5d6e70..ae26187 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -554,6 +554,7 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
 	master->gpio_mux = gpio;
 
 	master->master.n_links = 1;
+	master->master.flags = FSI_MASTER_FLAG_SWCLOCK;
 	master->master.read = fsi_master_gpio_read;
 	master->master.write = fsi_master_gpio_write;
 	master->master.term = fsi_master_gpio_term;
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 7764b00..12f7b11 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -19,6 +19,8 @@
 
 #include <linux/device.h>
 
+#define FSI_MASTER_FLAG_SWCLOCK		0x1
+
 struct fsi_master {
 	struct device	dev;
 	int		idx;
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH v8 24/24] drivers/fsi: Add module license to core driver
  2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
                   ` (22 preceding siblings ...)
  2017-06-06 21:08 ` [PATCH v8 23/24] drivers/fsi: Use asynchronous slave mode Christopher Bostic
@ 2017-06-06 21:08 ` Christopher Bostic
  23 siblings, 0 replies; 27+ messages in thread
From: Christopher Bostic @ 2017-06-06 21:08 UTC (permalink / raw)
  To: robh+dt, mark.rutland, linux, rostedt, mingo, gregkh, devicetree,
	linux-arm-kernel
  Cc: Christopher Bostic, joel, linux-kernel, andrew, alistair, benh

Add missing MODULE_LICENSE("GPL") to the core FSI driver.

Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
---
 drivers/fsi/fsi-core.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index b56f4ed..a485864 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -896,4 +896,5 @@ static void fsi_exit(void)
 module_init(fsi_init);
 module_exit(fsi_exit);
 module_param(discard_errors, int, 0664);
+MODULE_LICENSE("GPL");
 MODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses");
-- 
1.8.2.2

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations
  2017-06-06 21:08 ` [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations Christopher Bostic
@ 2017-06-07 19:16   ` Steven Rostedt
  2017-06-08  2:06     ` Jeremy Kerr
  0 siblings, 1 reply; 27+ messages in thread
From: Steven Rostedt @ 2017-06-07 19:16 UTC (permalink / raw)
  To: Christopher Bostic
  Cc: robh+dt, mark.rutland, linux, mingo, gregkh, devicetree,
	linux-arm-kernel, Jeremy Kerr, joel, linux-kernel, andrew,
	alistair, benh

On Tue,  6 Jun 2017 16:08:51 -0500
Christopher Bostic <cbostic@linux.vnet.ibm.com> wrote:

> From: Jeremy Kerr <jk@ozlabs.org>
> 
> Trace low level read and write FSI bus operations.
> 
> Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
> Signed-off-by: Joel Stanley <joel@jms.id.au>
> Signed-off-by: Christopher Bostic <cbostic@linux.vnet.ibm.com>
> ---
> v8 - type cast to 'int' to clean up build warnings
> ---
>  drivers/fsi/fsi-core.c     |  27 +++++++---
>  include/trace/events/fsi.h | 127 +++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 148 insertions(+), 6 deletions(-)
>  create mode 100644 include/trace/events/fsi.h
> 
> diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
> index 3681365..db54561 100644
> --- a/drivers/fsi/fsi-core.c
> +++ b/drivers/fsi/fsi-core.c
> @@ -23,6 +23,9 @@
>  
>  #include "fsi-master.h"
>  
> +#define CREATE_TRACE_POINTS
> +#include <trace/events/fsi.h>
> +
>  #define FSI_SLAVE_CONF_NEXT_MASK	GENMASK(31, 31)
>  #define FSI_SLAVE_CONF_SLOTS_MASK	GENMASK(23, 16)
>  #define FSI_SLAVE_CONF_SLOTS_SHIFT	16
> @@ -542,11 +545,16 @@ static int fsi_master_read(struct fsi_master *master, int link,
>  {
>  	int rc;
>  
> +	trace_fsi_master_read(master, link, slave_id, addr, size);
> +
>  	rc = fsi_check_access(addr, size);
> -	if (rc)
> -		return rc;
> +	if (!rc)
> +		rc = master->read(master, link, slave_id, addr, val, size);
> +
> +	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
> +			false, val, rc);
>  
> -	return master->read(master, link, slave_id, addr, val, size);
> +	return rc;
>  }
>  
>  static int fsi_master_write(struct fsi_master *master, int link,
> @@ -554,11 +562,16 @@ static int fsi_master_write(struct fsi_master *master, int link,
>  {
>  	int rc;
>  
> +	trace_fsi_master_write(master, link, slave_id, addr, size, val);
> +
>  	rc = fsi_check_access(addr, size);
> -	if (rc)
> -		return rc;
> +	if (!rc)
> +		rc = master->write(master, link, slave_id, addr, val, size);
>  
> -	return master->write(master, link, slave_id, addr, val, size);
> +	trace_fsi_master_rw_result(master, link, slave_id, addr, size,
> +			true, val, rc);
> +
> +	return rc;
>  }
>  
>  static int fsi_master_link_enable(struct fsi_master *master, int link)
> @@ -574,6 +587,8 @@ static int fsi_master_link_enable(struct fsi_master *master, int link)
>   */
>  static int fsi_master_break(struct fsi_master *master, int link)
>  {
> +	trace_fsi_master_break(master, link);
> +
>  	if (master->send_break)
>  		return master->send_break(master, link);
>  
> diff --git a/include/trace/events/fsi.h b/include/trace/events/fsi.h
> new file mode 100644
> index 0000000..697ee66
> --- /dev/null
> +++ b/include/trace/events/fsi.h
> @@ -0,0 +1,127 @@
> +
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM fsi
> +
> +#if !defined(_TRACE_FSI_H) || defined(TRACE_HEADER_MULTI_READ)
> +#define _TRACE_FSI_H
> +
> +#include <linux/tracepoint.h>
> +
> +TRACE_EVENT(fsi_master_read,
> +	TP_PROTO(const struct fsi_master *master, int link, int id,
> +			uint32_t addr, size_t size),
> +	TP_ARGS(master, link, id, addr, size),
> +	TP_STRUCT__entry(
> +		__field(int,	master_idx)
> +		__field(int,	link)
> +		__field(int,	id)
> +		__field(__u32,	addr)
> +		__field(size_t,	size)
> +	),
> +	TP_fast_assign(
> +		__entry->master_idx = master->idx;
> +		__entry->link = link;
> +		__entry->id = id;
> +		__entry->addr = addr;
> +		__entry->size = size;
> +	),
> +	TP_printk("fsi%d:%02d:%02d %08x[%zd]",
> +		__entry->master_idx,
> +		__entry->link,
> +		__entry->id,
> +		__entry->addr,
> +		__entry->size
> +	)
> +);
> +
> +TRACE_EVENT(fsi_master_write,
> +	TP_PROTO(const struct fsi_master *master, int link, int id,
> +			uint32_t addr, size_t size, const void *data),
> +	TP_ARGS(master, link, id, addr, size, data),
> +	TP_STRUCT__entry(
> +		__field(int,	master_idx)
> +		__field(int,	link)
> +		__field(int,	id)
> +		__field(__u32,	addr)
> +		__field(size_t,	size)
> +		__field(__u32,	data)
> +	),
> +	TP_fast_assign(
> +		__entry->master_idx = master->idx;
> +		__entry->link = link;
> +		__entry->id = id;
> +		__entry->addr = addr;
> +		__entry->size = size;
> +		__entry->data = 0;
> +		memcpy(&__entry->data, data, size);


Um, can size ever be greater than 4? If so, this is a bug.

I think you may want to use a dynamic array here.

	TP_STRUCT__entry(
		[..]
		__dynamic_array(char, data, size)
	[..]

	TP_fast_assign(
		[..]
		memcpy(__get_dynamic_array(data), data, size);
	[..]

	TP_printk(...
		[..]
		__entry->size,
		__get_dynamic_array(data)

You may also want to look at __print_array() too.

Same with below.

-- Steve


> +	),
> +	TP_printk("fsi%d:%02d:%02d %08x[%zd] <= {%*ph}",
> +		__entry->master_idx,
> +		__entry->link,
> +		__entry->id,
> +		__entry->addr,
> +		__entry->size,
> +		(int)__entry->size, &__entry->data
> +	)
> +);
> +
> +TRACE_EVENT(fsi_master_rw_result,
> +	TP_PROTO(const struct fsi_master *master, int link, int id,
> +			uint32_t addr, size_t size,
> +			bool write, const void *data, int ret),
> +	TP_ARGS(master, link, id, addr, size, write, data, ret),
> +	TP_STRUCT__entry(
> +		__field(int,	master_idx)
> +		__field(int,	link)
> +		__field(int,	id)
> +		__field(__u32,	addr)
> +		__field(size_t,	size)
> +		__field(bool,	write)
> +		__field(__u32,	data)
> +		__field(int,	ret)
> +	),
> +	TP_fast_assign(
> +		__entry->master_idx = master->idx;
> +		__entry->link = link;
> +		__entry->id = id;
> +		__entry->addr = addr;
> +		__entry->size = size;
> +		__entry->write = write;
> +		__entry->data = 0;
> +		__entry->ret = ret;
> +		if (__entry->write || !__entry->ret)
> +			memcpy(&__entry->data, data, size);
> +	),
> +	TP_printk("fsi%d:%02d:%02d %08x[%zd] %s {%*ph} ret %d",
> +		__entry->master_idx,
> +		__entry->link,
> +		__entry->id,
> +		__entry->addr,
> +		__entry->size,
> +		__entry->write ? "<=" : "=>",
> +		(int)__entry->size, &__entry->data,
> +		__entry->ret
> +	)
> +);
> +
> +TRACE_EVENT(fsi_master_break,
> +	TP_PROTO(const struct fsi_master *master, int link),
> +	TP_ARGS(master, link),
> +	TP_STRUCT__entry(
> +		__field(int,	master_idx)
> +		__field(int,	link)
> +	),
> +	TP_fast_assign(
> +		__entry->master_idx = master->idx;
> +		__entry->link = link;
> +	),
> +	TP_printk("fsi%d:%d",
> +		__entry->master_idx,
> +		__entry->link
> +	)
> +);
> +
> +
> +#endif /* _TRACE_FSI_H */
> +
> +#include <trace/define_trace.h>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations
  2017-06-07 19:16   ` Steven Rostedt
@ 2017-06-08  2:06     ` Jeremy Kerr
  0 siblings, 0 replies; 27+ messages in thread
From: Jeremy Kerr @ 2017-06-08  2:06 UTC (permalink / raw)
  To: Steven Rostedt, Christopher Bostic
  Cc: robh+dt, mark.rutland, linux, mingo, gregkh, devicetree,
	linux-arm-kernel, joel, linux-kernel, andrew, alistair, benh

Hi Steven,

Thanks for checking this out.

>> +TRACE_EVENT(fsi_master_write,
>> +	TP_PROTO(const struct fsi_master *master, int link, int id,
>> +			uint32_t addr, size_t size, const void *data),
>> +	TP_ARGS(master, link, id, addr, size, data),
>> +	TP_STRUCT__entry(
>> +		__field(int,	master_idx)
>> +		__field(int,	link)
>> +		__field(int,	id)
>> +		__field(__u32,	addr)
>> +		__field(size_t,	size)
>> +		__field(__u32,	data)
>> +	),
>> +	TP_fast_assign(
>> +		__entry->master_idx = master->idx;
>> +		__entry->link = link;
>> +		__entry->id = id;
>> +		__entry->addr = addr;
>> +		__entry->size = size;
>> +		__entry->data = 0;
>> +		memcpy(&__entry->data, data, size);
> 
> 
> Um, can size ever be greater than 4? If so, this is a bug.

No, size will only ever be 1, 2, or 4, as they're the only valid FSI bus
transactions. Hence storing ->data as a u32 to keep things simple.

> I think you may want to use a dynamic array here.
> 
> 	TP_STRUCT__entry(
> 		[..]
> 		__dynamic_array(char, data, size)
> 	[..]
> 
> 	TP_fast_assign(
> 		[..]
> 		memcpy(__get_dynamic_array(data), data, size);
> 	[..]
> 
> 	TP_printk(...
> 		[..]
> 		__entry->size,
> 		__get_dynamic_array(data)
> 
> You may also want to look at __print_array() too.

The %ph specifier seems to do a decent job though...

Cheers,


Jeremy

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2017-06-08  2:06 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-06-06 21:08 [PATCH v8 00/24] FSI device driver implementation Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 01/24] drivers/fsi: Add fsi master definition Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 02/24] drivers/fsi: Add slave definition Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 03/24] drivers/fsi: Add empty master scan Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 04/24] lib: Add crc4 module Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 05/24] drivers/fsi: Add slave & master read/write APIs Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 06/24] drivers/fsi: Set up links for slave communication Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 07/24] drivers/fsi: Implement slave initialisation Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 08/24] drivers/fsi: Set slave SMODE to init communication Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 09/24] drivers/fsi: scan slaves & register devices Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 10/24] drivers/fsi: Add device read/write/peek API Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 11/24] drivers/fsi: Add master unscan Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 12/24] drivers/fsi: Add documentation for GPIO bindings Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 13/24] drivers/fsi: Add client driver register utilities Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 14/24] drivers/fsi: Add sysfs files for FSI master & slave accesses Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 15/24] drivers/fsi: expose direct-access slave API Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 16/24] drivers/fsi: Add tracepoints for low-level operations Christopher Bostic
2017-06-07 19:16   ` Steven Rostedt
2017-06-08  2:06     ` Jeremy Kerr
2017-06-06 21:08 ` [PATCH v8 17/24] drivers/fsi: Add error handling for slave Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 18/24] drivers/fsi: Document FSI master sysfs files in ABI Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 19/24] drivers/fsi: Add GPIO based FSI master Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 20/24] drivers/fsi/gpio: Add tracepoints for GPIO master Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 21/24] drivers/fsi: Add SCOM FSI client device driver Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 22/24] drivers/fsi: Add hub master support Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 23/24] drivers/fsi: Use asynchronous slave mode Christopher Bostic
2017-06-06 21:08 ` [PATCH v8 24/24] drivers/fsi: Add module license to core driver Christopher Bostic

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).