ad525x_dpot: add support for one time programmable pots
New parts supported: AD5170, AD5171, AD5172, AD5173, AD5273 Signed-off-by: Michael Hennerich <michael.hennerich@analog.com> Signed-off-by: Mike Frysinger <vapier@gentoo.org> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
parent
c74cba610c
commit
59592d0ccc
@ -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 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 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 {
|
||||
|
Loading…
Reference in New Issue
Block a user