diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2016-02-08 08:27:03 +0100 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2016-02-08 08:27:03 +0100 |
commit | 7482ab4de107d6fe3136f741ad6a3d423661f125 (patch) | |
tree | 06bf853ab81348f0a4028fb3c41978b61dfb03f6 /drivers | |
parent | c0735348802c29cc46db3758b5e477f2bc8ff058 (diff) | |
parent | 2ee6bb35043de0bc994da4bf3f0139280229c3bd (diff) | |
download | barebox-7482ab4de107d6fe3136f741ad6a3d423661f125.tar.gz barebox-7482ab4de107d6fe3136f741ad6a3d423661f125.tar.xz |
Merge branch 'for-next/regmap'
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/Makefile | 1 | ||||
-rw-r--r-- | drivers/base/regmap/internal.h | 18 | ||||
-rw-r--r-- | drivers/base/regmap/regmap.c | 354 | ||||
-rw-r--r-- | drivers/mfd/mc13xxx.c | 127 |
5 files changed, 430 insertions, 71 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile index e1f1c7a0ad..4bd4217745 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -2,3 +2,4 @@ obj-y += bus.o obj-y += driver.o obj-y += platform.o obj-y += resource.o +obj-y += regmap/
\ No newline at end of file diff --git a/drivers/base/regmap/Makefile b/drivers/base/regmap/Makefile new file mode 100644 index 0000000000..4dc3d8c510 --- /dev/null +++ b/drivers/base/regmap/Makefile @@ -0,0 +1 @@ +obj-y += regmap.o
\ No newline at end of file diff --git a/drivers/base/regmap/internal.h b/drivers/base/regmap/internal.h new file mode 100644 index 0000000000..52df5290a1 --- /dev/null +++ b/drivers/base/regmap/internal.h @@ -0,0 +1,18 @@ + +#include <linux/list.h> + +struct regmap { + struct device_d *dev; + const struct regmap_bus *bus; + const char *name; + void *bus_context; + struct list_head list; + int reg_bits; + int reg_stride; + int pad_bits; + int val_bits; + int val_bytes; + unsigned int max_register; + + struct cdev cdev; +};
\ No newline at end of file diff --git a/drivers/base/regmap/regmap.c b/drivers/base/regmap/regmap.c new file mode 100644 index 0000000000..a042a1a24e --- /dev/null +++ b/drivers/base/regmap/regmap.c @@ -0,0 +1,354 @@ +/* + * Register map access API + * + * Copyright 2016 Sascha Hauer <s.hauer@pengutronix.de> + * + * based on Kernel code: + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Mark Brown <broonie@opensource.wolfsonmicro.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; version 2. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + */ + +#include <common.h> +#include <regmap.h> +#include <malloc.h> +#include <linux/log2.h> + +#include "internal.h" + +static LIST_HEAD(regmaps); + +/* + * regmap_init - initialize and register a regmap + * + * @dev: The device the new regmap belongs to + * @bus: The regmap_bus providing read/write access to the map + * @bus_context: Context pointer for the bus ops + * @config: Configuration options for this map + * + * Returns a pointer to the new map or a ERR_PTR value on failure + */ +struct regmap *regmap_init(struct device_d *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config) +{ + struct regmap *map; + + map = xzalloc(sizeof(*map)); + map->dev = dev; + map->bus = bus; + map->name = config->name; + map->bus_context = bus_context; + map->reg_bits = config->reg_bits; + map->reg_stride = config->reg_stride; + if (!map->reg_stride) + map->reg_stride = 1; + map->pad_bits = config->pad_bits; + map->val_bits = config->val_bits; + map->val_bytes = DIV_ROUND_UP(config->val_bits, 8); + map->max_register = config->max_register; + + list_add_tail(&map->list, ®maps); + + return map; +} + +/* + * regmap_init - initialize and register a regmap + * + * @dev: The device the maps is attached to + * @name: Optional name for the map. If given it must match. + * + * Returns a pointer to the regmap or a ERR_PTR value on failure + */ +struct regmap *dev_get_regmap(struct device_d *dev, const char *name) +{ + struct regmap *map; + + list_for_each_entry(map, ®maps, list) { + if (map->dev != dev) + continue; + if (!name) + return map; + if (!strcmp(map->name, name)) + return map; + } + + return ERR_PTR(-ENOENT); +} + +/* + * of_node_to_regmap - get a regmap from a device node + * + * node: The device node + * + * Returns a pointer to the regmap or a ERR_PTR if the node has no + * regmap attached. + */ +struct regmap *of_node_to_regmap(struct device_node *node) +{ + struct regmap *map; + + list_for_each_entry(map, ®maps, list) { + if (map->dev->device_node == node) + return map; + } + + return ERR_PTR(-ENOENT); +} + +/* + * regmap_write - write a register in a map + * + * @map: The map + * @reg: The register offset of the register + * @val: The value to be written + * + * Returns 0 for success or negative error code on failure + */ +int regmap_write(struct regmap *map, unsigned int reg, unsigned int val) +{ + return map->bus->reg_write(map->bus_context, reg, val); +} + +/* + * regmap_write - read a register from a map + * + * @map: The map + * @reg: The register offset of the register + * @val: pointer to value read + * + * Returns 0 for success or negative error code on failure + */ +int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val) +{ + return map->bus->reg_read(map->bus_context, reg, val); +} + +/** + * regmap_bulk_read(): Read data from the device + * + * @map: Register map to read from + * @reg: First register to be read from + * @val: Pointer to store read value + * @val_len: Size of data to read + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, + size_t val_len) +{ + size_t val_bytes = map->val_bytes; + size_t val_count = val_len / val_bytes; + unsigned int v; + int ret, i; + + if (val_len % val_bytes) + return -EINVAL; + if (!IS_ALIGNED(reg, map->reg_stride)) + return -EINVAL; + if (val_count == 0) + return -EINVAL; + + for (i = 0; i < val_count; i++) { + u32 *u32 = val; + u16 *u16 = val; + u8 *u8 = val; + + ret = regmap_read(map, reg + (i * map->reg_stride), &v); + if (ret != 0) + goto out; + + switch (map->val_bytes) { + case 4: + u32[i] = v; + break; + case 2: + u16[i] = v; + break; + case 1: + u8[i] = v; + break; + default: + return -EINVAL; + } + } + + out: + return ret; +} + +/** + * regmap_bulk_write(): Write values to one or more registers + * + * @map: Register map to write to + * @reg: Initial register to write to + * @val: Block of data to be written, laid out for direct transmission to the + * device + * @val_len: Length of data pointed to by val. + * + * A value of zero will be returned on success, a negative errno will + * be returned in error cases. + */ +int regmap_bulk_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len) +{ + size_t val_bytes = map->val_bytes; + size_t val_count = val_len / val_bytes; + int ret, i; + + if (val_len % val_bytes) + return -EINVAL; + if (!IS_ALIGNED(reg, map->reg_stride)) + return -EINVAL; + if (val_count == 0) + return -EINVAL; + + for (i = 0; i < val_count; i++) { + unsigned int ival; + + switch (val_bytes) { + case 1: + ival = *(u8 *)(val + (i * val_bytes)); + break; + case 2: + ival = *(u16 *)(val + (i * val_bytes)); + break; + case 4: + ival = *(u32 *)(val + (i * val_bytes)); + break; + default: + ret = -EINVAL; + goto out; + } + + ret = regmap_write(map, reg + (i * map->reg_stride), + ival); + if (ret != 0) + goto out; + } + + out: + return ret; +} + +int regmap_get_val_bytes(struct regmap *map) +{ + return map->val_bytes; +} + +int regmap_get_max_register(struct regmap *map) +{ + return map->max_register; +} + +int regmap_get_reg_stride(struct regmap *map) +{ + return map->reg_stride; +} + +static int regmap_round_val_bytes(struct regmap *map) +{ + int val_bytes; + + val_bytes = roundup_pow_of_two(map->val_bits) >> 3; + if (!val_bytes) + val_bytes = 1; + + return val_bytes; +} + +static ssize_t regmap_cdev_read(struct cdev *cdev, void *buf, size_t count, loff_t offset, + unsigned long flags) +{ + struct regmap *map = container_of(cdev, struct regmap, cdev); + int ret; + + ret = regmap_bulk_read(map, offset, buf, count); + if (ret) + return ret; + + return count; +} + +static ssize_t regmap_cdev_write(struct cdev *cdev, const void *buf, size_t count, loff_t offset, + unsigned long flags) +{ + struct regmap *map = container_of(cdev, struct regmap, cdev); + int ret; + + ret = regmap_bulk_write(map, offset, buf, count); + if (ret) + return ret; + + return count; +} + +static struct file_operations regmap_fops = { + .lseek = dev_lseek_default, + .read = regmap_cdev_read, + .write = regmap_cdev_write, +}; + +/* + * regmap_register_cdev - register a devfs file for a regmap + * + * @map: The map + * @name: Optional name of the device file + * + * Returns 0 for success or negative error code on failure + */ +int regmap_register_cdev(struct regmap *map, const char *name) +{ + int ret; + + if (map->cdev.name) + return -EBUSY; + + if (!map->max_register) + return -EINVAL; + + if (name) { + map->cdev.name = xstrdup(name); + } else { + if (map->name) + map->cdev.name = xasprintf("%s-%s", dev_name(map->dev), map->name); + else + map->cdev.name = xstrdup(dev_name(map->dev)); + } + + map->cdev.size = regmap_round_val_bytes(map) * (map->max_register + 1) / + map->reg_stride; + map->cdev.dev = map->dev; + map->cdev.ops = ®map_fops; + + ret = devfs_create(&map->cdev); + if (ret) + return ret; + + return 0; +} + +void regmap_exit(struct regmap *map) +{ + list_del(&map->list); + + if (map->cdev.name) { + devfs_remove(&map->cdev); + free(map->cdev.name); + } + + free(map); +}
\ No newline at end of file diff --git a/drivers/mfd/mc13xxx.c b/drivers/mfd/mc13xxx.c index 0ebfc9e153..68d70c53d5 100644 --- a/drivers/mfd/mc13xxx.c +++ b/drivers/mfd/mc13xxx.c @@ -22,6 +22,7 @@ #include <errno.h> #include <malloc.h> #include <of.h> +#include <regmap.h> #include <i2c/i2c.h> #include <spi/spi.h> @@ -29,15 +30,16 @@ #define DRIVERNAME "mc13xxx" +#define MC13XXX_NUMREGS 0x3f + struct mc13xxx { - struct cdev cdev; + struct device_d *dev; + struct regmap *map; union { struct i2c_client *client; struct spi_device *spi; }; int revision; - int (*reg_read)(struct mc13xxx*, u8, u32*); - int (*reg_write)(struct mc13xxx*, u8, u32); }; struct mc13xxx_devtype { @@ -100,8 +102,9 @@ static int spi_rw(struct spi_device *spi, void * buf, size_t len) #define MXC_PMIC_REG_NUM(reg) (((reg) & 0x3f) << 25) #define MXC_PMIC_WRITE (1 << 31) -static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx, u8 reg, u32 *val) +static int mc13xxx_spi_reg_read(void *ctx, unsigned int reg, unsigned int *val) { + struct mc13xxx *mc13xxx = ctx; uint32_t buf; buf = MXC_PMIC_REG_NUM(reg); @@ -113,8 +116,9 @@ static int mc13xxx_spi_reg_read(struct mc13xxx *mc13xxx, u8 reg, u32 *val) return 0; } -static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, u8 reg, u32 val) +static int mc13xxx_spi_reg_write(void *ctx, unsigned int reg, unsigned int val) { + struct mc13xxx *mc13xxx = ctx; uint32_t buf = MXC_PMIC_REG_NUM(reg) | MXC_PMIC_WRITE | (val & 0xffffff); spi_rw(mc13xxx->spi, &buf, 4); @@ -124,8 +128,9 @@ static int mc13xxx_spi_reg_write(struct mc13xxx *mc13xxx, u8 reg, u32 val) #endif #ifdef CONFIG_I2C -static int mc13xxx_i2c_reg_read(struct mc13xxx *mc13xxx, u8 reg, u32 *val) +static int mc13xxx_i2c_reg_read(void *ctx, unsigned int reg, unsigned int *val) { + struct mc13xxx *mc13xxx = ctx; u8 buf[3]; int ret; @@ -135,8 +140,9 @@ static int mc13xxx_i2c_reg_read(struct mc13xxx *mc13xxx, u8 reg, u32 *val) return ret == 3 ? 0 : ret; } -static int mc13xxx_i2c_reg_write(struct mc13xxx *mc13xxx, u8 reg, u32 val) +static int mc13xxx_i2c_reg_write(void *ctx, unsigned int reg, unsigned int val) { + struct mc13xxx *mc13xxx = ctx; u8 buf[] = { val >> 16, val >> 8, @@ -152,13 +158,13 @@ static int mc13xxx_i2c_reg_write(struct mc13xxx *mc13xxx, u8 reg, u32 val) int mc13xxx_reg_write(struct mc13xxx *mc13xxx, u8 reg, u32 val) { - return mc13xxx->reg_write(mc13xxx, reg, val); + return regmap_write(mc13xxx->map, reg, val); } EXPORT_SYMBOL(mc13xxx_reg_write); int mc13xxx_reg_read(struct mc13xxx *mc13xxx, u8 reg, u32 *val) { - return mc13xxx->reg_read(mc13xxx, reg, val); + return regmap_read(mc13xxx->map, reg, val); } EXPORT_SYMBOL(mc13xxx_reg_read); @@ -177,54 +183,6 @@ int mc13xxx_set_bits(struct mc13xxx *mc13xxx, u8 reg, u32 mask, u32 val) } EXPORT_SYMBOL(mc13xxx_set_bits); -static ssize_t mc_read(struct cdev *cdev, void *_buf, size_t count, loff_t offset, ulong flags) -{ - struct mc13xxx *mc13xxx = to_mc13xxx(cdev); - u32 *buf = _buf; - size_t i = count >> 2; - int err; - - offset >>= 2; - - while (i) { - err = mc13xxx_reg_read(mc13xxx, offset, buf); - if (err) - return (ssize_t)err; - buf++; - i--; - offset++; - } - - return count; -} - -static ssize_t mc_write(struct cdev *cdev, const void *_buf, size_t count, loff_t offset, ulong flags) -{ - struct mc13xxx *mc13xxx = to_mc13xxx(cdev); - const u32 *buf = _buf; - size_t i = count >> 2; - int err; - - offset >>= 2; - - while (i) { - err = mc13xxx_reg_write(mc13xxx, offset, *buf); - if (err) - return (ssize_t)err; - buf++; - i--; - offset++; - } - - return count; -} - -static struct file_operations mc_fops = { - .lseek = dev_lseek_default, - .read = mc_read, - .write = mc_write, -}; - static int __init mc13783_revision(struct mc13xxx *mc13xxx) { unsigned int rev_id; @@ -245,7 +203,7 @@ static int __init mc13783_revision(struct mc13xxx *mc13xxx) } else sprintf(revstr, "%d.%d", rev / 0x10, rev % 10); - dev_info(mc_dev->cdev.dev, "Found MC13783 ID: 0x%06x [Rev: %s]\n", + dev_info(mc_dev->dev, "Found MC13783 ID: 0x%06x [Rev: %s]\n", rev_id, revstr); return rev; @@ -297,7 +255,7 @@ static int __init mc13892_revision(struct mc13xxx *mc13xxx) } } - dev_info(mc_dev->cdev.dev, "Found MC13892 ID: 0x%06x [Rev: %s]\n", + dev_info(mc_dev->dev, "Found MC13892 ID: 0x%06x [Rev: %s]\n", rev_id, revstr); return rev; @@ -312,11 +270,38 @@ static int __init mc34708_revision(struct mc13xxx *mc13xxx) if (rev_id > 0xfff) return -ENODEV; - dev_info(mc_dev->cdev.dev, "Found MC34708 ID: 0x%03x\n", rev_id); + dev_info(mc_dev->dev, "Found MC34708 ID: 0x%03x\n", rev_id); return (int)rev_id; } +#ifdef CONFIG_SPI +static struct regmap_bus regmap_mc13xxx_spi_bus = { + .reg_write = mc13xxx_spi_reg_write, + .reg_read = mc13xxx_spi_reg_read, +}; + +static const struct regmap_config mc13xxx_regmap_spi_config = { + .reg_bits = 7, + .pad_bits = 1, + .val_bits = 24, + .max_register = MC13XXX_NUMREGS, +}; +#endif + +#ifdef CONFIG_I2C +static struct regmap_bus regmap_mc13xxx_i2c_bus = { + .reg_write = mc13xxx_i2c_reg_write, + .reg_read = mc13xxx_i2c_reg_read, +}; + +static const struct regmap_config mc13xxx_regmap_i2c_config = { + .reg_bits = 8, + .val_bits = 24, + .max_register = MC13XXX_NUMREGS, +}; +#endif + static int __init mc13xxx_probe(struct device_d *dev) { struct mc13xxx_devtype *devtype; @@ -330,13 +315,14 @@ static int __init mc13xxx_probe(struct device_d *dev) return ret; mc_dev = xzalloc(sizeof(*mc_dev)); - mc_dev->cdev.name = DRIVERNAME; + mc_dev->dev = dev; #ifdef CONFIG_I2C if (dev->bus == &i2c_bus) { mc_dev->client = to_i2c_client(dev); - mc_dev->reg_read = mc13xxx_i2c_reg_read; - mc_dev->reg_write = mc13xxx_i2c_reg_write; + + mc_dev->map = regmap_init(dev, ®map_mc13xxx_i2c_bus, + mc_dev, &mc13xxx_regmap_i2c_config); } #endif #ifdef CONFIG_SPI @@ -345,25 +331,24 @@ static int __init mc13xxx_probe(struct device_d *dev) mc_dev->spi->mode = SPI_MODE_0 | SPI_CS_HIGH; mc_dev->spi->bits_per_word = 32; mc_dev->spi->max_speed_hz = 20000000; - mc_dev->reg_read = mc13xxx_spi_reg_read; - mc_dev->reg_write = mc13xxx_spi_reg_write; + mc_dev->map = regmap_init(dev, ®map_mc13xxx_spi_bus, + mc_dev, &mc13xxx_regmap_spi_config); } #endif - mc_dev->cdev.size = 256; - mc_dev->cdev.dev = dev; - mc_dev->cdev.ops = &mc_fops; - rev = devtype->revision(mc_dev); if (rev < 0) { - dev_err(mc_dev->cdev.dev, "No PMIC detected.\n"); + dev_err(mc_dev->dev, "No PMIC detected.\n"); free(mc_dev); mc_dev = NULL; return rev; } mc_dev->revision = rev; - devfs_create(&mc_dev->cdev); + + ret = regmap_register_cdev(mc_dev->map, NULL); + if (ret) + return ret; if (mc13xxx_init_callback) mc13xxx_init_callback(mc_dev); |