Loading drivers/mfd/twl-core.c +163 −27 Original line number Original line Diff line number Diff line Loading @@ -47,6 +47,9 @@ #include <linux/i2c.h> #include <linux/i2c.h> #include <linux/i2c/twl.h> #include <linux/i2c/twl.h> /* Register descriptions for audio */ #include <linux/mfd/twl4030-audio.h> #include "twl-core.h" #include "twl-core.h" /* /* Loading Loading @@ -200,6 +203,105 @@ static struct twl_mapping twl4030_map[] = { { 2, TWL5031_BASEADD_INTERRUPTS }, { 2, TWL5031_BASEADD_INTERRUPTS }, }; }; static struct reg_default twl4030_49_defaults[] = { /* Audio Registers */ { 0x01, 0x00}, /* CODEC_MODE */ { 0x02, 0x00}, /* OPTION */ /* 0x03 Unused */ { 0x04, 0x00}, /* MICBIAS_CTL */ { 0x05, 0x00}, /* ANAMICL */ { 0x06, 0x00}, /* ANAMICR */ { 0x07, 0x00}, /* AVADC_CTL */ { 0x08, 0x00}, /* ADCMICSEL */ { 0x09, 0x00}, /* DIGMIXING */ { 0x0a, 0x0f}, /* ATXL1PGA */ { 0x0b, 0x0f}, /* ATXR1PGA */ { 0x0c, 0x0f}, /* AVTXL2PGA */ { 0x0d, 0x0f}, /* AVTXR2PGA */ { 0x0e, 0x00}, /* AUDIO_IF */ { 0x0f, 0x00}, /* VOICE_IF */ { 0x10, 0x3f}, /* ARXR1PGA */ { 0x11, 0x3f}, /* ARXL1PGA */ { 0x12, 0x3f}, /* ARXR2PGA */ { 0x13, 0x3f}, /* ARXL2PGA */ { 0x14, 0x25}, /* VRXPGA */ { 0x15, 0x00}, /* VSTPGA */ { 0x16, 0x00}, /* VRX2ARXPGA */ { 0x17, 0x00}, /* AVDAC_CTL */ { 0x18, 0x00}, /* ARX2VTXPGA */ { 0x19, 0x32}, /* ARXL1_APGA_CTL*/ { 0x1a, 0x32}, /* ARXR1_APGA_CTL*/ { 0x1b, 0x32}, /* ARXL2_APGA_CTL*/ { 0x1c, 0x32}, /* ARXR2_APGA_CTL*/ { 0x1d, 0x00}, /* ATX2ARXPGA */ { 0x1e, 0x00}, /* BT_IF */ { 0x1f, 0x55}, /* BTPGA */ { 0x20, 0x00}, /* BTSTPGA */ { 0x21, 0x00}, /* EAR_CTL */ { 0x22, 0x00}, /* HS_SEL */ { 0x23, 0x00}, /* HS_GAIN_SET */ { 0x24, 0x00}, /* HS_POPN_SET */ { 0x25, 0x00}, /* PREDL_CTL */ { 0x26, 0x00}, /* PREDR_CTL */ { 0x27, 0x00}, /* PRECKL_CTL */ { 0x28, 0x00}, /* PRECKR_CTL */ { 0x29, 0x00}, /* HFL_CTL */ { 0x2a, 0x00}, /* HFR_CTL */ { 0x2b, 0x05}, /* ALC_CTL */ { 0x2c, 0x00}, /* ALC_SET1 */ { 0x2d, 0x00}, /* ALC_SET2 */ { 0x2e, 0x00}, /* BOOST_CTL */ { 0x2f, 0x00}, /* SOFTVOL_CTL */ { 0x30, 0x13}, /* DTMF_FREQSEL */ { 0x31, 0x00}, /* DTMF_TONEXT1H */ { 0x32, 0x00}, /* DTMF_TONEXT1L */ { 0x33, 0x00}, /* DTMF_TONEXT2H */ { 0x34, 0x00}, /* DTMF_TONEXT2L */ { 0x35, 0x79}, /* DTMF_TONOFF */ { 0x36, 0x11}, /* DTMF_WANONOFF */ { 0x37, 0x00}, /* I2S_RX_SCRAMBLE_H */ { 0x38, 0x00}, /* I2S_RX_SCRAMBLE_M */ { 0x39, 0x00}, /* I2S_RX_SCRAMBLE_L */ { 0x3a, 0x06}, /* APLL_CTL */ { 0x3b, 0x00}, /* DTMF_CTL */ { 0x3c, 0x44}, /* DTMF_PGA_CTL2 (0x3C) */ { 0x3d, 0x69}, /* DTMF_PGA_CTL1 (0x3D) */ { 0x3e, 0x00}, /* MISC_SET_1 */ { 0x3f, 0x00}, /* PCMBTMUX */ /* 0x40 - 0x42 Unused */ { 0x43, 0x00}, /* RX_PATH_SEL */ { 0x44, 0x32}, /* VDL_APGA_CTL */ { 0x45, 0x00}, /* VIBRA_CTL */ { 0x46, 0x00}, /* VIBRA_SET */ { 0x47, 0x00}, /* VIBRA_PWM_SET */ { 0x48, 0x00}, /* ANAMIC_GAIN */ { 0x49, 0x00}, /* MISC_SET_2 */ /* End of Audio Registers */ }; static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg) { switch (reg) { case 0: case 3: case 40: case 41: case 42: return false; default: return true; } } static const struct regmap_range twl4030_49_volatile_ranges[] = { regmap_reg_range(TWL4030_BASEADD_TEST, 0xff), }; static const struct regmap_access_table twl4030_49_volatile_table = { .yes_ranges = twl4030_49_volatile_ranges, .n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges), }; static struct regmap_config twl4030_regmap_config[4] = { static struct regmap_config twl4030_regmap_config[4] = { { { /* Address 0x48 */ /* Address 0x48 */ Loading @@ -212,6 +314,15 @@ static struct regmap_config twl4030_regmap_config[4] = { .reg_bits = 8, .reg_bits = 8, .val_bits = 8, .val_bits = 8, .max_register = 0xff, .max_register = 0xff, .readable_reg = twl4030_49_nop_reg, .writeable_reg = twl4030_49_nop_reg, .volatile_table = &twl4030_49_volatile_table, .reg_defaults = twl4030_49_defaults, .num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults), .cache_type = REGCACHE_RBTREE, }, }, { { /* Address 0x4a */ /* Address 0x4a */ Loading Loading @@ -302,35 +413,50 @@ unsigned int twl_rev(void) EXPORT_SYMBOL(twl_rev); EXPORT_SYMBOL(twl_rev); /** /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * twl_get_regmap - Get the regmap associated with the given module * @mod_no: module number * @mod_no: module number * @value: an array of num_bytes+1 containing data to write * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * * Returns the result of operation - 0 is success * Returns the regmap pointer or NULL in case of failure. */ */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) static struct regmap *twl_get_regmap(u8 mod_no) { { int ret; int sid; int sid; struct twl_client *twl; struct twl_client *twl; if (unlikely(!twl_priv || !twl_priv->ready)) { if (unlikely(!twl_priv || !twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; return NULL; } } if (unlikely(mod_no >= twl_get_last_module())) { if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; return NULL; } } sid = twl_priv->twl_map[mod_no].sid; sid = twl_priv->twl_map[mod_no].sid; twl = &twl_priv->twl_modules[sid]; twl = &twl_priv->twl_modules[sid]; ret = regmap_bulk_write(twl->regmap, return twl->regmap; twl_priv->twl_map[mod_no].base + reg, value, } num_bytes); /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number * @value: an array of num_bytes+1 containing data to write * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * Returns the result of operation - 0 is success */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { struct regmap *regmap = twl_get_regmap(mod_no); int ret; if (!regmap) return -EPERM; ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg, value, num_bytes); if (ret) if (ret) pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", Loading @@ -351,25 +477,14 @@ EXPORT_SYMBOL(twl_i2c_write); */ */ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { { struct regmap *regmap = twl_get_regmap(mod_no); int ret; int ret; int sid; struct twl_client *twl; if (unlikely(!twl_priv || !twl_priv->ready)) { if (!regmap) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; return -EPERM; } sid = twl_priv->twl_map[mod_no].sid; twl = &twl_priv->twl_modules[sid]; ret = regmap_bulk_read(twl->regmap, ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg, twl_priv->twl_map[mod_no].base + reg, value, value, num_bytes); num_bytes); if (ret) if (ret) pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", Loading @@ -379,6 +494,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } } EXPORT_SYMBOL(twl_i2c_read); EXPORT_SYMBOL(twl_i2c_read); /** * twl_regcache_bypass - Configure the regcache bypass for the regmap associated * with the module * @mod_no: module number * @enable: Regcache bypass state * * Returns 0 else failure. */ int twl_set_regcache_bypass(u8 mod_no, bool enable) { struct regmap *regmap = twl_get_regmap(mod_no); if (!regmap) return -EPERM; regcache_cache_bypass(regmap, enable); return 0; } EXPORT_SYMBOL(twl_set_regcache_bypass); /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /** /** Loading drivers/mfd/twl6040.c +89 −3 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,54 @@ #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define TWL6040_NUM_SUPPLIES (2) #define TWL6040_NUM_SUPPLIES (2) static struct reg_default twl6040_defaults[] = { { 0x01, 0x4B }, /* REG_ASICID (ro) */ { 0x02, 0x00 }, /* REG_ASICREV (ro) */ { 0x03, 0x00 }, /* REG_INTID */ { 0x04, 0x00 }, /* REG_INTMR */ { 0x05, 0x00 }, /* REG_NCPCTRL */ { 0x06, 0x00 }, /* REG_LDOCTL */ { 0x07, 0x60 }, /* REG_HPPLLCTL */ { 0x08, 0x00 }, /* REG_LPPLLCTL */ { 0x09, 0x4A }, /* REG_LPPLLDIV */ { 0x0A, 0x00 }, /* REG_AMICBCTL */ { 0x0B, 0x00 }, /* REG_DMICBCTL */ { 0x0C, 0x00 }, /* REG_MICLCTL */ { 0x0D, 0x00 }, /* REG_MICRCTL */ { 0x0E, 0x00 }, /* REG_MICGAIN */ { 0x0F, 0x1B }, /* REG_LINEGAIN */ { 0x10, 0x00 }, /* REG_HSLCTL */ { 0x11, 0x00 }, /* REG_HSRCTL */ { 0x12, 0x00 }, /* REG_HSGAIN */ { 0x13, 0x00 }, /* REG_EARCTL */ { 0x14, 0x00 }, /* REG_HFLCTL */ { 0x15, 0x00 }, /* REG_HFLGAIN */ { 0x16, 0x00 }, /* REG_HFRCTL */ { 0x17, 0x00 }, /* REG_HFRGAIN */ { 0x18, 0x00 }, /* REG_VIBCTLL */ { 0x19, 0x00 }, /* REG_VIBDATL */ { 0x1A, 0x00 }, /* REG_VIBCTLR */ { 0x1B, 0x00 }, /* REG_VIBDATR */ { 0x1C, 0x00 }, /* REG_HKCTL1 */ { 0x1D, 0x00 }, /* REG_HKCTL2 */ { 0x1E, 0x00 }, /* REG_GPOCTL */ { 0x1F, 0x00 }, /* REG_ALB */ { 0x20, 0x00 }, /* REG_DLB */ /* 0x28, REG_TRIM1 */ /* 0x29, REG_TRIM2 */ /* 0x2A, REG_TRIM3 */ /* 0x2B, REG_HSOTRIM */ /* 0x2C, REG_HFOTRIM */ { 0x2D, 0x08 }, /* REG_ACCCTL */ { 0x2E, 0x00 }, /* REG_STATUS (ro) */ }; struct reg_default twl6040_patch[] = { /* Select I2C bus access to dual access registers */ { TWL6040_REG_ACCCTL, 0x09 }, }; static bool twl6040_has_vibra(struct device_node *node) static bool twl6040_has_vibra(struct device_node *node) { { #ifdef CONFIG_OF #ifdef CONFIG_OF Loading Loading @@ -238,6 +286,9 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (twl6040->power_count++) if (twl6040->power_count++) goto out; goto out; /* Allow writes to the chip */ regcache_cache_only(twl6040->regmap, false); if (gpio_is_valid(twl6040->audpwron)) { if (gpio_is_valid(twl6040->audpwron)) { /* use automatic power-up sequence */ /* use automatic power-up sequence */ ret = twl6040_power_up_automatic(twl6040); ret = twl6040_power_up_automatic(twl6040); Loading @@ -253,6 +304,10 @@ int twl6040_power(struct twl6040 *twl6040, int on) goto out; goto out; } } } } /* Sync with the HW */ regcache_sync(twl6040->regmap); /* Default PLL configuration after power up */ /* Default PLL configuration after power up */ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->sysclk = 19200000; twl6040->sysclk = 19200000; Loading @@ -279,6 +334,11 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use manual power-down sequence */ /* use manual power-down sequence */ twl6040_power_down_manual(twl6040); twl6040_power_down_manual(twl6040); } } /* Set regmap to cache only and mark it as dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); twl6040->sysclk = 0; twl6040->sysclk = 0; twl6040->mclk = 0; twl6040->mclk = 0; } } Loading Loading @@ -490,9 +550,24 @@ static bool twl6040_readable_reg(struct device *dev, unsigned int reg) static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) { { switch (reg) { switch (reg) { case TWL6040_REG_VIBCTLL: case TWL6040_REG_ASICID: case TWL6040_REG_VIBCTLR: case TWL6040_REG_ASICREV: case TWL6040_REG_INTMR: case TWL6040_REG_INTID: case TWL6040_REG_LPPLLCTL: case TWL6040_REG_HPPLLCTL: case TWL6040_REG_STATUS: return true; default: return false; } } static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: case TWL6040_REG_STATUS: return false; return false; default: default: return true; return true; Loading @@ -502,10 +577,15 @@ static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) static struct regmap_config twl6040_regmap_config = { static struct regmap_config twl6040_regmap_config = { .reg_bits = 8, .reg_bits = 8, .val_bits = 8, .val_bits = 8, .reg_defaults = twl6040_defaults, .num_reg_defaults = ARRAY_SIZE(twl6040_defaults), .max_register = TWL6040_REG_STATUS, /* 0x2e */ .max_register = TWL6040_REG_STATUS, /* 0x2e */ .readable_reg = twl6040_readable_reg, .readable_reg = twl6040_readable_reg, .volatile_reg = twl6040_volatile_reg, .volatile_reg = twl6040_volatile_reg, .writeable_reg = twl6040_writeable_reg, .cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE, }; }; Loading Loading @@ -624,6 +704,8 @@ static int twl6040_probe(struct i2c_client *client, /* dual-access registers controlled by I2C only */ /* dual-access registers controlled by I2C only */ twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); regmap_register_patch(twl6040->regmap, twl6040_patch, ARRAY_SIZE(twl6040_patch)); /* /* * The main functionality of twl6040 to provide audio on OMAP4+ systems. * The main functionality of twl6040 to provide audio on OMAP4+ systems. Loading Loading @@ -656,6 +738,10 @@ static int twl6040_probe(struct i2c_client *client, cell->name = "twl6040-gpo"; cell->name = "twl6040-gpo"; children++; children++; /* The chip is powered down so mark regmap to cache only and dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, NULL, 0, NULL); NULL, 0, NULL); if (ret) if (ret) Loading include/linux/i2c/twl.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -175,6 +175,9 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) /* Set the regcache bypass for the regmap associated with the nodule */ int twl_set_regcache_bypass(u8 mod_no, bool enable); /* /* * Read and write several 8-bit registers at once. * Read and write several 8-bit registers at once. */ */ Loading Loading
drivers/mfd/twl-core.c +163 −27 Original line number Original line Diff line number Diff line Loading @@ -47,6 +47,9 @@ #include <linux/i2c.h> #include <linux/i2c.h> #include <linux/i2c/twl.h> #include <linux/i2c/twl.h> /* Register descriptions for audio */ #include <linux/mfd/twl4030-audio.h> #include "twl-core.h" #include "twl-core.h" /* /* Loading Loading @@ -200,6 +203,105 @@ static struct twl_mapping twl4030_map[] = { { 2, TWL5031_BASEADD_INTERRUPTS }, { 2, TWL5031_BASEADD_INTERRUPTS }, }; }; static struct reg_default twl4030_49_defaults[] = { /* Audio Registers */ { 0x01, 0x00}, /* CODEC_MODE */ { 0x02, 0x00}, /* OPTION */ /* 0x03 Unused */ { 0x04, 0x00}, /* MICBIAS_CTL */ { 0x05, 0x00}, /* ANAMICL */ { 0x06, 0x00}, /* ANAMICR */ { 0x07, 0x00}, /* AVADC_CTL */ { 0x08, 0x00}, /* ADCMICSEL */ { 0x09, 0x00}, /* DIGMIXING */ { 0x0a, 0x0f}, /* ATXL1PGA */ { 0x0b, 0x0f}, /* ATXR1PGA */ { 0x0c, 0x0f}, /* AVTXL2PGA */ { 0x0d, 0x0f}, /* AVTXR2PGA */ { 0x0e, 0x00}, /* AUDIO_IF */ { 0x0f, 0x00}, /* VOICE_IF */ { 0x10, 0x3f}, /* ARXR1PGA */ { 0x11, 0x3f}, /* ARXL1PGA */ { 0x12, 0x3f}, /* ARXR2PGA */ { 0x13, 0x3f}, /* ARXL2PGA */ { 0x14, 0x25}, /* VRXPGA */ { 0x15, 0x00}, /* VSTPGA */ { 0x16, 0x00}, /* VRX2ARXPGA */ { 0x17, 0x00}, /* AVDAC_CTL */ { 0x18, 0x00}, /* ARX2VTXPGA */ { 0x19, 0x32}, /* ARXL1_APGA_CTL*/ { 0x1a, 0x32}, /* ARXR1_APGA_CTL*/ { 0x1b, 0x32}, /* ARXL2_APGA_CTL*/ { 0x1c, 0x32}, /* ARXR2_APGA_CTL*/ { 0x1d, 0x00}, /* ATX2ARXPGA */ { 0x1e, 0x00}, /* BT_IF */ { 0x1f, 0x55}, /* BTPGA */ { 0x20, 0x00}, /* BTSTPGA */ { 0x21, 0x00}, /* EAR_CTL */ { 0x22, 0x00}, /* HS_SEL */ { 0x23, 0x00}, /* HS_GAIN_SET */ { 0x24, 0x00}, /* HS_POPN_SET */ { 0x25, 0x00}, /* PREDL_CTL */ { 0x26, 0x00}, /* PREDR_CTL */ { 0x27, 0x00}, /* PRECKL_CTL */ { 0x28, 0x00}, /* PRECKR_CTL */ { 0x29, 0x00}, /* HFL_CTL */ { 0x2a, 0x00}, /* HFR_CTL */ { 0x2b, 0x05}, /* ALC_CTL */ { 0x2c, 0x00}, /* ALC_SET1 */ { 0x2d, 0x00}, /* ALC_SET2 */ { 0x2e, 0x00}, /* BOOST_CTL */ { 0x2f, 0x00}, /* SOFTVOL_CTL */ { 0x30, 0x13}, /* DTMF_FREQSEL */ { 0x31, 0x00}, /* DTMF_TONEXT1H */ { 0x32, 0x00}, /* DTMF_TONEXT1L */ { 0x33, 0x00}, /* DTMF_TONEXT2H */ { 0x34, 0x00}, /* DTMF_TONEXT2L */ { 0x35, 0x79}, /* DTMF_TONOFF */ { 0x36, 0x11}, /* DTMF_WANONOFF */ { 0x37, 0x00}, /* I2S_RX_SCRAMBLE_H */ { 0x38, 0x00}, /* I2S_RX_SCRAMBLE_M */ { 0x39, 0x00}, /* I2S_RX_SCRAMBLE_L */ { 0x3a, 0x06}, /* APLL_CTL */ { 0x3b, 0x00}, /* DTMF_CTL */ { 0x3c, 0x44}, /* DTMF_PGA_CTL2 (0x3C) */ { 0x3d, 0x69}, /* DTMF_PGA_CTL1 (0x3D) */ { 0x3e, 0x00}, /* MISC_SET_1 */ { 0x3f, 0x00}, /* PCMBTMUX */ /* 0x40 - 0x42 Unused */ { 0x43, 0x00}, /* RX_PATH_SEL */ { 0x44, 0x32}, /* VDL_APGA_CTL */ { 0x45, 0x00}, /* VIBRA_CTL */ { 0x46, 0x00}, /* VIBRA_SET */ { 0x47, 0x00}, /* VIBRA_PWM_SET */ { 0x48, 0x00}, /* ANAMIC_GAIN */ { 0x49, 0x00}, /* MISC_SET_2 */ /* End of Audio Registers */ }; static bool twl4030_49_nop_reg(struct device *dev, unsigned int reg) { switch (reg) { case 0: case 3: case 40: case 41: case 42: return false; default: return true; } } static const struct regmap_range twl4030_49_volatile_ranges[] = { regmap_reg_range(TWL4030_BASEADD_TEST, 0xff), }; static const struct regmap_access_table twl4030_49_volatile_table = { .yes_ranges = twl4030_49_volatile_ranges, .n_yes_ranges = ARRAY_SIZE(twl4030_49_volatile_ranges), }; static struct regmap_config twl4030_regmap_config[4] = { static struct regmap_config twl4030_regmap_config[4] = { { { /* Address 0x48 */ /* Address 0x48 */ Loading @@ -212,6 +314,15 @@ static struct regmap_config twl4030_regmap_config[4] = { .reg_bits = 8, .reg_bits = 8, .val_bits = 8, .val_bits = 8, .max_register = 0xff, .max_register = 0xff, .readable_reg = twl4030_49_nop_reg, .writeable_reg = twl4030_49_nop_reg, .volatile_table = &twl4030_49_volatile_table, .reg_defaults = twl4030_49_defaults, .num_reg_defaults = ARRAY_SIZE(twl4030_49_defaults), .cache_type = REGCACHE_RBTREE, }, }, { { /* Address 0x4a */ /* Address 0x4a */ Loading Loading @@ -302,35 +413,50 @@ unsigned int twl_rev(void) EXPORT_SYMBOL(twl_rev); EXPORT_SYMBOL(twl_rev); /** /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * twl_get_regmap - Get the regmap associated with the given module * @mod_no: module number * @mod_no: module number * @value: an array of num_bytes+1 containing data to write * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * * Returns the result of operation - 0 is success * Returns the regmap pointer or NULL in case of failure. */ */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) static struct regmap *twl_get_regmap(u8 mod_no) { { int ret; int sid; int sid; struct twl_client *twl; struct twl_client *twl; if (unlikely(!twl_priv || !twl_priv->ready)) { if (unlikely(!twl_priv || !twl_priv->ready)) { pr_err("%s: not initialized\n", DRIVER_NAME); pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; return NULL; } } if (unlikely(mod_no >= twl_get_last_module())) { if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; return NULL; } } sid = twl_priv->twl_map[mod_no].sid; sid = twl_priv->twl_map[mod_no].sid; twl = &twl_priv->twl_modules[sid]; twl = &twl_priv->twl_modules[sid]; ret = regmap_bulk_write(twl->regmap, return twl->regmap; twl_priv->twl_map[mod_no].base + reg, value, } num_bytes); /** * twl_i2c_write - Writes a n bit register in TWL4030/TWL5030/TWL60X0 * @mod_no: module number * @value: an array of num_bytes+1 containing data to write * @reg: register address (just offset will do) * @num_bytes: number of bytes to transfer * * Returns the result of operation - 0 is success */ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { struct regmap *regmap = twl_get_regmap(mod_no); int ret; if (!regmap) return -EPERM; ret = regmap_bulk_write(regmap, twl_priv->twl_map[mod_no].base + reg, value, num_bytes); if (ret) if (ret) pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", Loading @@ -351,25 +477,14 @@ EXPORT_SYMBOL(twl_i2c_write); */ */ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) { { struct regmap *regmap = twl_get_regmap(mod_no); int ret; int ret; int sid; struct twl_client *twl; if (unlikely(!twl_priv || !twl_priv->ready)) { if (!regmap) pr_err("%s: not initialized\n", DRIVER_NAME); return -EPERM; } if (unlikely(mod_no >= twl_get_last_module())) { pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); return -EPERM; return -EPERM; } sid = twl_priv->twl_map[mod_no].sid; twl = &twl_priv->twl_modules[sid]; ret = regmap_bulk_read(twl->regmap, ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg, twl_priv->twl_map[mod_no].base + reg, value, value, num_bytes); num_bytes); if (ret) if (ret) pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", Loading @@ -379,6 +494,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes) } } EXPORT_SYMBOL(twl_i2c_read); EXPORT_SYMBOL(twl_i2c_read); /** * twl_regcache_bypass - Configure the regcache bypass for the regmap associated * with the module * @mod_no: module number * @enable: Regcache bypass state * * Returns 0 else failure. */ int twl_set_regcache_bypass(u8 mod_no, bool enable) { struct regmap *regmap = twl_get_regmap(mod_no); if (!regmap) return -EPERM; regcache_cache_bypass(regmap, enable); return 0; } EXPORT_SYMBOL(twl_set_regcache_bypass); /*----------------------------------------------------------------------*/ /*----------------------------------------------------------------------*/ /** /** Loading
drivers/mfd/twl6040.c +89 −3 Original line number Original line Diff line number Diff line Loading @@ -44,6 +44,54 @@ #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) #define TWL6040_NUM_SUPPLIES (2) #define TWL6040_NUM_SUPPLIES (2) static struct reg_default twl6040_defaults[] = { { 0x01, 0x4B }, /* REG_ASICID (ro) */ { 0x02, 0x00 }, /* REG_ASICREV (ro) */ { 0x03, 0x00 }, /* REG_INTID */ { 0x04, 0x00 }, /* REG_INTMR */ { 0x05, 0x00 }, /* REG_NCPCTRL */ { 0x06, 0x00 }, /* REG_LDOCTL */ { 0x07, 0x60 }, /* REG_HPPLLCTL */ { 0x08, 0x00 }, /* REG_LPPLLCTL */ { 0x09, 0x4A }, /* REG_LPPLLDIV */ { 0x0A, 0x00 }, /* REG_AMICBCTL */ { 0x0B, 0x00 }, /* REG_DMICBCTL */ { 0x0C, 0x00 }, /* REG_MICLCTL */ { 0x0D, 0x00 }, /* REG_MICRCTL */ { 0x0E, 0x00 }, /* REG_MICGAIN */ { 0x0F, 0x1B }, /* REG_LINEGAIN */ { 0x10, 0x00 }, /* REG_HSLCTL */ { 0x11, 0x00 }, /* REG_HSRCTL */ { 0x12, 0x00 }, /* REG_HSGAIN */ { 0x13, 0x00 }, /* REG_EARCTL */ { 0x14, 0x00 }, /* REG_HFLCTL */ { 0x15, 0x00 }, /* REG_HFLGAIN */ { 0x16, 0x00 }, /* REG_HFRCTL */ { 0x17, 0x00 }, /* REG_HFRGAIN */ { 0x18, 0x00 }, /* REG_VIBCTLL */ { 0x19, 0x00 }, /* REG_VIBDATL */ { 0x1A, 0x00 }, /* REG_VIBCTLR */ { 0x1B, 0x00 }, /* REG_VIBDATR */ { 0x1C, 0x00 }, /* REG_HKCTL1 */ { 0x1D, 0x00 }, /* REG_HKCTL2 */ { 0x1E, 0x00 }, /* REG_GPOCTL */ { 0x1F, 0x00 }, /* REG_ALB */ { 0x20, 0x00 }, /* REG_DLB */ /* 0x28, REG_TRIM1 */ /* 0x29, REG_TRIM2 */ /* 0x2A, REG_TRIM3 */ /* 0x2B, REG_HSOTRIM */ /* 0x2C, REG_HFOTRIM */ { 0x2D, 0x08 }, /* REG_ACCCTL */ { 0x2E, 0x00 }, /* REG_STATUS (ro) */ }; struct reg_default twl6040_patch[] = { /* Select I2C bus access to dual access registers */ { TWL6040_REG_ACCCTL, 0x09 }, }; static bool twl6040_has_vibra(struct device_node *node) static bool twl6040_has_vibra(struct device_node *node) { { #ifdef CONFIG_OF #ifdef CONFIG_OF Loading Loading @@ -238,6 +286,9 @@ int twl6040_power(struct twl6040 *twl6040, int on) if (twl6040->power_count++) if (twl6040->power_count++) goto out; goto out; /* Allow writes to the chip */ regcache_cache_only(twl6040->regmap, false); if (gpio_is_valid(twl6040->audpwron)) { if (gpio_is_valid(twl6040->audpwron)) { /* use automatic power-up sequence */ /* use automatic power-up sequence */ ret = twl6040_power_up_automatic(twl6040); ret = twl6040_power_up_automatic(twl6040); Loading @@ -253,6 +304,10 @@ int twl6040_power(struct twl6040 *twl6040, int on) goto out; goto out; } } } } /* Sync with the HW */ regcache_sync(twl6040->regmap); /* Default PLL configuration after power up */ /* Default PLL configuration after power up */ twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; twl6040->sysclk = 19200000; twl6040->sysclk = 19200000; Loading @@ -279,6 +334,11 @@ int twl6040_power(struct twl6040 *twl6040, int on) /* use manual power-down sequence */ /* use manual power-down sequence */ twl6040_power_down_manual(twl6040); twl6040_power_down_manual(twl6040); } } /* Set regmap to cache only and mark it as dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); twl6040->sysclk = 0; twl6040->sysclk = 0; twl6040->mclk = 0; twl6040->mclk = 0; } } Loading Loading @@ -490,9 +550,24 @@ static bool twl6040_readable_reg(struct device *dev, unsigned int reg) static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) { { switch (reg) { switch (reg) { case TWL6040_REG_VIBCTLL: case TWL6040_REG_ASICID: case TWL6040_REG_VIBCTLR: case TWL6040_REG_ASICREV: case TWL6040_REG_INTMR: case TWL6040_REG_INTID: case TWL6040_REG_LPPLLCTL: case TWL6040_REG_HPPLLCTL: case TWL6040_REG_STATUS: return true; default: return false; } } static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) { switch (reg) { case TWL6040_REG_ASICID: case TWL6040_REG_ASICREV: case TWL6040_REG_STATUS: return false; return false; default: default: return true; return true; Loading @@ -502,10 +577,15 @@ static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) static struct regmap_config twl6040_regmap_config = { static struct regmap_config twl6040_regmap_config = { .reg_bits = 8, .reg_bits = 8, .val_bits = 8, .val_bits = 8, .reg_defaults = twl6040_defaults, .num_reg_defaults = ARRAY_SIZE(twl6040_defaults), .max_register = TWL6040_REG_STATUS, /* 0x2e */ .max_register = TWL6040_REG_STATUS, /* 0x2e */ .readable_reg = twl6040_readable_reg, .readable_reg = twl6040_readable_reg, .volatile_reg = twl6040_volatile_reg, .volatile_reg = twl6040_volatile_reg, .writeable_reg = twl6040_writeable_reg, .cache_type = REGCACHE_RBTREE, .cache_type = REGCACHE_RBTREE, }; }; Loading Loading @@ -624,6 +704,8 @@ static int twl6040_probe(struct i2c_client *client, /* dual-access registers controlled by I2C only */ /* dual-access registers controlled by I2C only */ twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); regmap_register_patch(twl6040->regmap, twl6040_patch, ARRAY_SIZE(twl6040_patch)); /* /* * The main functionality of twl6040 to provide audio on OMAP4+ systems. * The main functionality of twl6040 to provide audio on OMAP4+ systems. Loading Loading @@ -656,6 +738,10 @@ static int twl6040_probe(struct i2c_client *client, cell->name = "twl6040-gpo"; cell->name = "twl6040-gpo"; children++; children++; /* The chip is powered down so mark regmap to cache only and dirty */ regcache_cache_only(twl6040->regmap, true); regcache_mark_dirty(twl6040->regmap); ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, NULL, 0, NULL); NULL, 0, NULL); if (ret) if (ret) Loading
include/linux/i2c/twl.h +3 −0 Original line number Original line Diff line number Diff line Loading @@ -175,6 +175,9 @@ static inline int twl_class_is_ ##class(void) \ TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(4030, TWL4030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) TWL_CLASS_IS(6030, TWL6030_CLASS_ID) /* Set the regcache bypass for the regmap associated with the nodule */ int twl_set_regcache_bypass(u8 mod_no, bool enable); /* /* * Read and write several 8-bit registers at once. * Read and write several 8-bit registers at once. */ */ Loading