summaryrefslogtreecommitdiff
path: root/drivers/fsi/fsi-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/fsi/fsi-core.c')
-rw-r--r--drivers/fsi/fsi-core.c181
1 files changed, 102 insertions, 79 deletions
diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c
index 59ddc9fd5bca..c6c115993ebc 100644
--- a/drivers/fsi/fsi-core.c
+++ b/drivers/fsi/fsi-core.c
@@ -16,6 +16,8 @@
#include <linux/idr.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
#include <linux/slab.h>
#include <linux/bitops.h>
#include <linux/cdev.h>
@@ -23,6 +25,7 @@
#include <linux/uaccess.h>
#include "fsi-master.h"
+#include "fsi-slave.h"
#define CREATE_TRACE_POINTS
#include <trace/events/fsi.h>
@@ -81,23 +84,6 @@ static const int engine_page_size = 0x400;
static DEFINE_IDA(master_ida);
-struct fsi_slave {
- struct device dev;
- struct fsi_master *master;
- struct cdev cdev;
- int cdev_idx;
- int id; /* FSI address */
- int link; /* FSI link# */
- u32 cfam_id;
- int chip_id;
- uint32_t size; /* size of slave address space */
- u8 t_send_delay;
- u8 t_echo_delay;
-};
-
-#define to_fsi_master(d) container_of(d, struct fsi_master, dev)
-#define to_fsi_slave(d) container_of(d, struct fsi_slave, dev)
-
static const int slave_retries = 2;
static int discard_errors;
@@ -392,8 +378,8 @@ int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
}
EXPORT_SYMBOL_GPL(fsi_slave_write);
-extern int fsi_slave_claim_range(struct fsi_slave *slave,
- uint32_t addr, uint32_t size)
+int fsi_slave_claim_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
{
if (addr + size < addr)
return -EINVAL;
@@ -406,8 +392,8 @@ extern int fsi_slave_claim_range(struct fsi_slave *slave,
}
EXPORT_SYMBOL_GPL(fsi_slave_claim_range);
-extern void fsi_slave_release_range(struct fsi_slave *slave,
- uint32_t addr, uint32_t size)
+void fsi_slave_release_range(struct fsi_slave *slave,
+ uint32_t addr, uint32_t size)
{
}
EXPORT_SYMBOL_GPL(fsi_slave_release_range);
@@ -415,28 +401,18 @@ EXPORT_SYMBOL_GPL(fsi_slave_release_range);
static bool fsi_device_node_matches(struct device *dev, struct device_node *np,
uint32_t addr, uint32_t size)
{
- unsigned int len, na, ns;
- const __be32 *prop;
- uint32_t psize;
-
- na = of_n_addr_cells(np);
- ns = of_n_size_cells(np);
+ u64 paddr, psize;
- if (na != 1 || ns != 1)
+ if (of_property_read_reg(np, 0, &paddr, &psize))
return false;
- prop = of_get_property(np, "reg", &len);
- if (!prop || len != 8)
+ if (paddr != addr)
return false;
- if (of_read_number(prop, 1) != addr)
- return false;
-
- psize = of_read_number(prop + 1, 1);
if (psize != size) {
dev_warn(dev,
- "node %s matches probed address, but not size (got 0x%x, expected 0x%x)",
- of_node_full_name(np), psize, size);
+ "node %pOF matches probed address, but not size (got 0x%llx, expected 0x%x)",
+ np, psize, size);
}
return true;
@@ -524,6 +500,8 @@ static int fsi_slave_scan(struct fsi_slave *slave)
dev->addr = engine_addr;
dev->size = slots * engine_page_size;
+ trace_fsi_dev_init(dev);
+
dev_dbg(&slave->dev,
"engine[%i]: type %x, version %x, addr %x size %x\n",
dev->unit, dev->engine_type, version,
@@ -576,7 +554,7 @@ static unsigned long aligned_access_size(size_t offset, size_t count)
}
static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
- struct kobject *kobj, struct bin_attribute *attr, char *buf,
+ struct kobject *kobj, const struct bin_attribute *attr, char *buf,
loff_t off, size_t count)
{
struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
@@ -603,7 +581,7 @@ static ssize_t fsi_slave_sysfs_raw_read(struct file *file,
}
static ssize_t fsi_slave_sysfs_raw_write(struct file *file,
- struct kobject *kobj, struct bin_attribute *attr,
+ struct kobject *kobj, const struct bin_attribute *attr,
char *buf, loff_t off, size_t count)
{
struct fsi_slave *slave = to_fsi_slave(kobj_to_dev(kobj));
@@ -651,24 +629,12 @@ static void fsi_slave_release(struct device *dev)
static bool fsi_slave_node_matches(struct device_node *np,
int link, uint8_t id)
{
- unsigned int len, na, ns;
- const __be32 *prop;
-
- na = of_n_addr_cells(np);
- ns = of_n_size_cells(np);
-
- /* Ensure we have the correct format for addresses and sizes in
- * reg properties
- */
- if (na != 2 || ns != 0)
- return false;
+ u64 addr;
- prop = of_get_property(np, "reg", &len);
- if (!prop || len != 8)
+ if (of_property_read_reg(np, 0, &addr, NULL))
return false;
- return (of_read_number(prop, 1) == link) &&
- (of_read_number(prop + 1, 1) == id);
+ return addr == (((u64)link << 32) | id);
}
/* Find a matching node for the slave at (link, id). Returns NULL if none
@@ -895,10 +861,10 @@ static const struct attribute_group *cfam_attr_groups[] = {
NULL,
};
-static char *cfam_devnode(struct device *dev, umode_t *mode,
+static char *cfam_devnode(const struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
- struct fsi_slave *slave = to_fsi_slave(dev);
+ const struct fsi_slave *slave = to_fsi_slave(dev);
#ifdef CONFIG_FSI_NEW_DEV_NODE
return kasprintf(GFP_KERNEL, "fsi/cfam%d", slave->cdev_idx);
@@ -913,7 +879,7 @@ static const struct device_type cfam_type = {
.groups = cfam_attr_groups
};
-static char *fsi_cdev_devnode(struct device *dev, umode_t *mode,
+static char *fsi_cdev_devnode(const struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
#ifdef CONFIG_FSI_NEW_DEV_NODE
@@ -947,9 +913,13 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
/* Check if we qualify for legacy numbering */
if (cid >= 0 && cid < 16 && type < 4) {
- /* Try reserving the legacy number */
- id = (cid << 4) | type;
- id = ida_simple_get(&fsi_minor_ida, id, id + 1, GFP_KERNEL);
+ /*
+ * Try reserving the legacy number, which has 0 - 0x3f reserved
+ * in the ida range. cid goes up to 0xf and type contains two
+ * bits, so construct the id with the below two bit shift.
+ */
+ id = (cid << 2) | type;
+ id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
if (id >= 0) {
*out_index = fsi_adjust_index(cid);
*out_dev = fsi_base_dev + id;
@@ -960,8 +930,8 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
return id;
/* Fallback to non-legacy allocation */
}
- id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
- FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
+ id = ida_alloc_range(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
+ FSI_CHAR_MAX_DEVICES - 1, GFP_KERNEL);
if (id < 0)
return id;
*out_index = fsi_adjust_index(id);
@@ -969,16 +939,42 @@ static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
return 0;
}
+static const char *const fsi_dev_type_names[] = {
+ "cfam",
+ "sbefifo",
+ "scom",
+ "occ",
+};
+
int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
dev_t *out_dev, int *out_index)
{
+ if (fdev->dev.of_node) {
+ int aid = of_alias_get_id(fdev->dev.of_node, fsi_dev_type_names[type]);
+
+ if (aid >= 0) {
+ /* Use the same scheme as the legacy numbers. */
+ int id = (aid << 2) | type;
+
+ id = ida_alloc_range(&fsi_minor_ida, id, id, GFP_KERNEL);
+ if (id >= 0) {
+ *out_index = aid;
+ *out_dev = fsi_base_dev + id;
+ return 0;
+ }
+
+ if (id != -ENOSPC)
+ return id;
+ }
+ }
+
return __fsi_get_new_minor(fdev->slave, type, out_dev, out_index);
}
EXPORT_SYMBOL_GPL(fsi_get_new_minor);
void fsi_free_minor(dev_t dev)
{
- ida_simple_remove(&fsi_minor_ida, MINOR(dev));
+ ida_free(&fsi_minor_ida, MINOR(dev));
}
EXPORT_SYMBOL_GPL(fsi_free_minor);
@@ -1006,6 +1002,7 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
crc = crc4(0, cfam_id, 32);
if (crc) {
+ trace_fsi_slave_invalid_cfam(master, link, cfam_id);
dev_warn(&master->dev, "slave %02x:%02x invalid cfam id CRC!\n",
link, id);
return -EIO;
@@ -1080,6 +1077,8 @@ static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
if (rc)
goto err_free;
+ trace_fsi_slave_init(slave);
+
/* Create chardev for userspace access */
cdev_init(&slave->cdev, &cfam_fops);
rc = cdev_device_add(&slave->cdev, &slave->dev);
@@ -1205,6 +1204,7 @@ static int fsi_master_scan(struct fsi_master *master)
{
int link, rc;
+ trace_fsi_master_scan(master, true);
for (link = 0; link < master->n_links; link++) {
rc = fsi_master_link_enable(master, link);
if (rc) {
@@ -1246,6 +1246,7 @@ static int fsi_master_remove_slave(struct device *dev, void *arg)
static void fsi_master_unscan(struct fsi_master *master)
{
+ trace_fsi_master_scan(master, false);
device_for_each_child(&master->dev, NULL, fsi_master_remove_slave);
}
@@ -1308,47 +1309,62 @@ int fsi_master_register(struct fsi_master *master)
struct device_node *np;
mutex_init(&master->scan_lock);
- master->idx = ida_simple_get(&master_ida, 0, INT_MAX, GFP_KERNEL);
- dev_set_name(&master->dev, "fsi%d", master->idx);
+
+ /* Alloc the requested index if it's non-zero */
+ if (master->idx) {
+ master->idx = ida_alloc_range(&master_ida, master->idx,
+ master->idx, GFP_KERNEL);
+ } else {
+ master->idx = ida_alloc(&master_ida, GFP_KERNEL);
+ }
+
+ if (master->idx < 0)
+ return master->idx;
+
+ if (!dev_name(&master->dev))
+ dev_set_name(&master->dev, "fsi%d", master->idx);
+
master->dev.class = &fsi_master_class;
+ mutex_lock(&master->scan_lock);
rc = device_register(&master->dev);
if (rc) {
- ida_simple_remove(&master_ida, master->idx);
- return rc;
+ ida_free(&master_ida, master->idx);
+ goto out;
}
np = dev_of_node(&master->dev);
if (!of_property_read_bool(np, "no-scan-on-init")) {
- mutex_lock(&master->scan_lock);
fsi_master_scan(master);
- mutex_unlock(&master->scan_lock);
}
-
- return 0;
+out:
+ mutex_unlock(&master->scan_lock);
+ return rc;
}
EXPORT_SYMBOL_GPL(fsi_master_register);
void fsi_master_unregister(struct fsi_master *master)
{
- if (master->idx >= 0) {
- ida_simple_remove(&master_ida, master->idx);
- master->idx = -1;
- }
+ int idx = master->idx;
+
+ trace_fsi_master_unregister(master);
mutex_lock(&master->scan_lock);
fsi_master_unscan(master);
+ master->n_links = 0;
mutex_unlock(&master->scan_lock);
+
device_unregister(&master->dev);
+ ida_free(&master_ida, idx);
}
EXPORT_SYMBOL_GPL(fsi_master_unregister);
/* FSI core & Linux bus type definitions */
-static int fsi_bus_match(struct device *dev, struct device_driver *drv)
+static int fsi_bus_match(struct device *dev, const 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_driver *fsi_drv = to_fsi_drv(drv);
const struct fsi_device_id *id;
if (!fsi_drv->id_table)
@@ -1358,8 +1374,14 @@ 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)
- return 1;
+ id->version == fsi_dev->version) {
+ if (drv->of_match_table) {
+ if (of_driver_match_device(dev, drv))
+ return 1;
+ } else {
+ return 1;
+ }
+ }
}
return 0;
@@ -1382,7 +1404,7 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv)
}
EXPORT_SYMBOL_GPL(fsi_driver_unregister);
-struct bus_type fsi_bus_type = {
+const struct bus_type fsi_bus_type = {
.name = "fsi",
.match = fsi_bus_match,
};
@@ -1422,5 +1444,6 @@ static void fsi_exit(void)
}
module_exit(fsi_exit);
module_param(discard_errors, int, 0664);
+MODULE_DESCRIPTION("FSI core driver");
MODULE_LICENSE("GPL");
MODULE_PARM_DESC(discard_errors, "Don't invoke error handling on bus accesses");