diff --git a/arch/arm/mach-imx/iim.c b/arch/arm/mach-imx/iim.c index c5751fe..c16a6c6 100644 --- a/arch/arm/mach-imx/iim.c +++ b/arch/arm/mach-imx/iim.c @@ -26,6 +26,7 @@ #include #include #include +#include #include #include @@ -40,14 +41,14 @@ struct iim_priv; struct iim_bank { - struct cdev cdev; + struct regmap *map; void __iomem *bankbase; int bank; struct iim_priv *iim; + struct regmap_config map_config; }; struct iim_priv { - struct cdev cdev; struct device_d dev; void __iomem *base; void __iomem *bankbase; @@ -102,34 +103,28 @@ return readb(reg_base + IIM_SDAT); } -static ssize_t imx_iim_cdev_read(struct cdev *cdev, void *buf, size_t count, - loff_t offset, ulong flags) +static int imx_iim_reg_read(void *ctx, unsigned int reg, unsigned int *val) { - ulong size, i; - struct iim_bank *bank = container_of(cdev, struct iim_bank, cdev); + struct iim_bank *bank = ctx; - size = min((loff_t)count, 32 - offset); if (bank->iim->sense_enable) { - for (i = 0; i < size; i++) { - int row_val; - - row_val = imx_iim_fuse_sense(bank, offset + i); - if (row_val < 0) - return row_val; - ((u8 *)buf)[i] = (u8)row_val; - } + int row_val; + row_val = imx_iim_fuse_sense(bank, reg); + if (row_val < 0) + return row_val; + *val = (u8)row_val; } else { - for (i = 0; i < size; i++) - ((u8 *)buf)[i] = ((u8 *)bank->bankbase)[(offset+i)*4]; + *val = readl(bank->bankbase + reg * 4); } - return size; + return 0; } int imx_iim_read(unsigned int banknum, int offset, void *buf, int count) { struct iim_priv *iim = imx_iim; struct iim_bank *bank; + int ret; if (!imx_iim) return -ENODEV; @@ -139,7 +134,11 @@ bank = iim->bank[banknum]; - return imx_iim_cdev_read(&bank->cdev, buf, count, offset, 0); + ret = regmap_bulk_read(bank->map, offset, buf, count); + if (ret) + return ret; + + return count; } static int imx_iim_fuse_blow_one(struct iim_bank *bank, unsigned int row, u8 value) @@ -198,11 +197,10 @@ return ret; } -static int imx_iim_fuse_blow(struct iim_bank *bank, unsigned offset, const void *buf, - unsigned size) +static int imx_iim_fuse_blow(struct iim_bank *bank, unsigned offset, unsigned val) { struct iim_priv *iim = bank->iim; - int ret, i; + int ret; if (IS_ERR(iim->fuse_supply)) { iim->fuse_supply = regulator_get(iim->dev.parent, "vdd-fuse"); @@ -218,11 +216,9 @@ if (iim->supply) iim->supply(1); - for (i = 0; i < size; i++) { - ret = imx_iim_fuse_blow_one(bank, offset + i, ((u8 *)buf)[i]); - if (ret < 0) - goto err_out; - } + ret = imx_iim_fuse_blow_one(bank, offset, val); + if (ret < 0) + goto err_out; if (iim->supply) iim->supply(0); @@ -235,52 +231,60 @@ return ret; } -static ssize_t imx_iim_cdev_write(struct cdev *cdev, const void *buf, size_t count, - loff_t offset, ulong flags) +static ssize_t imx_iim_reg_write(void *ctx, unsigned int reg, unsigned int val) { - ulong size, i; - struct iim_bank *bank = container_of(cdev, struct iim_bank, cdev); + struct iim_bank *bank = ctx; - size = min((loff_t)count, 32 - offset); + if (IS_ENABLED(CONFIG_IMX_IIM_FUSE_BLOW) && bank->iim->write_enable) + return imx_iim_fuse_blow(bank, reg, val); + else + writel(val, bank->bankbase + reg * 4); - if (IS_ENABLED(CONFIG_IMX_IIM_FUSE_BLOW) && bank->iim->write_enable) { - return imx_iim_fuse_blow(bank, offset, buf, size); - } else { - for (i = 0; i < size; i++) - ((u8 *)bank->bankbase)[(offset+i)*4] = ((u8 *)buf)[i]; - } - - return size; + return 0; } -static struct file_operations imx_iim_ops = { - .read = imx_iim_cdev_read, - .write = imx_iim_cdev_write, - .lseek = dev_lseek_default, +static struct regmap_bus imx_iim_regmap_bus = { + .reg_write = imx_iim_reg_write, + .reg_read = imx_iim_reg_read, }; static int imx_iim_add_bank(struct iim_priv *iim, int num) { struct iim_bank *bank; - struct cdev *cdev; + char *name; + int ret; bank = xzalloc(sizeof (*bank)); bank->bankbase = iim->base + 0x800 + 0x400 * num; bank->bank = num; bank->iim = iim; - cdev = &bank->cdev; - cdev->ops = &imx_iim_ops; - cdev->size = 32; - cdev->name = asprintf(DRIVERNAME "_bank%d", num); - if (cdev->name == NULL) - return -ENOMEM; iim->bank[num] = bank; - return devfs_create(cdev); + bank->map_config.reg_bits = 8, + bank->map_config.val_bits = 8, + bank->map_config.reg_stride = 1, + bank->map_config.max_register = 31, + bank->map_config.name = xasprintf("bank%d", num); + + bank->map = regmap_init(&iim->dev, &imx_iim_regmap_bus, bank, &bank->map_config); + if (IS_ERR(bank->map)) + return PTR_ERR(bank->map); + + name = xasprintf(DRIVERNAME "_bank%d", num); + + ret = regmap_register_cdev(bank->map, name); + + free(name); + + if (ret) + return ret; + + return 0; } + #if IS_ENABLED(CONFIG_OFDEVICE) #define MAC_BYTES 6 @@ -297,7 +301,7 @@ struct iim_bank *bank = iimmac->bank; int ret; - ret = imx_iim_cdev_read(&bank->cdev, iimmac->ethaddr, MAC_BYTES, iimmac->offset, 0); + ret = regmap_bulk_read(bank->map, iimmac->offset, iimmac->ethaddr, MAC_BYTES); if (ret < 0) return ret; @@ -310,7 +314,7 @@ struct iim_bank *bank = iimmac->bank; int ret; - ret = imx_iim_cdev_write(&bank->cdev, iimmac->ethaddr, MAC_BYTES, iimmac->offset, 0); + ret = regmap_bulk_write(bank->map, iimmac->offset, iimmac->ethaddr, MAC_BYTES); if (ret < 0) return ret; @@ -364,12 +368,11 @@ bank = be32_to_cpup(prop++); offset = be32_to_cpup(prop++); - ret = imx_iim_read(bank, offset, mac, 6); - if (ret == 6) { - of_eth_register_ethaddr(rnode, mac); - } else { + ret = regmap_bulk_read(iim->bank[bank]->map, offset, mac, MAC_BYTES); + if (ret) dev_err(dev, "cannot read: %s\n", strerror(-ret)); - } + else + of_eth_register_ethaddr(rnode, mac); if (IS_ENABLED(CONFIG_NET)) imx_iim_add_mac_param(iim, macnum, bank, offset); diff --git a/arch/arm/mach-imx/ocotp.c b/arch/arm/mach-imx/ocotp.c index c99a003..e2f10e1 100644 --- a/arch/arm/mach-imx/ocotp.c +++ b/arch/arm/mach-imx/ocotp.c @@ -26,6 +26,7 @@ #include #include #include +#include #include /* @@ -35,6 +36,7 @@ #define MAC_ADDRESS_PROPLEN (2 * sizeof(__be32)) /* OCOTP Registers offsets */ +#define OCOTP_CTRL 0x00 #define OCOTP_CTRL_SET 0x04 #define OCOTP_CTRL_CLR 0x08 #define OCOTP_TIMING 0x10 @@ -70,14 +72,19 @@ #define MAC_OFFSET (0x22 * 4) #define MAC_BYTES 8 +struct imx_ocotp_data { + int num_regs; +}; + struct ocotp_priv { - struct cdev cdev; + struct regmap *map; void __iomem *base; struct clk *clk; struct device_d dev; int permanent_write_enable; int sense_enable; char ethaddr[6]; + struct regmap_config map_config; }; static int imx6_ocotp_set_timing(struct ocotp_priv *priv) @@ -101,18 +108,13 @@ return 0; } -static int imx6_ocotp_wait_busy(u32 flags, struct ocotp_priv *priv) +static int imx6_ocotp_wait_busy(struct ocotp_priv *priv, u32 flags) { uint64_t start = get_time_ns(); - while ((OCOTP_CTRL_BUSY | OCOTP_CTRL_ERROR | flags) & - readl(priv->base)) { - if (is_timeout(start, MSECOND)) { - /* Clear ERROR bit */ - writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR); + while (readl(priv->base + OCOTP_CTRL) & (OCOTP_CTRL_BUSY | flags)) + if (is_timeout(start, MSECOND)) return -ETIMEDOUT; - } - } return 0; } @@ -125,103 +127,94 @@ if (ret) return ret; - ret = imx6_ocotp_wait_busy(0, priv); + ret = imx6_ocotp_wait_busy(priv, 0); if (ret) return ret; return 0; } -static int fuse_read_addr(u32 addr, u32 *pdata, struct ocotp_priv *priv) +static int fuse_read_addr(struct ocotp_priv *priv, u32 addr, u32 *pdata) { u32 ctrl_reg; int ret; - ctrl_reg = readl(priv->base); + writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR); + + ctrl_reg = readl(priv->base + OCOTP_CTRL); ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK; ctrl_reg &= ~OCOTP_CTRL_WR_UNLOCK_MASK; ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR); - writel(ctrl_reg, priv->base); + writel(ctrl_reg, priv->base + OCOTP_CTRL); writel(OCOTP_READ_CTRL_READ_FUSE, priv->base + OCOTP_READ_CTRL); - ret = imx6_ocotp_wait_busy(0, priv); + ret = imx6_ocotp_wait_busy(priv, 0); if (ret) return ret; - *pdata = readl(priv->base + OCOTP_READ_FUSE_DATA); + if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR) + *pdata = 0xbadabada; + else + *pdata = readl(priv->base + OCOTP_READ_FUSE_DATA); return 0; } -int imx6_ocotp_read_one_u32(u32 index, u32 *pdata, struct ocotp_priv *priv) +int imx6_ocotp_read_one_u32(struct ocotp_priv *priv, u32 index, u32 *pdata) { int ret; ret = imx6_ocotp_prepare(priv); if (ret) { - dev_err(priv->cdev.dev, "failed to prepare read fuse 0x%08x\n", + dev_err(&priv->dev, "failed to prepare read fuse 0x%08x\n", index); return ret; } - ret = fuse_read_addr(index, pdata, priv); + ret = fuse_read_addr(priv, index, pdata); if (ret) { - dev_err(priv->cdev.dev, "failed to read fuse 0x%08x\n", index); + dev_err(&priv->dev, "failed to read fuse 0x%08x\n", index); return ret; } - if (readl(priv->base) & OCOTP_CTRL_ERROR) { - dev_err(priv->cdev.dev, "bad read status at fuse 0x%08x\n", index); - return -EFAULT; - } - return 0; } -static ssize_t imx6_ocotp_cdev_read(struct cdev *cdev, void *buf, - size_t count, loff_t offset, unsigned long flags) +static int imx_ocotp_reg_read(void *ctx, unsigned int reg, unsigned int *val) { + struct ocotp_priv *priv = ctx; u32 index; - ssize_t read_count = 0; - int ret, i; - struct ocotp_priv *priv = container_of(cdev, struct ocotp_priv, cdev); + int ret; - index = offset >> 2; - count >>= 2; + index = reg >> 2; - if (count > (FUSE_REGS_COUNT - index)) - count = FUSE_REGS_COUNT - index - 1; - - for (i = index; i < (index + count); i++) { - if (priv->sense_enable) { - ret = imx6_ocotp_read_one_u32(i, buf, priv); - if (ret) - return ret; - } else { - *(u32 *)buf = readl(priv->base + 0x400 + i * 0x10); - } - - buf += 4; - read_count++; + if (priv->sense_enable) { + ret = imx6_ocotp_read_one_u32(priv, index, val); + if (ret) + return ret; + } else { + *(u32 *)val = readl(priv->base + 0x400 + index * 0x10); } - return read_count << 2; + return 0; } -static int fuse_blow_addr(u32 addr, u32 value, struct ocotp_priv *priv) +static int fuse_blow_addr(struct ocotp_priv *priv, u32 addr, u32 value) { u32 ctrl_reg; int ret; + writel(OCOTP_CTRL_ERROR, priv->base + OCOTP_CTRL_CLR); + /* Control register */ - ctrl_reg = readl(priv->base); + ctrl_reg = readl(priv->base + OCOTP_CTRL); ctrl_reg &= ~OCOTP_CTRL_ADDR_MASK; ctrl_reg |= BF(addr, OCOTP_CTRL_ADDR); ctrl_reg |= BF(OCOTP_CTRL_WR_UNLOCK_KEY, OCOTP_CTRL_WR_UNLOCK); - writel(ctrl_reg, priv->base); + writel(ctrl_reg, priv->base + OCOTP_CTRL); writel(value, priv->base + OCOTP_DATA); - ret = imx6_ocotp_wait_busy(0, priv); + ret = imx6_ocotp_wait_busy(priv, 0); if (ret) return ret; @@ -232,94 +225,63 @@ static int imx6_ocotp_reload_shadow(struct ocotp_priv *priv) { - dev_info(priv->cdev.dev, "reloading shadow registers...\n"); + dev_info(&priv->dev, "reloading shadow registers...\n"); writel(OCOTP_CTRL_RELOAD_SHADOWS, priv->base + OCOTP_CTRL_SET); udelay(1); - return imx6_ocotp_wait_busy(OCOTP_CTRL_RELOAD_SHADOWS, priv); + return imx6_ocotp_wait_busy(priv, OCOTP_CTRL_RELOAD_SHADOWS); } -int imx6_ocotp_blow_one_u32(u32 index, u32 data, u32 *pfused_value, - struct ocotp_priv *priv) +int imx6_ocotp_blow_one_u32(struct ocotp_priv *priv, u32 index, u32 data, + u32 *pfused_value) { int ret; ret = imx6_ocotp_prepare(priv); if (ret) { - dev_err(priv->cdev.dev, "prepare to write failed\n"); + dev_err(&priv->dev, "prepare to write failed\n"); return ret; } - ret = fuse_blow_addr(index, data, priv); + ret = fuse_blow_addr(priv, index, data); if (ret) { - dev_err(priv->cdev.dev, "fuse blow failed\n"); + dev_err(&priv->dev, "fuse blow failed\n"); return ret; } - if (readl(priv->base) & OCOTP_CTRL_ERROR) { - dev_err(priv->cdev.dev, "bad write status\n"); + if (readl(priv->base + OCOTP_CTRL) & OCOTP_CTRL_ERROR) { + dev_err(&priv->dev, "bad write status\n"); return -EFAULT; } - ret = imx6_ocotp_read_one_u32(index, pfused_value, priv); + ret = imx6_ocotp_read_one_u32(priv, index, pfused_value); return ret; } -static ssize_t imx6_ocotp_cdev_write(struct cdev *cdev, const void *buf, - size_t count, loff_t offset, unsigned long flags) +static int imx_ocotp_reg_write(void *ctx, unsigned int reg, unsigned int val) { - struct ocotp_priv *priv = cdev->priv; - int index, i; - ssize_t write_count = 0; - const u32 *data; + struct ocotp_priv *priv = ctx; + int index; u32 pfuse; int ret; - /* We could do better, but currently this is what's implemented */ - if (offset & 0x3 || count & 0x3) { - dev_err(cdev->dev, "only u32 aligned writes allowed\n"); - return -EINVAL; + index = reg >> 2; + + if (priv->permanent_write_enable) { + ret = imx6_ocotp_blow_one_u32(priv, index, val, &pfuse); + if (ret < 0) + return ret; + } else { + writel(val, priv->base + 0x400 + index * 0x10); } - index = offset >> 2; - count >>= 2; - - if (count > (FUSE_REGS_COUNT - index)) - count = FUSE_REGS_COUNT - index - 1; - - data = buf; - - for (i = index; i < (index + count); i++) { - if (priv->permanent_write_enable) { - ret = imx6_ocotp_blow_one_u32(i, *data, - &pfuse, priv); - if (ret < 0) { - goto out; - } - } else { - writel(*data, priv->base + 0x400 + i * 0x10); - } - - data++; - write_count++; - } - - ret = 0; - -out: if (priv->permanent_write_enable) imx6_ocotp_reload_shadow(priv); - return ret < 0 ? ret : (write_count << 2); + return 0; } -static struct file_operations imx6_ocotp_ops = { - .read = imx6_ocotp_cdev_read, - .write = imx6_ocotp_cdev_write, - .lseek = dev_lseek_default, -}; - static uint32_t inc_offset(uint32_t offset) { if ((offset & 0x3) == 0x3) @@ -373,9 +335,11 @@ { struct ocotp_priv *ocotp_priv = priv; char buf[8]; - int i; + int i, ret; - imx6_ocotp_cdev_read(&ocotp_priv->cdev, buf, MAC_BYTES, MAC_OFFSET, 0); + ret = regmap_bulk_read(ocotp_priv->map, MAC_OFFSET, buf, MAC_BYTES); + if (ret < 0) + return ret; for (i = 0; i < 6; i++) ocotp_priv->ethaddr[i] = buf[5 - i]; @@ -393,19 +357,28 @@ buf[5 - i] = ocotp_priv->ethaddr[i]; buf[6] = 0; buf[7] = 0; - ret = imx6_ocotp_cdev_write(&ocotp_priv->cdev, buf, MAC_BYTES, MAC_OFFSET, 0); + ret = regmap_bulk_write(ocotp_priv->map, MAC_OFFSET, buf, MAC_BYTES); if (ret < 0) return ret; return 0; } +static struct regmap_bus imx_ocotp_regmap_bus = { + .reg_write = imx_ocotp_reg_write, + .reg_read = imx_ocotp_reg_read, +}; + static int imx_ocotp_probe(struct device_d *dev) { void __iomem *base; struct ocotp_priv *priv; - struct cdev *cdev; int ret = 0; + struct imx_ocotp_data *data; + + ret = dev_get_drvdata(dev, (const void **)&data); + if (ret) + return ret; base = dev_request_mem_region(dev, 0); if (IS_ERR(base)) @@ -420,22 +393,23 @@ if (IS_ERR(priv->clk)) return PTR_ERR(priv->clk); - cdev = &priv->cdev; - cdev->dev = dev; - cdev->ops = &imx6_ocotp_ops; - cdev->priv = priv; - cdev->size = 192; - cdev->name = "imx-ocotp"; - - ret = devfs_create(cdev); - - if (ret < 0) - return ret; - strcpy(priv->dev.name, "ocotp"); priv->dev.parent = dev; register_device(&priv->dev); + priv->map_config.reg_bits = 32; + priv->map_config.val_bits = 32; + priv->map_config.reg_stride = 4; + priv->map_config.max_register = data->num_regs - 1; + + priv->map = regmap_init(&priv->dev, &imx_ocotp_regmap_bus, priv, &priv->map_config); + if (IS_ERR(priv->map)) + return PTR_ERR(priv->map); + + ret = regmap_register_cdev(priv->map, "imx-ocotp"); + if (ret) + return ret; + if (IS_ENABLED(CONFIG_IMX_OCOTP_WRITE)) { dev_add_param_bool(&(priv->dev), "permanent_write_enable", NULL, NULL, &priv->permanent_write_enable, NULL); @@ -450,11 +424,24 @@ return 0; } +static struct imx_ocotp_data imx6q_ocotp_data = { + .num_regs = 512, +}; + +static struct imx_ocotp_data imx6sl_ocotp_data = { + .num_regs = 256, +}; + static __maybe_unused struct of_device_id imx_ocotp_dt_ids[] = { { .compatible = "fsl,imx6q-ocotp", + .data = &imx6q_ocotp_data, }, { .compatible = "fsl,imx6sx-ocotp", + .data = &imx6q_ocotp_data, + }, { + .compatible = "fsl,imx6sl-ocotp", + .data = &imx6sl_ocotp_data, }, { /* sentinel */ } diff --git a/drivers/base/Makefile b/drivers/base/Makefile index e1f1c7a..4bd4217 100644 --- a/drivers/base/Makefile +++ b/drivers/base/Makefile @@ -2,3 +2,4 @@ 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 0000000..4dc3d8c --- /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 0000000..52df529 --- /dev/null +++ b/drivers/base/regmap/internal.h @@ -0,0 +1,18 @@ + +#include + +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 0000000..a042a1a --- /dev/null +++ b/drivers/base/regmap/regmap.c @@ -0,0 +1,354 @@ +/* + * Register map access API + * + * Copyright 2016 Sascha Hauer + * + * based on Kernel code: + * + * Copyright 2011 Wolfson Microelectronics plc + * + * Author: Mark Brown + * + * 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 +#include +#include +#include + +#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 0ebfc9e..68d70c5 100644 --- a/drivers/mfd/mc13xxx.c +++ b/drivers/mfd/mc13xxx.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -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 @@ #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 @@ 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 @@ #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 @@ 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 @@ 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 @@ } 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 @@ } 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 @@ } } - 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 @@ 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 @@ 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 @@ 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); diff --git a/include/linux/bitops.h b/include/linux/bitops.h index f3a740c..31345c2 100644 --- a/include/linux/bitops.h +++ b/include/linux/bitops.h @@ -18,8 +18,11 @@ * position @h. For example * GENMASK_ULL(39, 21) gives us the 64bit vector 0x000000ffffe00000. */ -#define GENMASK(h, l) (((U32_C(1) << ((h) - (l) + 1)) - 1) << (l)) -#define GENMASK_ULL(h, l) (((U64_C(1) << ((h) - (l) + 1)) - 1) << (l)) +#define GENMASK(h, l) \ + (((~0UL) << (l)) & (~0UL >> (BITS_PER_LONG - 1 - (h)))) + +#define GENMASK_ULL(h, l) \ + (((~0ULL) << (l)) & (~0ULL >> (BITS_PER_LONG_LONG - 1 - (h)))) extern unsigned int __sw_hweight8(unsigned int w); extern unsigned int __sw_hweight16(unsigned int w); diff --git a/include/regmap.h b/include/regmap.h new file mode 100644 index 0000000..4cb473c --- /dev/null +++ b/include/regmap.h @@ -0,0 +1,62 @@ +#ifndef __REGMAP_H +#define __REGMAP_H + +/** + * Configuration for the register map of a device. + * + * @name: Optional name of the regmap. Useful when a device has multiple + * register regions. + * + * @reg_bits: Number of bits in a register address, mandatory. + * @reg_stride: The register address stride. Valid register addresses are a + * multiple of this value. If set to 0, a value of 1 will be + * used. + * @pad_bits: Number of bits of padding between register and value. + * @val_bits: Number of bits in a register value, mandatory. + * + * @max_register: Optional, specifies the maximum valid register index. + */ +struct regmap_config { + const char *name; + + int reg_bits; + int reg_stride; + int pad_bits; + int val_bits; + + unsigned int max_register; +}; + +typedef int (*regmap_hw_reg_read)(void *context, unsigned int reg, + unsigned int *val); +typedef int (*regmap_hw_reg_write)(void *context, unsigned int reg, + unsigned int val); + +struct regmap_bus { + regmap_hw_reg_write reg_write; + regmap_hw_reg_read reg_read; +}; + +struct regmap *regmap_init(struct device_d *dev, + const struct regmap_bus *bus, + void *bus_context, + const struct regmap_config *config); + +struct regmap *dev_get_regmap(struct device_d *dev, const char *name); +struct regmap *of_node_to_regmap(struct device_node *node); + +int regmap_register_cdev(struct regmap *map, const char *name); + +int regmap_write(struct regmap *map, unsigned int reg, unsigned int val); +int regmap_read(struct regmap *map, unsigned int reg, unsigned int *val); + +int regmap_bulk_read(struct regmap *map, unsigned int reg, void *val, + size_t val_len); +int regmap_bulk_write(struct regmap *map, unsigned int reg, + const void *val, size_t val_len); + +int regmap_get_val_bytes(struct regmap *map); +int regmap_get_max_register(struct regmap *map); +int regmap_get_reg_stride(struct regmap *map); + +#endif /* __REGMAP_H */ \ No newline at end of file