summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Hauer <s.hauer@pengutronix.de>2016-02-08 08:27:03 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2016-02-08 08:27:03 +0100
commit7482ab4de107d6fe3136f741ad6a3d423661f125 (patch)
tree06bf853ab81348f0a4028fb3c41978b61dfb03f6
parentc0735348802c29cc46db3758b5e477f2bc8ff058 (diff)
parent2ee6bb35043de0bc994da4bf3f0139280229c3bd (diff)
downloadbarebox-7482ab4de107d6fe3136f741ad6a3d423661f125.tar.gz
barebox-7482ab4de107d6fe3136f741ad6a3d423661f125.tar.xz
Merge branch 'for-next/regmap'
-rw-r--r--arch/arm/mach-imx/iim.c121
-rw-r--r--arch/arm/mach-imx/ocotp.c227
-rw-r--r--drivers/base/Makefile1
-rw-r--r--drivers/base/regmap/Makefile1
-rw-r--r--drivers/base/regmap/internal.h18
-rw-r--r--drivers/base/regmap/regmap.c354
-rw-r--r--drivers/mfd/mc13xxx.c127
-rw-r--r--include/linux/bitops.h7
-rw-r--r--include/regmap.h62
9 files changed, 666 insertions, 252 deletions
diff --git a/arch/arm/mach-imx/iim.c b/arch/arm/mach-imx/iim.c
index c5751fe111..c16a6c6879 100644
--- a/arch/arm/mach-imx/iim.c
+++ b/arch/arm/mach-imx/iim.c
@@ -26,6 +26,7 @@
#include <malloc.h>
#include <of.h>
#include <io.h>
+#include <regmap.h>
#include <regulator.h>
#include <linux/err.h>
@@ -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 @@ static int imx_iim_fuse_sense(struct iim_bank *bank, unsigned int row)
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 @@ int imx_iim_read(unsigned int banknum, int offset, void *buf, int count)
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 @@ out:
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 @@ static int imx_iim_fuse_blow(struct iim_bank *bank, unsigned offset, const void
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 @@ err_out:
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, offset, buf, size);
- } else {
- for (i = 0; i < size; i++)
- ((u8 *)bank->bankbase)[(offset+i)*4] = ((u8 *)buf)[i];
- }
+ 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);
- 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 @@ static int imx_iim_get_mac(struct param_d *param, void *priv)
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 @@ static int imx_iim_set_mac(struct param_d *param, void *priv)
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 @@ static void imx_iim_init_dt(struct device_d *dev, struct iim_priv *iim)
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 c99a003bb0..e2f10e12a0 100644
--- a/arch/arm/mach-imx/ocotp.c
+++ b/arch/arm/mach-imx/ocotp.c
@@ -26,6 +26,7 @@
#include <io.h>
#include <of.h>
#include <clock.h>
+#include <regmap.h>
#include <linux/clk.h>
/*
@@ -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 @@ static int imx6_ocotp_set_timing(struct ocotp_priv *priv)
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 @@ static int imx6_ocotp_prepare(struct ocotp_priv *priv)
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);
-
- index = offset >> 2;
- count >>= 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++;
+ int ret;
+
+ index = reg >> 2;
+
+ 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 fuse_blow_addr(u32 addr, u32 value, struct ocotp_priv *priv)
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 = 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);
- }
+ index = reg >> 2;
- data++;
- write_count++;
+ 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);
}
- 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 @@ static int imx_ocotp_get_mac(struct param_d *param, void *priv)
{
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 @@ static int imx_ocotp_set_mac(struct param_d *param, void *priv)
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 @@ static int imx_ocotp_probe(struct device_d *dev)
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 @@ static int imx_ocotp_probe(struct device_d *dev)
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 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, &regmaps);
+
+ 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, &regmaps, 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, &regmaps, 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 = &regmap_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, &regmap_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, &regmap_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 f3a740c461..31345c219d 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 0000000000..4cb473cbfe
--- /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