Commit 59592d0c authored by Michael Hennerich's avatar Michael Hennerich Committed by Linus Torvalds

ad525x_dpot: add support for one time programmable pots

New parts supported:
	AD5170, AD5171, AD5172, AD5173, AD5273
Signed-off-by: default avatarMichael Hennerich <michael.hennerich@analog.com>
Signed-off-by: default avatarMike Frysinger <vapier@gentoo.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent c74cba61
......@@ -24,7 +24,7 @@ config AD525X_DPOT
AD5260, AD5262, AD5263, AD5290, AD5291, AD5292, AD5293,
AD7376, AD8400, AD8402, AD8403, ADN2850, AD5241, AD5242,
AD5243, AD5245, AD5246, AD5247, AD5248, AD5280, AD5282,
ADN2860
ADN2860, AD5273, AD5171, AD5170, AD5172, AD5173
digital potentiometer chips.
See Documentation/misc-devices/ad525x_dpot.txt for the
......
......@@ -97,6 +97,11 @@ static const struct i2c_device_id ad_dpot_id[] = {
{"ad5280", AD5280_ID},
{"ad5282", AD5282_ID},
{"adn2860", ADN2860_ID},
{"ad5273", AD5273_ID},
{"ad5171", AD5171_ID},
{"ad5170", AD5170_ID},
{"ad5172", AD5172_ID},
{"ad5173", AD5173_ID},
{}
};
MODULE_DEVICE_TABLE(i2c, ad_dpot_id);
......
......@@ -47,6 +47,11 @@
* AD5280 1 256 20, 50, 200
* AD5282 2 256 20, 50, 200
* ADN2860 3 512 25, 250
* AD5273 1 64 1, 10, 50, 100 (OTP)
* AD5171 1 64 5, 10, 50, 100 (OTP)
* AD5170 1 256 2.5, 10, 50, 100 (OTP)
* AD5172 2 256 2.5, 10, 50, 100 (OTP)
* AD5173 2 256 2.5, 10, 50, 100 (OTP)
*
* See Documentation/misc-devices/ad525x_dpot.txt for more info.
*
......@@ -84,7 +89,8 @@ struct dpot_data {
unsigned uid;
unsigned feat;
unsigned wipers;
u16 rdac_cache[8];
u16 rdac_cache[MAX_RDACS];
DECLARE_BITMAP(otp_en_mask, MAX_RDACS);
};
static inline int dpot_read_d8(struct dpot_data *dpot)
......@@ -162,6 +168,15 @@ static s32 dpot_read_i2c(struct dpot_data *dpot, u8 reg)
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
0 : DPOT_AD5291_RDAC_AB;
return dpot_read_r8d8(dpot, ctrl);
case DPOT_UID(AD5170_ID):
case DPOT_UID(AD5171_ID):
case DPOT_UID(AD5273_ID):
return dpot_read_d8(dpot);
case DPOT_UID(AD5172_ID):
case DPOT_UID(AD5173_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
0 : DPOT_AD5272_3_A0;
return dpot_read_r8d8(dpot, ctrl);
default:
if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
return dpot_read_r8d16(dpot, (reg & 0xF8) |
......@@ -242,7 +257,7 @@ static s32 dpot_write_spi(struct dpot_data *dpot, u8 reg, u16 value)
static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
{
/* Only write the instruction byte for certain commands */
unsigned ctrl = 0;
unsigned tmp = 0, ctrl = 0;
switch (dpot->uid) {
case DPOT_UID(AD5246_ID):
......@@ -261,6 +276,37 @@ static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
0 : DPOT_AD5291_RDAC_AB;
return dpot_write_r8d8(dpot, ctrl, value);
break;
case DPOT_UID(AD5171_ID):
case DPOT_UID(AD5273_ID):
if (reg & DPOT_ADDR_OTP) {
tmp = dpot_read_d8(dpot);
if (tmp >> 6) /* Ready to Program? */
return -EFAULT;
ctrl = DPOT_AD5273_FUSE;
}
return dpot_write_r8d8(dpot, ctrl, value);
break;
case DPOT_UID(AD5172_ID):
case DPOT_UID(AD5173_ID):
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
0 : DPOT_AD5272_3_A0;
if (reg & DPOT_ADDR_OTP) {
tmp = dpot_read_r8d16(dpot, ctrl);
if (tmp >> 14) /* Ready to Program? */
return -EFAULT;
ctrl |= DPOT_AD5270_2_3_FUSE;
}
return dpot_write_r8d8(dpot, ctrl, value);
break;
case DPOT_UID(AD5170_ID):
if (reg & DPOT_ADDR_OTP) {
tmp = dpot_read_r8d16(dpot, tmp);
if (tmp >> 14) /* Ready to Program? */
return -EFAULT;
ctrl = DPOT_AD5270_2_3_FUSE;
}
return dpot_write_r8d8(dpot, ctrl, value);
break;
default:
if (reg & DPOT_ADDR_CMD)
return dpot_write_d8(dpot, reg);
......@@ -292,6 +338,12 @@ static ssize_t sysfs_show_reg(struct device *dev,
struct dpot_data *data = dev_get_drvdata(dev);
s32 value;
if (reg & DPOT_ADDR_OTP_EN)
return sprintf(buf, "%s\n",
test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask) ?
"enabled" : "disabled");
mutex_lock(&data->update_lock);
value = dpot_read(data, reg);
mutex_unlock(&data->update_lock);
......@@ -320,6 +372,19 @@ static ssize_t sysfs_set_reg(struct device *dev,
unsigned long value;
int err;
if (reg & DPOT_ADDR_OTP_EN) {
if (!strncmp(buf, "enabled", sizeof("enabled")))
set_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask);
else
clear_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask);
return count;
}
if ((reg & DPOT_ADDR_OTP) &&
!test_bit(DPOT_RDAC_MASK & reg, data->otp_en_mask))
return -EPERM;
err = strict_strtoul(buf, 10, &value);
if (err)
return err;
......@@ -331,6 +396,8 @@ static ssize_t sysfs_set_reg(struct device *dev,
dpot_write(data, reg, value);
if (reg & DPOT_ADDR_EEPROM)
msleep(26); /* Sleep while the EEPROM updates */
else if (reg & DPOT_ADDR_OTP)
msleep(400); /* Sleep while the OTP updates */
mutex_unlock(&data->update_lock);
return count;
......@@ -378,26 +445,38 @@ static DEVICE_ATTR(name, S_IWUSR | S_IRUGO, show_##name, NULL);
DPOT_DEVICE_SHOW_SET(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0);
DPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | DPOT_RDAC0);
DPOT_DEVICE_SHOW_ONLY(tolerance0, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC0);
DPOT_DEVICE_SHOW_SET(otp0, DPOT_ADDR_OTP | DPOT_RDAC0);
DPOT_DEVICE_SHOW_SET(otp0en, DPOT_ADDR_OTP_EN | DPOT_RDAC0);
DPOT_DEVICE_SHOW_SET(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1);
DPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | DPOT_RDAC1);
DPOT_DEVICE_SHOW_ONLY(tolerance1, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC1);
DPOT_DEVICE_SHOW_SET(otp1, DPOT_ADDR_OTP | DPOT_RDAC1);
DPOT_DEVICE_SHOW_SET(otp1en, DPOT_ADDR_OTP_EN | DPOT_RDAC1);
DPOT_DEVICE_SHOW_SET(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2);
DPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | DPOT_RDAC2);
DPOT_DEVICE_SHOW_ONLY(tolerance2, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC2);
DPOT_DEVICE_SHOW_SET(otp2, DPOT_ADDR_OTP | DPOT_RDAC2);
DPOT_DEVICE_SHOW_SET(otp2en, DPOT_ADDR_OTP_EN | DPOT_RDAC2);
DPOT_DEVICE_SHOW_SET(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3);
DPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | DPOT_RDAC3);
DPOT_DEVICE_SHOW_ONLY(tolerance3, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC3);
DPOT_DEVICE_SHOW_SET(otp3, DPOT_ADDR_OTP | DPOT_RDAC3);
DPOT_DEVICE_SHOW_SET(otp3en, DPOT_ADDR_OTP_EN | DPOT_RDAC3);
DPOT_DEVICE_SHOW_SET(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4);
DPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | DPOT_RDAC4);
DPOT_DEVICE_SHOW_ONLY(tolerance4, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC4);
DPOT_DEVICE_SHOW_SET(otp4, DPOT_ADDR_OTP | DPOT_RDAC4);
DPOT_DEVICE_SHOW_SET(otp4en, DPOT_ADDR_OTP_EN | DPOT_RDAC4);
DPOT_DEVICE_SHOW_SET(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5);
DPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | DPOT_RDAC5);
DPOT_DEVICE_SHOW_ONLY(tolerance5, DPOT_ADDR_EEPROM | DPOT_TOL_RDAC5);
DPOT_DEVICE_SHOW_SET(otp5, DPOT_ADDR_OTP | DPOT_RDAC5);
DPOT_DEVICE_SHOW_SET(otp5en, DPOT_ADDR_OTP_EN | DPOT_RDAC5);
static const struct attribute *dpot_attrib_wipers[] = {
&dev_attr_rdac0.attr,
......@@ -419,6 +498,26 @@ static const struct attribute *dpot_attrib_eeprom[] = {
NULL
};
static const struct attribute *dpot_attrib_otp[] = {
&dev_attr_otp0.attr,
&dev_attr_otp1.attr,
&dev_attr_otp2.attr,
&dev_attr_otp3.attr,
&dev_attr_otp4.attr,
&dev_attr_otp5.attr,
NULL
};
static const struct attribute *dpot_attrib_otp_en[] = {
&dev_attr_otp0en.attr,
&dev_attr_otp1en.attr,
&dev_attr_otp2en.attr,
&dev_attr_otp3en.attr,
&dev_attr_otp4en.attr,
&dev_attr_otp5en.attr,
NULL
};
static const struct attribute *dpot_attrib_tolerance[] = {
&dev_attr_tolerance0.attr,
&dev_attr_tolerance1.attr,
......@@ -468,6 +567,12 @@ __devinit int ad_dpot_add_files(struct device *dev,
if (features & F_CMD_TOL)
err |= sysfs_create_file(&dev->kobj,
dpot_attrib_tolerance[rdac]);
if (features & F_CMD_OTP) {
err |= sysfs_create_file(&dev->kobj,
dpot_attrib_otp_en[rdac]);
err |= sysfs_create_file(&dev->kobj,
dpot_attrib_otp[rdac]);
}
if (err)
dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
......@@ -487,6 +592,12 @@ inline void ad_dpot_remove_files(struct device *dev,
if (features & F_CMD_TOL)
sysfs_remove_file(&dev->kobj,
dpot_attrib_tolerance[rdac]);
if (features & F_CMD_OTP) {
sysfs_remove_file(&dev->kobj,
dpot_attrib_otp_en[rdac]);
sysfs_remove_file(&dev->kobj,
dpot_attrib_otp[rdac]);
}
}
__devinit int ad_dpot_probe(struct device *dev,
......@@ -514,7 +625,7 @@ __devinit int ad_dpot_probe(struct device *dev,
data->uid = DPOT_UID(data->devid);
data->wipers = DPOT_WIPERS(data->devid);
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
if (data->wipers & (1 << i)) {
err = ad_dpot_add_files(dev, data->feat, i);
if (err)
......@@ -538,7 +649,7 @@ __devinit int ad_dpot_probe(struct device *dev,
return 0;
exit_remove_files:
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
if (data->wipers & (1 << i))
ad_dpot_remove_files(dev, data->feat, i);
......@@ -557,7 +668,7 @@ __devexit int ad_dpot_remove(struct device *dev)
struct dpot_data *data = dev_get_drvdata(dev);
int i;
for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
if (data->wipers & (1 << i))
ad_dpot_remove_files(dev, data->feat, i);
......
......@@ -15,17 +15,18 @@
(((features) << 18) | (((wipers) & 0xFF) << 10) | \
((max_pos & 0xF) << 6) | (uid & 0x3F))
#define DPOT_UID(conf) (conf & 0x3F)
#define DPOT_MAX_POS(conf) ((conf >> 6) & 0xF)
#define DPOT_WIPERS(conf) ((conf >> 10) & 0xFF)
#define DPOT_FEAT(conf) (conf >> 18)
#define BRDAC0 (1 << 0)
#define BRDAC1 (1 << 1)
#define BRDAC2 (1 << 2)
#define BRDAC3 (1 << 3)
#define BRDAC4 (1 << 4)
#define BRDAC5 (1 << 5)
#define DPOT_UID(conf) (conf & 0x3F)
#define DPOT_MAX_POS(conf) ((conf >> 6) & 0xF)
#define DPOT_WIPERS(conf) ((conf >> 10) & 0xFF)
#define DPOT_FEAT(conf) (conf >> 18)
#define BRDAC0 (1 << 0)
#define BRDAC1 (1 << 1)
#define BRDAC2 (1 << 2)
#define BRDAC3 (1 << 3)
#define BRDAC4 (1 << 4)
#define BRDAC5 (1 << 5)
#define MAX_RDACS 6
#define F_CMD_INC (1 << 0) /* Features INC/DEC ALL, 6dB */
#define F_CMD_EEP (1 << 1) /* Features EEPROM */
......@@ -116,6 +117,11 @@ enum dpot_devid {
AD5282_ID = DPOT_CONF(F_RDACS_RW, BRDAC0 | BRDAC1, 8, 41),
ADN2860_ID = DPOT_CONF(F_RDACS_RW_TOL | F_CMD_INC,
BRDAC0 | BRDAC1 | BRDAC2, 9, 42),
AD5273_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 6, 43),
AD5171_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 6, 44),
AD5170_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0, 8, 45),
AD5172_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 46),
AD5173_ID = DPOT_CONF(F_RDACS_RW | F_CMD_OTP, BRDAC0 | BRDAC1, 8, 47),
};
#define DPOT_RDAC0 0
......@@ -136,9 +142,11 @@ enum dpot_devid {
#define DPOT_TOL_RDAC5 (DPOT_REG_TOL | DPOT_RDAC5)
/* RDAC-to-EEPROM Interface Commands */
#define DPOT_ADDR_RDAC (0x00 << 5)
#define DPOT_ADDR_EEPROM (0x01 << 5)
#define DPOT_ADDR_CMD (0x80)
#define DPOT_ADDR_RDAC (0x0 << 5)
#define DPOT_ADDR_EEPROM (0x1 << 5)
#define DPOT_ADDR_OTP (0x1 << 6)
#define DPOT_ADDR_CMD (0x1 << 7)
#define DPOT_ADDR_OTP_EN (0x1 << 9)
#define DPOT_DEC_ALL_6DB (DPOT_ADDR_CMD | (0x4 << 3))
#define DPOT_INC_ALL_6DB (DPOT_ADDR_CMD | (0x9 << 3))
......@@ -161,6 +169,12 @@ enum dpot_devid {
/* AD524x use special commands */
#define DPOT_AD5291_RDAC_AB 0x80
#define DPOT_AD5273_FUSE 0x80
#define DPOT_AD5270_2_3_FUSE 0x20
#define DPOT_AD5270_2_3_OW 0x08
#define DPOT_AD5272_3_A0 0x08
#define DPOT_AD5270_2FUSE 0x80
struct dpot_data;
struct ad_dpot_bus_ops {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment