* [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 02/18] fsi: Add device & driver definitions christopher.lee.bostic
` (35 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
This change adds the initial (empty) fsi bus definition, and introduces
drivers/fsi/.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/Kconfig | 2 ++
drivers/Makefile | 1 +
drivers/fsi/Kconfig | 12 ++++++++++++
drivers/fsi/Makefile | 2 ++
drivers/fsi/fsi-core.c | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/fsi.h | 23 +++++++++++++++++++++++
6 files changed, 78 insertions(+)
create mode 100644 drivers/fsi/Kconfig
create mode 100644 drivers/fsi/Makefile
create mode 100644 drivers/fsi/fsi-core.c
create mode 100644 include/linux/fsi.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index e1e2066..117ca14c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fsi/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 0b6f3d6..7a1c96f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
+obj-$(CONFIG_FSI) += fsi/
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
new file mode 100644
index 0000000..04c1a0e
--- /dev/null
+++ b/drivers/fsi/Kconfig
@@ -0,0 +1,12 @@
+#
+# FSI subsystem
+#
+
+menu "FSI support"
+
+config FSI
+ tristate "FSI support"
+ ---help---
+ FSI - the FRU Support Interface - is a simple bus for low-level
+ access to POWER-based hardware.
+endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
new file mode 100644
index 0000000..db0e5e7
--- /dev/null
+++ b/drivers/fsi/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_FSI) += fsi-core.o
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
new file mode 100644
index 0000000..3e45306
--- /dev/null
+++ b/drivers/fsi/fsi-core.c
@@ -0,0 +1,38 @@
+/*
+ * FSI core driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+
+/* FSI core & Linux bus type definitions */
+
+struct bus_type fsi_bus_type = {
+ .name = "fsi",
+};
+EXPORT_SYMBOL_GPL(fsi_bus_type);
+
+static int fsi_init(void)
+{
+ return bus_register(&fsi_bus_type);
+}
+
+static void fsi_exit(void)
+{
+ bus_unregister(&fsi_bus_type);
+}
+
+module_init(fsi_init);
+module_exit(fsi_exit);
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
new file mode 100644
index 0000000..8e8bdea
--- /dev/null
+++ b/include/linux/fsi.h
@@ -0,0 +1,23 @@
+/*
+ * FSI device & driver interfaces
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef LINUX_FSI_H
+#define LINUX_FSI_H
+
+#include <linux/device.h>
+
+extern struct bus_type fsi_bus_type;
+
+#endif /* LINUX_FSI_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 02/18] fsi: Add device & driver definitions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 03/18] fsi: add driver to device matches christopher.lee.bostic
` (34 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Add structs for fsi devices & drivers, and struct device conversion
functions.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
include/linux/fsi.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 8e8bdea..66dcf25 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -18,6 +18,17 @@
#include <linux/device.h>
+struct fsi_device {
+ struct device dev;
+};
+
+struct fsi_driver {
+ struct device_driver drv;
+};
+
+#define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
+#define to_fsi_drv(drvp) container_of(drvp, struct fsi_driver, drv)
+
extern struct bus_type fsi_bus_type;
#endif /* LINUX_FSI_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 03/18] fsi: add driver to device matches
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 02/18] fsi: Add device & driver definitions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 04/18] fsi: Add fsi master definition christopher.lee.bostic
` (33 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Driver bind to devices based on the engine types & (optional) versions.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 21 +++++++++++++++++++++
include/linux/fsi.h | 21 +++++++++++++++++++--
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3e45306..3d55bd5 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -19,8 +19,29 @@
/* FSI core & Linux bus type definitions */
+static int fsi_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct fsi_driver *fsi_drv = to_fsi_drv(drv);
+ const struct fsi_device_id *id;
+
+ if (!fsi_drv->id_table)
+ return 0;
+
+ for (id = fsi_drv->id_table; id->engine_type; id++) {
+ if (id->engine_type != fsi_dev->engine_type)
+ continue;
+ if (id->version == FSI_VERSION_ANY ||
+ id->version == fsi_dev->version)
+ return 1;
+ }
+
+ return 0;
+}
+
struct bus_type fsi_bus_type = {
.name = "fsi",
+ .match = fsi_bus_match,
};
EXPORT_SYMBOL_GPL(fsi_bus_type);
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 66dcf25..6d843d4 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -19,11 +19,28 @@
#include <linux/device.h>
struct fsi_device {
- struct device dev;
+ struct device dev;
+ u8 engine_type;
+ u8 version;
};
+struct fsi_device_id {
+ u8 engine_type;
+ u8 version;
+};
+
+#define FSI_VERSION_ANY 0
+
+#define FSI_DEVICE(t) \
+ .engine_type = (t), .version = FSI_VERSION_ANY,
+
+#define FSI_DEVICE_VERSIONED(t, v) \
+ .engine_type = (t), .version = (v),
+
+
struct fsi_driver {
- struct device_driver drv;
+ struct device_driver drv;
+ const struct fsi_device_id *id_table;
};
#define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 04/18] fsi: Add fsi master definition
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (2 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 03/18] fsi: add driver to device matches christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 05/18] fsi: Add fake master driver christopher.lee.bostic
` (32 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 20 ++++++++++++++++++++
drivers/fsi/fsi-master.h | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+)
create mode 100644 drivers/fsi/fsi-master.h
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3d55bd5..ce9428d 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -17,6 +17,26 @@
#include <linux/fsi.h>
#include <linux/module.h>
+#include "fsi-master.h"
+
+static atomic_t master_idx = ATOMIC_INIT(-1);
+
+/* FSI master support */
+
+int fsi_master_register(struct fsi_master *master)
+{
+ master->idx = atomic_inc_return(&master_idx);
+ get_device(master->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_master_register);
+
+void fsi_master_unregister(struct fsi_master *master)
+{
+ put_device(master->dev);
+}
+EXPORT_SYMBOL_GPL(fsi_master_unregister);
+
/* FSI core & Linux bus type definitions */
static int fsi_bus_match(struct device *dev, struct device_driver *drv)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
new file mode 100644
index 0000000..e75a810
--- /dev/null
+++ b/drivers/fsi/fsi-master.h
@@ -0,0 +1,37 @@
+/*
+ * FSI master definitions. These comprise the core <--> master interface,
+ * to allow the core to interact with the (hardware-specific) masters.
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DRIVERS_FSI_MASTER_H
+#define DRIVERS_FSI_MASTER_H
+
+#include <linux/device.h>
+
+struct fsi_master {
+ struct device *dev;
+ int idx;
+ int n_links;
+ int (*read)(struct fsi_master *, int link,
+ uint8_t slave, uint32_t addr,
+ void *val, size_t size);
+ int (*write)(struct fsi_master *, int link,
+ uint8_t slave, uint32_t addr,
+ const void *val, size_t size);
+};
+
+extern int fsi_master_register(struct fsi_master *master);
+extern void fsi_master_unregister(struct fsi_master *master);
+
+#endif /* DRIVERS_FSI_MASTER_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 05/18] fsi: Add fake master driver
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (3 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 04/18] fsi: Add fsi master definition christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 06/18] fsi: enable debug christopher.lee.bostic
` (31 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr, Chris Bostic
From: Jeremy Kerr <jk@ozlabs.org>
For debugging, add a fake master driver, that only supports reads,
returning a fixed set of data.
Includes changes from Chris Bostic <cbostic@us.ibm.com>.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
drivers/fsi/Kconfig | 10 +++++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-fake.c | 95 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
create mode 100644 drivers/fsi/fsi-master-fake.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 04c1a0e..f065dbe 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -9,4 +9,14 @@ config FSI
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware.
+
+if FSI
+
+config FSI_MASTER_FAKE
+ tristate "Fake FSI master"
+ depends on FSI
+ ---help---
+ This option enables a fake FSI master driver for debugging.
+endif
+
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index db0e5e7..847c00c 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
diff --git a/drivers/fsi/fsi-master-fake.c b/drivers/fsi/fsi-master-fake.c
new file mode 100644
index 0000000..ec1ed5e
--- /dev/null
+++ b/drivers/fsi/fsi-master-fake.c
@@ -0,0 +1,95 @@
+/*
+ * Fake FSI master driver for FSI development
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include "fsi-master.h"
+
+const uint8_t data[] = {
+ 0xc0, 0x02, 0x08, 0x03, /* chip id */
+ 0x80, 0x01, 0x11, 0x00, /* peek */
+ 0x80, 0x01, 0x20, 0x3e, /* slave */
+ 0x00, 0x01, 0x10, 0xa5, /* i2c */
+};
+
+
+static int fsi_master_fake_read(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, void *val, size_t size)
+{
+ if (link != 0)
+ return -ENODEV;
+
+ if (addr + size > sizeof(data))
+ memset(val, 0, size);
+ else
+ memcpy(val, data + addr, size);
+
+ return 0;
+}
+
+static int fsi_master_fake_write(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, const void *val, size_t size)
+{
+ if (link != 0)
+ return -ENODEV;
+
+ return -EACCES;
+}
+
+static int fsi_master_fake_probe(struct platform_device *pdev)
+{
+ struct fsi_master *master;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->dev = &pdev->dev;
+ master->n_links = 1;
+ master->read = fsi_master_fake_read;
+ master->write = fsi_master_fake_write;
+
+ return fsi_master_register(master);
+}
+
+static const struct of_device_id fsi_master_fake_match[] = {
+ { .compatible = "ibm,fsi-master-fake" },
+ { },
+};
+
+static struct platform_driver fsi_master_fake_driver = {
+ .driver = {
+ .name = "fsi-master-fake",
+ .of_match_table = fsi_master_fake_match,
+ },
+ .probe = fsi_master_fake_probe,
+};
+
+static int __init fsi_master_fake_init(void)
+{
+ struct device_node *np;
+
+ platform_driver_register(&fsi_master_fake_driver);
+
+ for_each_compatible_node(np, NULL, "ibm,fsi-master-fake")
+ of_platform_device_create(np, NULL, NULL);
+
+ return 0;
+}
+
+module_init(fsi_master_fake_init);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 06/18] fsi: enable debug
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (4 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 05/18] fsi: Add fake master driver christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 07/18] fsi: Add slave definition christopher.lee.bostic
` (30 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Enable debug for the fsi core during development. Remove before
submission.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index ce9428d..db1a1ce 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
+#define DEBUG
+
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/module.h>
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 07/18] fsi: Add slave definition
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (5 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 06/18] fsi: enable debug christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 08/18] fsi: Add empty master scan christopher.lee.bostic
` (29 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
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>
---
drivers/fsi/fsi-core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index db1a1ce..7df4291 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -23,6 +23,15 @@
static atomic_t master_idx = ATOMIC_INIT(-1);
+struct fsi_slave {
+ struct device dev;
+ struct fsi_master *master;
+ int link;
+ uint8_t id;
+};
+
+#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+
/* FSI master support */
int fsi_master_register(struct fsi_master *master)
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 08/18] fsi: Add empty master scan
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (6 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 07/18] fsi: Add slave definition christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 09/18] fsi: Add crc4 helpers christopher.lee.bostic
` (28 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
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>
---
drivers/fsi/fsi-core.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 7df4291..9744a55 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -21,6 +21,8 @@
#include "fsi-master.h"
+#define FSI_N_SLAVES 4
+
static atomic_t master_idx = ATOMIC_INIT(-1);
struct fsi_slave {
@@ -32,12 +34,34 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* FSI slave support */
+static int fsi_slave_init(struct fsi_master *master,
+ int link, uint8_t slave_id)
+{
+ /* todo: initialise slave device, perform engine scan */
+
+ return -ENODEV;
+}
+
/* FSI master support */
+static int fsi_master_scan(struct fsi_master *master)
+{
+ int link, slave_id;
+
+ for (link = 0; link < master->n_links; link++)
+ for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
+ fsi_slave_init(master, link, slave_id);
+
+ return 0;
+
+}
+
int fsi_master_register(struct fsi_master *master)
{
master->idx = atomic_inc_return(&master_idx);
get_device(master->dev);
+ fsi_master_scan(master);
return 0;
}
EXPORT_SYMBOL_GPL(fsi_master_register);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 09/18] fsi: Add crc4 helpers
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (7 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 08/18] fsi: Add empty master scan christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 10/18] fsi: Implement slave initialisation christopher.lee.bostic
` (27 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Add some helpers for the crc checks for the slave configuration table.
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>
---
drivers/fsi/fsi-core.c | 25 +++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 21 +++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 9744a55..2d22320 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -34,6 +34,31 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* crc helpers */
+static const uint8_t crc4_tab[] = {
+ 0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
+ 0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
+};
+
+uint8_t fsi_crc4(uint8_t c, uint64_t x, int bits)
+{
+ int i;
+
+ /* Align to 4-bits */
+ bits = (bits + 3) & ~0x3;
+
+ /* Calculate crc4 over four-bit nibbles, starting at the MSbit */
+ for (i = bits; i >= 0; i -= 4)
+ c = crc4_tab[c ^ ((x >> i) & 0xf)];
+
+ return c;
+}
+
+static bool check_crc4_u32(uint32_t x)
+{
+ return fsi_crc4(0, x, 32) == 0;
+}
+
/* FSI slave support */
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index e75a810..cafb433 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -34,4 +34,25 @@ struct fsi_master {
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
+/**
+ * crc4 helper: Given a starting crc4 state @c, calculate the crc4 vaue of @x,
+ * which is @bits in length. This may be required by master implementations
+ * that do not provide their own hardware checksums.
+ *
+ * The crc4 is performed on 4-bit chunks (which is all we need for FSI
+ * calculations). Typically, we'll want a starting state of 0:
+ *
+ * c = fsi_crc4(0, msg, len);
+ *
+ * To crc4 a message that includes a single start bit, initialise crc4 state
+ * with:
+ *
+ * c = fsi_crc4(0, 1, 1);
+ *
+ * Then update with message data:
+ *
+ * c = fsi_crc4(c, msg, len);
+ */
+uint8_t fsi_crc4(uint8_t c, uint64_t x, int bits);
+
#endif /* DRIVERS_FSI_MASTER_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 10/18] fsi: Implement slave initialisation
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (8 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 09/18] fsi: Add crc4 helpers christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 11/18] fsi: scan slaves & register devices christopher.lee.bostic
` (26 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Create fsi_slave devices during the master scan.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 2d22320..4282370 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "fsi-master.h"
@@ -60,12 +61,58 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+
+static void fsi_slave_release(struct device *dev)
+{
+ struct fsi_slave *slave = to_fsi_slave(dev);
+
+ kfree(slave);
+}
+
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
{
- /* todo: initialise slave device, perform engine scan */
+ struct fsi_slave *slave;
+ uint32_t chip_id;
+ int rc;
+
+ rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
+ if (rc) {
+ dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
+ link, slave_id, rc);
+ return -ENODEV;
+ }
+
+ chip_id = be32_to_cpu(chip_id);
+ if (!check_crc4_u32(chip_id)) {
+ dev_warn(master->dev, "slave %02x:%02x: invalid chip id CRC!\n",
+ link, slave_id);
+ return -EIO;
+ }
+
+ pr_debug("fsi: found chip %08x at %02x:%02x:%02x\n",
+ master->idx, chip_id, link, slave_id);
+
+ /* we can communicate with a slave; create devices and scan */
+ slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+ if (!slave)
+ return -ENOMEM;
+
+ slave->master = master;
+ slave->id = slave_id;
+ slave->dev.parent = master->dev;
+ slave->dev.release = fsi_slave_release;
+
+ dev_set_name(&slave->dev, "slave@%02x:%02x", link, slave_id);
+ rc = device_register(&slave->dev);
+ if (rc < 0) {
+ dev_warn(master->dev, "failed to create slave device: %d\n",
+ rc);
+ put_device(&slave->dev);
+ return rc;
+ }
- return -ENODEV;
+ return rc;
}
/* FSI master support */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 11/18] fsi: scan slaves & register devices
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (9 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 10/18] fsi: Implement slave initialisation christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 12/18] fsi: Add device read/write/peek functions christopher.lee.bostic
` (25 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr, Chris 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 Chris Bostic <cbostic@us.ibm.com>.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
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 4282370..50e0616 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -24,6 +24,16 @@
#define FSI_N_SLAVES 4
+#define FSI_SLAVE_CONF_NEXT_MASK 0x80000000
+#define FSI_SLAVE_CONF_SLOTS_MASK 0x00ff0000
+#define FSI_SLAVE_CONF_SLOTS_SHIFT 16
+#define FSI_SLAVE_CONF_VERSION_MASK 0x0000f000
+#define FSI_SLAVE_CONF_VERSION_SHIFT 12
+#define FSI_SLAVE_CONF_TYPE_MASK 0x00000ff0
+#define FSI_SLAVE_CONF_TYPE_SHIFT 4
+
+static const int engine_page_size = 0x400;
+
static atomic_t master_idx = ATOMIC_INIT(-1);
struct fsi_slave {
@@ -35,6 +45,30 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* FSI endpoint-device support */
+
+static void fsi_device_release(struct device *_device)
+{
+ struct fsi_device *device = to_fsi_dev(_device);
+
+ kfree(device);
+}
+
+static struct fsi_device *fsi_create_device(struct fsi_slave *slave)
+{
+ struct fsi_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->dev.parent = &slave->dev;
+ dev->dev.bus = &fsi_bus_type;
+ dev->dev.release = fsi_device_release;
+
+ return dev;
+}
+
/* crc helpers */
static const uint8_t crc4_tab[] = {
0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
@@ -61,6 +95,97 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+ void *val, size_t size)
+{
+ return slave->master->read(slave->master, slave->link,
+ slave->id, addr, val, size);
+}
+
+static int fsi_slave_scan(struct fsi_slave *slave)
+{
+ uint32_t engine_addr;
+ uint32_t conf;
+ int rc, i;
+
+ /*
+ * scan engines
+ *
+ * We keep the peek mode and slave engines for the core; so start
+ * at the third slot in the configuration table. We also need to
+ * skip the chip ID entry at the start of the address space.
+ */
+ engine_addr = engine_page_size * 3;
+ for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) {
+ uint8_t slots, version, type;
+ struct fsi_device *dev;
+
+ rc = fsi_slave_read(slave, (i + 1) * sizeof(conf),
+ &conf, sizeof(conf));
+ if (rc) {
+ dev_warn(&slave->dev,
+ "error reading slave registers\n");
+ return -1;
+ }
+
+ conf = be32_to_cpu(conf);
+
+ if (!check_crc4_u32(conf)) {
+ dev_warn(&slave->dev,
+ "crc error in slave register at 0x%04x\n",
+ i);
+ return -1;
+ }
+
+ slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
+ >> FSI_SLAVE_CONF_SLOTS_SHIFT;
+ version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
+ >> FSI_SLAVE_CONF_VERSION_SHIFT;
+ type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
+ >> FSI_SLAVE_CONF_TYPE_SHIFT;
+
+ /*
+ * Unused address areas are marked by a zero type value; this
+ * skips the defined address areas
+ */
+ if (type != 0) {
+
+ /* create device */
+ dev = fsi_create_device(slave);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->engine_type = type;
+ dev->version = version;
+ dev->unit = i;
+ dev->addr = engine_addr;
+ dev->size = slots * engine_page_size;
+
+ dev_info(&slave->dev,
+ "engine[%i]: type %x, version %x, addr %x size %x\n",
+ dev->unit, dev->engine_type, version,
+ dev->addr, dev->size);
+
+ device_initialize(&dev->dev);
+ dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
+ slave->master->idx, slave->link,
+ slave->id, i - 2);
+
+ rc = device_add(&dev->dev);
+ if (rc) {
+ dev_warn(&slave->dev, "add failed: %d\n", rc);
+ put_device(&dev->dev);
+ }
+ }
+
+ engine_addr += slots * engine_page_size;
+
+ if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
+ break;
+ }
+
+ return 0;
+}
static void fsi_slave_release(struct device *dev)
{
@@ -112,7 +237,8 @@ static int fsi_slave_init(struct fsi_master *master,
return rc;
}
- return rc;
+ fsi_slave_scan(slave);
+ return 0;
}
/* FSI master support */
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 6d843d4..dfd3513 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -22,6 +22,10 @@ struct fsi_device {
struct device dev;
u8 engine_type;
u8 version;
+ u8 unit;
+ struct fsi_slave *slave;
+ uint32_t addr;
+ uint32_t size;
};
struct fsi_device_id {
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 12/18] fsi: Add device read/write/peek functions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (10 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 11/18] fsi: scan slaves & register devices christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core christopher.lee.bostic
` (24 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
This change introduces the fsi device API: simple read, write and peek
accessors for the devices' address spaces.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/fsi.h | 7 ++++++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 50e0616..565c7e3 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -32,6 +32,8 @@
#define FSI_SLAVE_CONF_TYPE_MASK 0x00000ff0
#define FSI_SLAVE_CONF_TYPE_SHIFT 4
+#define FSI_PEEK_BASE 0x410
+
static const int engine_page_size = 0x400;
static atomic_t master_idx = ATOMIC_INIT(-1);
@@ -45,7 +47,42 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+ void *val, size_t size);
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+ const void *val, size_t size);
+
/* FSI endpoint-device support */
+int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
+ size_t size)
+{
+ if (addr > dev->size)
+ return -EINVAL;
+
+ if (addr + size > dev->size)
+ return -EINVAL;
+
+ return fsi_slave_read(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
+ size_t size)
+{
+ if (addr > dev->size)
+ return -EINVAL;
+
+ if (addr + size > dev->size)
+ return -EINVAL;
+
+ return fsi_slave_write(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_peek(struct fsi_device *dev, void *val)
+{
+ uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t));
+
+ return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
+}
static void fsi_device_release(struct device *_device)
{
@@ -102,6 +139,13 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
slave->id, addr, val, size);
}
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+ const void *val, size_t size)
+{
+ return slave->master->write(slave->master, slave->link,
+ slave->id, addr, val, size);
+}
+
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index dfd3513..47af0af 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -28,6 +28,12 @@ struct fsi_device {
uint32_t size;
};
+extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
+ void *val, size_t size);
+extern int fsi_device_write(struct fsi_device *dev, uint32_t addr,
+ const void *val, size_t size);
+extern int fsi_device_peek(struct fsi_device *dev, void *val);
+
struct fsi_device_id {
u8 engine_type;
u8 version;
@@ -41,7 +47,6 @@ struct fsi_device_id {
#define FSI_DEVICE_VERSIONED(t, v) \
.engine_type = (t), .version = (v),
-
struct fsi_driver {
struct device_driver drv;
const struct fsi_device_id *id_table;
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (11 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 12/18] fsi: Add device read/write/peek functions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-31 1:24 ` Jeremy Kerr
2016-10-30 22:09 ` [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication christopher.lee.bostic
` (23 subsequent siblings)
36 siblings, 1 reply; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Added some export symbols for fsi_device_read/write for FSI
client drivers. Removed the byte swapping done on CFAM config
word reads - already in proper order. Removed fsi_crc4 checks,
they were incorectly reporting bad crc's. Need to evaluate
what is wrong with the method of calculation.
Add missing assignment of slave to fsi_device during scan.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
drivers/fsi/fsi-core.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 565c7e3..95e867e 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -64,6 +64,7 @@ int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
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)
@@ -76,6 +77,7 @@ int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
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)
{
@@ -171,16 +173,15 @@ static int fsi_slave_scan(struct fsi_slave *slave)
"error reading slave registers\n");
return -1;
}
-
- conf = be32_to_cpu(conf);
-
+/* todo: fix crc calc problems */
+#if 0
if (!check_crc4_u32(conf)) {
dev_warn(&slave->dev,
"crc error in slave register at 0x%04x\n",
i);
return -1;
}
-
+#endif
slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
>> FSI_SLAVE_CONF_SLOTS_SHIFT;
version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
@@ -199,6 +200,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
if (!dev)
return -ENOMEM;
+ dev->slave = slave;
dev->engine_type = type;
dev->version = version;
dev->unit = i;
@@ -252,11 +254,10 @@ static int fsi_slave_init(struct fsi_master *master,
return -ENODEV;
}
- chip_id = be32_to_cpu(chip_id);
if (!check_crc4_u32(chip_id)) {
dev_warn(master->dev, "slave %02x:%02x: invalid chip id CRC!\n",
link, slave_id);
- return -EIO;
+ /* todo, fix crc calc issues. Just warn for now */
}
pr_debug("fsi: found chip %08x at %02x:%02x:%02x\n",
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* Re: [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core
2016-10-30 22:09 ` [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core christopher.lee.bostic
@ 2016-10-31 1:24 ` Jeremy Kerr
2016-11-03 16:49 ` Christopher Bostic
0 siblings, 1 reply; 47+ messages in thread
From: Jeremy Kerr @ 2016-10-31 1:24 UTC (permalink / raw)
To: christopher.lee.bostic, openbmc; +Cc: xxpetri, zahrens
Hi Chris,
> Added some export symbols for fsi_device_read/write for FSI
> client drivers. Removed the byte swapping done on CFAM config
> word reads - already in proper order. Removed fsi_crc4 checks,
> they were incorectly reporting bad crc's. Need to evaluate
> what is wrong with the method of calculation.
>
> Add missing assignment of slave to fsi_device during scan.
I assume that this patch will change as you work out the issues from
some of the updates there. However, once you get this sorted, you should
squash the updates into their original patches.
Cheers,
Jeremy
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core
2016-10-31 1:24 ` Jeremy Kerr
@ 2016-11-03 16:49 ` Christopher Bostic
0 siblings, 0 replies; 47+ messages in thread
From: Christopher Bostic @ 2016-11-03 16:49 UTC (permalink / raw)
To: Jeremy Kerr; +Cc: OpenBMC Maillist, xxpetri, zahrens
On Sun, Oct 30, 2016 at 8:24 PM, Jeremy Kerr <jk@ozlabs.org> wrote:
> Hi Chris,
>
>> Added some export symbols for fsi_device_read/write for FSI
>> client drivers. Removed the byte swapping done on CFAM config
>> word reads - already in proper order. Removed fsi_crc4 checks,
>> they were incorectly reporting bad crc's. Need to evaluate
>> what is wrong with the method of calculation.
>>
>> Add missing assignment of slave to fsi_device during scan.
>
> I assume that this patch will change as you work out the issues from
> some of the updates there. However, once you get this sorted, you should
> squash the updates into their original patches.
Hi Jeremy,
Understood, will do that for next round.
Thanks,
Chris
>
> Cheers,
>
>
> Jeremy
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (12 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications christopher.lee.bostic
` (22 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Enable each link and send a break command in preparation
for scanning each link for slaves.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V3 - Remove definition of BREAK command from fsi-master.h
- Remove definitions of fsi_master_fake for set_smode
and break
- Remove master->set_smode master dependent function and
moved to a generic base master set_smode.
- Add fsi_master_link_enable with master type dependencies
V4 - Remove all references to set smode
- Remove file fsi-slave.h
- Move link break and enable up into master scan
- Change rc = func(); return rc; coding to return func();
Note this is still in place in fsi_master_send_break in
anticipation of changes coming up in next patch of this
series.
- Fix comment spelling error
- Use dev_dbg instead of dev_info when link enable or break
fails
- Remove explicit set of master->break and
master->link_enable to NULL due to kzalloc
v6 - Don't return error if master->link_enable or
master->break are not defined.
---
drivers/fsi/fsi-core.c | 38 +++++++++++++++++++++++++++++++++++---
drivers/fsi/fsi-master.h | 2 ++
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 95e867e..7fc15ab 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -288,16 +288,48 @@ static int fsi_slave_init(struct fsi_master *master,
/* FSI master support */
+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, slave_id;
+ int link, slave_id, 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 with:%d\n", link, rc);
+ continue;
+ }
+ rc = fsi_master_break(master, link);
+ if (rc) {
+ dev_dbg(master->dev,
+ "Break to link:%d failed with:%d\n", link, rc);
+ continue;
+ }
- for (link = 0; link < master->n_links; link++)
for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
fsi_slave_init(master, link, slave_id);
+ }
return 0;
-
}
int fsi_master_register(struct fsi_master *master)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index cafb433..56aad0e 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -29,6 +29,8 @@ struct fsi_master {
int (*write)(struct fsi_master *, int link,
uint8_t slave, uint32_t addr,
const void *val, size_t size);
+ int (*send_break)(struct fsi_master *, int link);
+ int (*link_enable)(struct fsi_master *, int link);
};
extern int fsi_master_register(struct fsi_master *master);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (13 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master christopher.lee.bostic
` (21 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Set CFAM to appropriate ID so that the controlling master
can manage link memory ranges. Add slave engine register
definitions.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V3 - Remove master->set_smode op and make it generic for
all masters.
V4 - 'Change rc = func(); return rc;' code to 'return func();'
- Change set_smode_defaults to return value instead of
passing back via reference.
- Rename fsi_master_set_smode to fsi_slave_set_smode
- Pass in slave id to fsi_slave_set_smode instead of
making assumptions internal to the function.
- Only allow slave inits on id 0 - a todo to accommodate
all slave ID's.
V5 - Move SMODE read at default ID out of break and into
the caller.
V6 - Change the slave ID from 0 to 3 when setting smode to
new value.
---
drivers/fsi/fsi-core.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 7fc15ab..6ac239a 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -33,6 +33,7 @@
#define FSI_SLAVE_CONF_TYPE_SHIFT 4
#define FSI_PEEK_BASE 0x410
+#define FSI_SLAVE_BASE 0x800
static const int engine_page_size = 0x400;
@@ -52,6 +53,25 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size);
+/*
+ * 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 */
+
/* FSI endpoint-device support */
int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
size_t size)
@@ -134,6 +154,31 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+
+/* 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 int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
void *val, size_t size)
{
@@ -240,6 +285,22 @@ static void fsi_slave_release(struct device *dev)
kfree(slave);
}
+static uint32_t set_smode_defaults(struct fsi_master *master)
+{
+ return FSI_SMODE_WSC | FSI_SMODE_ECRC
+ | fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+ | fsi_smode_lbcrr(1);
+}
+
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id)
+{
+ uint32_t smode = set_smode_defaults(master);
+
+ smode |= fsi_smode_sid(id);
+ return master->write(master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+ &smode, sizeof(smode));
+}
+
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
{
@@ -247,6 +308,21 @@ static int fsi_slave_init(struct fsi_master *master,
uint32_t chip_id;
int rc;
+ /*
+ * todo: Due to CFAM hardware issues related to BREAK commands we're
+ * limited to only one CFAM per link. Once issues are resolved this
+ * restriction can be removed.
+ */
+ if (slave_id > 0)
+ return 0;
+
+ rc = fsi_slave_set_smode(master, link, slave_id);
+ if (rc) {
+ dev_warn(master->dev, "can't set smode on slave:%02x:%02x %d\n",
+ link, slave_id, rc);
+ return -ENODEV;
+ }
+
rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
if (rc) {
dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
@@ -310,6 +386,7 @@ static int fsi_master_break(struct fsi_master *master, int link)
static int fsi_master_scan(struct fsi_master *master)
{
int link, slave_id, rc;
+ uint32_t smode;
for (link = 0; link < master->n_links; link++) {
rc = fsi_master_link_enable(master, link);
@@ -324,6 +401,18 @@ static int fsi_master_scan(struct fsi_master *master)
"Break to link:%d failed with:%d\n", link, rc);
continue;
}
+ /*
+ * Verify can read slave at default ID location. If fails
+ * there must be nothing on other end of link
+ */
+ rc = master->read(master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+ &smode, sizeof(smode));
+ if (rc) {
+ dev_dbg(master->dev,
+ "Read link:%d smode default id failed:%d\n",
+ link, rc);
+ continue;
+ }
for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
fsi_slave_init(master, link, slave_id);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (14 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-11-02 3:56 ` Alistair Popple
2016-10-30 22:09 ` [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities christopher.lee.bostic
` (20 subsequent siblings)
36 siblings, 1 reply; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic, Jeremy Kerr
From: Chris Bostic <cbostic@us.ibm.com>
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 Jeremy Kerr's
original GPIO master base commit.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
V3 - Added remainder of base I/O ops (excluding dev tree and crc calcs).
- Add encoding of all read/write commands and send data over sda
- Check for and decode response from slave.
- Add set enable pin in master->link_enable() master specific function.
V4 - Remove unnecessary comments about delays in various locations
- Define non clock and data pins as optional and check for gpio
descriptors == NULL before accessing. Don't return error if
optional pins cannot be retrieved via devm.
- serial_in: rearrange order of msg shift and input bit OR op.
- serial_out: Fix mirror image issue on shift data out and set
data out as necessary for bit count to be sent.
- serial_out: make message parm a const
- serial_out: fix 3x 'msg & data' repeat
- poll_for_response: bits remaining was calculated with
sizeof(size) should be size.
- fsi_master_gpio_break: remove smode read
- Replace dev_info with dev_dbg in proper places.
- Add a bit count parm to serial_in, increment msg->bits
on every bit received.
- Remove magic numbers in poll_for_response that indicate
how many bits to receive
- Utilize the crc utilities to check input data and set
crc for output data
- Remove add_crc stub
V5 - Rename pins from generic 'clk', 'data', etc in dts file to
'fsi_clk', 'fsi_data', etc...
- serial_in: invert data bit during message assembly
instead of inverting whole message after assembly.
- Remove all instances of clearing out message field prior
to calling serial_in, redundant.
- Change name of build_command() to build_abs_ar_command()
to make it more obvious its creating ABS AR type commands.
- Remove unlikely( ) checks.
- Indent dev_info string "master time out waiting for response"
- poll_for_response: return FSI_GPIO_MAX busy instead of
busy_count.
- fsi_master_gpio_break: move 'logic reset' comment to above
the delay call.
- Add crc4 calculation initialization since data includes
a start bit.
V6 - Invert polarity of SDA line for start bit, echo delay, pre-
D-POLL clocking, break command sequence (pre, during, post).
- Add echo delay after sending D-POLL
- Fill in user read data with received read data response
- Clock the slave after each command completes to prime it
for another operation
- Increase D-POLL checks on BUSY response before flagging
error
- Add Alistair's crc calculator from pdbg. Having issues
with the existing one.
- Revert V5 changes to rename gpio pin names.
- Return -EIO on all bus errors
- Condense slave id and response id into one serial-in
operation
- Change D-POLL command to accommodate right alignment
- Remove all dev_info( ) calls to reduce spam
- serial_in remove cmd->bits = 0 / cmd->bits++ in for
loop. Just set value at end.
- Change v translator line to direction output once
during init and use gpiod_set_value thereafter
- Remove set/clear clocks and do it all in clock_toggle
- Fill in slave id when encoding commands. Right align
the command prior to serializing out.
- gpio master defines number of links to 1, was undefined.
---
.../devicetree/bindings/fsi-master-gpio.txt | 21 +
.../devicetree/bindings/fsi/fsi-master-gpio.txt | 21 +
arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 36 ++
drivers/fsi/Kconfig | 6 +
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-gpio.c | 522 +++++++++++++++++++++
6 files changed, 607 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fsi-master-gpio.txt
create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
create mode 100644 drivers/fsi/fsi-master-gpio.c
diff --git a/Documentation/devicetree/bindings/fsi-master-gpio.txt b/Documentation/devicetree/bindings/fsi-master-gpio.txt
new file mode 100644
index 0000000..d46be27
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi-master-gpio.txt
@@ -0,0 +1,21 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,fsi-master-gpio";
+ - clk-gpio;
+ - data-gpio;
+
+Optional properties:
+ - enable-gpio;
+ - trans-gpio;
+ - mux-gpio;
+
+fsi-master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpio = <&gpio 0>;
+ data-gpio = <&gpio 1>;
+ enable-gpio = <&gpio 2>; /* Enable FSI data in/out */
+ trans-gpio = <&gpio 3>; /* Voltage translator direction */
+ mux-gpio = <&gpio 4>; /* Multiplexer for FSI pins */
+}
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..ff3a62e
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
@@ -0,0 +1,21 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,fsi-master-gpio";
+ - clk-gpios;
+ - data-gpios;
+
+Optional properties:
+ - enable-gpios;
+ - trans-gpios;
+ - mux-gpios;
+
+fsi-master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpios = <&gpio 0 &gpio 6>;
+ data-gpios = <&gpio 1 &gpio 7>;
+ enable-gpios = <&gpio 2 &gpio 8>; /* Enable FSI data in/out */
+ trans-gpios = <&gpio 3 &gpio 9>; /* Volts translator direction */
+ mux-gpios = <&gpio 4> &gpio 10>; /* Multiplexer for FSI pins */
+}
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
index 21619fd..9d9c2a9 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
@@ -167,6 +167,42 @@
output-low;
line-name = "func_mode2";
};
+
+ pin_fsi_clk {
+ gpios = <ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_clk";
+ };
+
+ pin_fsi_data {
+ gpios = <ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_data";
+ };
+
+ pin_fsi_trans {
+ gpios = <ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_trans";
+ };
+
+ pin_fsi_enable {
+ gpios = <ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_enable";
+ };
+
+ pin_fsi_mux {
+ gpios = <ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_mux";
+ };
+
+ fsi_master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpios = <&gpio 0 &gpio 6>;
+ data-gpios = <&gpio 1 &gpio 7>;
+ };
};
&vuart {
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index f065dbe..b21fe3c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -17,6 +17,12 @@ config FSI_MASTER_FAKE
depends on FSI
---help---
This option enables a fake FSI master driver for debugging.
+
+config FSI_MASTER_GPIO
+ tristate "GPIO-based FSI master"
+ depends on FSI
+ ---help---
+ This option enables a FSI master driver using GPIO lines.
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 847c00c..2021ce5 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
new file mode 100644
index 0000000..175281a
--- /dev/null
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -0,0 +1,522 @@
+/*
+ * A FSI master controller, using a simple GPIO bit-banging interface
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fsi.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 0x000000000000002AULL
+#define FSI_GPIO_CMD_DPOLL_SIZE 9
+#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to hang */
+#define FSI_GPIO_CMD_DEFAULT 0x2000000000000000ULL
+#define FSI_GPIO_CMD_WRITE 0
+#define FSI_GPIO_CMD_READ 0x0400000000000000ULL
+#define FSI_GPIO_CMD_SLAVE_MASK 0xC000000000000000ULL
+#define FSI_GPIO_CMD_ADDR_SHIFT 37
+#define FSI_GPIO_CMD_SLV_SHIFT 62
+#define FSI_GPIO_CMD_SIZE_16 0x0000001000000000ULL
+#define FSI_GPIO_CMD_SIZE_32 0x0000003000000000ULL
+#define FSI_GPIO_CMD_DT32_SHIFT 4
+#define FSI_GPIO_CMD_DT16_SHIFT 20
+#define FSI_GPIO_CMD_DT8_SHIFT 28
+#define FSI_GPIO_CMD_DFLT_LEN 28
+#define FSI_GPIO_CMD_CRC_SHIFT 60
+
+/* 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 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)
+{
+ ndelay(FSI_GPIO_STD_DLY);
+ return gpiod_get_value(master->gpio_data);
+}
+
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+ gpiod_set_value(master->gpio_data, value);
+}
+
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+ gpiod_direction_input(master->gpio_data);
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master, int value)
+{
+ gpiod_direction_output(master->gpio_data, value);
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 1);
+}
+
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd,
+ uint8_t num_bits)
+{
+ uint8_t bit;
+ uint64_t msg = 0;
+ uint8_t in_bit = 0;
+
+ set_sda_input(master);
+
+ for (bit = 0; bit < num_bits; bit++) {
+ clock_toggle(master, 1);
+ in_bit = sda_in(master);
+ msg <<= 1;
+ msg |= ~in_bit & 0x1; /* Data is negative active */
+ }
+ cmd->bits = num_bits;
+ cmd->msg = msg;
+}
+
+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 negative active */
+ uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
+ uint64_t last_bit = ~0;
+ int next_bit;
+
+ if (!cmd->bits) {
+ dev_warn(master->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;
+ }
+}
+
+/*
+ * Clock out some 0's after every message to ride out line reflections
+ */
+static void echo_delay(struct fsi_master_gpio *master)
+{
+ set_sda_output(master, 1);
+ clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+/*
+ * Used in bus error cases only. Clears out any remaining data the slave
+ * is attempting to send
+ */
+static void drain_response(struct fsi_master_gpio *master)
+{
+ struct fsi_gpio_msg msg;
+
+ serial_in(master, &msg, FSI_GPIO_DRAIN_BITS);
+}
+
+/*
+ * 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)
+{
+
+}
+
+/*
+ * Taken from Alistaire's PDBG
+ */
+static uint8_t crc4(uint8_t c, int b)
+{
+ uint8_t m = 0;
+
+ c &= 0xf;
+ m = b ^ ((c >> 3) & 0x1);
+ m = (m << 2) | (m << 1) | (m);
+ c <<= 1;
+ c ^= m;
+
+ return c & 0xf;
+}
+
+static int poll_for_response(struct fsi_master_gpio *master, uint8_t expected,
+ uint8_t size, void *data)
+{
+ int busy_count = 0, i;
+ struct fsi_gpio_msg response, cmd;
+ int bits_remaining = 0, bit_count, response_id, id;
+ uint64_t resp = 0;
+ uint8_t bits_received = FSI_GPIO_MSG_ID_SIZE +
+ FSI_GPIO_MSG_RESPID_SIZE;
+ uint8_t crc_in;
+
+ do {
+ for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+ serial_in(master, &response, 1);
+ if (response.msg)
+ break;
+ }
+ if (i >= FSI_GPIO_MTOE_COUNT) {
+ dev_dbg(master->master.dev,
+ "Master time out waiting for response\n");
+ drain_response(master);
+ fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+ return -EIO;
+ }
+
+ /* Response received */
+ bit_count = FSI_GPIO_MSG_ID_SIZE + FSI_GPIO_MSG_RESPID_SIZE;
+ serial_in(master, &response, bit_count);
+
+ response_id = response.msg & 0x3;
+ id = (response.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
+ dev_dbg(master->master.dev, "id:%d resp:%d\n", id, response_id);
+
+ resp = response.msg;
+
+ switch (response_id) {
+ case FSI_GPIO_RESP_ACK:
+ if (expected == FSI_GPIO_RESP_ACKD)
+ bits_remaining = 8 * size;
+ break;
+
+ case FSI_GPIO_RESP_BUSY:
+ /*
+ * Its necessary to clock slave before issuing
+ * d-poll, not indicated in the hardware protocol
+ * spec. < 20 clocks causes slave to hang, 21 ok.
+ */
+ set_sda_output(master, 1);
+ clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
+ cmd.msg = FSI_GPIO_CMD_DPOLL;
+ cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
+ serial_out(master, &cmd);
+ echo_delay(master);
+ continue;
+
+ case FSI_GPIO_RESP_ERRA:
+ case FSI_GPIO_RESP_ERRC:
+ dev_dbg(master->master.dev, "ERR received: %d\n",
+ (int)response.msg);
+ drain_response(master);
+ fsi_master_gpio_error(master, response.msg);
+ return -EIO;
+ }
+
+ /* Read in the data field if applicable */
+ if (bits_remaining) {
+ serial_in(master, &response, bits_remaining);
+ resp <<= bits_remaining;
+ resp |= response.msg;
+ bits_received += bits_remaining;
+ *((uint32_t *)data) = response.msg;
+ }
+
+ /* Include start bit */
+ crc_in = crc4(0, 1);
+ for (i = bits_received - 1; i >= 0; i--)
+ crc_in = crc4(crc_in, !!(resp & (1ULL << i)));
+
+ /* Read in the crc and check it */
+ serial_in(master, &response, FSI_GPIO_CRC_SIZE);
+ if (crc_in != response.msg) {
+
+ /* CRC's don't match - warn for now */
+ dev_dbg(master->master.dev, "ERR response CRC\n");
+ fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
+ }
+ /* Clock the slave enough to be ready for next operation */
+ clock_toggle(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
+ return 0;
+
+ } while (busy_count++ < FSI_GPIO_MAX_BUSY);
+
+ dev_dbg(master->master.dev, "ERR slave is stuck in busy state\n");
+ fsi_master_gpio_error(master, FSI_GPIO_ERR_BUSY);
+
+ return -EIO;
+}
+
+static void build_abs_ar_command(struct fsi_gpio_msg *cmd, uint64_t mode,
+ uint8_t slave, uint32_t addr, size_t size,
+ const void *data)
+{
+ uint8_t crc;
+ int i;
+
+ cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
+ cmd->msg = FSI_GPIO_CMD_DEFAULT;
+ cmd->msg |= mode;
+
+ /* todo: handle more than just slave id 0 */
+ cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
+ cmd->msg |= (((uint64_t)slave) << FSI_GPIO_CMD_SLV_SHIFT);
+ cmd->msg |= (((uint64_t)addr) << FSI_GPIO_CMD_ADDR_SHIFT);
+ if (size == sizeof(uint8_t)) {
+ if (data) {
+ uint8_t cmd_data = *((uint8_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT8_SHIFT;
+ }
+ } else if (size == sizeof(uint16_t)) {
+ cmd->msg |= FSI_GPIO_CMD_SIZE_16;
+ if (data) {
+ uint16_t cmd_data = *((uint16_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT16_SHIFT;
+ }
+ } else {
+ cmd->msg |= FSI_GPIO_CMD_SIZE_32;
+ if (data) {
+ uint32_t cmd_data = *((uint32_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT32_SHIFT;
+ }
+ }
+
+ if (mode == FSI_GPIO_CMD_WRITE)
+ cmd->bits += (8 * size);
+
+ /* Include start bit */
+ crc = crc4(0, 1);
+ for (i = 63; i >= (64 - cmd->bits); i--)
+ crc = crc4(crc, !!(cmd->msg & (1ULL << i)));
+
+ cmd->msg |= ((uint64_t)crc) << (FSI_GPIO_CMD_CRC_SHIFT - cmd->bits);
+ cmd->bits += FSI_GPIO_CRC_SIZE;
+
+ /* Right align message */
+ cmd->msg >>= (64 - cmd->bits);
+}
+
+static int fsi_master_gpio_read(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, void *val, size_t size)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
+
+ if (link != 0)
+ return -ENODEV;
+
+ build_abs_ar_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size, NULL);
+ serial_out(master, &cmd);
+ echo_delay(master);
+
+ return poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
+}
+
+static int fsi_master_gpio_write(struct fsi_master *_master, int link,
+ uint8_t slave, 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, FSI_GPIO_CMD_WRITE, slave, addr, size, val);
+ serial_out(master, &cmd);
+ echo_delay(master);
+
+ return poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
+}
+
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+
+ set_sda_output(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)
+{
+ if (master->gpio_mux)
+ gpiod_direction_output(master->gpio_mux, 1);
+ if (master->gpio_trans)
+ gpiod_direction_output(master->gpio_trans, 1);
+ if (master->gpio_enable)
+ gpiod_direction_output(master->gpio_enable, 0);
+ gpiod_direction_output(master->gpio_clk, 1);
+ gpiod_direction_output(master->gpio_data, 1);
+
+ /* todo: evaluate if clocks can be reduced */
+ clock_toggle(master, FSI_INIT_CLOCKS);
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+
+ if (master->gpio_enable)
+ 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;
+
+ gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_clk = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "data", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_data = gpio;
+
+ /* Optional pins */
+
+ gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+ if (!IS_ERR(gpio))
+ master->gpio_trans = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+ if (!IS_ERR(gpio))
+ master->gpio_enable = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+ if (!IS_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.send_break = fsi_master_gpio_break;
+ master->master.link_enable = fsi_master_gpio_link_enable;
+
+ fsi_master_gpio_init(master);
+
+ platform_set_drvdata(pdev, 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 = "ibm,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] 47+ messages in thread* Re: [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master
2016-10-30 22:09 ` [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master christopher.lee.bostic
@ 2016-11-02 3:56 ` Alistair Popple
2016-11-03 17:33 ` Christopher Bostic
0 siblings, 1 reply; 47+ messages in thread
From: Alistair Popple @ 2016-11-02 3:56 UTC (permalink / raw)
To: openbmc; +Cc: christopher.lee.bostic, xxpetri, zahrens
Comments below.
On Sun, 30 Oct 2016 05:09:18 PM christopher.lee.bostic@gmail.com wrote:
> From: Chris Bostic <cbostic@us.ibm.com>
>
> 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 Jeremy Kerr's
> original GPIO master base commit.
>
> Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
>
> ---
>
> V3 - Added remainder of base I/O ops (excluding dev tree and crc calcs).
>
> - Add encoding of all read/write commands and send data over sda
>
> - Check for and decode response from slave.
>
> - Add set enable pin in master->link_enable() master specific function.
>
> V4 - Remove unnecessary comments about delays in various locations
>
> - Define non clock and data pins as optional and check for gpio
> descriptors == NULL before accessing. Don't return error if
> optional pins cannot be retrieved via devm.
>
> - serial_in: rearrange order of msg shift and input bit OR op.
>
> - serial_out: Fix mirror image issue on shift data out and set
> data out as necessary for bit count to be sent.
>
> - serial_out: make message parm a const
>
> - serial_out: fix 3x 'msg & data' repeat
>
> - poll_for_response: bits remaining was calculated with
> sizeof(size) should be size.
>
> - fsi_master_gpio_break: remove smode read
>
> - Replace dev_info with dev_dbg in proper places.
>
> - Add a bit count parm to serial_in, increment msg->bits
> on every bit received.
>
> - Remove magic numbers in poll_for_response that indicate
> how many bits to receive
>
> - Utilize the crc utilities to check input data and set
> crc for output data
>
> - Remove add_crc stub
>
> V5 - Rename pins from generic 'clk', 'data', etc in dts file to
> 'fsi_clk', 'fsi_data', etc...
>
> - serial_in: invert data bit during message assembly
> instead of inverting whole message after assembly.
>
> - Remove all instances of clearing out message field prior
> to calling serial_in, redundant.
>
> - Change name of build_command() to build_abs_ar_command()
> to make it more obvious its creating ABS AR type commands.
>
> - Remove unlikely( ) checks.
>
> - Indent dev_info string "master time out waiting for response"
>
> - poll_for_response: return FSI_GPIO_MAX busy instead of
> busy_count.
>
> - fsi_master_gpio_break: move 'logic reset' comment to above
> the delay call.
>
> - Add crc4 calculation initialization since data includes
> a start bit.
>
> V6 - Invert polarity of SDA line for start bit, echo delay, pre-
> D-POLL clocking, break command sequence (pre, during, post).
>
> - Add echo delay after sending D-POLL
>
> - Fill in user read data with received read data response
>
> - Clock the slave after each command completes to prime it
> for another operation
>
> - Increase D-POLL checks on BUSY response before flagging
> error
>
> - Add Alistair's crc calculator from pdbg. Having issues
> with the existing one.
>
> - Revert V5 changes to rename gpio pin names.
>
> - Return -EIO on all bus errors
>
> - Condense slave id and response id into one serial-in
> operation
>
> - Change D-POLL command to accommodate right alignment
>
> - Remove all dev_info( ) calls to reduce spam
>
> - serial_in remove cmd->bits = 0 / cmd->bits++ in for
> loop. Just set value at end.
>
> - Change v translator line to direction output once
> during init and use gpiod_set_value thereafter
>
> - Remove set/clear clocks and do it all in clock_toggle
>
> - Fill in slave id when encoding commands. Right align
> the command prior to serializing out.
>
> - gpio master defines number of links to 1, was undefined.
> ---
> .../devicetree/bindings/fsi-master-gpio.txt | 21 +
> .../devicetree/bindings/fsi/fsi-master-gpio.txt | 21 +
> arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 36 ++
> drivers/fsi/Kconfig | 6 +
> drivers/fsi/Makefile | 1 +
> drivers/fsi/fsi-master-gpio.c | 522
+++++++++++++++++++++
> 6 files changed, 607 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/fsi-master-gpio.txt
> create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-
gpio.txt
> create mode 100644 drivers/fsi/fsi-master-gpio.c
>
> diff --git a/Documentation/devicetree/bindings/fsi-master-gpio.txt
b/Documentation/devicetree/bindings/fsi-master-gpio.txt
> new file mode 100644
> index 0000000..d46be27
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/fsi-master-gpio.txt
> @@ -0,0 +1,21 @@
> +Device-tree bindings for gpio-based FSI master driver
> +-----------------------------------------------------
> +
> +Required properties:
> + - compatible = "ibm,fsi-master-gpio";
> + - clk-gpio;
> + - data-gpio;
> +
> +Optional properties:
> + - enable-gpio;
> + - trans-gpio;
> + - mux-gpio;
> +
> +fsi-master {
> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
> + clk-gpio = <&gpio 0>;
> + data-gpio = <&gpio 1>;
> + enable-gpio = <&gpio 2>; /* Enable FSI data in/out */
> + trans-gpio = <&gpio 3>; /* Voltage translator direction */
> + mux-gpio = <&gpio 4>; /* Multiplexer for FSI pins */
> +}
> 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..ff3a62e
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
> @@ -0,0 +1,21 @@
> +Device-tree bindings for gpio-based FSI master driver
> +-----------------------------------------------------
> +
> +Required properties:
> + - compatible = "ibm,fsi-master-gpio";
> + - clk-gpios;
> + - data-gpios;
> +
> +Optional properties:
> + - enable-gpios;
> + - trans-gpios;
> + - mux-gpios;
> +
> +fsi-master {
> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
> + clk-gpios = <&gpio 0 &gpio 6>;
> + data-gpios = <&gpio 1 &gpio 7>;
> + enable-gpios = <&gpio 2 &gpio 8>; /* Enable FSI data in/out */
> + trans-gpios = <&gpio 3 &gpio 9>; /* Volts translator direction
*/
> + mux-gpios = <&gpio 4> &gpio 10>; /* Multiplexer for FSI pins */
> +}
> diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> index 21619fd..9d9c2a9 100644
> --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> @@ -167,6 +167,42 @@
> output-low;
> line-name = "func_mode2";
> };
> +
> + pin_fsi_clk {
> + gpios = <ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
> + output-low;
> + line-name = "fsi_clk";
> + };
> +
> + pin_fsi_data {
> + gpios = <ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
> + output-low;
> + line-name = "fsi_data";
> + };
> +
> + pin_fsi_trans {
> + gpios = <ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
> + output-low;
> + line-name = "fsi_trans";
> + };
> +
> + pin_fsi_enable {
> + gpios = <ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
> + output-low;
> + line-name = "fsi_enable";
> + };
> +
> + pin_fsi_mux {
> + gpios = <ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
> + output-low;
> + line-name = "fsi_mux";
> + };
> +
> + fsi_master {
> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
> + clk-gpios = <&gpio 0 &gpio 6>;
> + data-gpios = <&gpio 1 &gpio 7>;
> + };
> };
>
> &vuart {
> diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
> index f065dbe..b21fe3c 100644
> --- a/drivers/fsi/Kconfig
> +++ b/drivers/fsi/Kconfig
> @@ -17,6 +17,12 @@ config FSI_MASTER_FAKE
> depends on FSI
> ---help---
> This option enables a fake FSI master driver for debugging.
> +
> +config FSI_MASTER_GPIO
> + tristate "GPIO-based FSI master"
> + depends on FSI
> + ---help---
> + This option enables a FSI master driver using GPIO lines.
> endif
>
> endmenu
> diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
> index 847c00c..2021ce5 100644
> --- a/drivers/fsi/Makefile
> +++ b/drivers/fsi/Makefile
> @@ -1,3 +1,4 @@
>
> obj-$(CONFIG_FSI) += fsi-core.o
> obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
> +obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
> diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
> new file mode 100644
> index 0000000..175281a
> --- /dev/null
> +++ b/drivers/fsi/fsi-master-gpio.c
> @@ -0,0 +1,522 @@
> +/*
> + * A FSI master controller, using a simple GPIO bit-banging interface
> + */
> +
> +#include <linux/platform_device.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/fsi.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 0x000000000000002AULL
> +#define FSI_GPIO_CMD_DPOLL_SIZE 9
> +#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to
hang */
> +#define FSI_GPIO_CMD_DEFAULT 0x2000000000000000ULL
> +#define FSI_GPIO_CMD_WRITE 0
> +#define FSI_GPIO_CMD_READ 0x0400000000000000ULL
> +#define FSI_GPIO_CMD_SLAVE_MASK 0xC000000000000000ULL
> +#define FSI_GPIO_CMD_ADDR_SHIFT 37
> +#define FSI_GPIO_CMD_SLV_SHIFT 62
> +#define FSI_GPIO_CMD_SIZE_16 0x0000001000000000ULL
> +#define FSI_GPIO_CMD_SIZE_32 0x0000003000000000ULL
> +#define FSI_GPIO_CMD_DT32_SHIFT 4
> +#define FSI_GPIO_CMD_DT16_SHIFT 20
> +#define FSI_GPIO_CMD_DT8_SHIFT 28
> +#define FSI_GPIO_CMD_DFLT_LEN 28
> +#define FSI_GPIO_CMD_CRC_SHIFT 60
> +
> +/* 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 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)
> +{
> + ndelay(FSI_GPIO_STD_DLY);
> + return gpiod_get_value(master->gpio_data);
> +}
> +
> +static void sda_out(struct fsi_master_gpio *master, int value)
> +{
> + gpiod_set_value(master->gpio_data, value);
> +}
> +
> +static void set_sda_input(struct fsi_master_gpio *master)
> +{
> + gpiod_direction_input(master->gpio_data);
> + if (master->gpio_trans)
> + gpiod_set_value(master->gpio_trans, 0);
> +}
> +
> +static void set_sda_output(struct fsi_master_gpio *master, int value)
> +{
> + gpiod_direction_output(master->gpio_data, value);
> + if (master->gpio_trans)
> + gpiod_set_value(master->gpio_trans, 1);
> +}
> +
> +static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg
*cmd,
> + uint8_t num_bits)
> +{
> + uint8_t bit;
> + uint64_t msg = 0;
> + uint8_t in_bit = 0;
> +
> + set_sda_input(master);
> +
> + for (bit = 0; bit < num_bits; bit++) {
> + clock_toggle(master, 1);
> + in_bit = sda_in(master);
> + msg <<= 1;
> + msg |= ~in_bit & 0x1; /* Data is negative active */
> + }
> + cmd->bits = num_bits;
> + cmd->msg = msg;
> +}
> +
> +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 negative active */
> + uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
> + uint64_t last_bit = ~0;
> + int next_bit;
> +
> + if (!cmd->bits) {
> + dev_warn(master->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;
> + }
> +}
> +
> +/*
> + * Clock out some 0's after every message to ride out line reflections
> + */
> +static void echo_delay(struct fsi_master_gpio *master)
> +{
> + set_sda_output(master, 1);
> + clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
> +}
> +
> +/*
> + * Used in bus error cases only. Clears out any remaining data the slave
> + * is attempting to send
> + */
> +static void drain_response(struct fsi_master_gpio *master)
> +{
> + struct fsi_gpio_msg msg;
> +
> + serial_in(master, &msg, FSI_GPIO_DRAIN_BITS);
> +}
> +
> +/*
> + * 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)
> +{
> +
> +}
> +
> +/*
> + * Taken from Alistaire's PDBG
> + */
> +static uint8_t crc4(uint8_t c, int b)
> +{
> + uint8_t m = 0;
> +
> + c &= 0xf;
> + m = b ^ ((c >> 3) & 0x1);
> + m = (m << 2) | (m << 1) | (m);
> + c <<= 1;
> + c ^= m;
> +
> + return c & 0xf;
> +}
> +
> +static int poll_for_response(struct fsi_master_gpio *master, uint8_t
expected,
> + uint8_t size, void *data)
> +{
> + int busy_count = 0, i;
> + struct fsi_gpio_msg response, cmd;
> + int bits_remaining = 0, bit_count, response_id, id;
> + uint64_t resp = 0;
> + uint8_t bits_received = FSI_GPIO_MSG_ID_SIZE +
> + FSI_GPIO_MSG_RESPID_SIZE;
> + uint8_t crc_in;
> +
> + do {
> + for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
> + serial_in(master, &response, 1);
> + if (response.msg)
> + break;
> + }
> + if (i >= FSI_GPIO_MTOE_COUNT) {
> + dev_dbg(master->master.dev,
> + "Master time out waiting for response\n");
> + drain_response(master);
> + fsi_master_gpio_error(master, FSI_GPIO_MTOE);
> + return -EIO;
> + }
> +
> + /* Response received */
> + bit_count = FSI_GPIO_MSG_ID_SIZE + FSI_GPIO_MSG_RESPID_SIZE;
> + serial_in(master, &response, bit_count);
> +
> + response_id = response.msg & 0x3;
> + id = (response.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
> + dev_dbg(master->master.dev, "id:%d resp:%d\n", id,
response_id);
> +
> + resp = response.msg;
> +
> + switch (response_id) {
> + case FSI_GPIO_RESP_ACK:
> + if (expected == FSI_GPIO_RESP_ACKD)
> + bits_remaining = 8 * size;
> + break;
> +
> + case FSI_GPIO_RESP_BUSY:
> + /*
> + * Its necessary to clock slave before issuing
> + * d-poll, not indicated in the hardware protocol
> + * spec. < 20 clocks causes slave to hang, 21 ok.
> + */
Interesting. Have HW folk confirmed this? I've encountered similar issues on
occasion, so it would be good to get confirmation that we need a minimum of 20
slave clocks prior to sending a d-poll.
> + set_sda_output(master, 1);
> + clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
> + cmd.msg = FSI_GPIO_CMD_DPOLL;
> + cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
> + serial_out(master, &cmd);
> + echo_delay(master);
> + continue;
> +
> + case FSI_GPIO_RESP_ERRA:
> + case FSI_GPIO_RESP_ERRC:
> + dev_dbg(master->master.dev, "ERR received: %d\n",
> + (int)response.msg);
> + drain_response(master);
Why do we have to call drain_response() for an error code? Isn't ERRA/C a well
defined bit sequence?
> + fsi_master_gpio_error(master, response.msg);
> + return -EIO;
> + }
> +
> + /* Read in the data field if applicable */
> + if (bits_remaining) {
> + serial_in(master, &response, bits_remaining);
> + resp <<= bits_remaining;
> + resp |= response.msg;
> + bits_received += bits_remaining;
> + *((uint32_t *)data) = response.msg;
> + }
> +
> + /* Include start bit */
> + crc_in = crc4(0, 1);
> + for (i = bits_received - 1; i >= 0; i--)
> + crc_in = crc4(crc_in, !!(resp & (1ULL << i)));
> +
> + /* Read in the crc and check it */
> + serial_in(master, &response, FSI_GPIO_CRC_SIZE);
> + if (crc_in != response.msg) {
> +
> + /* CRC's don't match - warn for now */
> + dev_dbg(master->master.dev, "ERR response CRC\n");
> + fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
> + }
> + /* Clock the slave enough to be ready for next operation */
> + clock_toggle(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
> + return 0;
> +
> + } while (busy_count++ < FSI_GPIO_MAX_BUSY);
> +
> + dev_dbg(master->master.dev, "ERR slave is stuck in busy state\n");
> + fsi_master_gpio_error(master, FSI_GPIO_ERR_BUSY);
> +
> + return -EIO;
> +}
> +
> +static void build_abs_ar_command(struct fsi_gpio_msg *cmd, uint64_t mode,
> + uint8_t slave, uint32_t addr, size_t size,
> + const void *data)
> +{
> + uint8_t crc;
> + int i;
> +
> + cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
> + cmd->msg = FSI_GPIO_CMD_DEFAULT;
> + cmd->msg |= mode;
> +
> + /* todo: handle more than just slave id 0 */
This should be easily fixed and is needed to get the full 23-bit FSI address
space. For P8 at least I think you just use addr bits 22 & 23 as the slave id.
> + cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
> + cmd->msg |= (((uint64_t)slave) << FSI_GPIO_CMD_SLV_SHIFT);
> + cmd->msg |= (((uint64_t)addr) << FSI_GPIO_CMD_ADDR_SHIFT);
Wouldn't it be best to apply a mask to addr limiting it to 21-bits? Or a check
to ensure addr <= (1<<23) - 1. Otherwise wont invalid addresses result in
invalid commands and/or errors as a result?
> + if (size == sizeof(uint8_t)) {
> + if (data) {
> + uint8_t cmd_data = *((uint8_t *)data);
> +
> + cmd->msg |=
> + ((uint64_t)cmd_data) <<
FSI_GPIO_CMD_DT8_SHIFT;
> + }
> + } else if (size == sizeof(uint16_t)) {
> + cmd->msg |= FSI_GPIO_CMD_SIZE_16;
> + if (data) {
> + uint16_t cmd_data = *((uint16_t *)data);
It might be best to do a memcpy here or make *data a uint64_t *. There is no
guarantee that *data has the correct alignment which from memory will result
in a data abort fault/exception to fix-up the alignment on ARMv5 and below
(ie. AST2400).
> + cmd->msg |=
> + ((uint64_t)cmd_data) <<
FSI_GPIO_CMD_DT16_SHIFT;
> + }
> + } else {
> + cmd->msg |= FSI_GPIO_CMD_SIZE_32;
> + if (data) {
> + uint32_t cmd_data = *((uint32_t *)data);
Ditto.
> + cmd->msg |=
> + ((uint64_t)cmd_data) <<
FSI_GPIO_CMD_DT32_SHIFT;
> + }
> + }
> +
> + if (mode == FSI_GPIO_CMD_WRITE)
> + cmd->bits += (8 * size);
> +
> + /* Include start bit */
> + crc = crc4(0, 1);
> + for (i = 63; i >= (64 - cmd->bits); i--)
> + crc = crc4(crc, !!(cmd->msg & (1ULL << i)));
> +
> + cmd->msg |= ((uint64_t)crc) << (FSI_GPIO_CMD_CRC_SHIFT - cmd->bits);
> + cmd->bits += FSI_GPIO_CRC_SIZE;
> +
> + /* Right align message */
> + cmd->msg >>= (64 - cmd->bits);
> +}
> +
> +static int fsi_master_gpio_read(struct fsi_master *_master, int link,
> + uint8_t slave, uint32_t addr, void *val, size_t size)
> +{
> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
> + struct fsi_gpio_msg cmd;
> +
> + if (link != 0)
> + return -ENODEV;
> +
> + build_abs_ar_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size,
NULL);
> + serial_out(master, &cmd);
> + echo_delay(master);
> +
> + return poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
> +}
> +
> +static int fsi_master_gpio_write(struct fsi_master *_master, int link,
> + uint8_t slave, 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, FSI_GPIO_CMD_WRITE, slave, addr, size,
val);
> + serial_out(master, &cmd);
> + echo_delay(master);
> +
> + return poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
> +}
> +
> +/*
> + * Issue a break command on link
> + */
> +static int fsi_master_gpio_break(struct fsi_master *_master, int link)
> +{
> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
> +
> + if (link != 0)
> + return -ENODEV;
> +
> + set_sda_output(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)
> +{
> + if (master->gpio_mux)
> + gpiod_direction_output(master->gpio_mux, 1);
> + if (master->gpio_trans)
> + gpiod_direction_output(master->gpio_trans, 1);
> + if (master->gpio_enable)
> + gpiod_direction_output(master->gpio_enable, 0);
> + gpiod_direction_output(master->gpio_clk, 1);
> + gpiod_direction_output(master->gpio_data, 1);
> +
> + /* todo: evaluate if clocks can be reduced */
> + clock_toggle(master, FSI_INIT_CLOCKS);
> +}
> +
> +static int fsi_master_gpio_link_enable(struct fsi_master *_master, int
link)
> +{
> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
> +
> + if (link != 0)
> + return -ENODEV;
> +
> + if (master->gpio_enable)
> + 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;
> +
> + gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
> + if (IS_ERR(gpio))
> + return PTR_ERR(gpio);
> + master->gpio_clk = gpio;
> +
> + gpio = devm_gpiod_get(&pdev->dev, "data", 0);
> + if (IS_ERR(gpio))
> + return PTR_ERR(gpio);
> + master->gpio_data = gpio;
> +
> + /* Optional pins */
> +
> + gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
> + if (!IS_ERR(gpio))
> + master->gpio_trans = gpio;
> +
> + gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
> + if (!IS_ERR(gpio))
> + master->gpio_enable = gpio;
> +
> + gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
> + if (!IS_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.send_break = fsi_master_gpio_break;
> + master->master.link_enable = fsi_master_gpio_link_enable;
> +
> + fsi_master_gpio_init(master);
> +
> + platform_set_drvdata(pdev, 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 = "ibm,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");
>
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master
2016-11-02 3:56 ` Alistair Popple
@ 2016-11-03 17:33 ` Christopher Bostic
0 siblings, 0 replies; 47+ messages in thread
From: Christopher Bostic @ 2016-11-03 17:33 UTC (permalink / raw)
To: Alistair Popple; +Cc: OpenBMC Maillist, xxpetri, zahrens
On Tue, Nov 1, 2016 at 10:56 PM, Alistair Popple <alistair@popple.id.au> wrote:
> Comments below.
>
> On Sun, 30 Oct 2016 05:09:18 PM christopher.lee.bostic@gmail.com wrote:
>> From: Chris Bostic <cbostic@us.ibm.com>
>>
>> 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 Jeremy Kerr's
>> original GPIO master base commit.
>>
>> Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
>>
>> ---
>>
>> V3 - Added remainder of base I/O ops (excluding dev tree and crc calcs).
>>
>> - Add encoding of all read/write commands and send data over sda
>>
>> - Check for and decode response from slave.
>>
>> - Add set enable pin in master->link_enable() master specific function.
>>
>> V4 - Remove unnecessary comments about delays in various locations
>>
>> - Define non clock and data pins as optional and check for gpio
>> descriptors == NULL before accessing. Don't return error if
>> optional pins cannot be retrieved via devm.
>>
>> - serial_in: rearrange order of msg shift and input bit OR op.
>>
>> - serial_out: Fix mirror image issue on shift data out and set
>> data out as necessary for bit count to be sent.
>>
>> - serial_out: make message parm a const
>>
>> - serial_out: fix 3x 'msg & data' repeat
>>
>> - poll_for_response: bits remaining was calculated with
>> sizeof(size) should be size.
>>
>> - fsi_master_gpio_break: remove smode read
>>
>> - Replace dev_info with dev_dbg in proper places.
>>
>> - Add a bit count parm to serial_in, increment msg->bits
>> on every bit received.
>>
>> - Remove magic numbers in poll_for_response that indicate
>> how many bits to receive
>>
>> - Utilize the crc utilities to check input data and set
>> crc for output data
>>
>> - Remove add_crc stub
>>
>> V5 - Rename pins from generic 'clk', 'data', etc in dts file to
>> 'fsi_clk', 'fsi_data', etc...
>>
>> - serial_in: invert data bit during message assembly
>> instead of inverting whole message after assembly.
>>
>> - Remove all instances of clearing out message field prior
>> to calling serial_in, redundant.
>>
>> - Change name of build_command() to build_abs_ar_command()
>> to make it more obvious its creating ABS AR type commands.
>>
>> - Remove unlikely( ) checks.
>>
>> - Indent dev_info string "master time out waiting for response"
>>
>> - poll_for_response: return FSI_GPIO_MAX busy instead of
>> busy_count.
>>
>> - fsi_master_gpio_break: move 'logic reset' comment to above
>> the delay call.
>>
>> - Add crc4 calculation initialization since data includes
>> a start bit.
>>
>> V6 - Invert polarity of SDA line for start bit, echo delay, pre-
>> D-POLL clocking, break command sequence (pre, during, post).
>>
>> - Add echo delay after sending D-POLL
>>
>> - Fill in user read data with received read data response
>>
>> - Clock the slave after each command completes to prime it
>> for another operation
>>
>> - Increase D-POLL checks on BUSY response before flagging
>> error
>>
>> - Add Alistair's crc calculator from pdbg. Having issues
>> with the existing one.
>>
>> - Revert V5 changes to rename gpio pin names.
>>
>> - Return -EIO on all bus errors
>>
>> - Condense slave id and response id into one serial-in
>> operation
>>
>> - Change D-POLL command to accommodate right alignment
>>
>> - Remove all dev_info( ) calls to reduce spam
>>
>> - serial_in remove cmd->bits = 0 / cmd->bits++ in for
>> loop. Just set value at end.
>>
>> - Change v translator line to direction output once
>> during init and use gpiod_set_value thereafter
>>
>> - Remove set/clear clocks and do it all in clock_toggle
>>
>> - Fill in slave id when encoding commands. Right align
>> the command prior to serializing out.
>>
>> - gpio master defines number of links to 1, was undefined.
>> ---
>> .../devicetree/bindings/fsi-master-gpio.txt | 21 +
>> .../devicetree/bindings/fsi/fsi-master-gpio.txt | 21 +
>> arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 36 ++
>> drivers/fsi/Kconfig | 6 +
>> drivers/fsi/Makefile | 1 +
>> drivers/fsi/fsi-master-gpio.c | 522
> +++++++++++++++++++++
>> 6 files changed, 607 insertions(+)
>> create mode 100644 Documentation/devicetree/bindings/fsi-master-gpio.txt
>> create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-
> gpio.txt
>> create mode 100644 drivers/fsi/fsi-master-gpio.c
>>
>> diff --git a/Documentation/devicetree/bindings/fsi-master-gpio.txt
> b/Documentation/devicetree/bindings/fsi-master-gpio.txt
>> new file mode 100644
>> index 0000000..d46be27
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/fsi-master-gpio.txt
>> @@ -0,0 +1,21 @@
>> +Device-tree bindings for gpio-based FSI master driver
>> +-----------------------------------------------------
>> +
>> +Required properties:
>> + - compatible = "ibm,fsi-master-gpio";
>> + - clk-gpio;
>> + - data-gpio;
>> +
>> +Optional properties:
>> + - enable-gpio;
>> + - trans-gpio;
>> + - mux-gpio;
>> +
>> +fsi-master {
>> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
>> + clk-gpio = <&gpio 0>;
>> + data-gpio = <&gpio 1>;
>> + enable-gpio = <&gpio 2>; /* Enable FSI data in/out */
>> + trans-gpio = <&gpio 3>; /* Voltage translator direction */
>> + mux-gpio = <&gpio 4>; /* Multiplexer for FSI pins */
>> +}
>> 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..ff3a62e
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
>> @@ -0,0 +1,21 @@
>> +Device-tree bindings for gpio-based FSI master driver
>> +-----------------------------------------------------
>> +
>> +Required properties:
>> + - compatible = "ibm,fsi-master-gpio";
>> + - clk-gpios;
>> + - data-gpios;
>> +
>> +Optional properties:
>> + - enable-gpios;
>> + - trans-gpios;
>> + - mux-gpios;
>> +
>> +fsi-master {
>> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
>> + clk-gpios = <&gpio 0 &gpio 6>;
>> + data-gpios = <&gpio 1 &gpio 7>;
>> + enable-gpios = <&gpio 2 &gpio 8>; /* Enable FSI data in/out */
>> + trans-gpios = <&gpio 3 &gpio 9>; /* Volts translator direction
> */
>> + mux-gpios = <&gpio 4> &gpio 10>; /* Multiplexer for FSI pins */
>> +}
>> diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
> b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
>> index 21619fd..9d9c2a9 100644
>> --- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
>> +++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
>> @@ -167,6 +167,42 @@
>> output-low;
>> line-name = "func_mode2";
>> };
>> +
>> + pin_fsi_clk {
>> + gpios = <ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
>> + output-low;
>> + line-name = "fsi_clk";
>> + };
>> +
>> + pin_fsi_data {
>> + gpios = <ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
>> + output-low;
>> + line-name = "fsi_data";
>> + };
>> +
>> + pin_fsi_trans {
>> + gpios = <ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
>> + output-low;
>> + line-name = "fsi_trans";
>> + };
>> +
>> + pin_fsi_enable {
>> + gpios = <ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
>> + output-low;
>> + line-name = "fsi_enable";
>> + };
>> +
>> + pin_fsi_mux {
>> + gpios = <ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
>> + output-low;
>> + line-name = "fsi_mux";
>> + };
>> +
>> + fsi_master {
>> + compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
>> + clk-gpios = <&gpio 0 &gpio 6>;
>> + data-gpios = <&gpio 1 &gpio 7>;
>> + };
>> };
>>
>> &vuart {
>> diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
>> index f065dbe..b21fe3c 100644
>> --- a/drivers/fsi/Kconfig
>> +++ b/drivers/fsi/Kconfig
>> @@ -17,6 +17,12 @@ config FSI_MASTER_FAKE
>> depends on FSI
>> ---help---
>> This option enables a fake FSI master driver for debugging.
>> +
>> +config FSI_MASTER_GPIO
>> + tristate "GPIO-based FSI master"
>> + depends on FSI
>> + ---help---
>> + This option enables a FSI master driver using GPIO lines.
>> endif
>>
>> endmenu
>> diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
>> index 847c00c..2021ce5 100644
>> --- a/drivers/fsi/Makefile
>> +++ b/drivers/fsi/Makefile
>> @@ -1,3 +1,4 @@
>>
>> obj-$(CONFIG_FSI) += fsi-core.o
>> obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
>> +obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
>> diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
>> new file mode 100644
>> index 0000000..175281a
>> --- /dev/null
>> +++ b/drivers/fsi/fsi-master-gpio.c
>> @@ -0,0 +1,522 @@
>> +/*
>> + * A FSI master controller, using a simple GPIO bit-banging interface
>> + */
>> +
>> +#include <linux/platform_device.h>
>> +#include <linux/gpio/consumer.h>
>> +#include <linux/module.h>
>> +#include <linux/delay.h>
>> +#include <linux/fsi.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 0x000000000000002AULL
>> +#define FSI_GPIO_CMD_DPOLL_SIZE 9
>> +#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to
> hang */
>> +#define FSI_GPIO_CMD_DEFAULT 0x2000000000000000ULL
>> +#define FSI_GPIO_CMD_WRITE 0
>> +#define FSI_GPIO_CMD_READ 0x0400000000000000ULL
>> +#define FSI_GPIO_CMD_SLAVE_MASK 0xC000000000000000ULL
>> +#define FSI_GPIO_CMD_ADDR_SHIFT 37
>> +#define FSI_GPIO_CMD_SLV_SHIFT 62
>> +#define FSI_GPIO_CMD_SIZE_16 0x0000001000000000ULL
>> +#define FSI_GPIO_CMD_SIZE_32 0x0000003000000000ULL
>> +#define FSI_GPIO_CMD_DT32_SHIFT 4
>> +#define FSI_GPIO_CMD_DT16_SHIFT 20
>> +#define FSI_GPIO_CMD_DT8_SHIFT 28
>> +#define FSI_GPIO_CMD_DFLT_LEN 28
>> +#define FSI_GPIO_CMD_CRC_SHIFT 60
>> +
>> +/* 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 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)
>> +{
>> + ndelay(FSI_GPIO_STD_DLY);
>> + return gpiod_get_value(master->gpio_data);
>> +}
>> +
>> +static void sda_out(struct fsi_master_gpio *master, int value)
>> +{
>> + gpiod_set_value(master->gpio_data, value);
>> +}
>> +
>> +static void set_sda_input(struct fsi_master_gpio *master)
>> +{
>> + gpiod_direction_input(master->gpio_data);
>> + if (master->gpio_trans)
>> + gpiod_set_value(master->gpio_trans, 0);
>> +}
>> +
>> +static void set_sda_output(struct fsi_master_gpio *master, int value)
>> +{
>> + gpiod_direction_output(master->gpio_data, value);
>> + if (master->gpio_trans)
>> + gpiod_set_value(master->gpio_trans, 1);
>> +}
>> +
>> +static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg
> *cmd,
>> + uint8_t num_bits)
>> +{
>> + uint8_t bit;
>> + uint64_t msg = 0;
>> + uint8_t in_bit = 0;
>> +
>> + set_sda_input(master);
>> +
>> + for (bit = 0; bit < num_bits; bit++) {
>> + clock_toggle(master, 1);
>> + in_bit = sda_in(master);
>> + msg <<= 1;
>> + msg |= ~in_bit & 0x1; /* Data is negative active */
>> + }
>> + cmd->bits = num_bits;
>> + cmd->msg = msg;
>> +}
>> +
>> +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 negative active */
>> + uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
>> + uint64_t last_bit = ~0;
>> + int next_bit;
>> +
>> + if (!cmd->bits) {
>> + dev_warn(master->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;
>> + }
>> +}
>> +
>> +/*
>> + * Clock out some 0's after every message to ride out line reflections
>> + */
>> +static void echo_delay(struct fsi_master_gpio *master)
>> +{
>> + set_sda_output(master, 1);
>> + clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
>> +}
>> +
>> +/*
>> + * Used in bus error cases only. Clears out any remaining data the slave
>> + * is attempting to send
>> + */
>> +static void drain_response(struct fsi_master_gpio *master)
>> +{
>> + struct fsi_gpio_msg msg;
>> +
>> + serial_in(master, &msg, FSI_GPIO_DRAIN_BITS);
>> +}
>> +
>> +/*
>> + * 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)
>> +{
>> +
>> +}
>> +
>> +/*
>> + * Taken from Alistaire's PDBG
>> + */
>> +static uint8_t crc4(uint8_t c, int b)
>> +{
>> + uint8_t m = 0;
>> +
>> + c &= 0xf;
>> + m = b ^ ((c >> 3) & 0x1);
>> + m = (m << 2) | (m << 1) | (m);
>> + c <<= 1;
>> + c ^= m;
>> +
>> + return c & 0xf;
>> +}
>> +
>> +static int poll_for_response(struct fsi_master_gpio *master, uint8_t
> expected,
>> + uint8_t size, void *data)
>> +{
>> + int busy_count = 0, i;
>> + struct fsi_gpio_msg response, cmd;
>> + int bits_remaining = 0, bit_count, response_id, id;
>> + uint64_t resp = 0;
>> + uint8_t bits_received = FSI_GPIO_MSG_ID_SIZE +
>> + FSI_GPIO_MSG_RESPID_SIZE;
>> + uint8_t crc_in;
>> +
>> + do {
>> + for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
>> + serial_in(master, &response, 1);
>> + if (response.msg)
>> + break;
>> + }
>> + if (i >= FSI_GPIO_MTOE_COUNT) {
>> + dev_dbg(master->master.dev,
>> + "Master time out waiting for response\n");
>> + drain_response(master);
>> + fsi_master_gpio_error(master, FSI_GPIO_MTOE);
>> + return -EIO;
>> + }
>> +
>> + /* Response received */
>> + bit_count = FSI_GPIO_MSG_ID_SIZE + FSI_GPIO_MSG_RESPID_SIZE;
>> + serial_in(master, &response, bit_count);
>> +
>> + response_id = response.msg & 0x3;
>> + id = (response.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
>> + dev_dbg(master->master.dev, "id:%d resp:%d\n", id,
> response_id);
>> +
>> + resp = response.msg;
>> +
>> + switch (response_id) {
>> + case FSI_GPIO_RESP_ACK:
>> + if (expected == FSI_GPIO_RESP_ACKD)
>> + bits_remaining = 8 * size;
>> + break;
>> +
>> + case FSI_GPIO_RESP_BUSY:
>> + /*
>> + * Its necessary to clock slave before issuing
>> + * d-poll, not indicated in the hardware protocol
>> + * spec. < 20 clocks causes slave to hang, 21 ok.
>> + */
>
> Interesting. Have HW folk confirmed this? I've encountered similar issues on
> occasion, so it would be good to get confirmation that we need a minimum of 20
> slave clocks prior to sending a d-poll.
>
Hi Alistair,
I have not confirmed this. I'll request more info from Marcus Cebulla and copy
you. From experience this behavior though has been very consistent.
< 20 clocks prior to d-poll freezes the slave and it never responds again.
>> + set_sda_output(master, 1);
>> + clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
>> + cmd.msg = FSI_GPIO_CMD_DPOLL;
>> + cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
>> + serial_out(master, &cmd);
>> + echo_delay(master);
>> + continue;
>> +
>> + case FSI_GPIO_RESP_ERRA:
>> + case FSI_GPIO_RESP_ERRC:
>> + dev_dbg(master->master.dev, "ERR received: %d\n",
>> + (int)response.msg);
>> + drain_response(master);
>
> Why do we have to call drain_response() for an error code? Isn't ERRA/C a well
> defined bit sequence?
True, this in effect clocks out the remaining crc to be sent by slave.
I'll need to revise this to ensure all crc's are verified before acting on any
response received.
>
>> + fsi_master_gpio_error(master, response.msg);
>> + return -EIO;
>> + }
>> +
>> + /* Read in the data field if applicable */
>> + if (bits_remaining) {
>> + serial_in(master, &response, bits_remaining);
>> + resp <<= bits_remaining;
>> + resp |= response.msg;
>> + bits_received += bits_remaining;
>> + *((uint32_t *)data) = response.msg;
>> + }
>> +
>> + /* Include start bit */
>> + crc_in = crc4(0, 1);
>> + for (i = bits_received - 1; i >= 0; i--)
>> + crc_in = crc4(crc_in, !!(resp & (1ULL << i)));
>> +
>> + /* Read in the crc and check it */
>> + serial_in(master, &response, FSI_GPIO_CRC_SIZE);
>> + if (crc_in != response.msg) {
>> +
>> + /* CRC's don't match - warn for now */
>> + dev_dbg(master->master.dev, "ERR response CRC\n");
>> + fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
>> + }
>> + /* Clock the slave enough to be ready for next operation */
>> + clock_toggle(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
>> + return 0;
>> +
>> + } while (busy_count++ < FSI_GPIO_MAX_BUSY);
>> +
>> + dev_dbg(master->master.dev, "ERR slave is stuck in busy state\n");
>> + fsi_master_gpio_error(master, FSI_GPIO_ERR_BUSY);
>> +
>> + return -EIO;
>> +}
>> +
>> +static void build_abs_ar_command(struct fsi_gpio_msg *cmd, uint64_t mode,
>> + uint8_t slave, uint32_t addr, size_t size,
>> + const void *data)
>> +{
>> + uint8_t crc;
>> + int i;
>> +
>> + cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
>> + cmd->msg = FSI_GPIO_CMD_DEFAULT;
>> + cmd->msg |= mode;
>> +
>> + /* todo: handle more than just slave id 0 */
>
> This should be easily fixed and is needed to get the full 23-bit FSI address
> space. For P8 at least I think you just use addr bits 22 & 23 as the slave id.
>
The slave id was is added below. The 'todo' comment should have been removed
when I fixed that.
>> + cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
>> + cmd->msg |= (((uint64_t)slave) << FSI_GPIO_CMD_SLV_SHIFT);
>> + cmd->msg |= (((uint64_t)addr) << FSI_GPIO_CMD_ADDR_SHIFT);
>
> Wouldn't it be best to apply a mask to addr limiting it to 21-bits? Or a check
> to ensure addr <= (1<<23) - 1. Otherwise wont invalid addresses result in
> invalid commands and/or errors as a result?
>
Good point. Will fix.
>> + if (size == sizeof(uint8_t)) {
>> + if (data) {
>> + uint8_t cmd_data = *((uint8_t *)data);
>> +
>> + cmd->msg |=
>> + ((uint64_t)cmd_data) <<
> FSI_GPIO_CMD_DT8_SHIFT;
>> + }
>> + } else if (size == sizeof(uint16_t)) {
>> + cmd->msg |= FSI_GPIO_CMD_SIZE_16;
>> + if (data) {
>> + uint16_t cmd_data = *((uint16_t *)data);
>
> It might be best to do a memcpy here or make *data a uint64_t *. There is no
> guarantee that *data has the correct alignment which from memory will result
> in a data abort fault/exception to fix-up the alignment on ARMv5 and below
> (ie. AST2400).
>
Will add a memcpy.
>> + cmd->msg |=
>> + ((uint64_t)cmd_data) <<
> FSI_GPIO_CMD_DT16_SHIFT;
>> + }
>> + } else {
>> + cmd->msg |= FSI_GPIO_CMD_SIZE_32;
>> + if (data) {
>> + uint32_t cmd_data = *((uint32_t *)data);
>
> Ditto.
>
Will do.
Thanks for your comments,
Chris
>> + cmd->msg |=
>> + ((uint64_t)cmd_data) <<
> FSI_GPIO_CMD_DT32_SHIFT;
>> + }
>> + }
>> +
>> + if (mode == FSI_GPIO_CMD_WRITE)
>> + cmd->bits += (8 * size);
>> +
>> + /* Include start bit */
>> + crc = crc4(0, 1);
>> + for (i = 63; i >= (64 - cmd->bits); i--)
>> + crc = crc4(crc, !!(cmd->msg & (1ULL << i)));
>> +
>> + cmd->msg |= ((uint64_t)crc) << (FSI_GPIO_CMD_CRC_SHIFT - cmd->bits);
>> + cmd->bits += FSI_GPIO_CRC_SIZE;
>> +
>> + /* Right align message */
>> + cmd->msg >>= (64 - cmd->bits);
>> +}
>> +
>> +static int fsi_master_gpio_read(struct fsi_master *_master, int link,
>> + uint8_t slave, uint32_t addr, void *val, size_t size)
>> +{
>> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
>> + struct fsi_gpio_msg cmd;
>> +
>> + if (link != 0)
>> + return -ENODEV;
>> +
>> + build_abs_ar_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size,
> NULL);
>> + serial_out(master, &cmd);
>> + echo_delay(master);
>> +
>> + return poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
>> +}
>> +
>> +static int fsi_master_gpio_write(struct fsi_master *_master, int link,
>> + uint8_t slave, 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, FSI_GPIO_CMD_WRITE, slave, addr, size,
> val);
>> + serial_out(master, &cmd);
>> + echo_delay(master);
>> +
>> + return poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
>> +}
>> +
>> +/*
>> + * Issue a break command on link
>> + */
>> +static int fsi_master_gpio_break(struct fsi_master *_master, int link)
>> +{
>> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
>> +
>> + if (link != 0)
>> + return -ENODEV;
>> +
>> + set_sda_output(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)
>> +{
>> + if (master->gpio_mux)
>> + gpiod_direction_output(master->gpio_mux, 1);
>> + if (master->gpio_trans)
>> + gpiod_direction_output(master->gpio_trans, 1);
>> + if (master->gpio_enable)
>> + gpiod_direction_output(master->gpio_enable, 0);
>> + gpiod_direction_output(master->gpio_clk, 1);
>> + gpiod_direction_output(master->gpio_data, 1);
>> +
>> + /* todo: evaluate if clocks can be reduced */
>> + clock_toggle(master, FSI_INIT_CLOCKS);
>> +}
>> +
>> +static int fsi_master_gpio_link_enable(struct fsi_master *_master, int
> link)
>> +{
>> + struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
>> +
>> + if (link != 0)
>> + return -ENODEV;
>> +
>> + if (master->gpio_enable)
>> + 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;
>> +
>> + gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
>> + if (IS_ERR(gpio))
>> + return PTR_ERR(gpio);
>> + master->gpio_clk = gpio;
>> +
>> + gpio = devm_gpiod_get(&pdev->dev, "data", 0);
>> + if (IS_ERR(gpio))
>> + return PTR_ERR(gpio);
>> + master->gpio_data = gpio;
>> +
>> + /* Optional pins */
>> +
>> + gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
>> + if (!IS_ERR(gpio))
>> + master->gpio_trans = gpio;
>> +
>> + gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
>> + if (!IS_ERR(gpio))
>> + master->gpio_enable = gpio;
>> +
>> + gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
>> + if (!IS_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.send_break = fsi_master_gpio_break;
>> + master->master.link_enable = fsi_master_gpio_link_enable;
>> +
>> + fsi_master_gpio_init(master);
>> +
>> + platform_set_drvdata(pdev, 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 = "ibm,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");
>>
>
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (15 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
` (19 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Add driver_register and driver_unregister wrappers for FSI.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V4 - Separate out SCOM client driver registration for a follow
on patch.
V5 - Rename fsidrv_register, fsidrv_unregster to fsi_drv_register,
fsi_drv_unregister.
- Add module_fsi_driver() macro for clients that just need
to do boilerplate registration
V6 - Rename fsi_drv_register/unregister to fsi_driver_register/..
---
drivers/fsi/fsi-core.c | 18 ++++++++++++++++++
include/linux/fsi.h | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 6ac239a..88002fc 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -458,6 +458,24 @@ 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 47af0af..62453b7 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -55,6 +55,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 *);
+extern void fsi_driver_unregister(struct fsi_driver *);
+
+/* 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] 47+ messages in thread* [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (16 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-31 1:22 ` Jeremy Kerr
2016-11-13 23:44 ` Alistair Popple
2016-10-30 22:09 ` [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (18 subsequent siblings)
36 siblings, 2 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Create a simple SCOM engine device driver that reads and writes
across an FSI bus.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V4 - Add put_scom and get_scom operations
V5 - Add character device registration and fill in read/write
syscalls.
V6 - Add multi scom engine support. Add list of devices.
Use file private data to store and hand off scom structures.
- Add lseek, open, close
- Remove data/address structure to pass from user space to
kernel. Use offset provided by read/write and pass data
- Added list setup in init and so did not add the macro
module_fsi_driver
- Global structs made static where possible.
- Change from char dev to use misc device api as shown
in examples from Jeremy Kere.
---
drivers/fsi/Kconfig | 6 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-scom.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 245 insertions(+)
create mode 100644 drivers/fsi/fsi-scom.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index b21fe3c..1fa9bc0 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -23,6 +23,12 @@ config FSI_MASTER_GPIO
depends on FSI
---help---
This option enables a FSI master driver using GPIO lines.
+
+config FSI_SCOM
+ tristate "SCOM FSI client"
+ depends on FSI
+ ---help---
+ This option enables the SCOM FSI client device driver.
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 2021ce5..3e31d9a 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
new file mode 100644
index 0000000..27be082
--- /dev/null
+++ b/drivers/fsi/fsi-scom.c
@@ -0,0 +1,238 @@
+/*
+ * 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>
+
+#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];
+};
+
+#define to_scom_dev(x) container_of((x), struct scom_device, mdev)
+
+static struct list_head scom_devices;
+static atomic_t scom_idx = ATOMIC_INIT(0);
+static int scom_probe(struct device *);
+
+static int put_scom(struct scom_device *scom_dev, uint64_t value,
+ uint32_t addr)
+{
+ int rc;
+ uint32_t data = SCOM_RESET_CMD;
+
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = (value >> 32) & 0xffffffff;
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = value & 0xffffffff;
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = 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;
+
+ udelay(SCOM_FSI2PIB_DELAY);
+
+ data = 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) result << 32;
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= result;
+
+ return 0;
+}
+
+static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
+{
+ if (whence != 0) /* SEEK_SET */
+ return -EINVAL;
+
+ filep->f_pos = offset;
+ return offset;
+}
+
+static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
+ loff_t *offset)
+{
+ int rc = 0;
+ 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 = 0;
+ 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 = 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 ? rc : len;
+}
+
+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 = NULL;
+
+ scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+ if (!scom)
+ return -ENOMEM;
+
+ snprintf(scom->name, sizeof(scom->name),
+ "scom%d", atomic_inc_return(&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 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,
+ }
+};
+
+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 = NULL;
+
+ 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] 47+ messages in thread* Re: [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
@ 2016-10-31 1:22 ` Jeremy Kerr
2016-11-03 16:47 ` Christopher Bostic
2016-11-13 23:44 ` Alistair Popple
1 sibling, 1 reply; 47+ messages in thread
From: Jeremy Kerr @ 2016-10-31 1:22 UTC (permalink / raw)
To: christopher.lee.bostic, openbmc; +Cc: xxpetri, zahrens
Hi Chris,
> Create a simple SCOM engine device driver that reads and writes
> across an FSI bus.
Looking good. Is this functional at the moment?
A few comments:
> +static struct list_head scom_devices;
> +static atomic_t scom_idx = ATOMIC_INIT(0);
> +static int scom_probe(struct device *);
You don't need this forward declaration anymore
> +static int get_scom(struct scom_device *scom_dev, uint64_t *value,
> + uint32_t addr)
> +{
> + uint32_t result, data;
> + int rc;
> +
> + udelay(SCOM_FSI2PIB_DELAY);
Why this udelay()? Does put_scom() not need it too then? If it's a
matter of scom engine state, should we adjust this based on the last
operation?
If it's necessary, can you add a short comment?
> +static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
> +{
> + if (whence != 0) /* SEEK_SET */
> + return -EINVAL;
> +
> + filep->f_pos = offset;
> + return offset;
> +}
I think you can drop this and use .llseek = no_seek_end_llseek instead
(and then you get SEEK_CUR support too).
> +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
> + loff_t *offset)
> +{
> + int rc = 0;
> + 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;
> +}
What are the semantics of offset after reading & writing? Should two
reads (with no intervening llseek) read the same address, or subsequent
addresses?
> +static ssize_t scom_write(struct file *filep, const char __user *buf,
> + size_t len, loff_t *offset)
> +{
> + int rc = 0;
No need to initialise this (and in scom_read too).
> + 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 = copy_from_user(&val, buf, len);
> + if (rc) {
> + dev_dbg(dev, "copy from user failed:%d\n", rc);
> + return -EINVAL;
> + }
You don't want to squash that return value; just return rc there.
> +
> + rc = put_scom(scom, val, *offset);
> + if (rc)
> + dev_dbg(dev, "put_scom failed with:%d\n", rc);
> +
> +
> + return rc ? rc : len;
> +}
> +
> +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 = NULL;
No need to initialise this either.
> +
> + scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
> + if (!scom)
> + return -ENOMEM;
> +
> + snprintf(scom->name, sizeof(scom->name),
> + "scom%d", atomic_inc_return(&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 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,
> + }
> +};
> +
> +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 = NULL;
... or this.
> +
> + 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");
Cheers,
Jeremy
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-10-31 1:22 ` Jeremy Kerr
@ 2016-11-03 16:47 ` Christopher Bostic
0 siblings, 0 replies; 47+ messages in thread
From: Christopher Bostic @ 2016-11-03 16:47 UTC (permalink / raw)
To: Jeremy Kerr; +Cc: OpenBMC Maillist, xxpetri, zahrens
>> +static atomic_t scom_idx = ATOMIC_INIT(0);
>> +static int scom_probe(struct device *);
>
> You don't need this forward declaration anymore
>
Hi Jeremy,
OK will remove.
>> +static int get_scom(struct scom_device *scom_dev, uint64_t *value,
>> + uint32_t addr)
>> +{
>> + uint32_t result, data;
>> + int rc;
>> +
>> + udelay(SCOM_FSI2PIB_DELAY);
>
> Why this udelay()? Does put_scom() not need it too then? If it's a
> matter of scom engine state, should we adjust this based on the last
> operation?
This is a carry over from previous code. I don't know the history behind why
it was added. Will remove.
>
> If it's necessary, can you add a short comment?
>
>> +static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
>> +{
>> + if (whence != 0) /* SEEK_SET */
>> + return -EINVAL;
>> +
>> + filep->f_pos = offset;
>> + return offset;
>> +}
>
> I think you can drop this and use .llseek = no_seek_end_llseek instead
> (and then you get SEEK_CUR support too).
Will change.
>
>> +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
>> + loff_t *offset)
>> +{
>> + int rc = 0;
>> + 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;
>> +}
>
> What are the semantics of offset after reading & writing? Should two
> reads (with no intervening llseek) read the same address, or subsequent
> addresses?
The user is to specify the address for each scom read and write. No
support has been provided for any other address auto set.
>
>> +static ssize_t scom_write(struct file *filep, const char __user *buf,
>> + size_t len, loff_t *offset)
>> +{
>> + int rc = 0;
>
> No need to initialise this (and in scom_read too).
>
Will remove.
>> + 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 = copy_from_user(&val, buf, len);
>> + if (rc) {
>> + dev_dbg(dev, "copy from user failed:%d\n", rc);
>> + return -EINVAL;
>> + }
>
> You don't want to squash that return value; just return rc there.
>
OK, will change.
>> +
>> + rc = put_scom(scom, val, *offset);
>> + if (rc)
>> + dev_dbg(dev, "put_scom failed with:%d\n", rc);
>> +
>> +
>> + return rc ? rc : len;
>> +}
>> +
>> +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 = NULL;
>
> No need to initialise this either.
>
Removing
>> +
>> + scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
>> + if (!scom)
>> + return -ENOMEM;
>> +
>> + snprintf(scom->name, sizeof(scom->name),
>> + "scom%d", atomic_inc_return(&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 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,
>> + }
>> +};
>> +
>> +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 = NULL;
>
> ... or this.
>
Will remove
>> +
>> + 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");
>
Thanks,
Chris
> Cheers,
>
>
> Jeremy
^ permalink raw reply [flat|nested] 47+ messages in thread
* Re: [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
2016-10-31 1:22 ` Jeremy Kerr
@ 2016-11-13 23:44 ` Alistair Popple
2016-11-14 17:13 ` Christopher Bostic
1 sibling, 1 reply; 47+ messages in thread
From: Alistair Popple @ 2016-11-13 23:44 UTC (permalink / raw)
To: openbmc; +Cc: christopher.lee.bostic, xxpetri, zahrens
Hi Chris,
Is there a similar device driver for the CFAM/FSI that I missed? That is will
there be a chardev for read/writing FSI addresses directly (ie. a chardev to
call fsi_device_read/write)? Probably not critical for an initial release but
we will need one at some point as I'm pretty sure the cronus server for
example has get/putcfam commands as some registers aren't available via SCOM.
Regards,
Alistair
On Sun, 30 Oct 2016 05:09:20 PM christopher.lee.bostic@gmail.com wrote:
> From: Chris Bostic <cbostic@us.ibm.com>
>
> Create a simple SCOM engine device driver that reads and writes
> across an FSI bus.
>
> Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
>
> ---
>
> V4 - Add put_scom and get_scom operations
>
> V5 - Add character device registration and fill in read/write
> syscalls.
>
> V6 - Add multi scom engine support. Add list of devices.
> Use file private data to store and hand off scom structures.
>
> - Add lseek, open, close
>
> - Remove data/address structure to pass from user space to
> kernel. Use offset provided by read/write and pass data
>
> - Added list setup in init and so did not add the macro
> module_fsi_driver
>
> - Global structs made static where possible.
>
> - Change from char dev to use misc device api as shown
> in examples from Jeremy Kere.
> ---
> drivers/fsi/Kconfig | 6 ++
> drivers/fsi/Makefile | 1 +
> drivers/fsi/fsi-scom.c | 238
+++++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 245 insertions(+)
> create mode 100644 drivers/fsi/fsi-scom.c
>
> diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
> index b21fe3c..1fa9bc0 100644
> --- a/drivers/fsi/Kconfig
> +++ b/drivers/fsi/Kconfig
> @@ -23,6 +23,12 @@ config FSI_MASTER_GPIO
> depends on FSI
> ---help---
> This option enables a FSI master driver using GPIO lines.
> +
> +config FSI_SCOM
> + tristate "SCOM FSI client"
> + depends on FSI
> + ---help---
> + This option enables the SCOM FSI client device driver.
> endif
>
> endmenu
> diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
> index 2021ce5..3e31d9a 100644
> --- a/drivers/fsi/Makefile
> +++ b/drivers/fsi/Makefile
> @@ -2,3 +2,4 @@
> obj-$(CONFIG_FSI) += fsi-core.o
> obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
> obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
> +obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
> diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
> new file mode 100644
> index 0000000..27be082
> --- /dev/null
> +++ b/drivers/fsi/fsi-scom.c
> @@ -0,0 +1,238 @@
> +/*
> + * 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>
> +
> +#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];
> +};
> +
> +#define to_scom_dev(x) container_of((x), struct scom_device,
mdev)
> +
> +static struct list_head scom_devices;
> +static atomic_t scom_idx = ATOMIC_INIT(0);
> +static int scom_probe(struct device *);
> +
> +static int put_scom(struct scom_device *scom_dev, uint64_t value,
> + uint32_t addr)
> +{
> + int rc;
> + uint32_t data = SCOM_RESET_CMD;
> +
> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
> + sizeof(uint32_t));
> + if (rc)
> + return rc;
> +
> + data = (value >> 32) & 0xffffffff;
> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
> + sizeof(uint32_t));
> + if (rc)
> + return rc;
> +
> + data = value & 0xffffffff;
> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
> + sizeof(uint32_t));
> + if (rc)
> + return rc;
> +
> + data = 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;
> +
> + udelay(SCOM_FSI2PIB_DELAY);
> +
> + data = 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) result << 32;
> + rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
> + sizeof(uint32_t));
> + if (rc)
> + return rc;
> +
> + *value |= result;
> +
> + return 0;
> +}
> +
> +static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
> +{
> + if (whence != 0) /* SEEK_SET */
> + return -EINVAL;
> +
> + filep->f_pos = offset;
> + return offset;
> +}
> +
> +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
> + loff_t *offset)
> +{
> + int rc = 0;
> + 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 = 0;
> + 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 = 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 ? rc : len;
> +}
> +
> +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 = NULL;
> +
> + scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
> + if (!scom)
> + return -ENOMEM;
> +
> + snprintf(scom->name, sizeof(scom->name),
> + "scom%d", atomic_inc_return(&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 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,
> + }
> +};
> +
> +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 = NULL;
> +
> + 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");
>
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-11-13 23:44 ` Alistair Popple
@ 2016-11-14 17:13 ` Christopher Bostic
2016-11-14 22:43 ` Christopher Bostic
0 siblings, 1 reply; 47+ messages in thread
From: Christopher Bostic @ 2016-11-14 17:13 UTC (permalink / raw)
To: Alistair Popple; +Cc: OpenBMC Maillist, xxpetri, zahrens
On Sun, Nov 13, 2016 at 5:44 PM, Alistair Popple <alistair@popple.id.au> wrote:
> Hi Chris,
>
> Is there a similar device driver for the CFAM/FSI that I missed? That is will
> there be a chardev for read/writing FSI addresses directly (ie. a chardev to
> call fsi_device_read/write)? Probably not critical for an initial release but
> we will need one at some point as I'm pretty sure the cronus server for
> example has get/putcfam commands as some registers aren't available via SCOM.
>
> Regards,
>
> Alistair
>
Hi Alistair,
No there isn't a driver like you describe in the set as it is now. I
agree that would be important to have for general bringup debug.
Once this set is accepted I'll add that.
Thanks
> On Sun, 30 Oct 2016 05:09:20 PM christopher.lee.bostic@gmail.com wrote:
>> From: Chris Bostic <cbostic@us.ibm.com>
>>
>> Create a simple SCOM engine device driver that reads and writes
>> across an FSI bus.
>>
>> Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
>>
>> ---
>>
>> V4 - Add put_scom and get_scom operations
>>
>> V5 - Add character device registration and fill in read/write
>> syscalls.
>>
>> V6 - Add multi scom engine support. Add list of devices.
>> Use file private data to store and hand off scom structures.
>>
>> - Add lseek, open, close
>>
>> - Remove data/address structure to pass from user space to
>> kernel. Use offset provided by read/write and pass data
>>
>> - Added list setup in init and so did not add the macro
>> module_fsi_driver
>>
>> - Global structs made static where possible.
>>
>> - Change from char dev to use misc device api as shown
>> in examples from Jeremy Kere.
>> ---
>> drivers/fsi/Kconfig | 6 ++
>> drivers/fsi/Makefile | 1 +
>> drivers/fsi/fsi-scom.c | 238
> +++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 245 insertions(+)
>> create mode 100644 drivers/fsi/fsi-scom.c
>>
>> diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
>> index b21fe3c..1fa9bc0 100644
>> --- a/drivers/fsi/Kconfig
>> +++ b/drivers/fsi/Kconfig
>> @@ -23,6 +23,12 @@ config FSI_MASTER_GPIO
>> depends on FSI
>> ---help---
>> This option enables a FSI master driver using GPIO lines.
>> +
>> +config FSI_SCOM
>> + tristate "SCOM FSI client"
>> + depends on FSI
>> + ---help---
>> + This option enables the SCOM FSI client device driver.
>> endif
>>
>> endmenu
>> diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
>> index 2021ce5..3e31d9a 100644
>> --- a/drivers/fsi/Makefile
>> +++ b/drivers/fsi/Makefile
>> @@ -2,3 +2,4 @@
>> obj-$(CONFIG_FSI) += fsi-core.o
>> obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
>> obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
>> +obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
>> diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
>> new file mode 100644
>> index 0000000..27be082
>> --- /dev/null
>> +++ b/drivers/fsi/fsi-scom.c
>> @@ -0,0 +1,238 @@
>> +/*
>> + * 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>
>> +
>> +#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];
>> +};
>> +
>> +#define to_scom_dev(x) container_of((x), struct scom_device,
> mdev)
>> +
>> +static struct list_head scom_devices;
>> +static atomic_t scom_idx = ATOMIC_INIT(0);
>> +static int scom_probe(struct device *);
>> +
>> +static int put_scom(struct scom_device *scom_dev, uint64_t value,
>> + uint32_t addr)
>> +{
>> + int rc;
>> + uint32_t data = SCOM_RESET_CMD;
>> +
>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
>> + sizeof(uint32_t));
>> + if (rc)
>> + return rc;
>> +
>> + data = (value >> 32) & 0xffffffff;
>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
>> + sizeof(uint32_t));
>> + if (rc)
>> + return rc;
>> +
>> + data = value & 0xffffffff;
>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
>> + sizeof(uint32_t));
>> + if (rc)
>> + return rc;
>> +
>> + data = 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;
>> +
>> + udelay(SCOM_FSI2PIB_DELAY);
>> +
>> + data = 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) result << 32;
>> + rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
>> + sizeof(uint32_t));
>> + if (rc)
>> + return rc;
>> +
>> + *value |= result;
>> +
>> + return 0;
>> +}
>> +
>> +static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
>> +{
>> + if (whence != 0) /* SEEK_SET */
>> + return -EINVAL;
>> +
>> + filep->f_pos = offset;
>> + return offset;
>> +}
>> +
>> +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
>> + loff_t *offset)
>> +{
>> + int rc = 0;
>> + 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 = 0;
>> + 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 = 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 ? rc : len;
>> +}
>> +
>> +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 = NULL;
>> +
>> + scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
>> + if (!scom)
>> + return -ENOMEM;
>> +
>> + snprintf(scom->name, sizeof(scom->name),
>> + "scom%d", atomic_inc_return(&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 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,
>> + }
>> +};
>> +
>> +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 = NULL;
>> +
>> + 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");
>>
>
^ permalink raw reply [flat|nested] 47+ messages in thread* Re: [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-11-14 17:13 ` Christopher Bostic
@ 2016-11-14 22:43 ` Christopher Bostic
0 siblings, 0 replies; 47+ messages in thread
From: Christopher Bostic @ 2016-11-14 22:43 UTC (permalink / raw)
To: Alistair Popple; +Cc: OpenBMC Maillist, xxpetri, zahrens
On Mon, Nov 14, 2016 at 11:13 AM, Christopher Bostic
<christopher.lee.bostic@gmail.com> wrote:
> On Sun, Nov 13, 2016 at 5:44 PM, Alistair Popple <alistair@popple.id.au> wrote:
>> Hi Chris,
>>
>> Is there a similar device driver for the CFAM/FSI that I missed? That is will
>> there be a chardev for read/writing FSI addresses directly (ie. a chardev to
>> call fsi_device_read/write)? Probably not critical for an initial release but
>> we will need one at some point as I'm pretty sure the cronus server for
>> example has get/putcfam commands as some registers aren't available via SCOM.
>>
>> Regards,
>>
>> Alistair
>>
>
> Hi Alistair,
>
> No there isn't a driver like you describe in the set as it is now. I
> agree that would be important to have for general bringup debug.
> Once this set is accepted I'll add that.
>
To clarify, for each engine's address space in a cfam there will be a
'client' driver that
is responsible for accessing that range. SCOM driver for scom space, etc...
Right now there is only the SCOM client driver but I'll be filling in
the others shortly.
The idea though of having a general read/write driver in addition
still makes sense
to me for bringup purposes. This is I assume more in line with how
you've structured
pdbg.
Regards,
> Thanks
>
>> On Sun, 30 Oct 2016 05:09:20 PM christopher.lee.bostic@gmail.com wrote:
>>> From: Chris Bostic <cbostic@us.ibm.com>
>>>
>>> Create a simple SCOM engine device driver that reads and writes
>>> across an FSI bus.
>>>
>>> Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
>>>
>>> ---
>>>
>>> V4 - Add put_scom and get_scom operations
>>>
>>> V5 - Add character device registration and fill in read/write
>>> syscalls.
>>>
>>> V6 - Add multi scom engine support. Add list of devices.
>>> Use file private data to store and hand off scom structures.
>>>
>>> - Add lseek, open, close
>>>
>>> - Remove data/address structure to pass from user space to
>>> kernel. Use offset provided by read/write and pass data
>>>
>>> - Added list setup in init and so did not add the macro
>>> module_fsi_driver
>>>
>>> - Global structs made static where possible.
>>>
>>> - Change from char dev to use misc device api as shown
>>> in examples from Jeremy Kere.
>>> ---
>>> drivers/fsi/Kconfig | 6 ++
>>> drivers/fsi/Makefile | 1 +
>>> drivers/fsi/fsi-scom.c | 238
>> +++++++++++++++++++++++++++++++++++++++++++++++++
>>> 3 files changed, 245 insertions(+)
>>> create mode 100644 drivers/fsi/fsi-scom.c
>>>
>>> diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
>>> index b21fe3c..1fa9bc0 100644
>>> --- a/drivers/fsi/Kconfig
>>> +++ b/drivers/fsi/Kconfig
>>> @@ -23,6 +23,12 @@ config FSI_MASTER_GPIO
>>> depends on FSI
>>> ---help---
>>> This option enables a FSI master driver using GPIO lines.
>>> +
>>> +config FSI_SCOM
>>> + tristate "SCOM FSI client"
>>> + depends on FSI
>>> + ---help---
>>> + This option enables the SCOM FSI client device driver.
>>> endif
>>>
>>> endmenu
>>> diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
>>> index 2021ce5..3e31d9a 100644
>>> --- a/drivers/fsi/Makefile
>>> +++ b/drivers/fsi/Makefile
>>> @@ -2,3 +2,4 @@
>>> obj-$(CONFIG_FSI) += fsi-core.o
>>> obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
>>> obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
>>> +obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
>>> diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
>>> new file mode 100644
>>> index 0000000..27be082
>>> --- /dev/null
>>> +++ b/drivers/fsi/fsi-scom.c
>>> @@ -0,0 +1,238 @@
>>> +/*
>>> + * 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>
>>> +
>>> +#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];
>>> +};
>>> +
>>> +#define to_scom_dev(x) container_of((x), struct scom_device,
>> mdev)
>>> +
>>> +static struct list_head scom_devices;
>>> +static atomic_t scom_idx = ATOMIC_INIT(0);
>>> +static int scom_probe(struct device *);
>>> +
>>> +static int put_scom(struct scom_device *scom_dev, uint64_t value,
>>> + uint32_t addr)
>>> +{
>>> + int rc;
>>> + uint32_t data = SCOM_RESET_CMD;
>>> +
>>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
>>> + sizeof(uint32_t));
>>> + if (rc)
>>> + return rc;
>>> +
>>> + data = (value >> 32) & 0xffffffff;
>>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
>>> + sizeof(uint32_t));
>>> + if (rc)
>>> + return rc;
>>> +
>>> + data = value & 0xffffffff;
>>> + rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
>>> + sizeof(uint32_t));
>>> + if (rc)
>>> + return rc;
>>> +
>>> + data = 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;
>>> +
>>> + udelay(SCOM_FSI2PIB_DELAY);
>>> +
>>> + data = 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) result << 32;
>>> + rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
>>> + sizeof(uint32_t));
>>> + if (rc)
>>> + return rc;
>>> +
>>> + *value |= result;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
>>> +{
>>> + if (whence != 0) /* SEEK_SET */
>>> + return -EINVAL;
>>> +
>>> + filep->f_pos = offset;
>>> + return offset;
>>> +}
>>> +
>>> +static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
>>> + loff_t *offset)
>>> +{
>>> + int rc = 0;
>>> + 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 = 0;
>>> + 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 = 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 ? rc : len;
>>> +}
>>> +
>>> +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 = NULL;
>>> +
>>> + scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
>>> + if (!scom)
>>> + return -ENOMEM;
>>> +
>>> + snprintf(scom->name, sizeof(scom->name),
>>> + "scom%d", atomic_inc_return(&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 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,
>>> + }
>>> +};
>>> +
>>> +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 = NULL;
>>> +
>>> + 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");
>>>
>>
^ permalink raw reply [flat|nested] 47+ messages in thread
* [PATCH linux v6 00/18] FSI device driver introduction
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (17 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions christopher.lee.bostic
` (17 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Introduction of the IBM 'Flexible Support Interface' (FSI) bus device
driver. FSI is a high fan out serial bus consisting of a clock and a serial
data line capable of running at speeds up to 166 MHz.
This set provides the basic framework to add FSI extensions to the
Linux bus and device models. Master specific implementations are
defined to utilize the core FSI function.
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:
* Hub master support
* Cascaded master support
* Application layer hot plug notification
* Application layer FSI bus status interface
Common FSI terminology:
* Master
Controller of the FSI bus. Only the master is allowed to control the
clock line and is the initiator of all transactions on a bus.
* Slave
The receiver or target of a master initiated transaction. The slave
cannot initiate communications on a bus and must respond to any
master requests for data.
* CFAM
Stands for Common Field replaceable unit Access Macro. A CFAM is an
ASIC residing in any device requiring FSI communications. CFAMs
consist of an array of hardware 'engines' used for various purposes.
I2C masters, UARTs, General Purpose IO hardware are common types of
these engines.
* Configuration Space / Table
A table contained at the beginning of each CFAM address space.
This table lists information such as the CFAM's ID, which engine types
and versions it has available, as well as its addressing range.
* FSI Engine driver
A device driver that registers with the FSI core so that it can access
devices it owns on an FSI bus.
Patch Descripotions:
Patches 01-12 have been provided by Jeremy Kerr as an example of
how FSI function should be phased in. This includes the basic framework
to add FSI extensions to the Linux bus and device models.
Patch 13 Contains minor updates on top of Jeremy's base to allow
client/engine drivers to register with the core.
Patch 14 adds function to set up read/write access to a given CFAM.
Patch 15 sets up slave smode during scanning operations to allow
CFAM config space access.
Patch 16 Implementation of the FSI GPIO master. Contains base code
from Jeremy Kerr and finished by me.
Patch 17 core additions for client driver registration.
Patch 18 SCOM client device driver creation
Chris Bostic (6):
drivers/fsi: Minor updates to FSI core
drivers/fsi: Set up links for slave communication
drivers/fsi: Set slave SMODE to init communications
drivers/fsi: Add GPIO FSI master
drivers/fsi: Add engine driver register utilities
drivers/fsi: Add SCOM FSI engine device driver
Jeremy Kerr (12):
fsi: Add empty fsi bus definitions
fsi: Add device & driver definitions
fsi: add driver to device matches
fsi: Add fsi master definition
fsi: Add fake master driver
fsi: enable debug
fsi: Add slave definition
fsi: Add empty master scan
fsi: Add crc4 helpers
fsi: Implement slave initialisation
fsi: scan slaves & register devices
fsi: Add device read/write/peek functions
.../devicetree/bindings/fsi-master-gpio.txt | 21 +
.../devicetree/bindings/fsi/fsi-master-gpio.txt | 21 +
arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 36 ++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/fsi/Kconfig | 34 ++
drivers/fsi/Makefile | 5 +
drivers/fsi/fsi-core.c | 496 ++++++++++++++++++++
drivers/fsi/fsi-master-fake.c | 95 ++++
drivers/fsi/fsi-master-gpio.c | 522 +++++++++++++++++++++
drivers/fsi/fsi-master.h | 60 +++
drivers/fsi/fsi-scom.c | 238 ++++++++++
include/linux/fsi.h | 72 +++
13 files changed, 1603 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fsi-master-gpio.txt
create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
create mode 100644 drivers/fsi/Kconfig
create mode 100644 drivers/fsi/Makefile
create mode 100644 drivers/fsi/fsi-core.c
create mode 100644 drivers/fsi/fsi-master-fake.c
create mode 100644 drivers/fsi/fsi-master-gpio.c
create mode 100644 drivers/fsi/fsi-master.h
create mode 100644 drivers/fsi/fsi-scom.c
create mode 100644 include/linux/fsi.h
--
1.8.2.2
^ permalink raw reply [flat|nested] 47+ messages in thread* [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (18 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 02/18] fsi: Add device & driver definitions christopher.lee.bostic
` (16 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
This change adds the initial (empty) fsi bus definition, and introduces
drivers/fsi/.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/Kconfig | 2 ++
drivers/Makefile | 1 +
drivers/fsi/Kconfig | 12 ++++++++++++
drivers/fsi/Makefile | 2 ++
drivers/fsi/fsi-core.c | 38 ++++++++++++++++++++++++++++++++++++++
include/linux/fsi.h | 23 +++++++++++++++++++++++
6 files changed, 78 insertions(+)
create mode 100644 drivers/fsi/Kconfig
create mode 100644 drivers/fsi/Makefile
create mode 100644 drivers/fsi/fsi-core.c
create mode 100644 include/linux/fsi.h
diff --git a/drivers/Kconfig b/drivers/Kconfig
index e1e2066..117ca14c 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -202,4 +202,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
source "drivers/fpga/Kconfig"
+source "drivers/fsi/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 0b6f3d6..7a1c96f 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -173,3 +173,4 @@ obj-$(CONFIG_STM) += hwtracing/stm/
obj-$(CONFIG_ANDROID) += android/
obj-$(CONFIG_NVMEM) += nvmem/
obj-$(CONFIG_FPGA) += fpga/
+obj-$(CONFIG_FSI) += fsi/
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
new file mode 100644
index 0000000..04c1a0e
--- /dev/null
+++ b/drivers/fsi/Kconfig
@@ -0,0 +1,12 @@
+#
+# FSI subsystem
+#
+
+menu "FSI support"
+
+config FSI
+ tristate "FSI support"
+ ---help---
+ FSI - the FRU Support Interface - is a simple bus for low-level
+ access to POWER-based hardware.
+endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
new file mode 100644
index 0000000..db0e5e7
--- /dev/null
+++ b/drivers/fsi/Makefile
@@ -0,0 +1,2 @@
+
+obj-$(CONFIG_FSI) += fsi-core.o
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
new file mode 100644
index 0000000..3e45306
--- /dev/null
+++ b/drivers/fsi/fsi-core.c
@@ -0,0 +1,38 @@
+/*
+ * FSI core driver
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <linux/fsi.h>
+#include <linux/module.h>
+
+/* FSI core & Linux bus type definitions */
+
+struct bus_type fsi_bus_type = {
+ .name = "fsi",
+};
+EXPORT_SYMBOL_GPL(fsi_bus_type);
+
+static int fsi_init(void)
+{
+ return bus_register(&fsi_bus_type);
+}
+
+static void fsi_exit(void)
+{
+ bus_unregister(&fsi_bus_type);
+}
+
+module_init(fsi_init);
+module_exit(fsi_exit);
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
new file mode 100644
index 0000000..8e8bdea
--- /dev/null
+++ b/include/linux/fsi.h
@@ -0,0 +1,23 @@
+/*
+ * FSI device & driver interfaces
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef LINUX_FSI_H
+#define LINUX_FSI_H
+
+#include <linux/device.h>
+
+extern struct bus_type fsi_bus_type;
+
+#endif /* LINUX_FSI_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 02/18] fsi: Add device & driver definitions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (19 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 01/18] fsi: Add empty fsi bus definitions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 03/18] fsi: add driver to device matches christopher.lee.bostic
` (15 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Add structs for fsi devices & drivers, and struct device conversion
functions.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
include/linux/fsi.h | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 8e8bdea..66dcf25 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -18,6 +18,17 @@
#include <linux/device.h>
+struct fsi_device {
+ struct device dev;
+};
+
+struct fsi_driver {
+ struct device_driver drv;
+};
+
+#define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
+#define to_fsi_drv(drvp) container_of(drvp, struct fsi_driver, drv)
+
extern struct bus_type fsi_bus_type;
#endif /* LINUX_FSI_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 03/18] fsi: add driver to device matches
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (20 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 02/18] fsi: Add device & driver definitions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 04/18] fsi: Add fsi master definition christopher.lee.bostic
` (14 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Driver bind to devices based on the engine types & (optional) versions.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 21 +++++++++++++++++++++
include/linux/fsi.h | 21 +++++++++++++++++++--
2 files changed, 40 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3e45306..3d55bd5 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -19,8 +19,29 @@
/* FSI core & Linux bus type definitions */
+static int fsi_bus_match(struct device *dev, struct device_driver *drv)
+{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct fsi_driver *fsi_drv = to_fsi_drv(drv);
+ const struct fsi_device_id *id;
+
+ if (!fsi_drv->id_table)
+ return 0;
+
+ for (id = fsi_drv->id_table; id->engine_type; id++) {
+ if (id->engine_type != fsi_dev->engine_type)
+ continue;
+ if (id->version == FSI_VERSION_ANY ||
+ id->version == fsi_dev->version)
+ return 1;
+ }
+
+ return 0;
+}
+
struct bus_type fsi_bus_type = {
.name = "fsi",
+ .match = fsi_bus_match,
};
EXPORT_SYMBOL_GPL(fsi_bus_type);
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 66dcf25..6d843d4 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -19,11 +19,28 @@
#include <linux/device.h>
struct fsi_device {
- struct device dev;
+ struct device dev;
+ u8 engine_type;
+ u8 version;
};
+struct fsi_device_id {
+ u8 engine_type;
+ u8 version;
+};
+
+#define FSI_VERSION_ANY 0
+
+#define FSI_DEVICE(t) \
+ .engine_type = (t), .version = FSI_VERSION_ANY,
+
+#define FSI_DEVICE_VERSIONED(t, v) \
+ .engine_type = (t), .version = (v),
+
+
struct fsi_driver {
- struct device_driver drv;
+ struct device_driver drv;
+ const struct fsi_device_id *id_table;
};
#define to_fsi_dev(devp) container_of(devp, struct fsi_device, dev)
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 04/18] fsi: Add fsi master definition
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (21 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 03/18] fsi: add driver to device matches christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 05/18] fsi: Add fake master driver christopher.lee.bostic
` (13 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 20 ++++++++++++++++++++
drivers/fsi/fsi-master.h | 37 +++++++++++++++++++++++++++++++++++++
2 files changed, 57 insertions(+)
create mode 100644 drivers/fsi/fsi-master.h
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 3d55bd5..ce9428d 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -17,6 +17,26 @@
#include <linux/fsi.h>
#include <linux/module.h>
+#include "fsi-master.h"
+
+static atomic_t master_idx = ATOMIC_INIT(-1);
+
+/* FSI master support */
+
+int fsi_master_register(struct fsi_master *master)
+{
+ master->idx = atomic_inc_return(&master_idx);
+ get_device(master->dev);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(fsi_master_register);
+
+void fsi_master_unregister(struct fsi_master *master)
+{
+ put_device(master->dev);
+}
+EXPORT_SYMBOL_GPL(fsi_master_unregister);
+
/* FSI core & Linux bus type definitions */
static int fsi_bus_match(struct device *dev, struct device_driver *drv)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
new file mode 100644
index 0000000..e75a810
--- /dev/null
+++ b/drivers/fsi/fsi-master.h
@@ -0,0 +1,37 @@
+/*
+ * FSI master definitions. These comprise the core <--> master interface,
+ * to allow the core to interact with the (hardware-specific) masters.
+ *
+ * Copyright (C) IBM Corporation 2016
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef DRIVERS_FSI_MASTER_H
+#define DRIVERS_FSI_MASTER_H
+
+#include <linux/device.h>
+
+struct fsi_master {
+ struct device *dev;
+ int idx;
+ int n_links;
+ int (*read)(struct fsi_master *, int link,
+ uint8_t slave, uint32_t addr,
+ void *val, size_t size);
+ int (*write)(struct fsi_master *, int link,
+ uint8_t slave, uint32_t addr,
+ const void *val, size_t size);
+};
+
+extern int fsi_master_register(struct fsi_master *master);
+extern void fsi_master_unregister(struct fsi_master *master);
+
+#endif /* DRIVERS_FSI_MASTER_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 05/18] fsi: Add fake master driver
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (22 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 04/18] fsi: Add fsi master definition christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 06/18] fsi: enable debug christopher.lee.bostic
` (12 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr, Chris Bostic
From: Jeremy Kerr <jk@ozlabs.org>
For debugging, add a fake master driver, that only supports reads,
returning a fixed set of data.
Includes changes from Chris Bostic <cbostic@us.ibm.com>.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
drivers/fsi/Kconfig | 10 +++++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-fake.c | 95 +++++++++++++++++++++++++++++++++++++++++++
3 files changed, 106 insertions(+)
create mode 100644 drivers/fsi/fsi-master-fake.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index 04c1a0e..f065dbe 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -9,4 +9,14 @@ config FSI
---help---
FSI - the FRU Support Interface - is a simple bus for low-level
access to POWER-based hardware.
+
+if FSI
+
+config FSI_MASTER_FAKE
+ tristate "Fake FSI master"
+ depends on FSI
+ ---help---
+ This option enables a fake FSI master driver for debugging.
+endif
+
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index db0e5e7..847c00c 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_FSI) += fsi-core.o
+obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
diff --git a/drivers/fsi/fsi-master-fake.c b/drivers/fsi/fsi-master-fake.c
new file mode 100644
index 0000000..ec1ed5e
--- /dev/null
+++ b/drivers/fsi/fsi-master-fake.c
@@ -0,0 +1,95 @@
+/*
+ * Fake FSI master driver for FSI development
+ *
+ * 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/platform_device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+#include "fsi-master.h"
+
+const uint8_t data[] = {
+ 0xc0, 0x02, 0x08, 0x03, /* chip id */
+ 0x80, 0x01, 0x11, 0x00, /* peek */
+ 0x80, 0x01, 0x20, 0x3e, /* slave */
+ 0x00, 0x01, 0x10, 0xa5, /* i2c */
+};
+
+
+static int fsi_master_fake_read(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, void *val, size_t size)
+{
+ if (link != 0)
+ return -ENODEV;
+
+ if (addr + size > sizeof(data))
+ memset(val, 0, size);
+ else
+ memcpy(val, data + addr, size);
+
+ return 0;
+}
+
+static int fsi_master_fake_write(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, const void *val, size_t size)
+{
+ if (link != 0)
+ return -ENODEV;
+
+ return -EACCES;
+}
+
+static int fsi_master_fake_probe(struct platform_device *pdev)
+{
+ struct fsi_master *master;
+
+ master = devm_kzalloc(&pdev->dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ master->dev = &pdev->dev;
+ master->n_links = 1;
+ master->read = fsi_master_fake_read;
+ master->write = fsi_master_fake_write;
+
+ return fsi_master_register(master);
+}
+
+static const struct of_device_id fsi_master_fake_match[] = {
+ { .compatible = "ibm,fsi-master-fake" },
+ { },
+};
+
+static struct platform_driver fsi_master_fake_driver = {
+ .driver = {
+ .name = "fsi-master-fake",
+ .of_match_table = fsi_master_fake_match,
+ },
+ .probe = fsi_master_fake_probe,
+};
+
+static int __init fsi_master_fake_init(void)
+{
+ struct device_node *np;
+
+ platform_driver_register(&fsi_master_fake_driver);
+
+ for_each_compatible_node(np, NULL, "ibm,fsi-master-fake")
+ of_platform_device_create(np, NULL, NULL);
+
+ return 0;
+}
+
+module_init(fsi_master_fake_init);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 06/18] fsi: enable debug
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (23 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 05/18] fsi: Add fake master driver christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 07/18] fsi: Add slave definition christopher.lee.bostic
` (11 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Enable debug for the fsi core during development. Remove before
submission.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index ce9428d..db1a1ce 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
+#define DEBUG
+
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/module.h>
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 07/18] fsi: Add slave definition
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (24 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 06/18] fsi: enable debug christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 08/18] fsi: Add empty master scan christopher.lee.bostic
` (10 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
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>
---
drivers/fsi/fsi-core.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index db1a1ce..7df4291 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -23,6 +23,15 @@
static atomic_t master_idx = ATOMIC_INIT(-1);
+struct fsi_slave {
+ struct device dev;
+ struct fsi_master *master;
+ int link;
+ uint8_t id;
+};
+
+#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+
/* FSI master support */
int fsi_master_register(struct fsi_master *master)
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 08/18] fsi: Add empty master scan
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (25 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 07/18] fsi: Add slave definition christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 09/18] fsi: Add crc4 helpers christopher.lee.bostic
` (9 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
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>
---
drivers/fsi/fsi-core.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 7df4291..9744a55 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -21,6 +21,8 @@
#include "fsi-master.h"
+#define FSI_N_SLAVES 4
+
static atomic_t master_idx = ATOMIC_INIT(-1);
struct fsi_slave {
@@ -32,12 +34,34 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* FSI slave support */
+static int fsi_slave_init(struct fsi_master *master,
+ int link, uint8_t slave_id)
+{
+ /* todo: initialise slave device, perform engine scan */
+
+ return -ENODEV;
+}
+
/* FSI master support */
+static int fsi_master_scan(struct fsi_master *master)
+{
+ int link, slave_id;
+
+ for (link = 0; link < master->n_links; link++)
+ for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
+ fsi_slave_init(master, link, slave_id);
+
+ return 0;
+
+}
+
int fsi_master_register(struct fsi_master *master)
{
master->idx = atomic_inc_return(&master_idx);
get_device(master->dev);
+ fsi_master_scan(master);
return 0;
}
EXPORT_SYMBOL_GPL(fsi_master_register);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 09/18] fsi: Add crc4 helpers
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (26 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 08/18] fsi: Add empty master scan christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 10/18] fsi: Implement slave initialisation christopher.lee.bostic
` (8 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Add some helpers for the crc checks for the slave configuration table.
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>
---
drivers/fsi/fsi-core.c | 25 +++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 21 +++++++++++++++++++++
2 files changed, 46 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 9744a55..2d22320 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -34,6 +34,31 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* crc helpers */
+static const uint8_t crc4_tab[] = {
+ 0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
+ 0x1, 0x6, 0xf, 0x8, 0xa, 0xd, 0x4, 0x3,
+};
+
+uint8_t fsi_crc4(uint8_t c, uint64_t x, int bits)
+{
+ int i;
+
+ /* Align to 4-bits */
+ bits = (bits + 3) & ~0x3;
+
+ /* Calculate crc4 over four-bit nibbles, starting at the MSbit */
+ for (i = bits; i >= 0; i -= 4)
+ c = crc4_tab[c ^ ((x >> i) & 0xf)];
+
+ return c;
+}
+
+static bool check_crc4_u32(uint32_t x)
+{
+ return fsi_crc4(0, x, 32) == 0;
+}
+
/* FSI slave support */
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index e75a810..cafb433 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -34,4 +34,25 @@ struct fsi_master {
extern int fsi_master_register(struct fsi_master *master);
extern void fsi_master_unregister(struct fsi_master *master);
+/**
+ * crc4 helper: Given a starting crc4 state @c, calculate the crc4 vaue of @x,
+ * which is @bits in length. This may be required by master implementations
+ * that do not provide their own hardware checksums.
+ *
+ * The crc4 is performed on 4-bit chunks (which is all we need for FSI
+ * calculations). Typically, we'll want a starting state of 0:
+ *
+ * c = fsi_crc4(0, msg, len);
+ *
+ * To crc4 a message that includes a single start bit, initialise crc4 state
+ * with:
+ *
+ * c = fsi_crc4(0, 1, 1);
+ *
+ * Then update with message data:
+ *
+ * c = fsi_crc4(c, msg, len);
+ */
+uint8_t fsi_crc4(uint8_t c, uint64_t x, int bits);
+
#endif /* DRIVERS_FSI_MASTER_H */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 10/18] fsi: Implement slave initialisation
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (27 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 09/18] fsi: Add crc4 helpers christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 11/18] fsi: scan slaves & register devices christopher.lee.bostic
` (7 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
Create fsi_slave devices during the master scan.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 49 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 2d22320..4282370 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -18,6 +18,7 @@
#include <linux/device.h>
#include <linux/fsi.h>
#include <linux/module.h>
+#include <linux/slab.h>
#include "fsi-master.h"
@@ -60,12 +61,58 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+
+static void fsi_slave_release(struct device *dev)
+{
+ struct fsi_slave *slave = to_fsi_slave(dev);
+
+ kfree(slave);
+}
+
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
{
- /* todo: initialise slave device, perform engine scan */
+ struct fsi_slave *slave;
+ uint32_t chip_id;
+ int rc;
+
+ rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
+ if (rc) {
+ dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
+ link, slave_id, rc);
+ return -ENODEV;
+ }
+
+ chip_id = be32_to_cpu(chip_id);
+ if (!check_crc4_u32(chip_id)) {
+ dev_warn(master->dev, "slave %02x:%02x: invalid chip id CRC!\n",
+ link, slave_id);
+ return -EIO;
+ }
+
+ pr_debug("fsi: found chip %08x at %02x:%02x:%02x\n",
+ master->idx, chip_id, link, slave_id);
+
+ /* we can communicate with a slave; create devices and scan */
+ slave = kzalloc(sizeof(*slave), GFP_KERNEL);
+ if (!slave)
+ return -ENOMEM;
+
+ slave->master = master;
+ slave->id = slave_id;
+ slave->dev.parent = master->dev;
+ slave->dev.release = fsi_slave_release;
+
+ dev_set_name(&slave->dev, "slave@%02x:%02x", link, slave_id);
+ rc = device_register(&slave->dev);
+ if (rc < 0) {
+ dev_warn(master->dev, "failed to create slave device: %d\n",
+ rc);
+ put_device(&slave->dev);
+ return rc;
+ }
- return -ENODEV;
+ return rc;
}
/* FSI master support */
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 11/18] fsi: scan slaves & register devices
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (28 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 10/18] fsi: Implement slave initialisation christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 12/18] fsi: Add device read/write/peek functions christopher.lee.bostic
` (6 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr, Chris 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 Chris Bostic <cbostic@us.ibm.com>.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
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 4282370..50e0616 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -24,6 +24,16 @@
#define FSI_N_SLAVES 4
+#define FSI_SLAVE_CONF_NEXT_MASK 0x80000000
+#define FSI_SLAVE_CONF_SLOTS_MASK 0x00ff0000
+#define FSI_SLAVE_CONF_SLOTS_SHIFT 16
+#define FSI_SLAVE_CONF_VERSION_MASK 0x0000f000
+#define FSI_SLAVE_CONF_VERSION_SHIFT 12
+#define FSI_SLAVE_CONF_TYPE_MASK 0x00000ff0
+#define FSI_SLAVE_CONF_TYPE_SHIFT 4
+
+static const int engine_page_size = 0x400;
+
static atomic_t master_idx = ATOMIC_INIT(-1);
struct fsi_slave {
@@ -35,6 +45,30 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+/* FSI endpoint-device support */
+
+static void fsi_device_release(struct device *_device)
+{
+ struct fsi_device *device = to_fsi_dev(_device);
+
+ kfree(device);
+}
+
+static struct fsi_device *fsi_create_device(struct fsi_slave *slave)
+{
+ struct fsi_device *dev;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ dev->dev.parent = &slave->dev;
+ dev->dev.bus = &fsi_bus_type;
+ dev->dev.release = fsi_device_release;
+
+ return dev;
+}
+
/* crc helpers */
static const uint8_t crc4_tab[] = {
0x0, 0x7, 0xe, 0x9, 0xb, 0xc, 0x5, 0x2,
@@ -61,6 +95,97 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+ void *val, size_t size)
+{
+ return slave->master->read(slave->master, slave->link,
+ slave->id, addr, val, size);
+}
+
+static int fsi_slave_scan(struct fsi_slave *slave)
+{
+ uint32_t engine_addr;
+ uint32_t conf;
+ int rc, i;
+
+ /*
+ * scan engines
+ *
+ * We keep the peek mode and slave engines for the core; so start
+ * at the third slot in the configuration table. We also need to
+ * skip the chip ID entry at the start of the address space.
+ */
+ engine_addr = engine_page_size * 3;
+ for (i = 2; i < engine_page_size / sizeof(uint32_t); i++) {
+ uint8_t slots, version, type;
+ struct fsi_device *dev;
+
+ rc = fsi_slave_read(slave, (i + 1) * sizeof(conf),
+ &conf, sizeof(conf));
+ if (rc) {
+ dev_warn(&slave->dev,
+ "error reading slave registers\n");
+ return -1;
+ }
+
+ conf = be32_to_cpu(conf);
+
+ if (!check_crc4_u32(conf)) {
+ dev_warn(&slave->dev,
+ "crc error in slave register at 0x%04x\n",
+ i);
+ return -1;
+ }
+
+ slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
+ >> FSI_SLAVE_CONF_SLOTS_SHIFT;
+ version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
+ >> FSI_SLAVE_CONF_VERSION_SHIFT;
+ type = (conf & FSI_SLAVE_CONF_TYPE_MASK)
+ >> FSI_SLAVE_CONF_TYPE_SHIFT;
+
+ /*
+ * Unused address areas are marked by a zero type value; this
+ * skips the defined address areas
+ */
+ if (type != 0) {
+
+ /* create device */
+ dev = fsi_create_device(slave);
+ if (!dev)
+ return -ENOMEM;
+
+ dev->engine_type = type;
+ dev->version = version;
+ dev->unit = i;
+ dev->addr = engine_addr;
+ dev->size = slots * engine_page_size;
+
+ dev_info(&slave->dev,
+ "engine[%i]: type %x, version %x, addr %x size %x\n",
+ dev->unit, dev->engine_type, version,
+ dev->addr, dev->size);
+
+ device_initialize(&dev->dev);
+ dev_set_name(&dev->dev, "%02x:%02x:%02x:%02x",
+ slave->master->idx, slave->link,
+ slave->id, i - 2);
+
+ rc = device_add(&dev->dev);
+ if (rc) {
+ dev_warn(&slave->dev, "add failed: %d\n", rc);
+ put_device(&dev->dev);
+ }
+ }
+
+ engine_addr += slots * engine_page_size;
+
+ if (!(conf & FSI_SLAVE_CONF_NEXT_MASK))
+ break;
+ }
+
+ return 0;
+}
static void fsi_slave_release(struct device *dev)
{
@@ -112,7 +237,8 @@ static int fsi_slave_init(struct fsi_master *master,
return rc;
}
- return rc;
+ fsi_slave_scan(slave);
+ return 0;
}
/* FSI master support */
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index 6d843d4..dfd3513 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -22,6 +22,10 @@ struct fsi_device {
struct device dev;
u8 engine_type;
u8 version;
+ u8 unit;
+ struct fsi_slave *slave;
+ uint32_t addr;
+ uint32_t size;
};
struct fsi_device_id {
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 12/18] fsi: Add device read/write/peek functions
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (29 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 11/18] fsi: scan slaves & register devices christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core christopher.lee.bostic
` (5 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Jeremy Kerr
From: Jeremy Kerr <jk@ozlabs.org>
This change introduces the fsi device API: simple read, write and peek
accessors for the devices' address spaces.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
drivers/fsi/fsi-core.c | 44 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/fsi.h | 7 ++++++-
2 files changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 50e0616..565c7e3 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -32,6 +32,8 @@
#define FSI_SLAVE_CONF_TYPE_MASK 0x00000ff0
#define FSI_SLAVE_CONF_TYPE_SHIFT 4
+#define FSI_PEEK_BASE 0x410
+
static const int engine_page_size = 0x400;
static atomic_t master_idx = ATOMIC_INIT(-1);
@@ -45,7 +47,42 @@ struct fsi_slave {
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
+static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
+ void *val, size_t size);
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+ const void *val, size_t size);
+
/* FSI endpoint-device support */
+int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
+ size_t size)
+{
+ if (addr > dev->size)
+ return -EINVAL;
+
+ if (addr + size > dev->size)
+ return -EINVAL;
+
+ return fsi_slave_read(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
+ size_t size)
+{
+ if (addr > dev->size)
+ return -EINVAL;
+
+ if (addr + size > dev->size)
+ return -EINVAL;
+
+ return fsi_slave_write(dev->slave, dev->addr + addr, val, size);
+}
+
+int fsi_device_peek(struct fsi_device *dev, void *val)
+{
+ uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t));
+
+ return fsi_slave_read(dev->slave, addr, val, sizeof(uint32_t));
+}
static void fsi_device_release(struct device *_device)
{
@@ -102,6 +139,13 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
slave->id, addr, val, size);
}
+static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
+ const void *val, size_t size)
+{
+ return slave->master->write(slave->master, slave->link,
+ slave->id, addr, val, size);
+}
+
static int fsi_slave_scan(struct fsi_slave *slave)
{
uint32_t engine_addr;
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index dfd3513..47af0af 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -28,6 +28,12 @@ struct fsi_device {
uint32_t size;
};
+extern int fsi_device_read(struct fsi_device *dev, uint32_t addr,
+ void *val, size_t size);
+extern int fsi_device_write(struct fsi_device *dev, uint32_t addr,
+ const void *val, size_t size);
+extern int fsi_device_peek(struct fsi_device *dev, void *val);
+
struct fsi_device_id {
u8 engine_type;
u8 version;
@@ -41,7 +47,6 @@ struct fsi_device_id {
#define FSI_DEVICE_VERSIONED(t, v) \
.engine_type = (t), .version = (v),
-
struct fsi_driver {
struct device_driver drv;
const struct fsi_device_id *id_table;
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (30 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 12/18] fsi: Add device read/write/peek functions christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication christopher.lee.bostic
` (4 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Added some export symbols for fsi_device_read/write for FSI
client drivers. Removed the byte swapping done on CFAM config
word reads - already in proper order. Removed fsi_crc4 checks,
they were incorectly reporting bad crc's. Need to evaluate
what is wrong with the method of calculation.
Add missing assignment of slave to fsi_device during scan.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
drivers/fsi/fsi-core.c | 13 +++++++------
1 file changed, 7 insertions(+), 6 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 565c7e3..95e867e 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -64,6 +64,7 @@ int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
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)
@@ -76,6 +77,7 @@ int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
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)
{
@@ -171,16 +173,15 @@ static int fsi_slave_scan(struct fsi_slave *slave)
"error reading slave registers\n");
return -1;
}
-
- conf = be32_to_cpu(conf);
-
+/* todo: fix crc calc problems */
+#if 0
if (!check_crc4_u32(conf)) {
dev_warn(&slave->dev,
"crc error in slave register at 0x%04x\n",
i);
return -1;
}
-
+#endif
slots = (conf & FSI_SLAVE_CONF_SLOTS_MASK)
>> FSI_SLAVE_CONF_SLOTS_SHIFT;
version = (conf & FSI_SLAVE_CONF_VERSION_MASK)
@@ -199,6 +200,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
if (!dev)
return -ENOMEM;
+ dev->slave = slave;
dev->engine_type = type;
dev->version = version;
dev->unit = i;
@@ -252,11 +254,10 @@ static int fsi_slave_init(struct fsi_master *master,
return -ENODEV;
}
- chip_id = be32_to_cpu(chip_id);
if (!check_crc4_u32(chip_id)) {
dev_warn(master->dev, "slave %02x:%02x: invalid chip id CRC!\n",
link, slave_id);
- return -EIO;
+ /* todo, fix crc calc issues. Just warn for now */
}
pr_debug("fsi: found chip %08x at %02x:%02x:%02x\n",
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (31 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 13/18] drivers/fsi: Minor updates to FSI core christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications christopher.lee.bostic
` (3 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Enable each link and send a break command in preparation
for scanning each link for slaves.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V3 - Remove definition of BREAK command from fsi-master.h
- Remove definitions of fsi_master_fake for set_smode
and break
- Remove master->set_smode master dependent function and
moved to a generic base master set_smode.
- Add fsi_master_link_enable with master type dependencies
V4 - Remove all references to set smode
- Remove file fsi-slave.h
- Move link break and enable up into master scan
- Change rc = func(); return rc; coding to return func();
Note this is still in place in fsi_master_send_break in
anticipation of changes coming up in next patch of this
series.
- Fix comment spelling error
- Use dev_dbg instead of dev_info when link enable or break
fails
- Remove explicit set of master->break and
master->link_enable to NULL due to kzalloc
v6 - Don't return error if master->link_enable or
master->break are not defined.
---
drivers/fsi/fsi-core.c | 38 +++++++++++++++++++++++++++++++++++---
drivers/fsi/fsi-master.h | 2 ++
2 files changed, 37 insertions(+), 3 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 95e867e..7fc15ab 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -288,16 +288,48 @@ static int fsi_slave_init(struct fsi_master *master,
/* FSI master support */
+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, slave_id;
+ int link, slave_id, 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 with:%d\n", link, rc);
+ continue;
+ }
+ rc = fsi_master_break(master, link);
+ if (rc) {
+ dev_dbg(master->dev,
+ "Break to link:%d failed with:%d\n", link, rc);
+ continue;
+ }
- for (link = 0; link < master->n_links; link++)
for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
fsi_slave_init(master, link, slave_id);
+ }
return 0;
-
}
int fsi_master_register(struct fsi_master *master)
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index cafb433..56aad0e 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -29,6 +29,8 @@ struct fsi_master {
int (*write)(struct fsi_master *, int link,
uint8_t slave, uint32_t addr,
const void *val, size_t size);
+ int (*send_break)(struct fsi_master *, int link);
+ int (*link_enable)(struct fsi_master *, int link);
};
extern int fsi_master_register(struct fsi_master *master);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (32 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 14/18] drivers/fsi: Set up links for slave communication christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master christopher.lee.bostic
` (2 subsequent siblings)
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Set CFAM to appropriate ID so that the controlling master
can manage link memory ranges. Add slave engine register
definitions.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V3 - Remove master->set_smode op and make it generic for
all masters.
V4 - 'Change rc = func(); return rc;' code to 'return func();'
- Change set_smode_defaults to return value instead of
passing back via reference.
- Rename fsi_master_set_smode to fsi_slave_set_smode
- Pass in slave id to fsi_slave_set_smode instead of
making assumptions internal to the function.
- Only allow slave inits on id 0 - a todo to accommodate
all slave ID's.
V5 - Move SMODE read at default ID out of break and into
the caller.
V6 - Change the slave ID from 0 to 3 when setting smode to
new value.
---
drivers/fsi/fsi-core.c | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 89 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 7fc15ab..6ac239a 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -33,6 +33,7 @@
#define FSI_SLAVE_CONF_TYPE_SHIFT 4
#define FSI_PEEK_BASE 0x410
+#define FSI_SLAVE_BASE 0x800
static const int engine_page_size = 0x400;
@@ -52,6 +53,25 @@ static int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
static int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size);
+/*
+ * 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 */
+
/* FSI endpoint-device support */
int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val,
size_t size)
@@ -134,6 +154,31 @@ static bool check_crc4_u32(uint32_t x)
}
/* FSI slave support */
+
+/* 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 int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
void *val, size_t size)
{
@@ -240,6 +285,22 @@ static void fsi_slave_release(struct device *dev)
kfree(slave);
}
+static uint32_t set_smode_defaults(struct fsi_master *master)
+{
+ return FSI_SMODE_WSC | FSI_SMODE_ECRC
+ | fsi_smode_echodly(0xf) | fsi_smode_senddly(0xf)
+ | fsi_smode_lbcrr(1);
+}
+
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id)
+{
+ uint32_t smode = set_smode_defaults(master);
+
+ smode |= fsi_smode_sid(id);
+ return master->write(master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+ &smode, sizeof(smode));
+}
+
static int fsi_slave_init(struct fsi_master *master,
int link, uint8_t slave_id)
{
@@ -247,6 +308,21 @@ static int fsi_slave_init(struct fsi_master *master,
uint32_t chip_id;
int rc;
+ /*
+ * todo: Due to CFAM hardware issues related to BREAK commands we're
+ * limited to only one CFAM per link. Once issues are resolved this
+ * restriction can be removed.
+ */
+ if (slave_id > 0)
+ return 0;
+
+ rc = fsi_slave_set_smode(master, link, slave_id);
+ if (rc) {
+ dev_warn(master->dev, "can't set smode on slave:%02x:%02x %d\n",
+ link, slave_id, rc);
+ return -ENODEV;
+ }
+
rc = master->read(master, link, slave_id, 0, &chip_id, sizeof(chip_id));
if (rc) {
dev_warn(master->dev, "can't read slave %02x:%02x: %d\n",
@@ -310,6 +386,7 @@ static int fsi_master_break(struct fsi_master *master, int link)
static int fsi_master_scan(struct fsi_master *master)
{
int link, slave_id, rc;
+ uint32_t smode;
for (link = 0; link < master->n_links; link++) {
rc = fsi_master_link_enable(master, link);
@@ -324,6 +401,18 @@ static int fsi_master_scan(struct fsi_master *master)
"Break to link:%d failed with:%d\n", link, rc);
continue;
}
+ /*
+ * Verify can read slave at default ID location. If fails
+ * there must be nothing on other end of link
+ */
+ rc = master->read(master, link, 3, FSI_SLAVE_BASE + FSI_SMODE,
+ &smode, sizeof(smode));
+ if (rc) {
+ dev_dbg(master->dev,
+ "Read link:%d smode default id failed:%d\n",
+ link, rc);
+ continue;
+ }
for (slave_id = 0; slave_id < FSI_N_SLAVES; slave_id++)
fsi_slave_init(master, link, slave_id);
--
1.8.2.2
^ permalink raw reply related [flat|nested] 47+ messages in thread* [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (33 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 15/18] drivers/fsi: Set slave SMODE to init communications christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic, Jeremy Kerr
From: Chris Bostic <cbostic@us.ibm.com>
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 Jeremy Kerr's
original GPIO master base commit.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
---
V3 - Added remainder of base I/O ops (excluding dev tree and crc calcs).
- Add encoding of all read/write commands and send data over sda
- Check for and decode response from slave.
- Add set enable pin in master->link_enable() master specific function.
V4 - Remove unnecessary comments about delays in various locations
- Define non clock and data pins as optional and check for gpio
descriptors == NULL before accessing. Don't return error if
optional pins cannot be retrieved via devm.
- serial_in: rearrange order of msg shift and input bit OR op.
- serial_out: Fix mirror image issue on shift data out and set
data out as necessary for bit count to be sent.
- serial_out: make message parm a const
- serial_out: fix 3x 'msg & data' repeat
- poll_for_response: bits remaining was calculated with
sizeof(size) should be size.
- fsi_master_gpio_break: remove smode read
- Replace dev_info with dev_dbg in proper places.
- Add a bit count parm to serial_in, increment msg->bits
on every bit received.
- Remove magic numbers in poll_for_response that indicate
how many bits to receive
- Utilize the crc utilities to check input data and set
crc for output data
- Remove add_crc stub
V5 - Rename pins from generic 'clk', 'data', etc in dts file to
'fsi_clk', 'fsi_data', etc...
- serial_in: invert data bit during message assembly
instead of inverting whole message after assembly.
- Remove all instances of clearing out message field prior
to calling serial_in, redundant.
- Change name of build_command() to build_abs_ar_command()
to make it more obvious its creating ABS AR type commands.
- Remove unlikely( ) checks.
- Indent dev_info string "master time out waiting for response"
- poll_for_response: return FSI_GPIO_MAX busy instead of
busy_count.
- fsi_master_gpio_break: move 'logic reset' comment to above
the delay call.
- Add crc4 calculation initialization since data includes
a start bit.
V6 - Invert polarity of SDA line for start bit, echo delay, pre-
D-POLL clocking, break command sequence (pre, during, post).
- Add echo delay after sending D-POLL
- Fill in user read data with received read data response
- Clock the slave after each command completes to prime it
for another operation
- Increase D-POLL checks on BUSY response before flagging
error
- Add Alistair's crc calculator from pdbg. Having issues
with the existing one.
- Revert V5 changes to rename gpio pin names.
- Return -EIO on all bus errors
- Condense slave id and response id into one serial-in
operation
- Change D-POLL command to accommodate right alignment
- Remove all dev_info( ) calls to reduce spam
- serial_in remove cmd->bits = 0 / cmd->bits++ in for
loop. Just set value at end.
- Change v translator line to direction output once
during init and use gpiod_set_value thereafter
- Remove set/clear clocks and do it all in clock_toggle
- Fill in slave id when encoding commands. Right align
the command prior to serializing out.
- gpio master defines number of links to 1, was undefined.
---
.../devicetree/bindings/fsi-master-gpio.txt | 21 +
.../devicetree/bindings/fsi/fsi-master-gpio.txt | 21 +
arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts | 36 ++
drivers/fsi/Kconfig | 6 +
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-master-gpio.c | 522 +++++++++++++++++++++
6 files changed, 607 insertions(+)
create mode 100644 Documentation/devicetree/bindings/fsi-master-gpio.txt
create mode 100644 Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
create mode 100644 drivers/fsi/fsi-master-gpio.c
diff --git a/Documentation/devicetree/bindings/fsi-master-gpio.txt b/Documentation/devicetree/bindings/fsi-master-gpio.txt
new file mode 100644
index 0000000..d46be27
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi-master-gpio.txt
@@ -0,0 +1,21 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,fsi-master-gpio";
+ - clk-gpio;
+ - data-gpio;
+
+Optional properties:
+ - enable-gpio;
+ - trans-gpio;
+ - mux-gpio;
+
+fsi-master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpio = <&gpio 0>;
+ data-gpio = <&gpio 1>;
+ enable-gpio = <&gpio 2>; /* Enable FSI data in/out */
+ trans-gpio = <&gpio 3>; /* Voltage translator direction */
+ mux-gpio = <&gpio 4>; /* Multiplexer for FSI pins */
+}
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..ff3a62e
--- /dev/null
+++ b/Documentation/devicetree/bindings/fsi/fsi-master-gpio.txt
@@ -0,0 +1,21 @@
+Device-tree bindings for gpio-based FSI master driver
+-----------------------------------------------------
+
+Required properties:
+ - compatible = "ibm,fsi-master-gpio";
+ - clk-gpios;
+ - data-gpios;
+
+Optional properties:
+ - enable-gpios;
+ - trans-gpios;
+ - mux-gpios;
+
+fsi-master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpios = <&gpio 0 &gpio 6>;
+ data-gpios = <&gpio 1 &gpio 7>;
+ enable-gpios = <&gpio 2 &gpio 8>; /* Enable FSI data in/out */
+ trans-gpios = <&gpio 3 &gpio 9>; /* Volts translator direction */
+ mux-gpios = <&gpio 4> &gpio 10>; /* Multiplexer for FSI pins */
+}
diff --git a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
index 21619fd..9d9c2a9 100644
--- a/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
+++ b/arch/arm/boot/dts/aspeed-bmc-opp-palmetto.dts
@@ -167,6 +167,42 @@
output-low;
line-name = "func_mode2";
};
+
+ pin_fsi_clk {
+ gpios = <ASPEED_GPIO(A, 4) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_clk";
+ };
+
+ pin_fsi_data {
+ gpios = <ASPEED_GPIO(A, 5) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_data";
+ };
+
+ pin_fsi_trans {
+ gpios = <ASPEED_GPIO(H, 6) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_trans";
+ };
+
+ pin_fsi_enable {
+ gpios = <ASPEED_GPIO(D, 0) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_enable";
+ };
+
+ pin_fsi_mux {
+ gpios = <ASPEED_GPIO(A, 6) GPIO_ACTIVE_HIGH>;
+ output-low;
+ line-name = "fsi_mux";
+ };
+
+ fsi_master {
+ compatible = "ibm,fsi-master", "ibm,fsi-master-gpio";
+ clk-gpios = <&gpio 0 &gpio 6>;
+ data-gpios = <&gpio 1 &gpio 7>;
+ };
};
&vuart {
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index f065dbe..b21fe3c 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -17,6 +17,12 @@ config FSI_MASTER_FAKE
depends on FSI
---help---
This option enables a fake FSI master driver for debugging.
+
+config FSI_MASTER_GPIO
+ tristate "GPIO-based FSI master"
+ depends on FSI
+ ---help---
+ This option enables a FSI master driver using GPIO lines.
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 847c00c..2021ce5 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -1,3 +1,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
+obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
new file mode 100644
index 0000000..175281a
--- /dev/null
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -0,0 +1,522 @@
+/*
+ * A FSI master controller, using a simple GPIO bit-banging interface
+ */
+
+#include <linux/platform_device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/fsi.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 0x000000000000002AULL
+#define FSI_GPIO_CMD_DPOLL_SIZE 9
+#define FSI_GPIO_DPOLL_CLOCKS 100 /* < 21 will cause slave to hang */
+#define FSI_GPIO_CMD_DEFAULT 0x2000000000000000ULL
+#define FSI_GPIO_CMD_WRITE 0
+#define FSI_GPIO_CMD_READ 0x0400000000000000ULL
+#define FSI_GPIO_CMD_SLAVE_MASK 0xC000000000000000ULL
+#define FSI_GPIO_CMD_ADDR_SHIFT 37
+#define FSI_GPIO_CMD_SLV_SHIFT 62
+#define FSI_GPIO_CMD_SIZE_16 0x0000001000000000ULL
+#define FSI_GPIO_CMD_SIZE_32 0x0000003000000000ULL
+#define FSI_GPIO_CMD_DT32_SHIFT 4
+#define FSI_GPIO_CMD_DT16_SHIFT 20
+#define FSI_GPIO_CMD_DT8_SHIFT 28
+#define FSI_GPIO_CMD_DFLT_LEN 28
+#define FSI_GPIO_CMD_CRC_SHIFT 60
+
+/* 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 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)
+{
+ ndelay(FSI_GPIO_STD_DLY);
+ return gpiod_get_value(master->gpio_data);
+}
+
+static void sda_out(struct fsi_master_gpio *master, int value)
+{
+ gpiod_set_value(master->gpio_data, value);
+}
+
+static void set_sda_input(struct fsi_master_gpio *master)
+{
+ gpiod_direction_input(master->gpio_data);
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 0);
+}
+
+static void set_sda_output(struct fsi_master_gpio *master, int value)
+{
+ gpiod_direction_output(master->gpio_data, value);
+ if (master->gpio_trans)
+ gpiod_set_value(master->gpio_trans, 1);
+}
+
+static void serial_in(struct fsi_master_gpio *master, struct fsi_gpio_msg *cmd,
+ uint8_t num_bits)
+{
+ uint8_t bit;
+ uint64_t msg = 0;
+ uint8_t in_bit = 0;
+
+ set_sda_input(master);
+
+ for (bit = 0; bit < num_bits; bit++) {
+ clock_toggle(master, 1);
+ in_bit = sda_in(master);
+ msg <<= 1;
+ msg |= ~in_bit & 0x1; /* Data is negative active */
+ }
+ cmd->bits = num_bits;
+ cmd->msg = msg;
+}
+
+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 negative active */
+ uint64_t sda_mask = 0x1ULL << (cmd->bits - 1);
+ uint64_t last_bit = ~0;
+ int next_bit;
+
+ if (!cmd->bits) {
+ dev_warn(master->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;
+ }
+}
+
+/*
+ * Clock out some 0's after every message to ride out line reflections
+ */
+static void echo_delay(struct fsi_master_gpio *master)
+{
+ set_sda_output(master, 1);
+ clock_toggle(master, FSI_ECHO_DELAY_CLOCKS);
+}
+
+/*
+ * Used in bus error cases only. Clears out any remaining data the slave
+ * is attempting to send
+ */
+static void drain_response(struct fsi_master_gpio *master)
+{
+ struct fsi_gpio_msg msg;
+
+ serial_in(master, &msg, FSI_GPIO_DRAIN_BITS);
+}
+
+/*
+ * 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)
+{
+
+}
+
+/*
+ * Taken from Alistaire's PDBG
+ */
+static uint8_t crc4(uint8_t c, int b)
+{
+ uint8_t m = 0;
+
+ c &= 0xf;
+ m = b ^ ((c >> 3) & 0x1);
+ m = (m << 2) | (m << 1) | (m);
+ c <<= 1;
+ c ^= m;
+
+ return c & 0xf;
+}
+
+static int poll_for_response(struct fsi_master_gpio *master, uint8_t expected,
+ uint8_t size, void *data)
+{
+ int busy_count = 0, i;
+ struct fsi_gpio_msg response, cmd;
+ int bits_remaining = 0, bit_count, response_id, id;
+ uint64_t resp = 0;
+ uint8_t bits_received = FSI_GPIO_MSG_ID_SIZE +
+ FSI_GPIO_MSG_RESPID_SIZE;
+ uint8_t crc_in;
+
+ do {
+ for (i = 0; i < FSI_GPIO_MTOE_COUNT; i++) {
+ serial_in(master, &response, 1);
+ if (response.msg)
+ break;
+ }
+ if (i >= FSI_GPIO_MTOE_COUNT) {
+ dev_dbg(master->master.dev,
+ "Master time out waiting for response\n");
+ drain_response(master);
+ fsi_master_gpio_error(master, FSI_GPIO_MTOE);
+ return -EIO;
+ }
+
+ /* Response received */
+ bit_count = FSI_GPIO_MSG_ID_SIZE + FSI_GPIO_MSG_RESPID_SIZE;
+ serial_in(master, &response, bit_count);
+
+ response_id = response.msg & 0x3;
+ id = (response.msg >> FSI_GPIO_MSG_RESPID_SIZE) & 0x3;
+ dev_dbg(master->master.dev, "id:%d resp:%d\n", id, response_id);
+
+ resp = response.msg;
+
+ switch (response_id) {
+ case FSI_GPIO_RESP_ACK:
+ if (expected == FSI_GPIO_RESP_ACKD)
+ bits_remaining = 8 * size;
+ break;
+
+ case FSI_GPIO_RESP_BUSY:
+ /*
+ * Its necessary to clock slave before issuing
+ * d-poll, not indicated in the hardware protocol
+ * spec. < 20 clocks causes slave to hang, 21 ok.
+ */
+ set_sda_output(master, 1);
+ clock_toggle(master, FSI_GPIO_DPOLL_CLOCKS);
+ cmd.msg = FSI_GPIO_CMD_DPOLL;
+ cmd.bits = FSI_GPIO_CMD_DPOLL_SIZE;
+ serial_out(master, &cmd);
+ echo_delay(master);
+ continue;
+
+ case FSI_GPIO_RESP_ERRA:
+ case FSI_GPIO_RESP_ERRC:
+ dev_dbg(master->master.dev, "ERR received: %d\n",
+ (int)response.msg);
+ drain_response(master);
+ fsi_master_gpio_error(master, response.msg);
+ return -EIO;
+ }
+
+ /* Read in the data field if applicable */
+ if (bits_remaining) {
+ serial_in(master, &response, bits_remaining);
+ resp <<= bits_remaining;
+ resp |= response.msg;
+ bits_received += bits_remaining;
+ *((uint32_t *)data) = response.msg;
+ }
+
+ /* Include start bit */
+ crc_in = crc4(0, 1);
+ for (i = bits_received - 1; i >= 0; i--)
+ crc_in = crc4(crc_in, !!(resp & (1ULL << i)));
+
+ /* Read in the crc and check it */
+ serial_in(master, &response, FSI_GPIO_CRC_SIZE);
+ if (crc_in != response.msg) {
+
+ /* CRC's don't match - warn for now */
+ dev_dbg(master->master.dev, "ERR response CRC\n");
+ fsi_master_gpio_error(master, FSI_GPIO_CRC_INVAL);
+ }
+ /* Clock the slave enough to be ready for next operation */
+ clock_toggle(master, FSI_GPIO_PRIME_SLAVE_CLOCKS);
+ return 0;
+
+ } while (busy_count++ < FSI_GPIO_MAX_BUSY);
+
+ dev_dbg(master->master.dev, "ERR slave is stuck in busy state\n");
+ fsi_master_gpio_error(master, FSI_GPIO_ERR_BUSY);
+
+ return -EIO;
+}
+
+static void build_abs_ar_command(struct fsi_gpio_msg *cmd, uint64_t mode,
+ uint8_t slave, uint32_t addr, size_t size,
+ const void *data)
+{
+ uint8_t crc;
+ int i;
+
+ cmd->bits = FSI_GPIO_CMD_DFLT_LEN;
+ cmd->msg = FSI_GPIO_CMD_DEFAULT;
+ cmd->msg |= mode;
+
+ /* todo: handle more than just slave id 0 */
+ cmd->msg &= ~FSI_GPIO_CMD_SLAVE_MASK;
+ cmd->msg |= (((uint64_t)slave) << FSI_GPIO_CMD_SLV_SHIFT);
+ cmd->msg |= (((uint64_t)addr) << FSI_GPIO_CMD_ADDR_SHIFT);
+ if (size == sizeof(uint8_t)) {
+ if (data) {
+ uint8_t cmd_data = *((uint8_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT8_SHIFT;
+ }
+ } else if (size == sizeof(uint16_t)) {
+ cmd->msg |= FSI_GPIO_CMD_SIZE_16;
+ if (data) {
+ uint16_t cmd_data = *((uint16_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT16_SHIFT;
+ }
+ } else {
+ cmd->msg |= FSI_GPIO_CMD_SIZE_32;
+ if (data) {
+ uint32_t cmd_data = *((uint32_t *)data);
+
+ cmd->msg |=
+ ((uint64_t)cmd_data) << FSI_GPIO_CMD_DT32_SHIFT;
+ }
+ }
+
+ if (mode == FSI_GPIO_CMD_WRITE)
+ cmd->bits += (8 * size);
+
+ /* Include start bit */
+ crc = crc4(0, 1);
+ for (i = 63; i >= (64 - cmd->bits); i--)
+ crc = crc4(crc, !!(cmd->msg & (1ULL << i)));
+
+ cmd->msg |= ((uint64_t)crc) << (FSI_GPIO_CMD_CRC_SHIFT - cmd->bits);
+ cmd->bits += FSI_GPIO_CRC_SIZE;
+
+ /* Right align message */
+ cmd->msg >>= (64 - cmd->bits);
+}
+
+static int fsi_master_gpio_read(struct fsi_master *_master, int link,
+ uint8_t slave, uint32_t addr, void *val, size_t size)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+ struct fsi_gpio_msg cmd;
+
+ if (link != 0)
+ return -ENODEV;
+
+ build_abs_ar_command(&cmd, FSI_GPIO_CMD_READ, slave, addr, size, NULL);
+ serial_out(master, &cmd);
+ echo_delay(master);
+
+ return poll_for_response(master, FSI_GPIO_RESP_ACKD, size, val);
+}
+
+static int fsi_master_gpio_write(struct fsi_master *_master, int link,
+ uint8_t slave, 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, FSI_GPIO_CMD_WRITE, slave, addr, size, val);
+ serial_out(master, &cmd);
+ echo_delay(master);
+
+ return poll_for_response(master, FSI_GPIO_RESP_ACK, size, NULL);
+}
+
+/*
+ * Issue a break command on link
+ */
+static int fsi_master_gpio_break(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+
+ set_sda_output(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)
+{
+ if (master->gpio_mux)
+ gpiod_direction_output(master->gpio_mux, 1);
+ if (master->gpio_trans)
+ gpiod_direction_output(master->gpio_trans, 1);
+ if (master->gpio_enable)
+ gpiod_direction_output(master->gpio_enable, 0);
+ gpiod_direction_output(master->gpio_clk, 1);
+ gpiod_direction_output(master->gpio_data, 1);
+
+ /* todo: evaluate if clocks can be reduced */
+ clock_toggle(master, FSI_INIT_CLOCKS);
+}
+
+static int fsi_master_gpio_link_enable(struct fsi_master *_master, int link)
+{
+ struct fsi_master_gpio *master = to_fsi_master_gpio(_master);
+
+ if (link != 0)
+ return -ENODEV;
+
+ if (master->gpio_enable)
+ 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;
+
+ gpio = devm_gpiod_get(&pdev->dev, "clock", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_clk = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "data", 0);
+ if (IS_ERR(gpio))
+ return PTR_ERR(gpio);
+ master->gpio_data = gpio;
+
+ /* Optional pins */
+
+ gpio = devm_gpiod_get(&pdev->dev, "trans", 0);
+ if (!IS_ERR(gpio))
+ master->gpio_trans = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "enable", 0);
+ if (!IS_ERR(gpio))
+ master->gpio_enable = gpio;
+
+ gpio = devm_gpiod_get(&pdev->dev, "mux", 0);
+ if (!IS_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.send_break = fsi_master_gpio_break;
+ master->master.link_enable = fsi_master_gpio_link_enable;
+
+ fsi_master_gpio_init(master);
+
+ platform_set_drvdata(pdev, 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 = "ibm,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] 47+ messages in thread* [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (34 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 16/18] drivers/fsi: Add GPIO FSI master christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
2016-10-30 22:09 ` [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver christopher.lee.bostic
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Add driver_register and driver_unregister wrappers for FSI.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V4 - Separate out SCOM client driver registration for a follow
on patch.
V5 - Rename fsidrv_register, fsidrv_unregster to fsi_drv_register,
fsi_drv_unregister.
- Add module_fsi_driver() macro for clients that just need
to do boilerplate registration
V6 - Rename fsi_drv_register/unregister to fsi_driver_register/..
---
drivers/fsi/fsi-core.c | 18 ++++++++++++++++++
include/linux/fsi.h | 12 ++++++++++++
2 files changed, 30 insertions(+)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 6ac239a..88002fc 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -458,6 +458,24 @@ 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 47af0af..62453b7 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -55,6 +55,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 *);
+extern void fsi_driver_unregister(struct fsi_driver *);
+
+/* 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] 47+ messages in thread* [PATCH linux v6 18/18] drivers/fsi: Add SCOM FSI client device driver
2016-10-30 22:09 [PATCH linux v6 00/18] FSI device driver introduction christopher.lee.bostic
` (35 preceding siblings ...)
2016-10-30 22:09 ` [PATCH linux v6 17/18] drivers/fsi: Add client driver register utilities christopher.lee.bostic
@ 2016-10-30 22:09 ` christopher.lee.bostic
36 siblings, 0 replies; 47+ messages in thread
From: christopher.lee.bostic @ 2016-10-30 22:09 UTC (permalink / raw)
To: openbmc; +Cc: joel, zahrens, xxpetri, Chris Bostic
From: Chris Bostic <cbostic@us.ibm.com>
Create a simple SCOM engine device driver that reads and writes
across an FSI bus.
Signed-off-by: Chris Bostic <cbostic@us.ibm.com>
---
V4 - Add put_scom and get_scom operations
V5 - Add character device registration and fill in read/write
syscalls.
V6 - Add multi scom engine support. Add list of devices.
Use file private data to store and hand off scom structures.
- Add lseek, open, close
- Remove data/address structure to pass from user space to
kernel. Use offset provided by read/write and pass data
- Added list setup in init and so did not add the macro
module_fsi_driver
- Global structs made static where possible.
- Change from char dev to use misc device api as shown
in examples from Jeremy Kere.
---
drivers/fsi/Kconfig | 6 ++
drivers/fsi/Makefile | 1 +
drivers/fsi/fsi-scom.c | 238 +++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 245 insertions(+)
create mode 100644 drivers/fsi/fsi-scom.c
diff --git a/drivers/fsi/Kconfig b/drivers/fsi/Kconfig
index b21fe3c..1fa9bc0 100644
--- a/drivers/fsi/Kconfig
+++ b/drivers/fsi/Kconfig
@@ -23,6 +23,12 @@ config FSI_MASTER_GPIO
depends on FSI
---help---
This option enables a FSI master driver using GPIO lines.
+
+config FSI_SCOM
+ tristate "SCOM FSI client"
+ depends on FSI
+ ---help---
+ This option enables the SCOM FSI client device driver.
endif
endmenu
diff --git a/drivers/fsi/Makefile b/drivers/fsi/Makefile
index 2021ce5..3e31d9a 100644
--- a/drivers/fsi/Makefile
+++ b/drivers/fsi/Makefile
@@ -2,3 +2,4 @@
obj-$(CONFIG_FSI) += fsi-core.o
obj-$(CONFIG_FSI_MASTER_FAKE) += fsi-master-fake.o
obj-$(CONFIG_FSI_MASTER_GPIO) += fsi-master-gpio.o
+obj-$(CONFIG_FSI_SCOM) += fsi-scom.o
diff --git a/drivers/fsi/fsi-scom.c b/drivers/fsi/fsi-scom.c
new file mode 100644
index 0000000..27be082
--- /dev/null
+++ b/drivers/fsi/fsi-scom.c
@@ -0,0 +1,238 @@
+/*
+ * 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>
+
+#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];
+};
+
+#define to_scom_dev(x) container_of((x), struct scom_device, mdev)
+
+static struct list_head scom_devices;
+static atomic_t scom_idx = ATOMIC_INIT(0);
+static int scom_probe(struct device *);
+
+static int put_scom(struct scom_device *scom_dev, uint64_t value,
+ uint32_t addr)
+{
+ int rc;
+ uint32_t data = SCOM_RESET_CMD;
+
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_RESET_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = (value >> 32) & 0xffffffff;
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA0_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = value & 0xffffffff;
+ rc = fsi_device_write(scom_dev->fsi_dev, SCOM_DATA1_REG, &data,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ data = 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;
+
+ udelay(SCOM_FSI2PIB_DELAY);
+
+ data = 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) result << 32;
+ rc = fsi_device_read(scom_dev->fsi_dev, SCOM_DATA1_REG, &result,
+ sizeof(uint32_t));
+ if (rc)
+ return rc;
+
+ *value |= result;
+
+ return 0;
+}
+
+static loff_t scom_llseek(struct file *filep, loff_t offset, int whence)
+{
+ if (whence != 0) /* SEEK_SET */
+ return -EINVAL;
+
+ filep->f_pos = offset;
+ return offset;
+}
+
+static ssize_t scom_read(struct file *filep, char __user *buf, size_t len,
+ loff_t *offset)
+{
+ int rc = 0;
+ 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 = 0;
+ 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 = 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 ? rc : len;
+}
+
+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 = NULL;
+
+ scom = devm_kzalloc(dev, sizeof(*scom), GFP_KERNEL);
+ if (!scom)
+ return -ENOMEM;
+
+ snprintf(scom->name, sizeof(scom->name),
+ "scom%d", atomic_inc_return(&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 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,
+ }
+};
+
+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 = NULL;
+
+ 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] 47+ messages in thread