diff options
author | Ahmad Fatoum <a.fatoum@pengutronix.de> | 2023-01-11 14:29:51 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2023-01-12 15:57:51 +0100 |
commit | 6e7b4e09c6ab0dc691c58855a153965d46b41f06 (patch) | |
tree | adbcdcb258b16b33b7d9ca4b6ea8f9e373324988 /drivers/base | |
parent | cbf1e375816d2e819c01be09f8c7cbc133131d85 (diff) | |
download | barebox-6e7b4e09c6ab0dc691c58855a153965d46b41f06.tar.gz barebox-6e7b4e09c6ab0dc691c58855a153965d46b41f06.tar.xz |
net: dsa: ksz9477: create regmap cdev for switch registers
Now that we use regmap for the KSZ9477 driver, we can make the register
map available for introspection as a device file. As the KSZ driver has
a separate regmap for each of the three access sizes, we add a new
regmap_multi_register_cdev abstraction that multiplexes device file
access to the regmap with the best matching alignment.
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
Link: https://lore.barebox.org/20230111132956.1153359-7-a.fatoum@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/regmap-multi.c | 104 |
2 files changed, 105 insertions, 0 deletions
diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile index d99db42771..fe5beaaaa3 100644 --- a/drivers/base/regmap/Makefile +++ b/drivers/base/regmap/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only obj-y += regmap.o +obj-y += regmap-multi.o obj-y += regmap-mmio.o obj-$(CONFIG_REGMAP_FORMATTED) += regmap-fmt.o obj-$(CONFIG_I2C) += regmap-i2c.o diff --git a/drivers/base/regmap/regmap-multi.c b/drivers/base/regmap/regmap-multi.c new file mode 100644 index 0000000000..e3f5b9aec1 --- /dev/null +++ b/drivers/base/regmap/regmap-multi.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2022 Ahmad Fatoum <a.fatoum@pengutronix.de> + */ + +#include <common.h> +#include <fcntl.h> +#include <regmap.h> +#include <linux/bitfield.h> +#include <linux/export.h> + +#include "internal.h" + +enum { MULTI_MAP_8, MULTI_MAP_16, MULTI_MAP_32, MULTI_MAP_64, MULTI_MAP_COUNT }; +struct regmap_multi { + struct cdev cdev; + struct regmap *map[MULTI_MAP_COUNT]; +}; + +static struct regmap *regmap_multi_cdev_get_map(struct cdev *cdev, unsigned rwsize) +{ + struct regmap_multi *multi = container_of(cdev, struct regmap_multi, cdev); + + switch (rwsize) { + case 8: + return multi->map[MULTI_MAP_64]; + case 4: + return multi->map[MULTI_MAP_32]; + case 2: + return multi->map[MULTI_MAP_16]; + case 1: + return multi->map[MULTI_MAP_8]; + } + + return NULL; +} + +static ssize_t regmap_multi_cdev_read(struct cdev *cdev, void *buf, size_t count, + loff_t offset, unsigned long flags) +{ + unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags); + struct regmap *map; + + map = regmap_multi_cdev_get_map(cdev, rwsize); + if (!map) + return -EINVAL; + + count = ALIGN_DOWN(count, rwsize); + return regmap_bulk_read(map, offset, buf, count) ?: count; +} + +static ssize_t regmap_multi_cdev_write(struct cdev *cdev, const void *buf, size_t count, + loff_t offset, unsigned long flags) +{ + unsigned rwsize = FIELD_GET(O_RWSIZE_MASK, flags); + struct regmap *map; + + map = regmap_multi_cdev_get_map(cdev, rwsize); + if (!map) + return -EINVAL; + + count = ALIGN_DOWN(count, rwsize); + return regmap_bulk_write(map, offset, buf, count) ?: count; +} + +static struct cdev_operations regmap_multi_fops = { + .read = regmap_multi_cdev_read, + .write = regmap_multi_cdev_write, +}; + +int regmap_multi_register_cdev(struct regmap *map8, + struct regmap *map16, + struct regmap *map32, + struct regmap *map64) +{ + struct regmap *maps[MULTI_MAP_COUNT] = { map8, map16, map32, map64 }; + struct regmap_multi *multi; + struct cdev *cdev; + int i; + + multi = xzalloc(sizeof(*multi)); + cdev = &multi->cdev; + + cdev->ops = ®map_multi_fops; + cdev->size = LLONG_MAX; + + for (i = 0; i < MULTI_MAP_COUNT; i++) { + if (!maps[i]) + continue; + + multi->map[i] = maps[i]; + cdev->size = min_t(loff_t, regmap_size_bytes(maps[i]), cdev->size); + cdev->dev = cdev->dev ?: maps[i]->dev; + } + + if (!cdev->dev) { + free(multi); + return -EINVAL; + } + + cdev->name = xstrdup(dev_name(cdev->dev)); + + return devfs_create(cdev); +} |