diff --git a/drivers/misc/cs40l2x.c b/drivers/misc/cs40l2x.c index dac9a563909f..3843ab86a59b 100644 --- a/drivers/misc/cs40l2x.c +++ b/drivers/misc/cs40l2x.c @@ -46,6 +46,8 @@ struct cs40l2x_private { struct regmap *regmap; struct regulator_bulk_data supplies[2]; unsigned int num_supplies; + unsigned int devid; + unsigned int revid; struct work_struct vibe_start_work; struct work_struct vibe_stop_work; struct workqueue_struct *vibe_workqueue; @@ -78,6 +80,13 @@ static const char * const cs40l2x_supplies[] = { "VP", }; +static const char * const cs40l2x_part_nums[] = { + "CS40L20", + "CS40L25", + "CS40L25A", + "CS40L25B", +}; + static struct cs40l2x_private *cs40l2x_get_private(struct device *dev) { #ifdef CONFIG_ANDROID_TIMED_OUTPUT @@ -1537,40 +1546,8 @@ static int cs40l2x_otp_unpack(struct cs40l2x_private *cs40l2x) unsigned char row_offset, col_offset; unsigned int val, otp_map; unsigned int otp_mem[CS40L2X_NUM_OTP_WORDS]; - int otp_timeout = CS40L2X_OTP_TIMEOUT_COUNT; int ret, i; - while (otp_timeout > 0) { - usleep_range(10000, 10100); - - ret = regmap_read(regmap, CS40L2X_IRQ1_STATUS4, &val); - if (ret) { - dev_err(dev, "Failed to read OTP boot status\n"); - return ret; - } - - if (val & CS40L2X_OTP_BOOT_DONE) - break; - - otp_timeout--; - } - - if (otp_timeout == 0) { - dev_err(dev, "Timed out waiting for OTP boot\n"); - return -ETIME; - } - - ret = regmap_read(cs40l2x->regmap, CS40L2X_IRQ1_STATUS3, &val); - if (ret) { - dev_err(dev, "Failed to read OTP error status\n"); - return ret; - } - - if (val & CS40L2X_OTP_BOOT_ERR) { - dev_err(dev, "Encountered fatal OTP error\n"); - return -EIO; - } - ret = regmap_read(cs40l2x->regmap, CS40L2X_OTPID, &val); if (ret) { dev_err(dev, "Failed to read OTP ID\n"); @@ -1721,6 +1698,104 @@ static const struct reg_sequence cs40l2x_rev_a0_errata[] = { {CS40L2X_IRQ2_DB3, 0x00000000}, }; +static int cs40l2x_part_num_resolve(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val, devid, revid; + unsigned int part_num_index; + int otp_timeout = CS40L2X_OTP_TIMEOUT_COUNT; + int ret; + + while (otp_timeout > 0) { + usleep_range(10000, 10100); + + ret = regmap_read(regmap, CS40L2X_IRQ1_STATUS4, &val); + if (ret) { + dev_err(dev, "Failed to read OTP boot status\n"); + return ret; + } + + if (val & CS40L2X_OTP_BOOT_DONE) + break; + + otp_timeout--; + } + + if (otp_timeout == 0) { + dev_err(dev, "Timed out waiting for OTP boot\n"); + return -ETIME; + } + + ret = regmap_read(regmap, CS40L2X_IRQ1_STATUS3, &val); + if (ret) { + dev_err(dev, "Failed to read OTP error status\n"); + return ret; + } + + if (val & CS40L2X_OTP_BOOT_ERR) { + dev_err(dev, "Encountered fatal OTP error\n"); + return -EIO; + } + + ret = regmap_read(regmap, CS40L2X_DEVID, &devid); + if (ret) { + dev_err(dev, "Failed to read device ID\n"); + return ret; + } + + ret = regmap_read(regmap, CS40L2X_REVID, &revid); + if (ret) { + dev_err(dev, "Failed to read revision ID\n"); + return ret; + } + + switch (devid) { + case CS40L2X_DEVID_L20: + part_num_index = 0; + if (revid != CS40L2X_REVID_A0) + goto err_revid; + + ret = regmap_register_patch(regmap, cs40l2x_rev_a0_errata, + ARRAY_SIZE(cs40l2x_rev_a0_errata)); + if (ret) { + dev_err(dev, "Failed to apply revision %02X errata\n", + revid); + return ret; + } + + ret = cs40l2x_otp_unpack(cs40l2x); + if (ret) + return ret; + break; + case CS40L2X_DEVID_L25: + part_num_index = 1; + if (revid != CS40L2X_REVID_B0) + goto err_revid; + break; + case CS40L2X_DEVID_L25A: + case CS40L2X_DEVID_L25B: + part_num_index = devid - CS40L2X_DEVID_L25A + 2; + if (revid < CS40L2X_REVID_B1) + goto err_revid; + break; + default: + dev_err(dev, "Unrecognized device ID: 0x%06X\n", devid); + return -ENODEV; + } + + dev_info(dev, "Cirrus Logic %s revision %02X\n", + cs40l2x_part_nums[part_num_index], revid); + cs40l2x->devid = devid; + cs40l2x->revid = revid; + + return 0; +err_revid: + dev_err(dev, "Unexpected revision ID for %s: %02X\n", + cs40l2x_part_nums[part_num_index], revid); + return -ENODEV; +} + static struct regmap_config cs40l2x_regmap = { .reg_bits = 32, .val_bits = 32, @@ -1743,7 +1818,6 @@ static int cs40l2x_i2c_probe(struct i2c_client *i2c_client, struct cs40l2x_private *cs40l2x; struct device *dev = &i2c_client->dev; struct cs40l2x_platform_data *pdata = dev_get_platdata(dev); - unsigned int reg_devid, reg_revid; cs40l2x = devm_kzalloc(dev, sizeof(struct cs40l2x_private), GFP_KERNEL); if (!cs40l2x) @@ -1810,45 +1884,10 @@ static int cs40l2x_i2c_probe(struct i2c_client *i2c_client, /* satisfy control port delay specification (with margin) */ usleep_range(1000, 1100); - ret = regmap_read(cs40l2x->regmap, CS40L2X_DEVID, ®_devid); - if (ret) { - dev_err(dev, "Failed to read device ID\n"); - goto err; - } - - if (reg_devid != CS40L2X_CHIP_ID) { - dev_err(dev, "Failed to recognize device ID: %X\n", reg_devid); - ret = -ENODEV; - goto err; - } - - ret = regmap_read(cs40l2x->regmap, CS40L2X_REVID, ®_revid); - if (ret) { - dev_err(dev, "Failed to read revision ID\n"); - goto err; - } - - if (reg_revid != CS40L2X_REVID_A0) { - dev_err(dev, "Failed to recognize revision ID: %02X\n", - reg_revid); - ret = -ENODEV; - goto err; - } - - ret = regmap_register_patch(cs40l2x->regmap, cs40l2x_rev_a0_errata, - ARRAY_SIZE(cs40l2x_rev_a0_errata)); - if (ret) { - dev_err(dev, "Failed to apply revision %02X errata\n", - reg_revid); - goto err; - } - - ret = cs40l2x_otp_unpack(cs40l2x); + ret = cs40l2x_part_num_resolve(cs40l2x); if (ret) goto err; - dev_info(dev, "Cirrus Logic CS40L20 revision %02X\n", reg_revid); - ret = cs40l2x_init(cs40l2x); if (ret) goto err; diff --git a/drivers/misc/cs40l2x.h b/drivers/misc/cs40l2x.h index 9344f6b3ec8e..11d49707ac21 100644 --- a/drivers/misc/cs40l2x.h +++ b/drivers/misc/cs40l2x.h @@ -669,9 +669,14 @@ #define CS40L2X_INT1_UNMASK_PUP 0xFEFFFFFF #define CS40L2X_INT1_UNMASK_PDN 0xFF7FFFFF -#define CS40L2X_CHIP_ID 0x35A40 +#define CS40L2X_DEVID_L20 0x035A40 +#define CS40L2X_DEVID_L25 0x40A250 +#define CS40L2X_DEVID_L25A 0x40A25A +#define CS40L2X_DEVID_L25B 0x40A25B #define CS40L2X_REVID_A0 0xA0 +#define CS40L2X_REVID_B0 0xB0 +#define CS40L2X_REVID_B1 0xB1 #define CS40L2X_MPU_UNLOCK_CODE1 0x5555 #define CS40L2X_MPU_UNLOCK_CODE2 0xAAAA