[PATCH linux v5 6/7] drivers/fsi: Add FSI Bus Support for Clients
christopher.lee.bostic at gmail.com
christopher.lee.bostic at gmail.com
Thu Aug 25 05:54:11 AEST 2016
From: Chris Bostic <cbostic at us.ibm.com>
Provide means for a client to register with an FSI bus and get
notifications of hot plug events. A client is a device driver that
needs access to a physical FSI bus in order to communicate with
hardware it controls. Utilities added to allow client to retrieve
resource information on its hardware.
Signed-off-by: Chris Bostic <cbostic at us.ibm.com>
---
drivers/fsi/fsi.h | 15 +++++
drivers/fsi/fsicfam.h | 2 +
drivers/fsi/fsiinit.c | 8 +++
drivers/fsi/ldm.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++-
drivers/fsi/readwrite.c | 4 ++
5 files changed, 188 insertions(+), 2 deletions(-)
diff --git a/drivers/fsi/fsi.h b/drivers/fsi/fsi.h
index 67d4666..f0ed5b7 100644
--- a/drivers/fsi/fsi.h
+++ b/drivers/fsi/fsi.h
@@ -65,6 +65,10 @@ struct fsimap {
#define FSI_DEV_PROBE_OK 9 /* Probe succeeded */
#define FSI_DEV_PROBE_ERR 10 /* Probe failed */
+struct fsi_resource {
+ u32 start; /* Start of range */
+ u32 end; /* End of range */
+};
struct fsidevice {
struct fsi_engine_id id; /* Engine type/version */
@@ -91,4 +95,15 @@ struct fsidriver {
#define to_fsidriver(x) container_of((x), struct fsidriver, driver)
+int fsidev_register(struct fsidevice *, struct device_attribute **);
+void fsidev_unregister(struct fsidevice *);
+int fsi_get_resource(struct fsidevice *, int, struct fsi_resource *);
+char *fsidevice_name(struct fsidevice *, const char *, const char *,
+ char *, size_t);
+int fsidevice_get_minor(struct fsidevice *);
+int fsidrv_register(struct fsidriver *);
+void fsidrv_unregister(struct fsidriver *);
+void fsi_lock_mutex(void);
+void fsi_unlock_mutex(void);
+
#endif /* DRIVERS_FSI_H */
diff --git a/drivers/fsi/fsicfam.h b/drivers/fsi/fsicfam.h
index dde7036..8ccff5c 100644
--- a/drivers/fsi/fsicfam.h
+++ b/drivers/fsi/fsicfam.h
@@ -43,4 +43,6 @@ struct fsicfam { /* CFAM internal structure */
int fsi_cfamirq_request(int, struct fsicfam *);
void fsi_cfamirq_free(struct fsicfam *);
+#define FSI_CFAM_SLAVEIDX 2 /* Slave engine */
+
#endif /* DRIVERS_FSICFAM_H */
diff --git a/drivers/fsi/fsiinit.c b/drivers/fsi/fsiinit.c
index d83992e..4941062 100644
--- a/drivers/fsi/fsiinit.c
+++ b/drivers/fsi/fsiinit.c
@@ -34,6 +34,14 @@ struct fsidd fsidd = { /* FSI device driver structure definition */
.major = MKDEV(FSIDD_MAJOR, 0),
};
+void fsi_lock_mutex(void)
+{
+}
+
+void fsi_unlock_mutex(void)
+{
+}
+
static int fsi_start(void)
{
int rc = 0;
diff --git a/drivers/fsi/ldm.c b/drivers/fsi/ldm.c
index 9fe10b3..cb6679f 100644
--- a/drivers/fsi/ldm.c
+++ b/drivers/fsi/ldm.c
@@ -13,6 +13,48 @@
#include <linux/blkdev.h>
#include "fsi.h"
#include "fsi_private.h"
+#include "fsicfam.h"
+
+/*
+ * /sys/devices/ < name > format:
+ *
+ * fsi-LL.C.EE
+ *
+ * Where:
+ * L = link, C = CFAM, E = engine
+ *
+ * TODO: expand for cascaded and hub masters
+ */
+static void myname(struct fsidevice *fsidev)
+{
+ if (dev_name(&fsidev->dev))
+ return;
+
+ dev_set_name(&fsidev->dev, "fsi-%02d.%d.%02d",
+ fsidev->map.link, fsidev->map.cfam, fsidev->map.eng);
+}
+
+/*
+ * Unregister a FSI device with the Linux device model
+ */
+void fsidev_unregister_nolock(struct fsidevice *fsidev)
+{
+
+ dev_dbg(&fsidev->dev, "fsidev_unregister fsidev:%p\n", fsidev);
+ if (!test_and_clear_bit(FSI_DEV_ACTIVE, &fsidev->state)) {
+ dev_dbg(&fsidev->dev, "fsidev_unregister build incomplete\n");
+ return;
+ }
+ device_unregister(&fsidev->dev);
+}
+
+void fsidev_unregister(struct fsidevice *fsidev)
+{
+ fsi_lock_mutex();
+ fsidev_unregister_nolock(fsidev);
+ fsi_unlock_mutex();
+}
+EXPORT_SYMBOL(fsidev_unregister);
/*
* Register a FSI device with the Linux device model
@@ -20,15 +62,101 @@
int fsidev_register_nolock(struct fsidevice *fsidev,
struct device_attribute **ap)
{
- return 0;
+ int rc, slave_idx;
+
+ dev_dbg(&fsidev->dev, "register >> %d.%d.%d\n",
+ fsidev->map.link, fsidev->map.cfam, fsidev->map.eng);
+ myname(fsidev);
+
+ rc = device_register(&fsidev->dev);
+ if (rc) {
+ dev_dbg(&fsidev->dev, "register dev_register error:%d\n", rc);
+ if (fsidev->dev.release)
+ fsidev->dev.release(&fsidev->dev);
+ return rc;
+ }
+ set_bit(FSI_DEV_ACTIVE, &fsidev->state);
+ if (test_bit(FSI_DEV_PROBE_ERR, &fsidev->state)) {
+ dev_dbg(&fsidev->dev, "register dev probe error\n");
+ slave_idx = (fsidev->map.eng == FSI_CFAM_SLAVEIDX);
+ rc = slave_idx ? -ENODEV : 0;
+ fsidev_unregister_nolock(fsidev);
+ return rc;
+ }
+ dev_dbg(&fsidev->dev, "register << %d.%d.%d success\n",
+ fsidev->map.link, fsidev->map.cfam, fsidev->map.eng);
+
+ return rc;
}
int fsidev_register(struct fsidevice *fsidev, struct device_attribute **ap)
{
- return 0;
+ int rc;
+
+ fsi_lock_mutex();
+ rc = fsidev_register_nolock(fsidev, ap);
+ fsi_unlock_mutex();
+
+ return rc;
}
EXPORT_SYMBOL(fsidev_register);
+int fsi_get_resource(struct fsidevice *fsidev, int idx,
+ struct fsi_resource *res)
+{
+ switch (idx) {
+ case FSI_DEV_IRQRES:
+ if (fsidev->irq_start == 0)
+ res->end = 0;
+ else
+ res->end = fsidev->irq_start + fsidev->irq_range - 1;
+ break;
+ case FSI_DEV_MEMRES:
+ res->start = fsidev->map.va;
+ res->end = res->start + fsidev->map.kb * FSI_ENGINE_SIZE - 1;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return 0;
+}
+EXPORT_SYMBOL(fsi_get_resource);
+
+/*
+ * Return the fsidevice full engine name.
+ * Name consists of:
+ * - prefix
+ * - link number
+ * - a dot '.'
+ * - cfam number
+ * - a dot '.'
+ * - engine number
+ * - postfix (client's choosing)
+ *
+ * Returns NULL if buffer space is insufficient.
+ */
+char *fsidevice_name(struct fsidevice *fsidev, const char *prefix,
+ const char *postfix, char *buf, size_t len)
+{
+ int size;
+ struct fsimap *map = &fsidev->map;
+
+ size = snprintf(buf, len, "%sL%02dC%dE%02dP%s",
+ prefix, map->link, map->cfam, map->eng, postfix);
+
+ return (size >= len) ? NULL : buf;
+}
+EXPORT_SYMBOL(fsidevice_name);
+
+/*
+ * Retrieve minor number for the FSI device
+ */
+int fsidevice_get_minor(struct fsidevice *fsidev)
+{
+ return MINOR(fsidev->dev.devt);
+}
+EXPORT_SYMBOL(fsidevice_get_minor);
+
/*
* FSI bus functions.
*/
@@ -163,3 +291,32 @@ int fsibus_init(void)
{
return bus_register(&fsi_bus_type);
}
+
+/*
+ * Unregister a client FSI driver from the system
+ */
+void fsidrv_unregister(struct fsidriver *fsidrv)
+{
+ fsidrv->reset = NULL;
+ driver_unregister(&fsidrv->driver);
+}
+EXPORT_SYMBOL(fsidrv_unregister);
+
+/*
+ * Register a client FSI driver with the system
+ */
+int fsidrv_register(struct fsidriver *fsidrv)
+{
+ int rc = 0;
+
+ if (fsidrv->idlist == NULL)
+ return -EINVAL;
+ if (!fsidrv->driver.owner)
+ fsidrv->driver.owner = fsidrv->owner;
+
+ fsidrv->driver.bus = &fsi_bus_type;
+ rc = driver_register(&fsidrv->driver);
+
+ return rc;
+}
+EXPORT_SYMBOL(fsidrv_register);
diff --git a/drivers/fsi/readwrite.c b/drivers/fsi/readwrite.c
index 33d972c..3427e52 100644
--- a/drivers/fsi/readwrite.c
+++ b/drivers/fsi/readwrite.c
@@ -14,6 +14,10 @@
#include "fsi_private.h"
#include "fsimaster.h"
+/*
+ * Read/write functions to be used only by the FSI driver itself. FSI clients
+ * will use separate interfaces
+ */
int fsi_readw_int(struct fsimaster *master, u32 pa, u32 *value)
{
return 0;
--
1.8.2.2
More information about the openbmc
mailing list