[PATCH linux dev-4.7 2/5] drivers/fsi: Initialize hub master engine
Christopher Bostic
cbostic at linux.vnet.ibm.com
Fri Feb 17 09:04:31 AEDT 2017
Define hub master probe and add definitions for general
master registers and bitfields.
Signed-off-by: Christopher Bostic <cbostic at linux.vnet.ibm.com>
---
drivers/fsi/fsi-core.c | 44 ++++++++++--
drivers/fsi/fsi-master-gpio.c | 7 ++
drivers/fsi/fsi-master-hub.c | 154 ++++++++++++++++++++++++++++++++++++++++++
drivers/fsi/fsi-master.h | 64 ++++++++++++++++++
include/linux/fsi.h | 6 ++
5 files changed, 269 insertions(+), 6 deletions(-)
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 1e9c5a2..843389e 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -48,13 +48,19 @@ static unsigned int fsi_ipoll_period_ms = 100;
static atomic_t master_idx = ATOMIC_INIT(-1);
+static uint32_t set_smode_defaults(struct fsi_master *master);
+static int fsi_slave_set_smode(struct fsi_master *master, int link, int id);
+static int fsi_master_break(struct fsi_master *master, int link);
+
struct fsi_slave {
struct list_head list_link; /* Master's list of slaves */
struct list_head my_engines;
struct device dev;
- struct fsi_master *master;
+ struct fsi_master *master; /* Upstream master */
+ struct fsi_master *next_master; /* Downstream master */
int link;
uint8_t id;
+ uint32_t base; /* Base address */
};
#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
@@ -112,6 +118,11 @@ int fsi_device_write(struct fsi_device *dev, uint32_t addr, const void *val,
}
EXPORT_SYMBOL_GPL(fsi_device_write);
+struct fsi_master *fsi_get_master(struct fsi_device *fsi_dev)
+{
+ return fsi_dev->slave->master;
+}
+
int fsi_device_peek(struct fsi_device *dev, void *val)
{
uint32_t addr = FSI_PEEK_BASE + ((dev->unit - 2) * sizeof(uint32_t));
@@ -192,14 +203,14 @@ 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);
+ slave->id, slave->base + 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);
+ slave->id, slave->base + addr, val, size);
}
static int fsi_slave_scan(struct fsi_slave *slave)
@@ -251,7 +262,7 @@ static int fsi_slave_scan(struct fsi_slave *slave)
* Unused address areas are marked by a zero type value; this
* skips the defined address areas
*/
- if (type != 0 && slots != 0) {
+ if (type != 0) {
/* create device */
dev = fsi_create_device(slave);
@@ -413,6 +424,7 @@ static int fsi_slave_init(struct fsi_master *master,
slave->id = slave_id;
slave->dev.parent = master->dev;
slave->dev.release = fsi_slave_release;
+ slave->base = link * master->link_size;
dev_set_name(&slave->dev, "slave@%02x:%02x", link, slave_id);
rc = device_register(&slave->dev);
@@ -519,6 +531,24 @@ static void fsi_master_unscan(struct fsi_master *master)
master->slave_list = false;
}
+struct fsi_master *fsi_get_link_master(struct fsi_device *fsi_dev)
+{
+ if (fsi_dev && fsi_dev->slave)
+ return fsi_dev->slave->next_master;
+ return NULL;
+}
+EXPORT_SYMBOL_GPL(fsi_get_link_master);
+
+int fsi_set_next_master(struct fsi_device *fsi_dev, struct fsi_master *master)
+{
+ if (fsi_dev && fsi_dev->slave) {
+ fsi_dev->slave->next_master = master;
+ return 0;
+ }
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(fsi_set_next_master);
+
static void fsi_master_irq(struct fsi_master *master, int link, uint32_t si1s)
{
struct fsi_slave *slave;
@@ -595,7 +625,7 @@ DEVICE_ATTR(fsi_ipoll_period, S_IRUGO | S_IWUSR, fsi_ipoll_period_show,
int fsi_master_register(struct fsi_master *master)
{
- if (!master || !master->dev)
+ if (!master)
return -EINVAL;
master->idx = atomic_inc_return(&master_idx);
@@ -657,8 +687,10 @@ static int fsi_bus_match(struct device *dev, struct device_driver *drv)
if (id->engine_type != fsi_dev->engine_type)
continue;
if (id->version == FSI_VERSION_ANY ||
- id->version == fsi_dev->version)
+ id->version == fsi_dev->version) {
+ fsi_dev->id = id;
return 1;
+ }
}
return 0;
diff --git a/drivers/fsi/fsi-master-gpio.c b/drivers/fsi/fsi-master-gpio.c
index 91bdbf2..92af53d 100644
--- a/drivers/fsi/fsi-master-gpio.c
+++ b/drivers/fsi/fsi-master-gpio.c
@@ -13,6 +13,8 @@
#include "fsi-master.h"
+#define FSI_MASTER_GPIO_TYPE 0x1
+
#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 */
@@ -63,6 +65,8 @@
#define FSI_GPIO_MSG_RESPID_SIZE 2
#define FSI_GPIO_PRIME_SLAVE_CLOCKS 100
+#define FSI_MASTER_GPIO_LINK_SIZE 0x00800000
+
static DEFINE_SPINLOCK(fsi_gpio_cmd_lock); /* lock around fsi commands */
struct fsi_master_gpio {
@@ -512,11 +516,14 @@ static int fsi_master_gpio_probe(struct platform_device *pdev)
else
master->gpio_mux = gpio;
+ master->master.type = FSI_MASTER_GPIO_TYPE;
master->master.n_links = 1;
+ master->master.link_size = FSI_MASTER_GPIO_LINK_SIZE;
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;
+ master->master.dev = &pdev->dev;
platform_set_drvdata(pdev, master);
return device_create_file(&pdev->dev, &dev_attr_scan);
diff --git a/drivers/fsi/fsi-master-hub.c b/drivers/fsi/fsi-master-hub.c
index 19b7c77..e8c0726 100644
--- a/drivers/fsi/fsi-master-hub.c
+++ b/drivers/fsi/fsi-master-hub.c
@@ -18,14 +18,168 @@
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
#include "fsi-master.h"
+#define FSI_MASTER_HUB_TYPE 0x02
+
+#define FSI_MASTER_HUB_MAX_LINKS 8
+#define FSI_MASTER_HUB_LINK_SIZE 0x00080000
+
#define FSI_ENGID_HUB_MASTER 0x1C
#define FSI_ENGID_HUB_LINK 0x1D
+int hub_master_read(struct fsi_master *master, int linkno, uint8_t slave,
+ uint32_t addr, void *val, size_t size)
+{
+ return 0;
+}
+
+int hub_master_write(struct fsi_master *master, int linkno, uint8_t slave,
+ uint32_t addr, const void *val, size_t size)
+{
+ return 0;
+}
+
+int hub_master_break(struct fsi_master *master, int linkno)
+{
+ return 0;
+}
+
+int hub_master_link_enable(struct fsi_master *master, int link)
+{
+ return 0;
+}
+
static int hub_master_probe(struct device *dev)
{
+ struct fsi_device *fsi_dev = to_fsi_dev(dev);
+ struct fsi_master *master;
+ int rc = 0;
+ uint32_t mver;
+
+ master = fsi_get_master(fsi_dev);
+
+ /* Two Hub masters in communication chain not allowed */
+ if (master->type == FSI_MASTER_HUB_TYPE)
+ return 0;
+
+ if (master && (master->n_links >= FSI_MASTER_HUB_MAX_LINKS * 2))
+ return 0;
+
+ if (fsi_dev->id->engine_type == FSI_ENGID_HUB_MASTER) {
+ master = devm_kzalloc(dev, sizeof(*master), GFP_KERNEL);
+ if (!master)
+ return -ENOMEM;
+
+ fsi_dev->master = master;
+
+ /* TODO: can use master->dev to get at the engine possibly */
+ master->engine = fsi_dev;
+ master->read = hub_master_read;
+ master->write = hub_master_write;
+ master->send_break = hub_master_break;
+ master->link_enable = hub_master_link_enable;
+ master->link_size = FSI_MASTER_HUB_LINK_SIZE;
+ rc = fsi_set_next_master(fsi_dev, master);
+
+ /* Initialize the MFSI (hub master) engine */
+ rc = fsi_device_read(master->engine, FSI_MVER, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK
+ | FSI_MRESP_RST_MCR | FSI_MRESP_RST_PYE;
+ rc = fsi_device_write(master->engine, FSI_MRESP0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = FSI_MECTRL_EOAE | FSI_MECTRL_P8_AUTO_TERM;
+ rc = fsi_device_write(master->engine, FSI_MECTRL, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = FSI_MMODE_EIP | FSI_MMODE_ECRC | FSI_MMODE_EPC
+ | fsi_mmode_crs0(1) | fsi_mmode_crs1(1)
+ | FSI_MMODE_P8_TO_LSB;
+ rc = fsi_device_write(master->engine, FSI_MMODE, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = 0xffff0000;
+ rc = fsi_device_write(master->engine, FSI_MDLYR, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = ~0;
+ rc = fsi_device_write(master->engine, FSI_MSENP0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ /* Leave enabled long enough for master logic to set up */
+ udelay(1000);
+
+ rc = fsi_device_write(master->engine, FSI_MCENP0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(master->engine, FSI_MAEB, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = FSI_MRESP_RST_ALL_MASTER | FSI_MRESP_RST_ALL_LINK;
+ rc = fsi_device_write(master->engine, FSI_MRESP0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ rc = fsi_device_read(master->engine, FSI_MLEVP0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ /* Reset the master bridge */
+ mver = FSI_MRESB_RST_GEN;
+ rc = fsi_device_write(master->engine, FSI_MRESB0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+
+ mver = FSI_MRESB_RST_ERR;
+ rc = fsi_device_write(master->engine, FSI_MRESB0, &mver,
+ sizeof(mver));
+ if (rc)
+ return rc;
+ } else {
+ /*
+ * A hub link device.
+ */
+ master = fsi_get_link_master(fsi_dev);
+ if (!master)
+ return -ENODEV;
+
+ if (master->n_links == 0) {
+ /*
+ * CFAM Config space doesn't list correct size
+ * for hub links, fix this.
+ */
+ fsi_dev->size = fsi_dev->addr *
+ FSI_MASTER_HUB_MAX_LINKS;
+ master->link = fsi_dev;
+ }
+ master->n_links++;
+ }
+
return 0;
}
diff --git a/drivers/fsi/fsi-master.h b/drivers/fsi/fsi-master.h
index 3737404..367b8d9 100644
--- a/drivers/fsi/fsi-master.h
+++ b/drivers/fsi/fsi-master.h
@@ -17,8 +17,57 @@
#ifndef DRIVERS_FSI_MASTER_H
#define DRIVERS_FSI_MASTER_H
+#include <linux/fsi.h>
#include <linux/device.h>
+#define FSI_BREAK 0xc0de0000
+
+/* Control Registers */
+#define FSI_MMODE 0x0 /* R/W: mode */
+#define FSI_MDLYR 0x4 /* R/W: delay */
+#define FSI_MCRSP 0x8 /* R/W: clock rate */
+#define FSI_MENP0 0x10 /* R/W: enable */
+#define FSI_MLEVP0 0x18 /* R: plug detect */
+#define FSI_MSENP0 0x18 /* S: Set enable */
+#define FSI_MCENP0 0x20 /* C: Clear enable */
+#define FSI_MAEB 0x70 /* R: Error address */
+#define FSI_MVER 0x74 /* R: master version/type */
+#define FSI_MRESP0 0xd0 /* W: Port reset */
+#define FSI_MESRB0 0x1d0 /* R: Master error status */
+#define FSI_MRESB0 0x1d0 /* W: Reset bridge */
+#define FSI_MECTRL 0x2e0 /* W: Error control */
+
+/* MMODE: Mode control */
+#define FSI_MMODE_EIP 0x80000000 /* Enable interrupt polling */
+#define FSI_MMODE_ECRC 0x40000000 /* Enable error recovery */
+#define FSI_MMODE_EPC 0x10000000 /* Enable parity checking */
+#define FSI_MMODE_P8_TO_LSB 0x00000010 /* Timeout value LSB */
+ /* Rolf Fritz Nov 20, 2013: */
+ /* MSB=1, LSB=0 is 0.8 ms */
+ /* MSB=0, LSB=1 is 0.9 ms */
+#define FSI_MMODE_CRS0SHFT 18 /* Clk rate selection 0 shift */
+#define FSI_MMODE_CRS0MASK 0x3ff /* Clk rate selection 0 mask */
+#define FSI_MMODE_CRS1SHFT 8 /* Clk rate selection 1 shift */
+#define FSI_MMODE_CRS1MASK 0x3ff /* Clk rate selection 1 mask */
+
+/* MRESB: Reset brindge */
+#define FSI_MRESB_RST_GEN 0x80000000 /* General reset */
+#define FSI_MRESB_RST_ERR 0x40000000 /* Error Reset */
+
+/* MRESB: Reset port */
+#define FSI_MRESP_RST_ALL_MASTER 0x20000000 /* Reset all FSI masters */
+#define FSI_MRESP_RST_ALL_LINK 0x10000000 /* Reset all FSI port contr. */
+#define FSI_MRESP_RST_MCR 0x08000000 /* Reset FSI master reg. */
+#define FSI_MRESP_RST_PYE 0x04000000 /* Reset FSI parity error */
+#define FSI_MRESP_RST_ALL 0xfc000000 /* Reset any error */
+
+/* MECTRL: Error control */
+#define FSI_MECTRL_EOAE 0x8000 /* Enable machine check when */
+ /* master 0 in error */
+#define FSI_MECTRL_P8_AUTO_TERM 0x4000 /* Auto terminate */
+
+#define L_MSB_MASK(x) (0x80000000 >> (x))
+
struct fsi_master {
struct list_head my_slaves;
bool slave_list;
@@ -26,6 +75,10 @@ struct fsi_master {
int idx;
int n_links;
uint32_t ipoll;
+ struct fsi_device *engine;
+ struct fsi_device *link;
+ uint8_t type;
+ uint32_t link_size;
int (*read)(struct fsi_master *, int link,
uint8_t slave, uint32_t addr,
void *val, size_t size);
@@ -61,4 +114,15 @@ extern int fsi_master_start_ipoll(struct fsi_master *master);
*/
uint8_t fsi_crc4(uint8_t c, uint64_t x, int bits);
+/* mmode encoders */
+static inline u32 fsi_mmode_crs0(u32 x)
+{
+ return (x & FSI_MMODE_CRS0MASK) << FSI_MMODE_CRS0SHFT;
+}
+
+static inline u32 fsi_mmode_crs1(u32 x)
+{
+ return (x & FSI_MMODE_CRS1MASK) << FSI_MMODE_CRS1SHFT;
+}
+
#endif /* DRIVERS_FSI_MASTER_H */
diff --git a/include/linux/fsi.h b/include/linux/fsi.h
index d22d0c5..9bf0b06 100644
--- a/include/linux/fsi.h
+++ b/include/linux/fsi.h
@@ -20,6 +20,8 @@
struct fsi_device {
struct list_head link; /* for slave's list */
struct device dev;
+ const struct fsi_device_id *id;
+ struct fsi_master *master;
u8 engine_type;
u8 version;
u8 unit;
@@ -71,6 +73,10 @@ extern void fsi_driver_unregister(struct fsi_driver *);
extern struct bus_type fsi_bus_type;
+extern struct fsi_master *fsi_get_link_master(struct fsi_device *dev);
+extern int fsi_set_next_master(struct fsi_device *dev,
+ struct fsi_master *master);
+extern struct fsi_master *fsi_get_master(struct fsi_device *dev);
extern int fsi_enable_irq(struct fsi_device *dev);
extern void fsi_disable_irq(struct fsi_device *dev);
--
1.8.2.2
More information about the openbmc
mailing list