diff options
Diffstat (limited to 'drivers/fsi/fsi-core.c')
| -rw-r--r-- | drivers/fsi/fsi-core.c | 181 |
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"); |
