From 98d5fb77b8ac68aab704cbb0bd1fa70619faaa26 Mon Sep 17 00:00:00 2001 From: Jeff LaBundy Date: Thu, 12 Apr 2018 19:26:36 -0500 Subject: [PATCH] misc: cs40l2x: Add basic-mode haptics shutdown sequence Added a shutdown sequence to safely exit basic-mode haptics following device power-on, prior to re-initializing the device and booting the DSP into normal operation. Change-Id: I3955f60fa736071d0af80edc65777100ef9865b0 Signed-off-by: Jeff LaBundy Signed-off-by: Ravi Vembu --- drivers/misc/cs40l2x.c | 83 +++++++++++++++++++++++++++++++++++++++++- drivers/misc/cs40l2x.h | 31 ++++++++++++++++ 2 files changed, 112 insertions(+), 2 deletions(-) diff --git a/drivers/misc/cs40l2x.c b/drivers/misc/cs40l2x.c index 282178240a1b..e03cb6f94982 100644 --- a/drivers/misc/cs40l2x.c +++ b/drivers/misc/cs40l2x.c @@ -1591,8 +1591,6 @@ static void cs40l2x_dsp_start(struct cs40l2x_private *cs40l2x) return; } - dev_info(dev, "Haptics algorithm started\n"); - ret = regmap_write(regmap, cs40l2x_dsp_reg(cs40l2x, "TIMEOUT_MS", CS40L2X_XM_UNPACKED_TYPE), CS40L2X_TIMEOUT_MS_MAX); if (ret) { @@ -1612,6 +1610,8 @@ static void cs40l2x_dsp_start(struct cs40l2x_private *cs40l2x) return; } + dev_info(dev, "Normal-mode haptics successfully started\n"); + cs40l2x_vibe_init(cs40l2x); } @@ -2286,6 +2286,67 @@ static int cs40l2x_handle_of_data(struct i2c_client *i2c_client, return 0; } +static int cs40l2x_basic_mode_exit(struct cs40l2x_private *cs40l2x) +{ + struct regmap *regmap = cs40l2x->regmap; + struct device *dev = cs40l2x->dev; + unsigned int val; + int shutdown_timeout = CS40L2X_BASIC_TIMEOUT_COUNT; + int ret; + + ret = regmap_read(regmap, CS40L2X_BASIC_AMP_STATUS, &val); + if (ret) { + dev_err(dev, "Failed to read basic-mode status\n"); + return ret; + } + + if (val != CS40L2X_BASIC_BOOT_DONE) { + dev_err(dev, "Unexpected basic-mode status: %02X\n", val); + return -EIO; + } + + ret = regmap_write(regmap, CS40L2X_BASIC_SHUTDOWNREQUEST, 1); + if (ret) { + dev_err(dev, "Failed to write shutdown request\n"); + return ret; + } + + while (shutdown_timeout > 0) { + usleep_range(10000, 10100); + + ret = regmap_read(regmap, CS40L2X_BASIC_SHUTDOWNREQUEST, &val); + if (ret) { + dev_err(dev, "Failed to read shutdown request\n"); + return ret; + } + + if (!val) + break; + + shutdown_timeout--; + } + + if (shutdown_timeout == 0) { + dev_err(dev, "Timed out waiting for basic-mode shutdown\n"); + return -ETIME; + } + + ret = regmap_read(regmap, CS40L2X_BASIC_STATEMACHINE, &val); + if (ret) { + dev_err(dev, "Failed to read basic-mode state\n"); + return ret; + } + + if (val != CS40L2X_BASIC_SHUTDOWN) { + dev_err(dev, "Unexpected basic-mode state: %02X\n", val); + return -EBUSY; + } + + dev_info(dev, "Basic-mode haptics successfully stopped\n"); + + return 0; +} + static const struct reg_sequence cs40l2x_rev_a0_errata[] = { {CS40L2X_OTP_TRIM_30, 0x9091A1C8}, {CS40L2X_PLL_LOOP_PARAM, 0x00001837}, @@ -2301,6 +2362,13 @@ static const struct reg_sequence cs40l2x_rev_b0_errata[] = { {CS40L2X_PLL_MISC_CTRL, 0x03008E0E}, }; +static const struct reg_sequence cs40l2x_basic_mode_revert[] = { + {CS40L2X_PWR_CTRL1, 0x00000000}, + {CS40L2X_PWR_CTRL2, 0x00003321}, + {CS40L2X_IRQ2_MASK1, 0xFFFFFFFF}, + {CS40L2X_IRQ2_MASK2, 0xFFFFFFFF}, +}; + static int cs40l2x_part_num_resolve(struct cs40l2x_private *cs40l2x) { struct regmap *regmap = cs40l2x->regmap; @@ -2390,6 +2458,17 @@ static int cs40l2x_part_num_resolve(struct cs40l2x_private *cs40l2x) if (revid < CS40L2X_REVID_B1) goto err_revid; + ret = cs40l2x_basic_mode_exit(cs40l2x); + if (ret) + return ret; + + ret = regmap_multi_reg_write(regmap, cs40l2x_basic_mode_revert, + ARRAY_SIZE(cs40l2x_basic_mode_revert)); + if (ret) { + dev_err(dev, "Failed to revert basic-mode fields\n"); + return ret; + } + ret = regmap_register_patch(regmap, cs40l2x_rev_b0_errata, ARRAY_SIZE(cs40l2x_rev_b0_errata)); if (ret) { diff --git a/drivers/misc/cs40l2x.h b/drivers/misc/cs40l2x.h index f47d6dac1b21..654ac83e50b1 100644 --- a/drivers/misc/cs40l2x.h +++ b/drivers/misc/cs40l2x.h @@ -538,6 +538,35 @@ #define CS40L2X_OTP_TRIM_35 0x0000400C #define CS40L2X_OTP_TRIM_36 0x00002030 +/* basic mode (ROM) fields */ +#define CS40L2X_BASIC_HALO_STATE 0x02800154 +#define CS40L2X_BASIC_HALO_HEARTBEAT 0x02800158 +#define CS40L2X_BASIC_STATEMACHINE 0X0280015C +#define CS40L2X_BASIC_F0_STORED 0X02800174 +#define CS40L2X_BASIC_GPIO_TRIGGER_EDGE 0x02800178 +#define CS40L2X_BASIC_BUZZ_FREQ 0x0280017C +#define CS40L2X_BASIC_BUZZ_LEVEL 0x02800180 +#define CS40L2X_BASIC_BUZZ_DURATION 0x02800184 +#define CS40L2X_BASIC_BUZZ_TRIGGER 0x02800188 +#define CS40L2X_BASIC_AMP_STATUS 0x0280018C +#define CS40L2X_BASIC_SHUTDOWNREQUEST 0x02800190 + +/* basic mode (ROM) states */ +#define CS40L2X_BASIC_IDLE 0 +#define CS40L2X_BASIC_PLAY_TONE 1 +#define CS40L2X_BASIC_CONTINUE_TONE 2 +#define CS40L2X_BASIC_SHUTTINGDOWN 3 +#define CS40L2X_BASIC_SHUTDOWN 4 + +/* basic mode (ROM) status flags */ +#define CS40L2X_BASIC_NO_STATUS 0x00 +#define CS40L2X_BASIC_BOOT_DONE 0x01 +#define CS40L2X_BASIC_OTP_ERROR 0x02 +#define CS40L2X_BASIC_AMP_ERROR 0x04 +#define CS40L2X_BASIC_TEMP_RISE_WARN 0x08 +#define CS40L2X_BASIC_TEMP_ERROR 0x10 +#define CS40L2X_BASIC_SA_ERROR 0x20 + #define CS40L2X_GLOBAL_EN_MASK 0x00000001 #define CS40L2X_GLOBAL_EN_SHIFT 0 @@ -678,6 +707,8 @@ #define CS40L2X_DSP_TIMEOUT_COUNT 10 /* 1 count = 10 ms */ #define CS40L2X_OTP_TIMEOUT_COUNT 10 +#define CS40L2X_BASIC_TIMEOUT_COUNT 10 + #define CS40L2X_NUM_OTP_MAPS 3 #define CS40L2X_NUM_OTP_WORDS 32