Skip to content

Commit

Permalink
mfd: Convert WM831x to use regmap API
Browse files Browse the repository at this point in the history
Factor out the register read/write code to use the register map API.  We
still need some wm831x specific code and locking in place to check that
the user key is handled correctly but only on the write side, reads are
not affected by the key.

Signed-off-by: Mark Brown <[email protected]>
Acked-by: Samuel Ortiz <[email protected]>
  • Loading branch information
broonie committed Aug 22, 2011
1 parent bd20eb5 commit 1df5981
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 170 deletions.
2 changes: 2 additions & 0 deletions drivers/mfd/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ config MFD_WM831X_I2C
bool "Support Wolfson Microelectronics WM831x/2x PMICs with I2C"
select MFD_CORE
select MFD_WM831X
select REGMAP_I2C
depends on I2C=y && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
Expand All @@ -415,6 +416,7 @@ config MFD_WM831X_SPI
bool "Support Wolfson Microelectronics WM831x/2x PMICs with SPI"
select MFD_CORE
select MFD_WM831X
select REGMAP_SPI
depends on SPI_MASTER && GENERIC_HARDIRQS
help
Support for the Wolfson Microelecronics WM831x and WM832x PMICs
Expand Down
89 changes: 28 additions & 61 deletions drivers/mfd/wm831x-core.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/err.h>

#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>
Expand Down Expand Up @@ -160,29 +161,6 @@ int wm831x_reg_unlock(struct wm831x *wm831x)
}
EXPORT_SYMBOL_GPL(wm831x_reg_unlock);

static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest)
{
int ret, i;
u16 *buf = dest;

BUG_ON(bytes % 2);
BUG_ON(bytes <= 0);

ret = wm831x->read_dev(wm831x, reg, bytes, dest);
if (ret < 0)
return ret;

for (i = 0; i < bytes / 2; i++) {
buf[i] = be16_to_cpu(buf[i]);

dev_vdbg(wm831x->dev, "Read %04x from R%d(0x%x)\n",
buf[i], reg + i, reg + i);
}

return 0;
}

/**
* wm831x_reg_read: Read a single WM831x register.
*
Expand All @@ -191,14 +169,10 @@ static int wm831x_read(struct wm831x *wm831x, unsigned short reg,
*/
int wm831x_reg_read(struct wm831x *wm831x, unsigned short reg)
{
unsigned short val;
unsigned int val;
int ret;

mutex_lock(&wm831x->io_lock);

ret = wm831x_read(wm831x, reg, 2, &val);

mutex_unlock(&wm831x->io_lock);
ret = regmap_read(wm831x->regmap, reg, &val);

if (ret < 0)
return ret;
Expand All @@ -218,23 +192,15 @@ EXPORT_SYMBOL_GPL(wm831x_reg_read);
int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg,
int count, u16 *buf)
{
int ret;

mutex_lock(&wm831x->io_lock);

ret = wm831x_read(wm831x, reg, count * 2, buf);

mutex_unlock(&wm831x->io_lock);

return ret;
return regmap_bulk_read(wm831x->regmap, reg, buf, count);
}
EXPORT_SYMBOL_GPL(wm831x_bulk_read);

static int wm831x_write(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
u16 *buf = src;
int i;
int i, ret;

BUG_ON(bytes % 2);
BUG_ON(bytes <= 0);
Expand All @@ -245,11 +211,10 @@ static int wm831x_write(struct wm831x *wm831x, unsigned short reg,

dev_vdbg(wm831x->dev, "Write %04x to R%d(0x%x)\n",
buf[i], reg + i, reg + i);

buf[i] = cpu_to_be16(buf[i]);
ret = regmap_write(wm831x->regmap, reg + i, buf[i]);
}

return wm831x->write_dev(wm831x, reg, bytes, src);
return 0;
}

/**
Expand Down Expand Up @@ -286,20 +251,14 @@ int wm831x_set_bits(struct wm831x *wm831x, unsigned short reg,
unsigned short mask, unsigned short val)
{
int ret;
u16 r;

mutex_lock(&wm831x->io_lock);

ret = wm831x_read(wm831x, reg, 2, &r);
if (ret < 0)
goto out;

r &= ~mask;
r |= val & mask;

ret = wm831x_write(wm831x, reg, 2, &r);
if (!wm831x_reg_locked(wm831x, reg))
ret = regmap_update_bits(wm831x->regmap, reg, mask, val);
else
ret = -EPERM;

out:
mutex_unlock(&wm831x->io_lock);

return ret;
Expand Down Expand Up @@ -1292,6 +1251,12 @@ static struct mfd_cell backlight_devs[] = {
},
};

struct regmap_config wm831x_regmap_config = {
.reg_bits = 16,
.val_bits = 16,
};
EXPORT_SYMBOL_GPL(wm831x_regmap_config);

/*
* Instantiate the generic non-control parts of the device.
*/
Expand All @@ -1309,7 +1274,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read parent ID: %d\n", ret);
goto err;
goto err_regmap;
}
switch (ret) {
case 0x6204:
Expand All @@ -1318,20 +1283,20 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Device is not a WM831x: ID %x\n", ret);
ret = -EINVAL;
goto err;
goto err_regmap;
}

ret = wm831x_reg_read(wm831x, WM831X_REVISION);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read revision: %d\n", ret);
goto err;
goto err_regmap;
}
rev = (ret & WM831X_PARENT_REV_MASK) >> WM831X_PARENT_REV_SHIFT;

ret = wm831x_reg_read(wm831x, WM831X_RESET_ID);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read device ID: %d\n", ret);
goto err;
goto err_regmap;
}

/* Some engineering samples do not have the ID set, rely on
Expand Down Expand Up @@ -1406,7 +1371,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
default:
dev_err(wm831x->dev, "Unknown WM831x device %04x\n", ret);
ret = -EINVAL;
goto err;
goto err_regmap;
}

/* This will need revisiting in future but is OK for all
Expand All @@ -1420,7 +1385,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = wm831x_reg_read(wm831x, WM831X_SECURITY_KEY);
if (ret < 0) {
dev_err(wm831x->dev, "Failed to read security key: %d\n", ret);
goto err;
goto err_regmap;
}
if (ret != 0) {
dev_warn(wm831x->dev, "Security key had non-zero value %x\n",
Expand All @@ -1433,7 +1398,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)
ret = pdata->pre_init(wm831x);
if (ret != 0) {
dev_err(wm831x->dev, "pre_init() failed: %d\n", ret);
goto err;
goto err_regmap;
}
}

Expand All @@ -1456,7 +1421,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)

ret = wm831x_irq_init(wm831x, irq);
if (ret != 0)
goto err;
goto err_regmap;

wm831x_auxadc_init(wm831x);

Expand Down Expand Up @@ -1552,8 +1517,9 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq)

err_irq:
wm831x_irq_exit(wm831x);
err:
err_regmap:
mfd_remove_devices(wm831x->dev);
regmap_exit(wm831x->regmap);
kfree(wm831x);
return ret;
}
Expand All @@ -1565,6 +1531,7 @@ void wm831x_device_exit(struct wm831x *wm831x)
if (wm831x->irq_base)
free_irq(wm831x->irq_base + WM831X_IRQ_AUXADC_DATA, wm831x);
wm831x_irq_exit(wm831x);
regmap_exit(wm831x->regmap);
kfree(wm831x);
}

Expand Down
68 changes: 12 additions & 56 deletions drivers/mfd/wm831x-i2c.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,77 +18,33 @@
#include <linux/delay.h>
#include <linux/mfd/core.h>
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/regmap.h>

#include <linux/mfd/wm831x/core.h>
#include <linux/mfd/wm831x/pdata.h>

static int wm831x_i2c_read_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *dest)
{
struct i2c_client *i2c = wm831x->control_data;
int ret;
u16 r = cpu_to_be16(reg);

ret = i2c_master_send(i2c, (unsigned char *)&r, 2);
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;

ret = i2c_master_recv(i2c, dest, bytes);
if (ret < 0)
return ret;
if (ret != bytes)
return -EIO;
return 0;
}

/* Currently we allocate the write buffer on the stack; this is OK for
* small writes - if we need to do large writes this will need to be
* revised.
*/
static int wm831x_i2c_write_device(struct wm831x *wm831x, unsigned short reg,
int bytes, void *src)
{
struct i2c_client *i2c = wm831x->control_data;
struct i2c_msg xfer[2];
int ret;

reg = cpu_to_be16(reg);

xfer[0].addr = i2c->addr;
xfer[0].flags = 0;
xfer[0].len = 2;
xfer[0].buf = (char *)&reg;

xfer[1].addr = i2c->addr;
xfer[1].flags = I2C_M_NOSTART;
xfer[1].len = bytes;
xfer[1].buf = (char *)src;

ret = i2c_transfer(i2c->adapter, xfer, 2);
if (ret < 0)
return ret;
if (ret != 2)
return -EIO;

return 0;
}

static int wm831x_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct wm831x *wm831x;
int ret;

wm831x = kzalloc(sizeof(struct wm831x), GFP_KERNEL);
if (wm831x == NULL)
return -ENOMEM;

i2c_set_clientdata(i2c, wm831x);
wm831x->dev = &i2c->dev;
wm831x->control_data = i2c;
wm831x->read_dev = wm831x_i2c_read_device;
wm831x->write_dev = wm831x_i2c_write_device;

wm831x->regmap = regmap_init_i2c(i2c, &wm831x_regmap_config);
if (IS_ERR(wm831x->regmap)) {
ret = PTR_ERR(wm831x->regmap);
dev_err(wm831x->dev, "Failed to allocate register map: %d\n",
ret);
kfree(wm831x);
return ret;
}

return wm831x_device_init(wm831x, id->driver_data, i2c->irq);
}
Expand Down
Loading

0 comments on commit 1df5981

Please sign in to comment.