Skip to content

Commit

Permalink
fsi: Add new central chardev support
Browse files Browse the repository at this point in the history
The various FSI devices (sbefifo, occ, scom, more to come)
currently use misc devices.

This is problematic as the minor device space for misc is
limited and there can be a lot of them. Also it limits our
ability to move them to a dedicated /dev/fsi directory or
to be smart about device naming and numbering.

It also means we have IDAs on every single of these drivers

This creates a common fsi "device_type" for the optional
/dev/fsi grouping and a dev_t allocator for all FSI devices.

"Legacy" devices get to use a backward compatible numbering
scheme (as long as chip id <16 and there's only one copy
of a given unit type per chip).

A single major number and a single IDA are shared for all
FSI devices.

This doesn't convert the FSI device drivers to use the new
scheme yet, they will be converted individually.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
  • Loading branch information
Benjamin Herrenschmidt committed Jul 26, 2018
1 parent 537052d commit 0ab5fe5
Show file tree
Hide file tree
Showing 3 changed files with 119 additions and 3 deletions.
15 changes: 15 additions & 0 deletions drivers/fsi/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ menuconfig FSI

if FSI

config FSI_NEW_DEV_NODE
bool "Create '/dev/fsi' directory for char devices"
default n
---help---
This option causes char devices created for FSI devices to be
located under a common /dev/fsi/ directory. Set to N unless your
userspace has been updated to handle the new location.

Additionally, it also causes the char device names to be offset
by one so that chip 0 will have /dev/scom1 and chip1 /dev/scom2
to match old userspace expectations.

New userspace will use udev rules to generate predictable access
symlinks in /dev/fsi/by-path when this option is enabled.

config FSI_MASTER_GPIO
tristate "GPIO-based FSI master"
depends on GPIOLIB
Expand Down
95 changes: 93 additions & 2 deletions drivers/fsi/fsi-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,13 @@ struct fsi_slave {
static const int slave_retries = 2;
static int discard_errors;

static dev_t fsi_base_dev;
static DEFINE_IDA(fsi_minor_ida);
#define FSI_CHAR_MAX_DEVICES 0x1000

/* Legacy /dev numbering: 4 devices per chip, 16 chips */
#define FSI_CHAR_LEGACY_TOP 64

static int fsi_master_read(struct fsi_master *master, int link,
uint8_t slave_id, uint32_t addr, void *val, size_t size);
static int fsi_master_write(struct fsi_master *master, int link,
Expand Down Expand Up @@ -627,6 +634,7 @@ static void fsi_slave_release(struct device *dev)
{
struct fsi_slave *slave = to_fsi_slave(dev);

fsi_free_minor(slave->dev.devt);
of_node_put(dev->of_node);
kfree(slave);
}
Expand Down Expand Up @@ -729,6 +737,75 @@ static ssize_t chip_id_show(struct device *dev,

static DEVICE_ATTR_RO(chip_id);

static char *fsi_cdev_devnode(struct device *dev, umode_t *mode,
kuid_t *uid, kgid_t *gid)
{
#ifdef CONFIG_FSI_NEW_DEV_NODE
return kasprintf(GFP_KERNEL, "fsi/%s", dev_name(dev));
#else
return kasprintf(GFP_KERNEL, "%s", dev_name(dev));
#endif
}

const struct device_type fsi_cdev_type = {
.name = "fsi-cdev",
.devnode = fsi_cdev_devnode,
};
EXPORT_SYMBOL_GPL(fsi_cdev_type);

/* Backward compatible /dev/ numbering in "old style" mode */
static int fsi_adjust_index(int index)
{
#ifdef CONFIG_FSI_NEW_DEV_NODE
return index;
#else
return index + 1;
#endif
}

static int __fsi_get_new_minor(struct fsi_slave *slave, enum fsi_dev_type type,
dev_t *out_dev, int *out_index)
{
int cid = slave->chip_id;
int id;

/* 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);
if (id >= 0) {
*out_index = fsi_adjust_index(cid);
*out_dev = fsi_base_dev + id;
return 0;
}
/* Other failure */
if (id != -ENOSPC)
return id;
/* Fallback to non-legacy allocation */
}
id = ida_simple_get(&fsi_minor_ida, FSI_CHAR_LEGACY_TOP,
FSI_CHAR_MAX_DEVICES, GFP_KERNEL);
if (id < 0)
return id;
*out_index = fsi_adjust_index(id);
*out_dev = fsi_base_dev + id;
return 0;
}

int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
dev_t *out_dev, int *out_index)
{
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));
}
EXPORT_SYMBOL_GPL(fsi_free_minor);

static int fsi_slave_init(struct fsi_master *master, int link, uint8_t id)
{
uint32_t chip_id;
Expand Down Expand Up @@ -953,7 +1030,7 @@ static int fsi_slave_remove_device(struct device *dev, void *arg)
static int fsi_master_remove_slave(struct device *dev, void *arg)
{
device_for_each_child(dev, NULL, fsi_slave_remove_device);
device_unregister(dev);
put_device(dev);
return 0;
}

Expand Down Expand Up @@ -1091,13 +1168,27 @@ EXPORT_SYMBOL_GPL(fsi_bus_type);

static int __init fsi_init(void)
{
return bus_register(&fsi_bus_type);
int rc;

rc = alloc_chrdev_region(&fsi_base_dev, 0, FSI_CHAR_MAX_DEVICES, "fsi");
if (rc)
return rc;
rc = bus_register(&fsi_bus_type);
if (rc)
goto fail_bus;
return 0;

fail_bus:
unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
return rc;
}
postcore_initcall(fsi_init);

static void fsi_exit(void)
{
bus_unregister(&fsi_bus_type);
unregister_chrdev_region(fsi_base_dev, FSI_CHAR_MAX_DEVICES);
ida_destroy(&fsi_minor_ida);
}
module_exit(fsi_exit);
module_param(discard_errors, int, 0664);
Expand Down
12 changes: 11 additions & 1 deletion include/linux/fsi.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,18 @@ extern int fsi_slave_read(struct fsi_slave *slave, uint32_t addr,
extern int fsi_slave_write(struct fsi_slave *slave, uint32_t addr,
const void *val, size_t size);

extern struct bus_type fsi_bus_type;
extern const struct device_type fsi_cdev_type;

enum fsi_dev_type {
fsi_dev_cfam,
fsi_dev_sbefifo,
fsi_dev_scom,
fsi_dev_occ
};

extern struct bus_type fsi_bus_type;
extern int fsi_get_new_minor(struct fsi_device *fdev, enum fsi_dev_type type,
dev_t *out_dev, int *out_index);
extern void fsi_free_minor(dev_t dev);

#endif /* LINUX_FSI_H */

0 comments on commit 0ab5fe5

Please sign in to comment.