diff options
Diffstat (limited to 'drivers/mfd/twl6040.c')
-rw-r--r-- | drivers/mfd/twl6040.c | 92 |
1 files changed, 89 insertions, 3 deletions
diff --git a/drivers/mfd/twl6040.c b/drivers/mfd/twl6040.c index 0779d5ab9ab1..51b6df1a7949 100644 --- a/drivers/mfd/twl6040.c +++ b/drivers/mfd/twl6040.c | |||
@@ -44,6 +44,54 @@ | |||
44 | #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) | 44 | #define VIBRACTRL_MEMBER(reg) ((reg == TWL6040_REG_VIBCTLL) ? 0 : 1) |
45 | #define TWL6040_NUM_SUPPLIES (2) | 45 | #define TWL6040_NUM_SUPPLIES (2) |
46 | 46 | ||
47 | static struct reg_default twl6040_defaults[] = { | ||
48 | { 0x01, 0x4B }, /* REG_ASICID (ro) */ | ||
49 | { 0x02, 0x00 }, /* REG_ASICREV (ro) */ | ||
50 | { 0x03, 0x00 }, /* REG_INTID */ | ||
51 | { 0x04, 0x00 }, /* REG_INTMR */ | ||
52 | { 0x05, 0x00 }, /* REG_NCPCTRL */ | ||
53 | { 0x06, 0x00 }, /* REG_LDOCTL */ | ||
54 | { 0x07, 0x60 }, /* REG_HPPLLCTL */ | ||
55 | { 0x08, 0x00 }, /* REG_LPPLLCTL */ | ||
56 | { 0x09, 0x4A }, /* REG_LPPLLDIV */ | ||
57 | { 0x0A, 0x00 }, /* REG_AMICBCTL */ | ||
58 | { 0x0B, 0x00 }, /* REG_DMICBCTL */ | ||
59 | { 0x0C, 0x00 }, /* REG_MICLCTL */ | ||
60 | { 0x0D, 0x00 }, /* REG_MICRCTL */ | ||
61 | { 0x0E, 0x00 }, /* REG_MICGAIN */ | ||
62 | { 0x0F, 0x1B }, /* REG_LINEGAIN */ | ||
63 | { 0x10, 0x00 }, /* REG_HSLCTL */ | ||
64 | { 0x11, 0x00 }, /* REG_HSRCTL */ | ||
65 | { 0x12, 0x00 }, /* REG_HSGAIN */ | ||
66 | { 0x13, 0x00 }, /* REG_EARCTL */ | ||
67 | { 0x14, 0x00 }, /* REG_HFLCTL */ | ||
68 | { 0x15, 0x00 }, /* REG_HFLGAIN */ | ||
69 | { 0x16, 0x00 }, /* REG_HFRCTL */ | ||
70 | { 0x17, 0x00 }, /* REG_HFRGAIN */ | ||
71 | { 0x18, 0x00 }, /* REG_VIBCTLL */ | ||
72 | { 0x19, 0x00 }, /* REG_VIBDATL */ | ||
73 | { 0x1A, 0x00 }, /* REG_VIBCTLR */ | ||
74 | { 0x1B, 0x00 }, /* REG_VIBDATR */ | ||
75 | { 0x1C, 0x00 }, /* REG_HKCTL1 */ | ||
76 | { 0x1D, 0x00 }, /* REG_HKCTL2 */ | ||
77 | { 0x1E, 0x00 }, /* REG_GPOCTL */ | ||
78 | { 0x1F, 0x00 }, /* REG_ALB */ | ||
79 | { 0x20, 0x00 }, /* REG_DLB */ | ||
80 | /* 0x28, REG_TRIM1 */ | ||
81 | /* 0x29, REG_TRIM2 */ | ||
82 | /* 0x2A, REG_TRIM3 */ | ||
83 | /* 0x2B, REG_HSOTRIM */ | ||
84 | /* 0x2C, REG_HFOTRIM */ | ||
85 | { 0x2D, 0x08 }, /* REG_ACCCTL */ | ||
86 | { 0x2E, 0x00 }, /* REG_STATUS (ro) */ | ||
87 | }; | ||
88 | |||
89 | struct reg_default twl6040_patch[] = { | ||
90 | /* Select I2C bus access to dual access registers */ | ||
91 | { TWL6040_REG_ACCCTL, 0x09 }, | ||
92 | }; | ||
93 | |||
94 | |||
47 | static bool twl6040_has_vibra(struct device_node *node) | 95 | static bool twl6040_has_vibra(struct device_node *node) |
48 | { | 96 | { |
49 | #ifdef CONFIG_OF | 97 | #ifdef CONFIG_OF |
@@ -238,6 +286,9 @@ int twl6040_power(struct twl6040 *twl6040, int on) | |||
238 | if (twl6040->power_count++) | 286 | if (twl6040->power_count++) |
239 | goto out; | 287 | goto out; |
240 | 288 | ||
289 | /* Allow writes to the chip */ | ||
290 | regcache_cache_only(twl6040->regmap, false); | ||
291 | |||
241 | if (gpio_is_valid(twl6040->audpwron)) { | 292 | if (gpio_is_valid(twl6040->audpwron)) { |
242 | /* use automatic power-up sequence */ | 293 | /* use automatic power-up sequence */ |
243 | ret = twl6040_power_up_automatic(twl6040); | 294 | ret = twl6040_power_up_automatic(twl6040); |
@@ -253,6 +304,10 @@ int twl6040_power(struct twl6040 *twl6040, int on) | |||
253 | goto out; | 304 | goto out; |
254 | } | 305 | } |
255 | } | 306 | } |
307 | |||
308 | /* Sync with the HW */ | ||
309 | regcache_sync(twl6040->regmap); | ||
310 | |||
256 | /* Default PLL configuration after power up */ | 311 | /* Default PLL configuration after power up */ |
257 | twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; | 312 | twl6040->pll = TWL6040_SYSCLK_SEL_LPPLL; |
258 | twl6040->sysclk = 19200000; | 313 | twl6040->sysclk = 19200000; |
@@ -279,6 +334,11 @@ int twl6040_power(struct twl6040 *twl6040, int on) | |||
279 | /* use manual power-down sequence */ | 334 | /* use manual power-down sequence */ |
280 | twl6040_power_down_manual(twl6040); | 335 | twl6040_power_down_manual(twl6040); |
281 | } | 336 | } |
337 | |||
338 | /* Set regmap to cache only and mark it as dirty */ | ||
339 | regcache_cache_only(twl6040->regmap, true); | ||
340 | regcache_mark_dirty(twl6040->regmap); | ||
341 | |||
282 | twl6040->sysclk = 0; | 342 | twl6040->sysclk = 0; |
283 | twl6040->mclk = 0; | 343 | twl6040->mclk = 0; |
284 | } | 344 | } |
@@ -490,9 +550,24 @@ static bool twl6040_readable_reg(struct device *dev, unsigned int reg) | |||
490 | static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) | 550 | static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) |
491 | { | 551 | { |
492 | switch (reg) { | 552 | switch (reg) { |
493 | case TWL6040_REG_VIBCTLL: | 553 | case TWL6040_REG_ASICID: |
494 | case TWL6040_REG_VIBCTLR: | 554 | case TWL6040_REG_ASICREV: |
495 | case TWL6040_REG_INTMR: | 555 | case TWL6040_REG_INTID: |
556 | case TWL6040_REG_LPPLLCTL: | ||
557 | case TWL6040_REG_HPPLLCTL: | ||
558 | case TWL6040_REG_STATUS: | ||
559 | return true; | ||
560 | default: | ||
561 | return false; | ||
562 | } | ||
563 | } | ||
564 | |||
565 | static bool twl6040_writeable_reg(struct device *dev, unsigned int reg) | ||
566 | { | ||
567 | switch (reg) { | ||
568 | case TWL6040_REG_ASICID: | ||
569 | case TWL6040_REG_ASICREV: | ||
570 | case TWL6040_REG_STATUS: | ||
496 | return false; | 571 | return false; |
497 | default: | 572 | default: |
498 | return true; | 573 | return true; |
@@ -502,10 +577,15 @@ static bool twl6040_volatile_reg(struct device *dev, unsigned int reg) | |||
502 | static struct regmap_config twl6040_regmap_config = { | 577 | static struct regmap_config twl6040_regmap_config = { |
503 | .reg_bits = 8, | 578 | .reg_bits = 8, |
504 | .val_bits = 8, | 579 | .val_bits = 8, |
580 | |||
581 | .reg_defaults = twl6040_defaults, | ||
582 | .num_reg_defaults = ARRAY_SIZE(twl6040_defaults), | ||
583 | |||
505 | .max_register = TWL6040_REG_STATUS, /* 0x2e */ | 584 | .max_register = TWL6040_REG_STATUS, /* 0x2e */ |
506 | 585 | ||
507 | .readable_reg = twl6040_readable_reg, | 586 | .readable_reg = twl6040_readable_reg, |
508 | .volatile_reg = twl6040_volatile_reg, | 587 | .volatile_reg = twl6040_volatile_reg, |
588 | .writeable_reg = twl6040_writeable_reg, | ||
509 | 589 | ||
510 | .cache_type = REGCACHE_RBTREE, | 590 | .cache_type = REGCACHE_RBTREE, |
511 | }; | 591 | }; |
@@ -624,6 +704,8 @@ static int twl6040_probe(struct i2c_client *client, | |||
624 | 704 | ||
625 | /* dual-access registers controlled by I2C only */ | 705 | /* dual-access registers controlled by I2C only */ |
626 | twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); | 706 | twl6040_set_bits(twl6040, TWL6040_REG_ACCCTL, TWL6040_I2CSEL); |
707 | regmap_register_patch(twl6040->regmap, twl6040_patch, | ||
708 | ARRAY_SIZE(twl6040_patch)); | ||
627 | 709 | ||
628 | /* | 710 | /* |
629 | * The main functionality of twl6040 to provide audio on OMAP4+ systems. | 711 | * The main functionality of twl6040 to provide audio on OMAP4+ systems. |
@@ -656,6 +738,10 @@ static int twl6040_probe(struct i2c_client *client, | |||
656 | cell->name = "twl6040-gpo"; | 738 | cell->name = "twl6040-gpo"; |
657 | children++; | 739 | children++; |
658 | 740 | ||
741 | /* The chip is powered down so mark regmap to cache only and dirty */ | ||
742 | regcache_cache_only(twl6040->regmap, true); | ||
743 | regcache_mark_dirty(twl6040->regmap); | ||
744 | |||
659 | ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, | 745 | ret = mfd_add_devices(&client->dev, -1, twl6040->cells, children, |
660 | NULL, 0, NULL); | 746 | NULL, 0, NULL); |
661 | if (ret) | 747 | if (ret) |