diff options
Diffstat (limited to 'drivers/mfd/twl-core.c')
| -rw-r--r-- | drivers/mfd/twl-core.c | 403 | 
1 files changed, 175 insertions, 228 deletions
diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 29473c2c95ae..ed718328eff1 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -47,6 +47,9 @@  #include <linux/i2c.h>  #include <linux/i2c/twl.h> +/* Register descriptions for audio */ +#include <linux/mfd/twl4030-audio.h> +  #include "twl-core.h"  /* @@ -200,6 +203,105 @@ static struct twl_mapping twl4030_map[] = {  	{ 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] = {  	{  		/* Address 0x48 */ @@ -212,6 +314,15 @@ static struct regmap_config twl4030_regmap_config[4] = {  		.reg_bits = 8,  		.val_bits = 8,  		.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 */ @@ -302,35 +413,50 @@ unsigned int twl_rev(void)  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 - * @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;  	struct twl_client *twl;  	if (unlikely(!twl_priv || !twl_priv->ready)) {  		pr_err("%s: not initialized\n", DRIVER_NAME); -		return -EPERM; +		return NULL;  	}  	if (unlikely(mod_no >= twl_get_last_module())) {  		pr_err("%s: invalid module number %d\n", DRIVER_NAME, mod_no); -		return -EPERM; +		return NULL;  	}  	sid = twl_priv->twl_map[mod_no].sid;  	twl = &twl_priv->twl_modules[sid]; -	ret = regmap_bulk_write(twl->regmap, -				twl_priv->twl_map[mod_no].base + reg, value, -				num_bytes); +	return twl->regmap; +} + +/** + * 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)  		pr_err("%s: Write failed (mod %d, reg 0x%02x count %d)\n", @@ -351,25 +477,14 @@ EXPORT_SYMBOL(twl_i2c_write);   */  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 sid; -	struct twl_client *twl; -	if (unlikely(!twl_priv || !twl_priv->ready)) { -		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); +	if (!regmap)  		return -EPERM; -	} -	sid = twl_priv->twl_map[mod_no].sid; -	twl = &twl_priv->twl_modules[sid]; - -	ret = regmap_bulk_read(twl->regmap, -			       twl_priv->twl_map[mod_no].base + reg, value, -			       num_bytes); +	ret = regmap_bulk_read(regmap, twl_priv->twl_map[mod_no].base + reg, +			       value, num_bytes);  	if (ret)  		pr_err("%s: Read failed (mod %d, reg 0x%02x count %d)\n", @@ -379,6 +494,27 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)  }  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); +  /*----------------------------------------------------------------------*/  /** @@ -701,62 +837,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,  			usb3v1[0].dev_name = dev_name(child);  		}  	} -	if (IS_ENABLED(CONFIG_TWL6030_USB) && pdata->usb && -	    twl_class_is_6030()) { - -		static struct regulator_consumer_supply usb3v3; -		int regulator; - -		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030)) { -			/* this is a template that gets copied */ -			struct regulator_init_data usb_fixed = { -				.constraints.valid_modes_mask = -					REGULATOR_MODE_NORMAL -					| REGULATOR_MODE_STANDBY, -				.constraints.valid_ops_mask = -					REGULATOR_CHANGE_MODE -					| REGULATOR_CHANGE_STATUS, -			}; - -			if (features & TWL6032_SUBCLASS) { -				usb3v3.supply =	"ldousb"; -				regulator = TWL6032_REG_LDOUSB; -			} else { -				usb3v3.supply = "vusb"; -				regulator = TWL6030_REG_VUSB; -			} -			child = add_regulator_linked(regulator, &usb_fixed, -							&usb3v3, 1, -							features); -			if (IS_ERR(child)) -				return PTR_ERR(child); -		} - -		pdata->usb->features = features; - -		child = add_child(TWL_MODULE_USB, "twl6030_usb", -			pdata->usb, sizeof(*pdata->usb), true, -			/* irq1 = VBUS_PRES, irq0 = USB ID */ -			irq_base + USBOTG_INTR_OFFSET, -			irq_base + USB_PRES_INTR_OFFSET); - -		if (IS_ERR(child)) -			return PTR_ERR(child); -		/* we need to connect regulators to this transceiver */ -		if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && child) -			usb3v3.dev_name = dev_name(child); -	} else if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && -		   twl_class_is_6030()) { -		if (features & TWL6032_SUBCLASS) -			child = add_regulator(TWL6032_REG_LDOUSB, -						pdata->ldousb, features); -		else -			child = add_regulator(TWL6030_REG_VUSB, -						pdata->vusb, features); - -			if (IS_ERR(child)) -					return PTR_ERR(child); -	}  	if (IS_ENABLED(CONFIG_TWL4030_WATCHDOG) && twl_class_is_4030()) {  		child = add_child(TWL_MODULE_PM_RECEIVER, "twl4030_wdt", NULL, @@ -870,148 +950,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned irq_base,  			return PTR_ERR(child);  	} -	/* twl6030 regulators */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() && -			!(features & TWL6032_SUBCLASS)) { -		child = add_regulator(TWL6030_REG_VDD1, pdata->vdd1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDD2, pdata->vdd2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDD3, pdata->vdd3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_V1V8, pdata->v1v8, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_V2V1, pdata->v2v1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VMMC, pdata->vmmc, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VPP, pdata->vpp, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VUSIM, pdata->vusim, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VCXIO, pdata->vcxio, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VDAC, pdata->vdac, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX1_6030, pdata->vaux1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX2_6030, pdata->vaux2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_VAUX3_6030, pdata->vaux3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6030_REG_CLK32KG, pdata->clk32kg, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); -	} - -	/* 6030 and 6025 share this regulator */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030()) { -		child = add_regulator(TWL6030_REG_VANA, pdata->vana, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); -	} - -	/* twl6032 regulators */ -	if (IS_ENABLED(CONFIG_REGULATOR_TWL4030) && twl_class_is_6030() && -			(features & TWL6032_SUBCLASS)) { -		child = add_regulator(TWL6032_REG_LDO5, pdata->ldo5, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO1, pdata->ldo1, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO7, pdata->ldo7, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO6, pdata->ldo6, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDOLN, pdata->ldoln, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO2, pdata->ldo2, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO4, pdata->ldo4, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_LDO3, pdata->ldo3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_SMPS3, pdata->smps3, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_SMPS4, pdata->smps4, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -		child = add_regulator(TWL6032_REG_VIO, pdata->vio6025, -					features); -		if (IS_ERR(child)) -			return PTR_ERR(child); - -	} -  	if (IS_ENABLED(CONFIG_CHARGER_TWL4030) && pdata->bci &&  			!(features & (TPS_SUBSET | TWL5031))) {  		child = add_child(TWL_MODULE_MAIN_CHARGE, "twl4030_bci", @@ -1133,6 +1071,11 @@ static int twl_remove(struct i2c_client *client)  	return 0;  } +static struct of_dev_auxdata twl_auxdata_lookup[] = { +	OF_DEV_AUXDATA("ti,twl4030-gpio", 0, "twl4030-gpio", NULL), +	{ /* sentinel */ }, +}; +  /* NOTE: This driver only handles a single twl4030/tps659x0 chip */  static int  twl_probe(struct i2c_client *client, const struct i2c_device_id *id) @@ -1271,10 +1214,14 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);  	} -	if (node) -		status = of_platform_populate(node, NULL, NULL, &client->dev); -	else +	if (node) { +		if (pdata) +			twl_auxdata_lookup[0].platform_data = pdata->gpio; +		status = of_platform_populate(node, NULL, twl_auxdata_lookup, +					      &client->dev); +	} else {  		status = add_children(pdata, irq_base, id->driver_data); +	}  fail:  	if (status < 0)  |