|
@@ -47,6 +47,11 @@
|
|
* AD5280 1 256 20, 50, 200
|
|
* AD5280 1 256 20, 50, 200
|
|
* AD5282 2 256 20, 50, 200
|
|
* AD5282 2 256 20, 50, 200
|
|
* ADN2860 3 512 25, 250
|
|
* 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.
|
|
* See Documentation/misc-devices/ad525x_dpot.txt for more info.
|
|
*
|
|
*
|
|
@@ -84,7 +89,8 @@ struct dpot_data {
|
|
unsigned uid;
|
|
unsigned uid;
|
|
unsigned feat;
|
|
unsigned feat;
|
|
unsigned wipers;
|
|
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)
|
|
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) ?
|
|
ctrl = ((reg & DPOT_RDAC_MASK) == DPOT_RDAC0) ?
|
|
0 : DPOT_AD5291_RDAC_AB;
|
|
0 : DPOT_AD5291_RDAC_AB;
|
|
return dpot_read_r8d8(dpot, ctrl);
|
|
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:
|
|
default:
|
|
if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
|
|
if ((reg & DPOT_REG_TOL) || (dpot->max_pos > 256))
|
|
return dpot_read_r8d16(dpot, (reg & 0xF8) |
|
|
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)
|
|
static s32 dpot_write_i2c(struct dpot_data *dpot, u8 reg, u16 value)
|
|
{
|
|
{
|
|
/* Only write the instruction byte for certain commands */
|
|
/* Only write the instruction byte for certain commands */
|
|
- unsigned ctrl = 0;
|
|
|
|
|
|
+ unsigned tmp = 0, ctrl = 0;
|
|
|
|
|
|
switch (dpot->uid) {
|
|
switch (dpot->uid) {
|
|
case DPOT_UID(AD5246_ID):
|
|
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;
|
|
0 : DPOT_AD5291_RDAC_AB;
|
|
return dpot_write_r8d8(dpot, ctrl, value);
|
|
return dpot_write_r8d8(dpot, ctrl, value);
|
|
break;
|
|
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:
|
|
default:
|
|
if (reg & DPOT_ADDR_CMD)
|
|
if (reg & DPOT_ADDR_CMD)
|
|
return dpot_write_d8(dpot, reg);
|
|
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);
|
|
struct dpot_data *data = dev_get_drvdata(dev);
|
|
s32 value;
|
|
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);
|
|
mutex_lock(&data->update_lock);
|
|
value = dpot_read(data, reg);
|
|
value = dpot_read(data, reg);
|
|
mutex_unlock(&data->update_lock);
|
|
mutex_unlock(&data->update_lock);
|
|
@@ -320,6 +372,19 @@ static ssize_t sysfs_set_reg(struct device *dev,
|
|
unsigned long value;
|
|
unsigned long value;
|
|
int err;
|
|
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);
|
|
err = strict_strtoul(buf, 10, &value);
|
|
if (err)
|
|
if (err)
|
|
return err;
|
|
return err;
|
|
@@ -331,6 +396,8 @@ static ssize_t sysfs_set_reg(struct device *dev,
|
|
dpot_write(data, reg, value);
|
|
dpot_write(data, reg, value);
|
|
if (reg & DPOT_ADDR_EEPROM)
|
|
if (reg & DPOT_ADDR_EEPROM)
|
|
msleep(26); /* Sleep while the EEPROM updates */
|
|
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);
|
|
mutex_unlock(&data->update_lock);
|
|
|
|
|
|
return count;
|
|
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(rdac0, DPOT_ADDR_RDAC | DPOT_RDAC0);
|
|
DPOT_DEVICE_SHOW_SET(eeprom0, DPOT_ADDR_EEPROM | 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_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(rdac1, DPOT_ADDR_RDAC | DPOT_RDAC1);
|
|
DPOT_DEVICE_SHOW_SET(eeprom1, DPOT_ADDR_EEPROM | 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_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(rdac2, DPOT_ADDR_RDAC | DPOT_RDAC2);
|
|
DPOT_DEVICE_SHOW_SET(eeprom2, DPOT_ADDR_EEPROM | 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_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(rdac3, DPOT_ADDR_RDAC | DPOT_RDAC3);
|
|
DPOT_DEVICE_SHOW_SET(eeprom3, DPOT_ADDR_EEPROM | 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_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(rdac4, DPOT_ADDR_RDAC | DPOT_RDAC4);
|
|
DPOT_DEVICE_SHOW_SET(eeprom4, DPOT_ADDR_EEPROM | 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_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(rdac5, DPOT_ADDR_RDAC | DPOT_RDAC5);
|
|
DPOT_DEVICE_SHOW_SET(eeprom5, DPOT_ADDR_EEPROM | 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_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[] = {
|
|
static const struct attribute *dpot_attrib_wipers[] = {
|
|
&dev_attr_rdac0.attr,
|
|
&dev_attr_rdac0.attr,
|
|
@@ -419,6 +498,26 @@ static const struct attribute *dpot_attrib_eeprom[] = {
|
|
NULL
|
|
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[] = {
|
|
static const struct attribute *dpot_attrib_tolerance[] = {
|
|
&dev_attr_tolerance0.attr,
|
|
&dev_attr_tolerance0.attr,
|
|
&dev_attr_tolerance1.attr,
|
|
&dev_attr_tolerance1.attr,
|
|
@@ -468,6 +567,12 @@ __devinit int ad_dpot_add_files(struct device *dev,
|
|
if (features & F_CMD_TOL)
|
|
if (features & F_CMD_TOL)
|
|
err |= sysfs_create_file(&dev->kobj,
|
|
err |= sysfs_create_file(&dev->kobj,
|
|
dpot_attrib_tolerance[rdac]);
|
|
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)
|
|
if (err)
|
|
dev_err(dev, "failed to register sysfs hooks for RDAC%d\n",
|
|
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)
|
|
if (features & F_CMD_TOL)
|
|
sysfs_remove_file(&dev->kobj,
|
|
sysfs_remove_file(&dev->kobj,
|
|
dpot_attrib_tolerance[rdac]);
|
|
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,
|
|
__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->uid = DPOT_UID(data->devid);
|
|
data->wipers = DPOT_WIPERS(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)) {
|
|
if (data->wipers & (1 << i)) {
|
|
err = ad_dpot_add_files(dev, data->feat, i);
|
|
err = ad_dpot_add_files(dev, data->feat, i);
|
|
if (err)
|
|
if (err)
|
|
@@ -538,7 +649,7 @@ __devinit int ad_dpot_probe(struct device *dev,
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
exit_remove_files:
|
|
exit_remove_files:
|
|
- for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
|
|
|
|
|
|
+ for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
|
|
if (data->wipers & (1 << i))
|
|
if (data->wipers & (1 << i))
|
|
ad_dpot_remove_files(dev, data->feat, 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);
|
|
struct dpot_data *data = dev_get_drvdata(dev);
|
|
int i;
|
|
int i;
|
|
|
|
|
|
- for (i = DPOT_RDAC0; i <= DPOT_RDAC5; i++)
|
|
|
|
|
|
+ for (i = DPOT_RDAC0; i < MAX_RDACS; i++)
|
|
if (data->wipers & (1 << i))
|
|
if (data->wipers & (1 << i))
|
|
ad_dpot_remove_files(dev, data->feat, i);
|
|
ad_dpot_remove_files(dev, data->feat, i);
|
|
|
|
|