diff options
Diffstat (limited to 'drivers')
46 files changed, 3676 insertions, 945 deletions
diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index f5002015d82..a22ffa5bff9 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -22,11 +22,11 @@ static struct cpufreq_frequency_table freq_table[] = {  	},  	[1] = {  		.index = 1, -		.frequency = 300000, +		.frequency = 400000,  	},  	[2] = {  		.index = 2, -		.frequency = 600000, +		.frequency = 800000,  	},  	[3] = {  		/* Used for MAX_OPP, if available */ @@ -113,12 +113,9 @@ static int __cpuinit db8500_cpufreq_init(struct cpufreq_policy *policy)  	BUILD_BUG_ON(ARRAY_SIZE(idx2opp) + 1 != ARRAY_SIZE(freq_table)); -	if (!prcmu_is_u8400()) { -		freq_table[1].frequency = 400000; -		freq_table[2].frequency = 800000; -		if (prcmu_has_arm_maxopp()) -			freq_table[3].frequency = 1000000; -	} +	if (prcmu_has_arm_maxopp()) +		freq_table[3].frequency = 1000000; +  	pr_info("db8500-cpufreq : Available frequencies:\n");  	for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++)  		pr_info("  %d Mhz\n", freq_table[i].frequency/1000); diff --git a/drivers/gpio/gpio-stmpe.c b/drivers/gpio/gpio-stmpe.c index 87a68a896ab..094c5c4fd7f 100644 --- a/drivers/gpio/gpio-stmpe.c +++ b/drivers/gpio/gpio-stmpe.c @@ -307,13 +307,11 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)  	struct stmpe_gpio_platform_data *pdata;  	struct stmpe_gpio *stmpe_gpio;  	int ret; -	int irq; +	int irq = 0;  	pdata = stmpe->pdata->gpio;  	irq = platform_get_irq(pdev, 0); -	if (irq < 0) -		return irq;  	stmpe_gpio = kzalloc(sizeof(struct stmpe_gpio), GFP_KERNEL);  	if (!stmpe_gpio) @@ -330,21 +328,28 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)  	stmpe_gpio->chip.dev = &pdev->dev;  	stmpe_gpio->chip.base = pdata ? pdata->gpio_base : -1; -	stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); +	if (irq >= 0) +		stmpe_gpio->irq_base = stmpe->irq_base + STMPE_INT_GPIO(0); +	else +		dev_info(&pdev->dev, +			"device configured in no-irq mode; " +			"irqs are not available\n");  	ret = stmpe_enable(stmpe, STMPE_BLOCK_GPIO);  	if (ret)  		goto out_free; -	ret = stmpe_gpio_irq_init(stmpe_gpio); -	if (ret) -		goto out_disable; +	if (irq >= 0) { +		ret = stmpe_gpio_irq_init(stmpe_gpio); +		if (ret) +			goto out_disable; -	ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, IRQF_ONESHOT, -				   "stmpe-gpio", stmpe_gpio); -	if (ret) { -		dev_err(&pdev->dev, "unable to get irq: %d\n", ret); -		goto out_removeirq; +		ret = request_threaded_irq(irq, NULL, stmpe_gpio_irq, +				IRQF_ONESHOT, "stmpe-gpio", stmpe_gpio); +		if (ret) { +			dev_err(&pdev->dev, "unable to get irq: %d\n", ret); +			goto out_removeirq; +		}  	}  	ret = gpiochip_add(&stmpe_gpio->chip); @@ -361,9 +366,11 @@ static int __devinit stmpe_gpio_probe(struct platform_device *pdev)  	return 0;  out_freeirq: -	free_irq(irq, stmpe_gpio); +	if (irq >= 0) +		free_irq(irq, stmpe_gpio);  out_removeirq: -	stmpe_gpio_irq_remove(stmpe_gpio); +	if (irq >= 0) +		stmpe_gpio_irq_remove(stmpe_gpio);  out_disable:  	stmpe_disable(stmpe, STMPE_BLOCK_GPIO);  out_free: @@ -391,8 +398,10 @@ static int __devexit stmpe_gpio_remove(struct platform_device *pdev)  	stmpe_disable(stmpe, STMPE_BLOCK_GPIO); -	free_irq(irq, stmpe_gpio); -	stmpe_gpio_irq_remove(stmpe_gpio); +	if (irq >= 0) { +		free_irq(irq, stmpe_gpio); +		stmpe_gpio_irq_remove(stmpe_gpio); +	}  	platform_set_drvdata(pdev, NULL);  	kfree(stmpe_gpio); diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index b8b4f228757..94256fe7bf3 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -32,6 +32,8 @@  #include <linux/irq.h>  #include <linux/gpio.h>  #include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/irqdomain.h>  #include <linux/i2c/twl.h> @@ -256,7 +258,8 @@ static int twl_request(struct gpio_chip *chip, unsigned offset)  		 * and vMMC2 power supplies based on card presence.  		 */  		pdata = chip->dev->platform_data; -		value |= pdata->mmc_cd & 0x03; +		if (pdata) +			value |= pdata->mmc_cd & 0x03;  		status = gpio_twl4030_write(REG_GPIO_CTRL, value);  	} @@ -395,59 +398,70 @@ static int gpio_twl4030_remove(struct platform_device *pdev);  static int __devinit gpio_twl4030_probe(struct platform_device *pdev)  {  	struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data; -	int ret; +	struct device_node *node = pdev->dev.of_node; +	int ret, irq_base;  	/* maybe setup IRQs */ -	if (pdata->irq_base) { -		if (is_module()) { -			dev_err(&pdev->dev, -				"can't dispatch IRQs from modules\n"); -			goto no_irqs; -		} -		ret = twl4030_sih_setup(TWL4030_MODULE_GPIO); -		if (ret < 0) -			return ret; -		WARN_ON(ret != pdata->irq_base); -		twl4030_gpio_irq_base = ret; +	if (is_module()) { +		dev_err(&pdev->dev, "can't dispatch IRQs from modules\n"); +		goto no_irqs;  	} -no_irqs: -	/* -	 * NOTE:  boards may waste power if they don't set pullups -	 * and pulldowns correctly ... default for non-ULPI pins is -	 * pulldown, and some other pins may have external pullups -	 * or pulldowns.  Careful! -	 */ -	ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); -	if (ret) -		dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", -				pdata->pullups, pdata->pulldowns, -				ret); +	irq_base = irq_alloc_descs(-1, 0, TWL4030_GPIO_MAX, 0); +	if (irq_base < 0) { +		dev_err(&pdev->dev, "Failed to alloc irq_descs\n"); +		return irq_base; +	} -	ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); -	if (ret) -		dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", -				pdata->debounce, pdata->mmc_cd, -				ret); +	irq_domain_add_legacy(node, TWL4030_GPIO_MAX, irq_base, 0, +			      &irq_domain_simple_ops, NULL); -	twl_gpiochip.base = pdata->gpio_base; +	ret = twl4030_sih_setup(&pdev->dev, TWL4030_MODULE_GPIO, irq_base); +	if (ret < 0) +		return ret; + +	twl4030_gpio_irq_base = irq_base; + +no_irqs: +	twl_gpiochip.base = -1;  	twl_gpiochip.ngpio = TWL4030_GPIO_MAX;  	twl_gpiochip.dev = &pdev->dev; -	/* NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, -	 * is (still) clear if use_leds is set. -	 */ -	if (pdata->use_leds) -		twl_gpiochip.ngpio += 2; +	if (pdata) { +		twl_gpiochip.base = pdata->gpio_base; + +		/* +		 * NOTE:  boards may waste power if they don't set pullups +		 * and pulldowns correctly ... default for non-ULPI pins is +		 * pulldown, and some other pins may have external pullups +		 * or pulldowns.  Careful! +		 */ +		ret = gpio_twl4030_pulls(pdata->pullups, pdata->pulldowns); +		if (ret) +			dev_dbg(&pdev->dev, "pullups %.05x %.05x --> %d\n", +					pdata->pullups, pdata->pulldowns, +					ret); + +		ret = gpio_twl4030_debounce(pdata->debounce, pdata->mmc_cd); +		if (ret) +			dev_dbg(&pdev->dev, "debounce %.03x %.01x --> %d\n", +					pdata->debounce, pdata->mmc_cd, +					ret); + +		/* +		 * NOTE: we assume VIBRA_CTL.VIBRA_EN, in MODULE_AUDIO_VOICE, +		 * is (still) clear if use_leds is set. +		 */ +		if (pdata->use_leds) +			twl_gpiochip.ngpio += 2; +	}  	ret = gpiochip_add(&twl_gpiochip);  	if (ret < 0) { -		dev_err(&pdev->dev, -				"could not register gpiochip, %d\n", -				ret); +		dev_err(&pdev->dev, "could not register gpiochip, %d\n", ret);  		twl_gpiochip.ngpio = 0;  		gpio_twl4030_remove(pdev); -	} else if (pdata->setup) { +	} else if (pdata && pdata->setup) {  		int status;  		status = pdata->setup(&pdev->dev, @@ -465,7 +479,7 @@ static int gpio_twl4030_remove(struct platform_device *pdev)  	struct twl4030_gpio_platform_data *pdata = pdev->dev.platform_data;  	int status; -	if (pdata->teardown) { +	if (pdata && pdata->teardown) {  		status = pdata->teardown(&pdev->dev,  				pdata->gpio_base, TWL4030_GPIO_MAX);  		if (status) { @@ -486,12 +500,21 @@ static int gpio_twl4030_remove(struct platform_device *pdev)  	return -EIO;  } +static const struct of_device_id twl_gpio_match[] = { +	{ .compatible = "ti,twl4030-gpio", }, +	{ }, +}; +MODULE_DEVICE_TABLE(of, twl_gpio_match); +  /* Note:  this hardware lives inside an I2C-based multi-function device. */  MODULE_ALIAS("platform:twl4030_gpio");  static struct platform_driver gpio_twl4030_driver = { -	.driver.name	= "twl4030_gpio", -	.driver.owner	= THIS_MODULE, +	.driver = { +		.name	= "twl4030_gpio", +		.owner	= THIS_MODULE, +		.of_match_table = of_match_ptr(twl_gpio_match), +	},  	.probe		= gpio_twl4030_probe,  	.remove		= gpio_twl4030_remove,  }; diff --git a/drivers/hwmon/mc13783-adc.c b/drivers/hwmon/mc13783-adc.c index 6c6b240a782..ce86c5e3c2c 100644 --- a/drivers/hwmon/mc13783-adc.c +++ b/drivers/hwmon/mc13783-adc.c @@ -59,7 +59,7 @@ static int mc13783_adc_read(struct device *dev,  	ret = mc13xxx_adc_do_conversion(priv->mc13xxx,  			MC13XXX_ADC_MODE_MULT_CHAN, -			channel, sample); +			channel, 0, 0, sample);  	if (ret)  		return ret; diff --git a/drivers/input/misc/88pm860x_onkey.c b/drivers/input/misc/88pm860x_onkey.c index f2e0cbc5ab6..f9ce1835e4d 100644 --- a/drivers/input/misc/88pm860x_onkey.c +++ b/drivers/input/misc/88pm860x_onkey.c @@ -105,6 +105,8 @@ static int __devinit pm860x_onkey_probe(struct platform_device *pdev)  	}  	platform_set_drvdata(pdev, info); +	device_init_wakeup(&pdev->dev, 1); +  	return 0;  out_irq: @@ -129,10 +131,34 @@ static int __devexit pm860x_onkey_remove(struct platform_device *pdev)  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int pm860x_onkey_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + +	if (device_may_wakeup(dev)) +		chip->wakeup_flag |= 1 << PM8607_IRQ_ONKEY; +	return 0; +} +static int pm860x_onkey_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + +	if (device_may_wakeup(dev)) +		chip->wakeup_flag &= ~(1 << PM8607_IRQ_ONKEY); +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_onkey_pm_ops, pm860x_onkey_suspend, pm860x_onkey_resume); +  static struct platform_driver pm860x_onkey_driver = {  	.driver		= {  		.name	= "88pm860x-onkey",  		.owner	= THIS_MODULE, +		.pm	= &pm860x_onkey_pm_ops,  	},  	.probe		= pm860x_onkey_probe,  	.remove		= __devexit_p(pm860x_onkey_remove), diff --git a/drivers/input/touchscreen/mc13783_ts.c b/drivers/input/touchscreen/mc13783_ts.c index ede02743eac..48dc5b0d26f 100644 --- a/drivers/input/touchscreen/mc13783_ts.c +++ b/drivers/input/touchscreen/mc13783_ts.c @@ -39,6 +39,7 @@ struct mc13783_ts_priv {  	struct delayed_work work;  	struct workqueue_struct *workq;  	unsigned int sample[4]; +	struct mc13xxx_ts_platform_data *touch;  };  static irqreturn_t mc13783_ts_handler(int irq, void *data) @@ -125,7 +126,9 @@ static void mc13783_ts_work(struct work_struct *work)  	unsigned int channel = 12;  	if (mc13xxx_adc_do_conversion(priv->mc13xxx, -				mode, channel, priv->sample) == 0) +				mode, channel, +				priv->touch->ato, priv->touch->atox, +				priv->sample) == 0)  		mc13783_ts_report_sample(priv);  } @@ -179,6 +182,12 @@ static int __init mc13783_ts_probe(struct platform_device *pdev)  	INIT_DELAYED_WORK(&priv->work, mc13783_ts_work);  	priv->mc13xxx = dev_get_drvdata(pdev->dev.parent);  	priv->idev = idev; +	priv->touch = dev_get_platdata(&pdev->dev); +	if (!priv->touch) { +		dev_err(&pdev->dev, "missing platform data\n"); +		ret = -ENODEV; +		goto err_free_mem; +	}  	/*  	 * We need separate workqueue because mc13783_adc_do_conversion diff --git a/drivers/leds/leds-88pm860x.c b/drivers/leds/leds-88pm860x.c index 4ca00624bd1..5b61aaf7ac0 100644 --- a/drivers/leds/leds-88pm860x.c +++ b/drivers/leds/leds-88pm860x.c @@ -114,6 +114,27 @@ static inline int __blink_ctl_mask(int port)  	return ret;  } +static int led_power_set(struct pm860x_chip *chip, int port, int on) +{ +	int ret = -EINVAL; + +	switch (port) { +	case PM8606_LED1_RED: +	case PM8606_LED1_GREEN: +	case PM8606_LED1_BLUE: +		ret = on ? pm8606_osc_enable(chip, RGB1_ENABLE) : +			pm8606_osc_disable(chip, RGB1_ENABLE); +		break; +	case PM8606_LED2_RED: +	case PM8606_LED2_GREEN: +	case PM8606_LED2_BLUE: +		ret = on ? pm8606_osc_enable(chip, RGB2_ENABLE) : +			pm8606_osc_disable(chip, RGB2_ENABLE); +		break; +	} +	return ret; +} +  static void pm860x_led_work(struct work_struct *work)  { @@ -126,6 +147,7 @@ static void pm860x_led_work(struct work_struct *work)  	chip = led->chip;  	mutex_lock(&led->lock);  	if ((led->current_brightness == 0) && led->brightness) { +		led_power_set(chip, led->port, 1);  		if (led->iset) {  			pm860x_set_bits(led->i2c, __led_off(led->port),  					LED_CURRENT_MASK, led->iset); @@ -149,6 +171,7 @@ static void pm860x_led_work(struct work_struct *work)  					LED_CURRENT_MASK, 0);  			mask = __blink_ctl_mask(led->port);  			pm860x_set_bits(led->i2c, PM8606_WLED3B, mask, 0); +			led_power_set(chip, led->port, 0);  		}  	}  	led->current_brightness = led->brightness; diff --git a/drivers/mfd/88pm860x-core.c b/drivers/mfd/88pm860x-core.c index 17dfe9bb6d2..87bd5ba38d5 100644 --- a/drivers/mfd/88pm860x-core.c +++ b/drivers/mfd/88pm860x-core.c @@ -503,6 +503,101 @@ static void device_irq_exit(struct pm860x_chip *chip)  		free_irq(chip->core_irq, chip);  } +int pm8606_osc_enable(struct pm860x_chip *chip, unsigned short client) +{ +	int ret = -EIO; +	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? +		chip->client : chip->companion; + +	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); +	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", +			__func__, chip->osc_vote, +			chip->osc_status); + +	mutex_lock(&chip->osc_lock); +	/* Update voting status */ +	chip->osc_vote |= client; +	/* If reference group is off - turn on*/ +	if (chip->osc_status != PM8606_REF_GP_OSC_ON) { +		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; +		/* Enable Reference group Vsys */ +		if (pm860x_set_bits(i2c, PM8606_VSYS, +				PM8606_VSYS_EN, PM8606_VSYS_EN)) +			goto out; + +		/*Enable Internal Oscillator */ +		if (pm860x_set_bits(i2c, PM8606_MISC, +				PM8606_MISC_OSC_EN, PM8606_MISC_OSC_EN)) +			goto out; +		/* Update status (only if writes succeed) */ +		chip->osc_status = PM8606_REF_GP_OSC_ON; +	} +	mutex_unlock(&chip->osc_lock); + +	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", +			__func__, chip->osc_vote, +			chip->osc_status, ret); +	return 0; +out: +	mutex_unlock(&chip->osc_lock); +	return ret; +} +EXPORT_SYMBOL(pm8606_osc_enable); + +int pm8606_osc_disable(struct pm860x_chip *chip, unsigned short client) +{ +	int ret = -EIO; +	struct i2c_client *i2c = (chip->id == CHIP_PM8606) ? +		chip->client : chip->companion; + +	dev_dbg(chip->dev, "%s(B): client=0x%x\n", __func__, client); +	dev_dbg(chip->dev, "%s(B): vote=0x%x status=%d\n", +			__func__, chip->osc_vote, +			chip->osc_status); + +	mutex_lock(&chip->osc_lock); +	/*Update voting status */ +	chip->osc_vote &= ~(client); +	/* If reference group is off and this is the last client to release +	 * - turn off */ +	if ((chip->osc_status != PM8606_REF_GP_OSC_OFF) && +			(chip->osc_vote == REF_GP_NO_CLIENTS)) { +		chip->osc_status = PM8606_REF_GP_OSC_UNKNOWN; +		/* Disable Reference group Vsys */ +		if (pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0)) +			goto out; +		/* Disable Internal Oscillator */ +		if (pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0)) +			goto out; +		chip->osc_status = PM8606_REF_GP_OSC_OFF; +	} +	mutex_unlock(&chip->osc_lock); + +	dev_dbg(chip->dev, "%s(A): vote=0x%x status=%d ret=%d\n", +			__func__, chip->osc_vote, +			chip->osc_status, ret); +	return 0; +out: +	mutex_unlock(&chip->osc_lock); +	return ret; +} +EXPORT_SYMBOL(pm8606_osc_disable); + +static void __devinit device_osc_init(struct i2c_client *i2c) +{ +	struct pm860x_chip *chip = i2c_get_clientdata(i2c); + +	mutex_init(&chip->osc_lock); +	/* init portofino reference group voting and status */ +	/* Disable Reference group Vsys */ +	pm860x_set_bits(i2c, PM8606_VSYS, PM8606_VSYS_EN, 0); +	/* Disable Internal Oscillator */ +	pm860x_set_bits(i2c, PM8606_MISC, PM8606_MISC_OSC_EN, 0); + +	chip->osc_vote = REF_GP_NO_CLIENTS; +	chip->osc_status = PM8606_REF_GP_OSC_OFF; +} +  static void __devinit device_bk_init(struct pm860x_chip *chip,  				     struct pm860x_platform_data *pdata)  { @@ -767,6 +862,15 @@ out:  	return;  } +static void __devinit device_8606_init(struct pm860x_chip *chip, +				       struct i2c_client *i2c, +				       struct pm860x_platform_data *pdata) +{ +	device_osc_init(i2c); +	device_bk_init(chip, pdata); +	device_led_init(chip, pdata); +} +  int __devinit pm860x_device_init(struct pm860x_chip *chip,  		       struct pm860x_platform_data *pdata)  { @@ -774,8 +878,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,  	switch (chip->id) {  	case CHIP_PM8606: -		device_bk_init(chip, pdata); -		device_led_init(chip, pdata); +		device_8606_init(chip, chip->client, pdata);  		break;  	case CHIP_PM8607:  		device_8607_init(chip, chip->client, pdata); @@ -785,8 +888,7 @@ int __devinit pm860x_device_init(struct pm860x_chip *chip,  	if (chip->companion) {  		switch (chip->id) {  		case CHIP_PM8607: -			device_bk_init(chip, pdata); -			device_led_init(chip, pdata); +			device_8606_init(chip, chip->companion, pdata);  			break;  		case CHIP_PM8606:  			device_8607_init(chip, chip->companion, pdata); diff --git a/drivers/mfd/88pm860x-i2c.c b/drivers/mfd/88pm860x-i2c.c index f93dd9571c3..b2cfdc45856 100644 --- a/drivers/mfd/88pm860x-i2c.c +++ b/drivers/mfd/88pm860x-i2c.c @@ -334,10 +334,35 @@ static int __devexit pm860x_remove(struct i2c_client *client)  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int pm860x_suspend(struct device *dev) +{ +	struct i2c_client *client = container_of(dev, struct i2c_client, dev); +	struct pm860x_chip *chip = i2c_get_clientdata(client); + +	if (device_may_wakeup(dev) && chip->wakeup_flag) +		enable_irq_wake(chip->core_irq); +	return 0; +} + +static int pm860x_resume(struct device *dev) +{ +	struct i2c_client *client = container_of(dev, struct i2c_client, dev); +	struct pm860x_chip *chip = i2c_get_clientdata(client); + +	if (device_may_wakeup(dev) && chip->wakeup_flag) +		disable_irq_wake(chip->core_irq); +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_pm_ops, pm860x_suspend, pm860x_resume); +  static struct i2c_driver pm860x_driver = {  	.driver	= {  		.name	= "88PM860x",  		.owner	= THIS_MODULE, +		.pm     = &pm860x_pm_ops,  	},  	.probe		= pm860x_probe,  	.remove		= __devexit_p(pm860x_remove), diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 243e0c663c3..29f463cc09c 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -143,6 +143,21 @@ config TPS6507X  	  This driver can also be built as a module.  If so, the module  	  will be called tps6507x. +config MFD_TPS65217 +	tristate "TPS65217 Power Management / White LED chips" +	depends on I2C +	select MFD_CORE +	select REGMAP_I2C +	help +	  If you say yes here you get support for the TPS65217 series of +	  Power Management / White LED chips. +	  These include voltage regulators, lithium ion/polymer battery +	  charger, wled and other features that are often used in portable +	  devices. + +	  This driver can also be built as a module.  If so, the module +	  will be called tps65217. +  config MFD_TPS6586X  	bool "TPS6586x Power Management chips"  	depends on I2C=y && GPIOLIB && GENERIC_HARDIRQS @@ -162,6 +177,7 @@ config MFD_TPS65910  	depends on I2C=y && GPIOLIB  	select MFD_CORE  	select GPIO_TPS65910 +	select REGMAP_I2C  	help  	  if you say yes here you get support for the TPS65910 series of  	  Power Management chips. @@ -171,7 +187,7 @@ config MFD_TPS65912  	depends on GPIOLIB  config MFD_TPS65912_I2C -	bool "TPS95612 Power Management chip with I2C" +	bool "TPS65912 Power Management chip with I2C"  	select MFD_CORE  	select MFD_TPS65912  	depends on I2C=y && GPIOLIB @@ -400,7 +416,7 @@ config MFD_MAX8997  	depends on I2C=y && GENERIC_HARDIRQS  	select MFD_CORE  	help -	  Say yes here to support for Maxim Semiconductor MAX8998/8966. +	  Say yes here to support for Maxim Semiconductor MAX8997/8966.  	  This is a Power Management IC with RTC, Flash, Fuel Gauge, Haptic,  	  MUIC controls on chip.  	  This driver provides common support for accessing the device; @@ -812,6 +828,18 @@ config MFD_PM8XXX_IRQ  config TPS65911_COMPARATOR  	tristate +config MFD_TPS65090 +	bool "TPS65090 Power Management chips" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	help +	  If you say yes here you get support for the TPS65090 series of +	  Power Management chips. +	  This driver provides common support for accessing the device, +	  additional drivers must be enabled in order to use the +	  functionality of the device. +  config MFD_AAT2870_CORE  	bool "Support for the AnalogicTech AAT2870"  	select MFD_CORE @@ -831,6 +859,28 @@ config MFD_INTEL_MSIC  	  Passage) chip. This chip embeds audio, battery, GPIO, etc.  	  devices used in Intel Medfield platforms. +config MFD_RC5T583 +	bool "Ricoh RC5T583 Power Management system device" +	depends on I2C=y && GENERIC_HARDIRQS +	select MFD_CORE +	select REGMAP_I2C +	help +	  Select this option to get support for the RICOH583 Power +	  Management system device. +	  This driver provides common support for accessing the device +	  through i2c interface. The device supports multiple sub-devices +	  like GPIO, interrupts, RTC, LDO and DCDC regulators, onkey. +	  Additional drivers must be enabled in order to use the +	  different functionality of the device. + +config MFD_ANATOP +	bool "Support for Freescale i.MX on-chip ANATOP controller" +	depends on SOC_IMX6Q +	help +	  Select this option to enable Freescale i.MX on-chip ANATOP +	  MFD controller. This controller embeds regulator and +	  thermal devices for Freescale i.MX platforms. +  endmenu  endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index b953bab934f..05fa538c5ef 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -38,6 +38,7 @@ obj-$(CONFIG_MFD_WM8994)	+= wm8994-core.o wm8994-irq.o wm8994-regmap.o  obj-$(CONFIG_TPS6105X)		+= tps6105x.o  obj-$(CONFIG_TPS65010)		+= tps65010.o  obj-$(CONFIG_TPS6507X)		+= tps6507x.o +obj-$(CONFIG_MFD_TPS65217)	+= tps65217.o  obj-$(CONFIG_MFD_TPS65910)	+= tps65910.o tps65910-irq.o  tps65912-objs                   := tps65912-core.o tps65912-irq.o  obj-$(CONFIG_MFD_TPS65912)	+= tps65912.o @@ -109,6 +110,9 @@ obj-$(CONFIG_MFD_OMAP_USB_HOST)	+= omap-usb-host.o  obj-$(CONFIG_MFD_PM8921_CORE) 	+= pm8921-core.o  obj-$(CONFIG_MFD_PM8XXX_IRQ) 	+= pm8xxx-irq.o  obj-$(CONFIG_TPS65911_COMPARATOR)	+= tps65911-comparator.o +obj-$(CONFIG_MFD_TPS65090)	+= tps65090.o  obj-$(CONFIG_MFD_AAT2870_CORE)	+= aat2870-core.o  obj-$(CONFIG_MFD_INTEL_MSIC)	+= intel_msic.o +obj-$(CONFIG_MFD_RC5T583)	+= rc5t583.o rc5t583-irq.o  obj-$(CONFIG_MFD_S5M_CORE)	+= s5m-core.o s5m-irq.o +obj-$(CONFIG_MFD_ANATOP)	+= anatop-mfd.o diff --git a/drivers/mfd/ab8500-core.c b/drivers/mfd/ab8500-core.c index d295941c9a3..1f08704f7ae 100644 --- a/drivers/mfd/ab8500-core.c +++ b/drivers/mfd/ab8500-core.c @@ -32,6 +32,7 @@  #define AB8500_IT_SOURCE6_REG		0x05  #define AB8500_IT_SOURCE7_REG		0x06  #define AB8500_IT_SOURCE8_REG		0x07 +#define AB9540_IT_SOURCE13_REG		0x0C  #define AB8500_IT_SOURCE19_REG		0x12  #define AB8500_IT_SOURCE20_REG		0x13  #define AB8500_IT_SOURCE21_REG		0x14 @@ -53,6 +54,7 @@  #define AB8500_IT_LATCH9_REG		0x28  #define AB8500_IT_LATCH10_REG		0x29  #define AB8500_IT_LATCH12_REG		0x2B +#define AB9540_IT_LATCH13_REG		0x2C  #define AB8500_IT_LATCH19_REG		0x32  #define AB8500_IT_LATCH20_REG		0x33  #define AB8500_IT_LATCH21_REG		0x34 @@ -90,21 +92,39 @@  #define AB8500_IT_MASK24_REG		0x57  #define AB8500_REV_REG			0x80 +#define AB8500_IC_NAME_REG		0x82  #define AB8500_SWITCH_OFF_STATUS	0x00  #define AB8500_TURN_ON_STATUS		0x00 +#define AB9540_MODEM_CTRL2_REG			0x23 +#define AB9540_MODEM_CTRL2_SWDBBRSTN_BIT	BIT(2) +  /*   * Map interrupt numbers to the LATCH and MASK register offsets, Interrupt - * numbers are indexed into this array with (num / 8). + * numbers are indexed into this array with (num / 8). The interupts are + * defined in linux/mfd/ab8500.h   *   * This is one off from the register names, i.e. AB8500_IT_MASK1_REG is at   * offset 0.   */ +/* AB8500 support */  static const int ab8500_irq_regoffset[AB8500_NUM_IRQ_REGS] = {  	0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21,  }; +/* AB9540 support */ +static const int ab9540_irq_regoffset[AB9540_NUM_IRQ_REGS] = { +	0, 1, 2, 3, 4, 6, 7, 8, 9, 11, 18, 19, 20, 21, 12, 13, 24, +}; + +static const char ab8500_version_str[][7] = { +	[AB8500_VERSION_AB8500] = "AB8500", +	[AB8500_VERSION_AB8505] = "AB8505", +	[AB8500_VERSION_AB9540] = "AB9540", +	[AB8500_VERSION_AB8540] = "AB8540", +}; +  static int ab8500_get_chip_id(struct device *dev)  {  	struct ab8500 *ab8500; @@ -127,9 +147,7 @@ static int set_register_interruptible(struct ab8500 *ab8500, u8 bank,  	dev_vdbg(ab8500->dev, "wr: addr %#x <= %#x\n", addr, data); -	ret = mutex_lock_interruptible(&ab8500->lock); -	if (ret) -		return ret; +	mutex_lock(&ab8500->lock);  	ret = ab8500->write(ab8500, addr, data);  	if (ret < 0) @@ -156,9 +174,7 @@ static int get_register_interruptible(struct ab8500 *ab8500, u8 bank,  	 * bank on higher 8 bits and reg in lower */  	u16 addr = ((u16)bank) << 8 | reg; -	ret = mutex_lock_interruptible(&ab8500->lock); -	if (ret) -		return ret; +	mutex_lock(&ab8500->lock);  	ret = ab8500->read(ab8500, addr);  	if (ret < 0) @@ -185,31 +201,38 @@ static int mask_and_set_register_interruptible(struct ab8500 *ab8500, u8 bank,  	u8 reg, u8 bitmask, u8 bitvalues)  {  	int ret; -	u8 data;  	/* put the u8 bank and u8 reg together into a an u16.  	 * bank on higher 8 bits and reg in lower */  	u16 addr = ((u16)bank) << 8 | reg; -	ret = mutex_lock_interruptible(&ab8500->lock); -	if (ret) -		return ret; +	mutex_lock(&ab8500->lock); -	ret = ab8500->read(ab8500, addr); -	if (ret < 0) { -		dev_err(ab8500->dev, "failed to read reg %#x: %d\n", -			addr, ret); -		goto out; -	} +	if (ab8500->write_masked == NULL) { +		u8 data; -	data = (u8)ret; -	data = (~bitmask & data) | (bitmask & bitvalues); +		ret = ab8500->read(ab8500, addr); +		if (ret < 0) { +			dev_err(ab8500->dev, "failed to read reg %#x: %d\n", +				addr, ret); +			goto out; +		} -	ret = ab8500->write(ab8500, addr, data); -	if (ret < 0) -		dev_err(ab8500->dev, "failed to write reg %#x: %d\n", -			addr, ret); +		data = (u8)ret; +		data = (~bitmask & data) | (bitmask & bitvalues); + +		ret = ab8500->write(ab8500, addr, data); +		if (ret < 0) +			dev_err(ab8500->dev, "failed to write reg %#x: %d\n", +				addr, ret); -	dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, data); +		dev_vdbg(ab8500->dev, "mask: addr %#x => data %#x\n", addr, +			data); +		goto out; +	} +	ret = ab8500->write_masked(ab8500, addr, bitmask, bitvalues); +	if (ret < 0) +		dev_err(ab8500->dev, "failed to modify reg %#x: %d\n", addr, +			ret);  out:  	mutex_unlock(&ab8500->lock);  	return ret; @@ -248,7 +271,7 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)  	struct ab8500 *ab8500 = irq_data_get_irq_chip_data(data);  	int i; -	for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { +	for (i = 0; i < ab8500->mask_size; i++) {  		u8 old = ab8500->oldmask[i];  		u8 new = ab8500->mask[i];  		int reg; @@ -256,14 +279,17 @@ static void ab8500_irq_sync_unlock(struct irq_data *data)  		if (new == old)  			continue; -		/* Interrupt register 12 doesn't exist prior to version 2.0 */ -		if (ab8500_irq_regoffset[i] == 11 && -			ab8500->chip_id < AB8500_CUT2P0) +		/* +		 * Interrupt register 12 doesn't exist prior to AB8500 version +		 * 2.0 +		 */ +		if (ab8500->irq_reg_offset[i] == 11 && +			is_ab8500_1p1_or_earlier(ab8500))  			continue;  		ab8500->oldmask[i] = new; -		reg = AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i]; +		reg = AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i];  		set_register_interruptible(ab8500, AB8500_INTERRUPT, reg, new);  	} @@ -306,13 +332,16 @@ static irqreturn_t ab8500_irq(int irq, void *dev)  	dev_vdbg(ab8500->dev, "interrupt\n"); -	for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { -		int regoffset = ab8500_irq_regoffset[i]; +	for (i = 0; i < ab8500->mask_size; i++) { +		int regoffset = ab8500->irq_reg_offset[i];  		int status;  		u8 value; -		/* Interrupt register 12 doesn't exist prior to version 2.0 */ -		if (regoffset == 11 && ab8500->chip_id < AB8500_CUT2P0) +		/* +		 * Interrupt register 12 doesn't exist prior to AB8500 version +		 * 2.0 +		 */ +		if (regoffset == 11 && is_ab8500_1p1_or_earlier(ab8500))  			continue;  		status = get_register_interruptible(ab8500, AB8500_INTERRUPT, @@ -336,8 +365,16 @@ static int ab8500_irq_init(struct ab8500 *ab8500)  {  	int base = ab8500->irq_base;  	int irq; +	int num_irqs; + +	if (is_ab9540(ab8500)) +		num_irqs = AB9540_NR_IRQS; +	else if (is_ab8505(ab8500)) +		num_irqs = AB8505_NR_IRQS; +	else +		num_irqs = AB8500_NR_IRQS; -	for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { +	for (irq = base; irq < base + num_irqs; irq++) {  		irq_set_chip_data(irq, ab8500);  		irq_set_chip_and_handler(irq, &ab8500_irq_chip,  					 handle_simple_irq); @@ -356,8 +393,16 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)  {  	int base = ab8500->irq_base;  	int irq; +	int num_irqs; + +	if (is_ab9540(ab8500)) +		num_irqs = AB9540_NR_IRQS; +	else if (is_ab8505(ab8500)) +		num_irqs = AB8505_NR_IRQS; +	else +		num_irqs = AB8500_NR_IRQS; -	for (irq = base; irq < base + AB8500_NR_IRQS; irq++) { +	for (irq = base; irq < base + num_irqs; irq++) {  #ifdef CONFIG_ARM  		set_irq_flags(irq, 0);  #endif @@ -366,6 +411,7 @@ static void ab8500_irq_remove(struct ab8500 *ab8500)  	}  } +/* AB8500 GPIO Resources */  static struct resource __devinitdata ab8500_gpio_resources[] = {  	{  		.name	= "GPIO_INT6", @@ -375,6 +421,28 @@ static struct resource __devinitdata ab8500_gpio_resources[] = {  	}  }; +/* AB9540 GPIO Resources */ +static struct resource __devinitdata ab9540_gpio_resources[] = { +	{ +		.name	= "GPIO_INT6", +		.start	= AB8500_INT_GPIO6R, +		.end	= AB8500_INT_GPIO41F, +		.flags	= IORESOURCE_IRQ, +	}, +	{ +		.name	= "GPIO_INT14", +		.start	= AB9540_INT_GPIO50R, +		.end	= AB9540_INT_GPIO54R, +		.flags	= IORESOURCE_IRQ, +	}, +	{ +		.name	= "GPIO_INT15", +		.start	= AB9540_INT_GPIO50F, +		.end	= AB9540_INT_GPIO54F, +		.flags	= IORESOURCE_IRQ, +	} +}; +  static struct resource __devinitdata ab8500_gpadc_resources[] = {  	{  		.name	= "HW_CONV_END", @@ -491,12 +559,6 @@ static struct resource __devinitdata ab8500_charger_resources[] = {  		.flags = IORESOURCE_IRQ,  	},  	{ -		.name = "USB_CHARGE_DET_DONE", -		.start = AB8500_INT_USB_CHG_DET_DONE, -		.end = AB8500_INT_USB_CHG_DET_DONE, -		.flags = IORESOURCE_IRQ, -	}, -	{  		.name = "VBUS_OVV",  		.start = AB8500_INT_VBUS_OVV,  		.end = AB8500_INT_VBUS_OVV, @@ -534,14 +596,8 @@ static struct resource __devinitdata ab8500_charger_resources[] = {  	},  	{  		.name = "USB_CHARGER_NOT_OKR", -		.start = AB8500_INT_USB_CHARGER_NOT_OK, -		.end = AB8500_INT_USB_CHARGER_NOT_OK, -		.flags = IORESOURCE_IRQ, -	}, -	{ -		.name = "USB_CHARGER_NOT_OKF", -		.start = AB8500_INT_USB_CHARGER_NOT_OKF, -		.end = AB8500_INT_USB_CHARGER_NOT_OKF, +		.start = AB8500_INT_USB_CHARGER_NOT_OKR, +		.end = AB8500_INT_USB_CHARGER_NOT_OKR,  		.flags = IORESOURCE_IRQ,  	},  	{ @@ -616,6 +672,12 @@ static struct resource __devinitdata ab8500_fg_resources[] = {  		.end = AB8500_INT_CC_INT_CALIB,  		.flags = IORESOURCE_IRQ,  	}, +	{ +		.name = "CCEOC", +		.start = AB8500_INT_CCEOC, +		.end = AB8500_INT_CCEOC, +		.flags = IORESOURCE_IRQ, +	},  };  static struct resource __devinitdata ab8500_chargalg_resources[] = {}; @@ -630,8 +692,8 @@ static struct resource __devinitdata ab8500_debug_resources[] = {  	},  	{  		.name	= "IRQ_LAST", -		.start	= AB8500_INT_USB_CHARGER_NOT_OKF, -		.end	= AB8500_INT_USB_CHARGER_NOT_OKF, +		.start	= AB8500_INT_XTAL32K_KO, +		.end	= AB8500_INT_XTAL32K_KO,  		.flags	= IORESOURCE_IRQ,  	},  }; @@ -691,7 +753,7 @@ static struct resource __devinitdata ab8500_temp_resources[] = {  	},  }; -static struct mfd_cell __devinitdata ab8500_devs[] = { +static struct mfd_cell __devinitdata abx500_common_devs[] = {  #ifdef CONFIG_DEBUG_FS  	{  		.name = "ab8500-debug", @@ -706,11 +768,6 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {  		.name = "ab8500-regulator",  	},  	{ -		.name = "ab8500-gpio", -		.num_resources = ARRAY_SIZE(ab8500_gpio_resources), -		.resources = ab8500_gpio_resources, -	}, -	{  		.name = "ab8500-gpadc",  		.num_resources = ARRAY_SIZE(ab8500_gpadc_resources),  		.resources = ab8500_gpadc_resources, @@ -748,11 +805,7 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {  	{  		.name = "ab8500-codec",  	}, -	{ -		.name = "ab8500-usb", -		.num_resources = ARRAY_SIZE(ab8500_usb_resources), -		.resources = ab8500_usb_resources, -	}, +  	{  		.name = "ab8500-poweron-key",  		.num_resources = ARRAY_SIZE(ab8500_poweronkey_db_resources), @@ -781,6 +834,32 @@ static struct mfd_cell __devinitdata ab8500_devs[] = {  	},  }; +static struct mfd_cell __devinitdata ab8500_devs[] = { +	{ +		.name = "ab8500-gpio", +		.num_resources = ARRAY_SIZE(ab8500_gpio_resources), +		.resources = ab8500_gpio_resources, +	}, +	{ +		.name = "ab8500-usb", +		.num_resources = ARRAY_SIZE(ab8500_usb_resources), +		.resources = ab8500_usb_resources, +	}, +}; + +static struct mfd_cell __devinitdata ab9540_devs[] = { +	{ +		.name = "ab8500-gpio", +		.num_resources = ARRAY_SIZE(ab9540_gpio_resources), +		.resources = ab9540_gpio_resources, +	}, +	{ +		.name = "ab9540-usb", +		.num_resources = ARRAY_SIZE(ab8500_usb_resources), +		.resources = ab8500_usb_resources, +	}, +}; +  static ssize_t show_chip_id(struct device *dev,  				struct device_attribute *attr, char *buf)  { @@ -842,9 +921,64 @@ static ssize_t show_turn_on_status(struct device *dev,  	return sprintf(buf, "%#x\n", value);  } +static ssize_t show_ab9540_dbbrstn(struct device *dev, +				struct device_attribute *attr, char *buf) +{ +	struct ab8500 *ab8500; +	int ret; +	u8 value; + +	ab8500 = dev_get_drvdata(dev); + +	ret = get_register_interruptible(ab8500, AB8500_REGU_CTRL2, +		AB9540_MODEM_CTRL2_REG, &value); +	if (ret < 0) +		return ret; + +	return sprintf(buf, "%d\n", +			(value & AB9540_MODEM_CTRL2_SWDBBRSTN_BIT) ? 1 : 0); +} + +static ssize_t store_ab9540_dbbrstn(struct device *dev, +	struct device_attribute *attr, const char *buf, size_t count) +{ +	struct ab8500 *ab8500; +	int ret = count; +	int err; +	u8 bitvalues; + +	ab8500 = dev_get_drvdata(dev); + +	if (count > 0) { +		switch (buf[0]) { +		case '0': +			bitvalues = 0; +			break; +		case '1': +			bitvalues = AB9540_MODEM_CTRL2_SWDBBRSTN_BIT; +			break; +		default: +			goto exit; +		} + +		err = mask_and_set_register_interruptible(ab8500, +			AB8500_REGU_CTRL2, AB9540_MODEM_CTRL2_REG, +			AB9540_MODEM_CTRL2_SWDBBRSTN_BIT, bitvalues); +		if (err) +			dev_info(ab8500->dev, +				"Failed to set DBBRSTN %c, err %#x\n", +				buf[0], err); +	} + +exit: +	return ret; +} +  static DEVICE_ATTR(chip_id, S_IRUGO, show_chip_id, NULL);  static DEVICE_ATTR(switch_off_status, S_IRUGO, show_switch_off_status, NULL);  static DEVICE_ATTR(turn_on_status, S_IRUGO, show_turn_on_status, NULL); +static DEVICE_ATTR(dbbrstn, S_IRUGO | S_IWUSR, +			show_ab9540_dbbrstn, store_ab9540_dbbrstn);  static struct attribute *ab8500_sysfs_entries[] = {  	&dev_attr_chip_id.attr, @@ -853,11 +987,23 @@ static struct attribute *ab8500_sysfs_entries[] = {  	NULL,  }; +static struct attribute *ab9540_sysfs_entries[] = { +	&dev_attr_chip_id.attr, +	&dev_attr_switch_off_status.attr, +	&dev_attr_turn_on_status.attr, +	&dev_attr_dbbrstn.attr, +	NULL, +}; +  static struct attribute_group ab8500_attr_group = {  	.attrs	= ab8500_sysfs_entries,  }; -int __devinit ab8500_init(struct ab8500 *ab8500) +static struct attribute_group ab9540_attr_group = { +	.attrs	= ab9540_sysfs_entries, +}; + +int __devinit ab8500_init(struct ab8500 *ab8500, enum ab8500_version version)  {  	struct ab8500_platform_data *plat = dev_get_platdata(ab8500->dev);  	int ret; @@ -870,25 +1016,45 @@ int __devinit ab8500_init(struct ab8500 *ab8500)  	mutex_init(&ab8500->lock);  	mutex_init(&ab8500->irq_lock); +	if (version != AB8500_VERSION_UNDEFINED) +		ab8500->version = version; +	else { +		ret = get_register_interruptible(ab8500, AB8500_MISC, +			AB8500_IC_NAME_REG, &value); +		if (ret < 0) +			return ret; + +		ab8500->version = value; +	} +  	ret = get_register_interruptible(ab8500, AB8500_MISC,  		AB8500_REV_REG, &value);  	if (ret < 0)  		return ret; -	switch (value) { -	case AB8500_CUT1P0: -	case AB8500_CUT1P1: -	case AB8500_CUT2P0: -	case AB8500_CUT3P0: -	case AB8500_CUT3P3: -		dev_info(ab8500->dev, "detected chip, revision: %#x\n", value); -		break; -	default: -		dev_err(ab8500->dev, "unknown chip, revision: %#x\n", value); -		return -EINVAL; -	}  	ab8500->chip_id = value; +	dev_info(ab8500->dev, "detected chip, %s rev. %1x.%1x\n", +			ab8500_version_str[ab8500->version], +			ab8500->chip_id >> 4, +			ab8500->chip_id & 0x0F); + +	/* Configure AB8500 or AB9540 IRQ */ +	if (is_ab9540(ab8500) || is_ab8505(ab8500)) { +		ab8500->mask_size = AB9540_NUM_IRQ_REGS; +		ab8500->irq_reg_offset = ab9540_irq_regoffset; +	} else { +		ab8500->mask_size = AB8500_NUM_IRQ_REGS; +		ab8500->irq_reg_offset = ab8500_irq_regoffset; +	} +	ab8500->mask = kzalloc(ab8500->mask_size, GFP_KERNEL); +	if (!ab8500->mask) +		return -ENOMEM; +	ab8500->oldmask = kzalloc(ab8500->mask_size, GFP_KERNEL); +	if (!ab8500->oldmask) { +		ret = -ENOMEM; +		goto out_freemask; +	}  	/*  	 * ab8500 has switched off due to (SWITCH_OFF_STATUS):  	 * 0x01 Swoff bit programming @@ -911,30 +1077,33 @@ int __devinit ab8500_init(struct ab8500 *ab8500)  		plat->init(ab8500);  	/* Clear and mask all interrupts */ -	for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) { -		/* Interrupt register 12 doesn't exist prior to version 2.0 */ -		if (ab8500_irq_regoffset[i] == 11 && -			ab8500->chip_id < AB8500_CUT2P0) +	for (i = 0; i < ab8500->mask_size; i++) { +		/* +		 * Interrupt register 12 doesn't exist prior to AB8500 version +		 * 2.0 +		 */ +		if (ab8500->irq_reg_offset[i] == 11 && +				is_ab8500_1p1_or_earlier(ab8500))  			continue;  		get_register_interruptible(ab8500, AB8500_INTERRUPT, -			AB8500_IT_LATCH1_REG + ab8500_irq_regoffset[i], +			AB8500_IT_LATCH1_REG + ab8500->irq_reg_offset[i],  			&value);  		set_register_interruptible(ab8500, AB8500_INTERRUPT, -			AB8500_IT_MASK1_REG + ab8500_irq_regoffset[i], 0xff); +			AB8500_IT_MASK1_REG + ab8500->irq_reg_offset[i], 0xff);  	}  	ret = abx500_register_ops(ab8500->dev, &ab8500_ops);  	if (ret) -		return ret; +		goto out_freeoldmask; -	for (i = 0; i < AB8500_NUM_IRQ_REGS; i++) +	for (i = 0; i < ab8500->mask_size; i++)  		ab8500->mask[i] = ab8500->oldmask[i] = 0xff;  	if (ab8500->irq_base) {  		ret = ab8500_irq_init(ab8500);  		if (ret) -			return ret; +			goto out_freeoldmask;  		ret = request_threaded_irq(ab8500->irq, NULL, ab8500_irq,  					   IRQF_ONESHOT | IRQF_NO_SUSPEND, @@ -943,17 +1112,34 @@ int __devinit ab8500_init(struct ab8500 *ab8500)  			goto out_removeirq;  	} -	ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, -			      ARRAY_SIZE(ab8500_devs), NULL, +	ret = mfd_add_devices(ab8500->dev, 0, abx500_common_devs, +			      ARRAY_SIZE(abx500_common_devs), NULL,  			      ab8500->irq_base); +  	if (ret)  		goto out_freeirq; -	ret = sysfs_create_group(&ab8500->dev->kobj, &ab8500_attr_group); +	if (is_ab9540(ab8500)) +		ret = mfd_add_devices(ab8500->dev, 0, ab9540_devs, +			      ARRAY_SIZE(ab9540_devs), NULL, +			      ab8500->irq_base); +	else +		ret = mfd_add_devices(ab8500->dev, 0, ab8500_devs, +			      ARRAY_SIZE(ab9540_devs), NULL, +			      ab8500->irq_base);  	if (ret) -		dev_err(ab8500->dev, "error creating sysfs entries\n"); +		goto out_freeirq; -	return ret; +	if (is_ab9540(ab8500)) +		ret = sysfs_create_group(&ab8500->dev->kobj, +					&ab9540_attr_group); +	else +		ret = sysfs_create_group(&ab8500->dev->kobj, +					&ab8500_attr_group); +	if (ret) +		dev_err(ab8500->dev, "error creating sysfs entries\n"); +	else +		return ret;  out_freeirq:  	if (ab8500->irq_base) @@ -961,18 +1147,27 @@ out_freeirq:  out_removeirq:  	if (ab8500->irq_base)  		ab8500_irq_remove(ab8500); +out_freeoldmask: +	kfree(ab8500->oldmask); +out_freemask: +	kfree(ab8500->mask);  	return ret;  }  int __devexit ab8500_exit(struct ab8500 *ab8500)  { -	sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group); +	if (is_ab9540(ab8500)) +		sysfs_remove_group(&ab8500->dev->kobj, &ab9540_attr_group); +	else +		sysfs_remove_group(&ab8500->dev->kobj, &ab8500_attr_group);  	mfd_remove_devices(ab8500->dev);  	if (ab8500->irq_base) {  		free_irq(ab8500->irq, ab8500);  		ab8500_irq_remove(ab8500);  	} +	kfree(ab8500->oldmask); +	kfree(ab8500->mask);  	return 0;  } diff --git a/drivers/mfd/ab8500-i2c.c b/drivers/mfd/ab8500-i2c.c index 087fecd71ce..b83045f102b 100644 --- a/drivers/mfd/ab8500-i2c.c +++ b/drivers/mfd/ab8500-i2c.c @@ -11,7 +11,7 @@  #include <linux/module.h>  #include <linux/platform_device.h>  #include <linux/mfd/abx500/ab8500.h> -#include <linux/mfd/db8500-prcmu.h> +#include <linux/mfd/dbx500-prcmu.h>  static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)  { @@ -23,6 +23,18 @@ static int ab8500_i2c_write(struct ab8500 *ab8500, u16 addr, u8 data)  	return ret;  } +static int ab8500_i2c_write_masked(struct ab8500 *ab8500, u16 addr, u8 mask, +	u8 data) +{ +	int ret; + +	ret = prcmu_abb_write_masked((u8)(addr >> 8), (u8)(addr & 0xFF), &data, +		&mask, 1); +	if (ret < 0) +		dev_err(ab8500->dev, "prcmu i2c error %d\n", ret); +	return ret; +} +  static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)  {  	int ret; @@ -38,6 +50,7 @@ static int ab8500_i2c_read(struct ab8500 *ab8500, u16 addr)  static int __devinit ab8500_i2c_probe(struct platform_device *plf)  { +	const struct platform_device_id *platid = platform_get_device_id(plf);  	struct ab8500 *ab8500;  	struct resource *resource;  	int ret; @@ -58,13 +71,15 @@ static int __devinit ab8500_i2c_probe(struct platform_device *plf)  	ab8500->read = ab8500_i2c_read;  	ab8500->write = ab8500_i2c_write; +	ab8500->write_masked = ab8500_i2c_write_masked;  	platform_set_drvdata(plf, ab8500); -	ret = ab8500_init(ab8500); +	ret = ab8500_init(ab8500, platid->driver_data);  	if (ret)  		kfree(ab8500); +  	return ret;  } @@ -78,13 +93,22 @@ static int __devexit ab8500_i2c_remove(struct platform_device *plf)  	return 0;  } +static const struct platform_device_id ab8500_id[] = { +	{ "ab8500-i2c", AB8500_VERSION_AB8500 }, +	{ "ab8505-i2c", AB8500_VERSION_AB8505 }, +	{ "ab9540-i2c", AB8500_VERSION_AB9540 }, +	{ "ab8540-i2c", AB8500_VERSION_AB8540 }, +	{ } +}; +  static struct platform_driver ab8500_i2c_driver = {  	.driver = {  		.name = "ab8500-i2c",  		.owner = THIS_MODULE,  	},  	.probe	= ab8500_i2c_probe, -	.remove	= __devexit_p(ab8500_i2c_remove) +	.remove	= __devexit_p(ab8500_i2c_remove), +	.id_table = ab8500_id,  };  static int __init ab8500_i2c_init(void) diff --git a/drivers/mfd/anatop-mfd.c b/drivers/mfd/anatop-mfd.c new file mode 100644 index 00000000000..2af42480635 --- /dev/null +++ b/drivers/mfd/anatop-mfd.c @@ -0,0 +1,137 @@ +/* + * Anatop MFD driver + * + * Copyright (C) 2012 Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org> + * Copyright (C) 2012 Linaro + * + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *   (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License along + *  with this program; if not, write to the Free Software Foundation, Inc., + *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + *  This program is free software; you can redistribute it and/or modify + *  it under the terms of the GNU General Public License as published by + *  the Free Software Foundation; either version 2 of the License, or + *  (at your option) any later version. + * + *  This program is distributed in the hope that it will be useful, + *  but WITHOUT ANY WARRANTY; without even the implied warranty of + *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + *  GNU General Public License for more details. + * + *  You should have received a copy of the GNU General Public License along + *  with this program; if not, write to the Free Software Foundation, Inc., + *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include <linux/io.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/of.h> +#include <linux/of_platform.h> +#include <linux/of_address.h> +#include <linux/mfd/anatop.h> + +u32 anatop_get_bits(struct anatop *adata, u32 addr, int bit_shift, +		    int bit_width) +{ +	u32 val, mask; + +	if (bit_width == 32) +		mask = ~0; +	else +		mask = (1 << bit_width) - 1; + +	val = readl(adata->ioreg + addr); +	val = (val >> bit_shift) & mask; + +	return val; +} +EXPORT_SYMBOL_GPL(anatop_get_bits); + +void anatop_set_bits(struct anatop *adata, u32 addr, int bit_shift, +		     int bit_width, u32 data) +{ +	u32 val, mask; + +	if (bit_width == 32) +		mask = ~0; +	else +		mask = (1 << bit_width) - 1; + +	spin_lock(&adata->reglock); +	val = readl(adata->ioreg + addr) & ~(mask << bit_shift); +	writel((data << bit_shift) | val, adata->ioreg + addr); +	spin_unlock(&adata->reglock); +} +EXPORT_SYMBOL_GPL(anatop_set_bits); + +static const struct of_device_id of_anatop_match[] = { +	{ .compatible = "fsl,imx6q-anatop", }, +	{ }, +}; + +static int __devinit of_anatop_probe(struct platform_device *pdev) +{ +	struct device *dev = &pdev->dev; +	struct device_node *np = dev->of_node; +	void *ioreg; +	struct anatop *drvdata; + +	ioreg = of_iomap(np, 0); +	if (!ioreg) +		return -EADDRNOTAVAIL; +	drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL); +	if (!drvdata) +		return -ENOMEM; +	drvdata->ioreg = ioreg; +	spin_lock_init(&drvdata->reglock); +	platform_set_drvdata(pdev, drvdata); +	of_platform_populate(np, of_anatop_match, NULL, dev); + +	return 0; +} + +static int __devexit of_anatop_remove(struct platform_device *pdev) +{ +	struct anatop *drvdata; +	drvdata = platform_get_drvdata(pdev); +	iounmap(drvdata->ioreg); + +	return 0; +} + +static struct platform_driver anatop_of_driver = { +	.driver = { +		.name = "anatop-mfd", +		.owner = THIS_MODULE, +		.of_match_table = of_anatop_match, +	}, +	.probe		= of_anatop_probe, +	.remove		= of_anatop_remove, +}; + +static int __init anatop_init(void) +{ +	return platform_driver_register(&anatop_of_driver); +} +postcore_initcall(anatop_init); + +static void __exit anatop_exit(void) +{ +	platform_driver_unregister(&anatop_of_driver); +} +module_exit(anatop_exit); + +MODULE_AUTHOR("Ying-Chun Liu (PaulLiu) <paul.liu@linaro.org>"); +MODULE_DESCRIPTION("ANATOP MFD driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/asic3.c b/drivers/mfd/asic3.c index b85bbd7f0d1..1895cf9fab8 100644 --- a/drivers/mfd/asic3.c +++ b/drivers/mfd/asic3.c @@ -525,6 +525,11 @@ static void asic3_gpio_set(struct gpio_chip *chip,  	return;  } +static int asic3_gpio_to_irq(struct gpio_chip *chip, unsigned offset) +{ +	return (offset < ASIC3_NUM_GPIOS) ? IRQ_BOARD_START + offset : -ENXIO; +} +  static __init int asic3_gpio_probe(struct platform_device *pdev,  				   u16 *gpio_config, int num)  { @@ -976,6 +981,7 @@ static int __init asic3_probe(struct platform_device *pdev)  	asic->gpio.set = asic3_gpio_set;  	asic->gpio.direction_input = asic3_gpio_direction_input;  	asic->gpio.direction_output = asic3_gpio_direction_output; +	asic->gpio.to_irq = asic3_gpio_to_irq;  	ret = asic3_gpio_probe(pdev,  			       pdata->gpio_config, diff --git a/drivers/mfd/da9052-core.c b/drivers/mfd/da9052-core.c index 5ddde2a9176..7ff313fe9fb 100644 --- a/drivers/mfd/da9052-core.c +++ b/drivers/mfd/da9052-core.c @@ -16,7 +16,6 @@  #include <linux/input.h>  #include <linux/interrupt.h>  #include <linux/irq.h> -#include <linux/mutex.h>  #include <linux/mfd/core.h>  #include <linux/slab.h>  #include <linux/module.h> @@ -647,8 +646,6 @@ int __devinit da9052_device_init(struct da9052 *da9052, u8 chip_id)  	struct irq_desc *desc;  	int ret; -	mutex_init(&da9052->io_lock); -  	if (pdata && pdata->init != NULL)  		pdata->init(da9052); diff --git a/drivers/mfd/da9052-i2c.c b/drivers/mfd/da9052-i2c.c index 44b97c70a61..36b88e39549 100644 --- a/drivers/mfd/da9052-i2c.c +++ b/drivers/mfd/da9052-i2c.c @@ -74,24 +74,27 @@ static int __devinit da9052_i2c_probe(struct i2c_client *client,  	ret = da9052_i2c_enable_multiwrite(da9052);  	if (ret < 0) -		goto err; +		goto err_regmap;  	ret = da9052_device_init(da9052, id->driver_data);  	if (ret != 0) -		goto err; +		goto err_regmap;  	return 0; +err_regmap: +	regmap_exit(da9052->regmap);  err:  	kfree(da9052);  	return ret;  } -static int da9052_i2c_remove(struct i2c_client *client) +static int __devexit da9052_i2c_remove(struct i2c_client *client)  {  	struct da9052 *da9052 = i2c_get_clientdata(client);  	da9052_device_exit(da9052); +	regmap_exit(da9052->regmap);  	kfree(da9052);  	return 0; @@ -107,7 +110,7 @@ static struct i2c_device_id da9052_i2c_id[] = {  static struct i2c_driver da9052_i2c_driver = {  	.probe = da9052_i2c_probe, -	.remove = da9052_i2c_remove, +	.remove = __devexit_p(da9052_i2c_remove),  	.id_table = da9052_i2c_id,  	.driver = {  		.name = "da9052", diff --git a/drivers/mfd/da9052-spi.c b/drivers/mfd/da9052-spi.c index cdbc7cad326..6faf149e8d9 100644 --- a/drivers/mfd/da9052-spi.c +++ b/drivers/mfd/da9052-spi.c @@ -21,7 +21,7 @@  #include <linux/mfd/da9052/da9052.h> -static int da9052_spi_probe(struct spi_device *spi) +static int __devinit da9052_spi_probe(struct spi_device *spi)  {  	int ret;  	const struct spi_device_id *id = spi_get_device_id(spi); @@ -52,20 +52,23 @@ static int da9052_spi_probe(struct spi_device *spi)  	ret = da9052_device_init(da9052, id->driver_data);  	if (ret != 0) -		goto err; +		goto err_regmap;  	return 0; +err_regmap: +	regmap_exit(da9052->regmap);  err:  	kfree(da9052);  	return ret;  } -static int da9052_spi_remove(struct spi_device *spi) +static int __devexit da9052_spi_remove(struct spi_device *spi)  {  	struct da9052 *da9052 = dev_get_drvdata(&spi->dev);  	da9052_device_exit(da9052); +	regmap_exit(da9052->regmap);  	kfree(da9052);  	return 0; diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index af8e0efedbe..ebc1e865822 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -30,6 +30,7 @@  #include <linux/mfd/dbx500-prcmu.h>  #include <linux/regulator/db8500-prcmu.h>  #include <linux/regulator/machine.h> +#include <asm/hardware/gic.h>  #include <mach/hardware.h>  #include <mach/irqs.h>  #include <mach/db8500-regs.h> @@ -39,11 +40,6 @@  /* Offset for the firmware version within the TCPM */  #define PRCMU_FW_VERSION_OFFSET 0xA4 -/* PRCMU project numbers, defined by PRCMU FW */ -#define PRCMU_PROJECT_ID_8500V1_0 1 -#define PRCMU_PROJECT_ID_8500V2_0 2 -#define PRCMU_PROJECT_ID_8400V2_0 3 -  /* Index of different voltages to be used when accessing AVSData */  #define PRCM_AVS_BASE		0x2FC  #define PRCM_AVS_VBB_RET	(PRCM_AVS_BASE + 0x0) @@ -137,6 +133,8 @@  #define PRCM_REQ_MB1_ARM_OPP			(PRCM_REQ_MB1 + 0x0)  #define PRCM_REQ_MB1_APE_OPP			(PRCM_REQ_MB1 + 0x1)  #define PRCM_REQ_MB1_PLL_ON_OFF			(PRCM_REQ_MB1 + 0x4) +#define PLL_SOC0_OFF	0x1 +#define PLL_SOC0_ON	0x2  #define PLL_SOC1_OFF	0x4  #define PLL_SOC1_ON	0x8 @@ -266,6 +264,11 @@  #define WAKEUP_BIT_GPIO7 BIT(30)  #define WAKEUP_BIT_GPIO8 BIT(31) +static struct { +	bool valid; +	struct prcmu_fw_version version; +} fw_info; +  /*   * This vector maps irq numbers to the bits in the bit field used in   * communication with the PRCMU firmware. @@ -341,11 +344,13 @@ static struct {   * mb1_transfer - state needed for mailbox 1 communication.   * @lock:	The transaction lock.   * @work:	The transaction completion structure. + * @ape_opp:	The current APE OPP.   * @ack:	Reply ("acknowledge") data.   */  static struct {  	struct mutex lock;  	struct completion work; +	u8 ape_opp;  	struct {  		u8 header;  		u8 arm_opp; @@ -413,79 +418,102 @@ static struct {  static atomic_t ac_wake_req_state = ATOMIC_INIT(0);  /* Spinlocks */ +static DEFINE_SPINLOCK(prcmu_lock);  static DEFINE_SPINLOCK(clkout_lock); -static DEFINE_SPINLOCK(gpiocr_lock);  /* Global var to runtime determine TCDM base for v2 or v1 */  static __iomem void *tcdm_base;  struct clk_mgt { -	unsigned int offset; +	void __iomem *reg;  	u32 pllsw; +	int branch; +	bool clk38div; +}; + +enum { +	PLL_RAW, +	PLL_FIX, +	PLL_DIV  };  static DEFINE_SPINLOCK(clk_mgt_lock); -#define CLK_MGT_ENTRY(_name)[PRCMU_##_name] = { (PRCM_##_name##_MGT_OFF), 0 } +#define CLK_MGT_ENTRY(_name, _branch, _clk38div)[PRCMU_##_name] = \ +	{ (PRCM_##_name##_MGT), 0 , _branch, _clk38div}  struct clk_mgt clk_mgt[PRCMU_NUM_REG_CLOCKS] = { -	CLK_MGT_ENTRY(SGACLK), -	CLK_MGT_ENTRY(UARTCLK), -	CLK_MGT_ENTRY(MSP02CLK), -	CLK_MGT_ENTRY(MSP1CLK), -	CLK_MGT_ENTRY(I2CCLK), -	CLK_MGT_ENTRY(SDMMCCLK), -	CLK_MGT_ENTRY(SLIMCLK), -	CLK_MGT_ENTRY(PER1CLK), -	CLK_MGT_ENTRY(PER2CLK), -	CLK_MGT_ENTRY(PER3CLK), -	CLK_MGT_ENTRY(PER5CLK), -	CLK_MGT_ENTRY(PER6CLK), -	CLK_MGT_ENTRY(PER7CLK), -	CLK_MGT_ENTRY(LCDCLK), -	CLK_MGT_ENTRY(BMLCLK), -	CLK_MGT_ENTRY(HSITXCLK), -	CLK_MGT_ENTRY(HSIRXCLK), -	CLK_MGT_ENTRY(HDMICLK), -	CLK_MGT_ENTRY(APEATCLK), -	CLK_MGT_ENTRY(APETRACECLK), -	CLK_MGT_ENTRY(MCDECLK), -	CLK_MGT_ENTRY(IPI2CCLK), -	CLK_MGT_ENTRY(DSIALTCLK), -	CLK_MGT_ENTRY(DMACLK), -	CLK_MGT_ENTRY(B2R2CLK), -	CLK_MGT_ENTRY(TVCLK), -	CLK_MGT_ENTRY(SSPCLK), -	CLK_MGT_ENTRY(RNGCLK), -	CLK_MGT_ENTRY(UICCCLK), +	CLK_MGT_ENTRY(SGACLK, PLL_DIV, false), +	CLK_MGT_ENTRY(UARTCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(MSP02CLK, PLL_FIX, true), +	CLK_MGT_ENTRY(MSP1CLK, PLL_FIX, true), +	CLK_MGT_ENTRY(I2CCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(SDMMCCLK, PLL_DIV, true), +	CLK_MGT_ENTRY(SLIMCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(PER1CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(PER2CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(PER3CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(PER5CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(PER6CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(PER7CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(LCDCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(BMLCLK, PLL_DIV, true), +	CLK_MGT_ENTRY(HSITXCLK, PLL_DIV, true), +	CLK_MGT_ENTRY(HSIRXCLK, PLL_DIV, true), +	CLK_MGT_ENTRY(HDMICLK, PLL_FIX, false), +	CLK_MGT_ENTRY(APEATCLK, PLL_DIV, true), +	CLK_MGT_ENTRY(APETRACECLK, PLL_DIV, true), +	CLK_MGT_ENTRY(MCDECLK, PLL_DIV, true), +	CLK_MGT_ENTRY(IPI2CCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(DSIALTCLK, PLL_FIX, false), +	CLK_MGT_ENTRY(DMACLK, PLL_DIV, true), +	CLK_MGT_ENTRY(B2R2CLK, PLL_DIV, true), +	CLK_MGT_ENTRY(TVCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(SSPCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(RNGCLK, PLL_FIX, true), +	CLK_MGT_ENTRY(UICCCLK, PLL_FIX, false),  }; -static struct regulator *hwacc_regulator[NUM_HW_ACC]; -static struct regulator *hwacc_ret_regulator[NUM_HW_ACC]; +struct dsiclk { +	u32 divsel_mask; +	u32 divsel_shift; +	u32 divsel; +}; -static bool hwacc_enabled[NUM_HW_ACC]; -static bool hwacc_ret_enabled[NUM_HW_ACC]; +static struct dsiclk dsiclk[2] = { +	{ +		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK, +		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT, +		.divsel = PRCM_DSI_PLLOUT_SEL_PHI, +	}, +	{ +		.divsel_mask = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK, +		.divsel_shift = PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT, +		.divsel = PRCM_DSI_PLLOUT_SEL_PHI, +	} +}; -static const char *hwacc_regulator_name[NUM_HW_ACC] = { -	[HW_ACC_SVAMMDSP]	= "hwacc-sva-mmdsp", -	[HW_ACC_SVAPIPE]	= "hwacc-sva-pipe", -	[HW_ACC_SIAMMDSP]	= "hwacc-sia-mmdsp", -	[HW_ACC_SIAPIPE]	= "hwacc-sia-pipe", -	[HW_ACC_SGA]		= "hwacc-sga", -	[HW_ACC_B2R2]		= "hwacc-b2r2", -	[HW_ACC_MCDE]		= "hwacc-mcde", -	[HW_ACC_ESRAM1]		= "hwacc-esram1", -	[HW_ACC_ESRAM2]		= "hwacc-esram2", -	[HW_ACC_ESRAM3]		= "hwacc-esram3", -	[HW_ACC_ESRAM4]		= "hwacc-esram4", +struct dsiescclk { +	u32 en; +	u32 div_mask; +	u32 div_shift;  }; -static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = { -	[HW_ACC_SVAMMDSP]	= "hwacc-sva-mmdsp-ret", -	[HW_ACC_SIAMMDSP]	= "hwacc-sia-mmdsp-ret", -	[HW_ACC_ESRAM1]		= "hwacc-esram1-ret", -	[HW_ACC_ESRAM2]		= "hwacc-esram2-ret", -	[HW_ACC_ESRAM3]		= "hwacc-esram3-ret", -	[HW_ACC_ESRAM4]		= "hwacc-esram4-ret", +static struct dsiescclk dsiescclk[3] = { +	{ +		.en = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN, +		.div_mask = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK, +		.div_shift = PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT, +	}, +	{ +		.en = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN, +		.div_mask = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK, +		.div_shift = PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT, +	}, +	{ +		.en = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN, +		.div_mask = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK, +		.div_shift = PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT, +	}  };  /* @@ -503,9 +531,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {  /* PLLDIV=12, PLLSW=4 (PLLDDR) */  #define PRCMU_DSI_CLOCK_SETTING		0x0000008C -/* PLLDIV=8, PLLSW=4 (PLLDDR) */ -#define PRCMU_DSI_CLOCK_SETTING_U8400	0x00000088 -  /* DPI 50000000 Hz */  #define PRCMU_DPI_CLOCK_SETTING		((1 << PRCMU_CLK_PLL_SW_SHIFT) | \  					  (16 << PRCMU_CLK_PLL_DIV_SHIFT)) @@ -514,9 +539,6 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {  /* D=101, N=1, R=4, SELDIV2=0 */  #define PRCMU_PLLDSI_FREQ_SETTING	0x00040165 -/* D=70, N=1, R=3, SELDIV2=0 */ -#define PRCMU_PLLDSI_FREQ_SETTING_U8400	0x00030146 -  #define PRCMU_ENABLE_PLLDSI		0x00000001  #define PRCMU_DISABLE_PLLDSI		0x00000000  #define PRCMU_RELEASE_RESET_DSS		0x0000400C @@ -528,30 +550,17 @@ static const char *hwacc_ret_regulator_name[NUM_HW_ACC] = {  #define PRCMU_PLLDSI_LOCKP_LOCKED	0x3 -static struct { -	u8 project_number; -	u8 api_version; -	u8 func_version; -	u8 errata; -} prcmu_version; - -  int db8500_prcmu_enable_dsipll(void)  {  	int i; -	unsigned int plldsifreq;  	/* Clear DSIPLL_RESETN */  	writel(PRCMU_RESET_DSIPLL, PRCM_APE_RESETN_CLR);  	/* Unclamp DSIPLL in/out */  	writel(PRCMU_UNCLAMP_DSIPLL, PRCM_MMIP_LS_CLAMP_CLR); -	if (prcmu_is_u8400()) -		plldsifreq = PRCMU_PLLDSI_FREQ_SETTING_U8400; -	else -		plldsifreq = PRCMU_PLLDSI_FREQ_SETTING;  	/* Set DSI PLL FREQ */ -	writel(plldsifreq, PRCM_PLLDSI_FREQ); +	writel(PRCMU_PLLDSI_FREQ_SETTING, PRCM_PLLDSI_FREQ);  	writel(PRCMU_DSI_PLLOUT_SEL_SETTING, PRCM_DSI_PLLOUT_SEL);  	/* Enable Escape clocks */  	writel(PRCMU_ENABLE_ESCAPE_CLOCK_DIV, PRCM_DSITVCLK_DIV); @@ -583,12 +592,6 @@ int db8500_prcmu_disable_dsipll(void)  int db8500_prcmu_set_display_clocks(void)  {  	unsigned long flags; -	unsigned int dsiclk; - -	if (prcmu_is_u8400()) -		dsiclk = PRCMU_DSI_CLOCK_SETTING_U8400; -	else -		dsiclk = PRCMU_DSI_CLOCK_SETTING;  	spin_lock_irqsave(&clk_mgt_lock, flags); @@ -596,7 +599,7 @@ int db8500_prcmu_set_display_clocks(void)  	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)  		cpu_relax(); -	writel(dsiclk, PRCM_HDMICLK_MGT); +	writel(PRCMU_DSI_CLOCK_SETTING, PRCM_HDMICLK_MGT);  	writel(PRCMU_DSI_LP_CLOCK_SETTING, PRCM_TVCLK_MGT);  	writel(PRCMU_DPI_CLOCK_SETTING, PRCM_LCDCLK_MGT); @@ -608,43 +611,41 @@ int db8500_prcmu_set_display_clocks(void)  	return 0;  } -/** - * prcmu_enable_spi2 - Enables pin muxing for SPI2 on OtherAlternateC1. - */ -void prcmu_enable_spi2(void) +u32 db8500_prcmu_read(unsigned int reg) +{ +	return readl(_PRCMU_BASE + reg); +} + +void db8500_prcmu_write(unsigned int reg, u32 value)  { -	u32 reg;  	unsigned long flags; -	spin_lock_irqsave(&gpiocr_lock, flags); -	reg = readl(PRCM_GPIOCR); -	writel(reg | PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); -	spin_unlock_irqrestore(&gpiocr_lock, flags); +	spin_lock_irqsave(&prcmu_lock, flags); +	writel(value, (_PRCMU_BASE + reg)); +	spin_unlock_irqrestore(&prcmu_lock, flags);  } -/** - * prcmu_disable_spi2 - Disables pin muxing for SPI2 on OtherAlternateC1. - */ -void prcmu_disable_spi2(void) +void db8500_prcmu_write_masked(unsigned int reg, u32 mask, u32 value)  { -	u32 reg; +	u32 val;  	unsigned long flags; -	spin_lock_irqsave(&gpiocr_lock, flags); -	reg = readl(PRCM_GPIOCR); -	writel(reg & ~PRCM_GPIOCR_SPI2_SELECT, PRCM_GPIOCR); -	spin_unlock_irqrestore(&gpiocr_lock, flags); +	spin_lock_irqsave(&prcmu_lock, flags); +	val = readl(_PRCMU_BASE + reg); +	val = ((val & ~mask) | (value & mask)); +	writel(val, (_PRCMU_BASE + reg)); +	spin_unlock_irqrestore(&prcmu_lock, flags);  } -bool prcmu_has_arm_maxopp(void) +struct prcmu_fw_version *prcmu_get_fw_version(void)  { -	return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) & -		PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK; +	return fw_info.valid ? &fw_info.version : NULL;  } -bool prcmu_is_u8400(void) +bool prcmu_has_arm_maxopp(void)  { -	return prcmu_version.project_number == PRCMU_PROJECT_ID_8400V2_0; +	return (readb(tcdm_base + PRCM_AVS_VARM_MAX_OPP) & +		PRCM_AVS_ISMODEENABLE_MASK) == PRCM_AVS_ISMODEENABLE_MASK;  }  /** @@ -787,6 +788,124 @@ int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll)  	return 0;  } +u8 db8500_prcmu_get_power_state_result(void) +{ +	return readb(tcdm_base + PRCM_ACK_MB0_AP_PWRSTTR_STATUS); +} + +/* This function decouple the gic from the prcmu */ +int db8500_prcmu_gic_decouple(void) +{ +	u32 val = readl(PRCM_A9_MASK_REQ); + +	/* Set bit 0 register value to 1 */ +	writel(val | PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, +	       PRCM_A9_MASK_REQ); + +	/* Make sure the register is updated */ +	readl(PRCM_A9_MASK_REQ); + +	/* Wait a few cycles for the gic mask completion */ +	udelay(1); + +	return 0; +} + +/* This function recouple the gic with the prcmu */ +int db8500_prcmu_gic_recouple(void) +{ +	u32 val = readl(PRCM_A9_MASK_REQ); + +	/* Set bit 0 register value to 0 */ +	writel(val & ~PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ, PRCM_A9_MASK_REQ); + +	return 0; +} + +#define PRCMU_GIC_NUMBER_REGS 5 + +/* + * This function checks if there are pending irq on the gic. It only + * makes sense if the gic has been decoupled before with the + * db8500_prcmu_gic_decouple function. Disabling an interrupt only + * disables the forwarding of the interrupt to any CPU interface. It + * does not prevent the interrupt from changing state, for example + * becoming pending, or active and pending if it is already + * active. Hence, we have to check the interrupt is pending *and* is + * active. + */ +bool db8500_prcmu_gic_pending_irq(void) +{ +	u32 pr; /* Pending register */ +	u32 er; /* Enable register */ +	void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); +	int i; + +        /* 5 registers. STI & PPI not skipped */ +	for (i = 0; i < PRCMU_GIC_NUMBER_REGS; i++) { + +		pr = readl_relaxed(dist_base + GIC_DIST_PENDING_SET + i * 4); +		er = readl_relaxed(dist_base + GIC_DIST_ENABLE_SET + i * 4); + +		if (pr & er) +			return true; /* There is a pending interrupt */ +	} + +	return false; +} + +/* + * This function checks if there are pending interrupt on the + * prcmu which has been delegated to monitor the irqs with the + * db8500_prcmu_copy_gic_settings function. + */ +bool db8500_prcmu_pending_irq(void) +{ +	u32 it, im; +	int i; + +	for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { +		it = readl(PRCM_ARMITVAL31TO0 + i * 4); +		im = readl(PRCM_ARMITMSK31TO0 + i * 4); +		if (it & im) +			return true; /* There is a pending interrupt */ +	} + +	return false; +} + +/* + * This function checks if the specified cpu is in in WFI. It's usage + * makes sense only if the gic is decoupled with the db8500_prcmu_gic_decouple + * function. Of course passing smp_processor_id() to this function will + * always return false... + */ +bool db8500_prcmu_is_cpu_in_wfi(int cpu) +{ +	return readl(PRCM_ARM_WFI_STANDBY) & cpu ? PRCM_ARM_WFI_STANDBY_WFI1 : +		     PRCM_ARM_WFI_STANDBY_WFI0; +} + +/* + * This function copies the gic SPI settings to the prcmu in order to + * monitor them and abort/finish the retention/off sequence or state. + */ +int db8500_prcmu_copy_gic_settings(void) +{ +	u32 er; /* Enable register */ +	void __iomem *dist_base = __io_address(U8500_GIC_DIST_BASE); +	int i; + +        /* We skip the STI and PPI */ +	for (i = 0; i < PRCMU_GIC_NUMBER_REGS - 1; i++) { +		er = readl_relaxed(dist_base + +				   GIC_DIST_ENABLE_SET + (i + 1) * 4); +		writel(er, PRCM_ARMITMSK31TO0 + i * 4); +	} + +	return 0; +} +  /* This function should only be called while mb0_transfer.lock is held. */  static void config_wakeups(void)  { @@ -909,23 +1028,23 @@ int db8500_prcmu_get_arm_opp(void)  }  /** - * prcmu_get_ddr_opp - get the current DDR OPP + * db8500_prcmu_get_ddr_opp - get the current DDR OPP   *   * Returns: the current DDR OPP   */ -int prcmu_get_ddr_opp(void) +int db8500_prcmu_get_ddr_opp(void)  {  	return readb(PRCM_DDR_SUBSYS_APE_MINBW);  }  /** - * set_ddr_opp - set the appropriate DDR OPP + * db8500_set_ddr_opp - set the appropriate DDR OPP   * @opp: The new DDR operating point to which transition is to be made   * Returns: 0 on success, non-zero on failure   *   * This function sets the operating point of the DDR.   */ -int prcmu_set_ddr_opp(u8 opp) +int db8500_prcmu_set_ddr_opp(u8 opp)  {  	if (opp < DDR_100_OPP || opp > DDR_25_OPP)  		return -EINVAL; @@ -935,25 +1054,82 @@ int prcmu_set_ddr_opp(u8 opp)  	return 0;  } + +/* Divide the frequency of certain clocks by 2 for APE_50_PARTLY_25_OPP. */ +static void request_even_slower_clocks(bool enable) +{ +	void __iomem *clock_reg[] = { +		PRCM_ACLK_MGT, +		PRCM_DMACLK_MGT +	}; +	unsigned long flags; +	unsigned int i; + +	spin_lock_irqsave(&clk_mgt_lock, flags); + +	/* Grab the HW semaphore. */ +	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) +		cpu_relax(); + +	for (i = 0; i < ARRAY_SIZE(clock_reg); i++) { +		u32 val; +		u32 div; + +		val = readl(clock_reg[i]); +		div = (val & PRCM_CLK_MGT_CLKPLLDIV_MASK); +		if (enable) { +			if ((div <= 1) || (div > 15)) { +				pr_err("prcmu: Bad clock divider %d in %s\n", +					div, __func__); +				goto unlock_and_return; +			} +			div <<= 1; +		} else { +			if (div <= 2) +				goto unlock_and_return; +			div >>= 1; +		} +		val = ((val & ~PRCM_CLK_MGT_CLKPLLDIV_MASK) | +			(div & PRCM_CLK_MGT_CLKPLLDIV_MASK)); +		writel(val, clock_reg[i]); +	} + +unlock_and_return: +	/* Release the HW semaphore. */ +	writel(0, PRCM_SEM); + +	spin_unlock_irqrestore(&clk_mgt_lock, flags); +} +  /** - * set_ape_opp - set the appropriate APE OPP + * db8500_set_ape_opp - set the appropriate APE OPP   * @opp: The new APE operating point to which transition is to be made   * Returns: 0 on success, non-zero on failure   *   * This function sets the operating point of the APE.   */ -int prcmu_set_ape_opp(u8 opp) +int db8500_prcmu_set_ape_opp(u8 opp)  {  	int r = 0; +	if (opp == mb1_transfer.ape_opp) +		return 0; +  	mutex_lock(&mb1_transfer.lock); +	if (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP) +		request_even_slower_clocks(false); + +	if ((opp != APE_100_OPP) && (mb1_transfer.ape_opp != APE_100_OPP)) +		goto skip_message; +  	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(1))  		cpu_relax();  	writeb(MB1H_ARM_APE_OPP, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB1));  	writeb(ARM_NO_CHANGE, (tcdm_base + PRCM_REQ_MB1_ARM_OPP)); -	writeb(opp, (tcdm_base + PRCM_REQ_MB1_APE_OPP)); +	writeb(((opp == APE_50_PARTLY_25_OPP) ? APE_50_OPP : opp), +		(tcdm_base + PRCM_REQ_MB1_APE_OPP));  	writel(MBOX_BIT(1), PRCM_MBOX_CPU_SET);  	wait_for_completion(&mb1_transfer.work); @@ -962,17 +1138,24 @@ int prcmu_set_ape_opp(u8 opp)  		(mb1_transfer.ack.ape_opp != opp))  		r = -EIO; +skip_message: +	if ((!r && (opp == APE_50_PARTLY_25_OPP)) || +		(r && (mb1_transfer.ape_opp == APE_50_PARTLY_25_OPP))) +		request_even_slower_clocks(true); +	if (!r) +		mb1_transfer.ape_opp = opp; +  	mutex_unlock(&mb1_transfer.lock);  	return r;  }  /** - * prcmu_get_ape_opp - get the current APE OPP + * db8500_prcmu_get_ape_opp - get the current APE OPP   *   * Returns: the current APE OPP   */ -int prcmu_get_ape_opp(void) +int db8500_prcmu_get_ape_opp(void)  {  	return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_APE_OPP);  } @@ -1056,7 +1239,9 @@ static int request_pll(u8 clock, bool enable)  {  	int r = 0; -	if (clock == PRCMU_PLLSOC1) +	if (clock == PRCMU_PLLSOC0) +		clock = (enable ? PLL_SOC0_ON : PLL_SOC0_OFF); +	else if (clock == PRCMU_PLLSOC1)  		clock = (enable ? PLL_SOC1_ON : PLL_SOC1_OFF);  	else  		return -EINVAL; @@ -1081,132 +1266,6 @@ static int request_pll(u8 clock, bool enable)  }  /** - * prcmu_set_hwacc - set the power state of a h/w accelerator - * @hwacc_dev: The hardware accelerator (enum hw_acc_dev). - * @state: The new power state (enum hw_acc_state). - * - * This function sets the power state of a hardware accelerator. - * This function should not be called from interrupt context. - * - * NOTE! Deprecated, to be removed when all users switched over to use the - * regulator framework API. - */ -int prcmu_set_hwacc(u16 hwacc_dev, u8 state) -{ -	int r = 0; -	bool ram_retention = false; -	bool enable, enable_ret; - -	/* check argument */ -	BUG_ON(hwacc_dev >= NUM_HW_ACC); - -	/* get state of switches */ -	enable = hwacc_enabled[hwacc_dev]; -	enable_ret = hwacc_ret_enabled[hwacc_dev]; - -	/* set flag if retention is possible */ -	switch (hwacc_dev) { -	case HW_ACC_SVAMMDSP: -	case HW_ACC_SIAMMDSP: -	case HW_ACC_ESRAM1: -	case HW_ACC_ESRAM2: -	case HW_ACC_ESRAM3: -	case HW_ACC_ESRAM4: -		ram_retention = true; -		break; -	} - -	/* check argument */ -	BUG_ON(state > HW_ON); -	BUG_ON(state == HW_OFF_RAMRET && !ram_retention); - -	/* modify enable flags */ -	switch (state) { -	case HW_OFF: -		enable_ret = false; -		enable = false; -		break; -	case HW_ON: -		enable = true; -		break; -	case HW_OFF_RAMRET: -		enable_ret = true; -		enable = false; -		break; -	} - -	/* get regulator (lazy) */ -	if (hwacc_regulator[hwacc_dev] == NULL) { -		hwacc_regulator[hwacc_dev] = regulator_get(NULL, -			hwacc_regulator_name[hwacc_dev]); -		if (IS_ERR(hwacc_regulator[hwacc_dev])) { -			pr_err("prcmu: failed to get supply %s\n", -				hwacc_regulator_name[hwacc_dev]); -			r = PTR_ERR(hwacc_regulator[hwacc_dev]); -			goto out; -		} -	} - -	if (ram_retention) { -		if (hwacc_ret_regulator[hwacc_dev] == NULL) { -			hwacc_ret_regulator[hwacc_dev] = regulator_get(NULL, -				hwacc_ret_regulator_name[hwacc_dev]); -			if (IS_ERR(hwacc_ret_regulator[hwacc_dev])) { -				pr_err("prcmu: failed to get supply %s\n", -					hwacc_ret_regulator_name[hwacc_dev]); -				r = PTR_ERR(hwacc_ret_regulator[hwacc_dev]); -				goto out; -			} -		} -	} - -	/* set regulators */ -	if (ram_retention) { -		if (enable_ret && !hwacc_ret_enabled[hwacc_dev]) { -			r = regulator_enable(hwacc_ret_regulator[hwacc_dev]); -			if (r < 0) { -				pr_err("prcmu_set_hwacc: ret enable failed\n"); -				goto out; -			} -			hwacc_ret_enabled[hwacc_dev] = true; -		} -	} - -	if (enable && !hwacc_enabled[hwacc_dev]) { -		r = regulator_enable(hwacc_regulator[hwacc_dev]); -		if (r < 0) { -			pr_err("prcmu_set_hwacc: enable failed\n"); -			goto out; -		} -		hwacc_enabled[hwacc_dev] = true; -	} - -	if (!enable && hwacc_enabled[hwacc_dev]) { -		r = regulator_disable(hwacc_regulator[hwacc_dev]); -		if (r < 0) { -			pr_err("prcmu_set_hwacc: disable failed\n"); -			goto out; -		} -		hwacc_enabled[hwacc_dev] = false; -	} - -	if (ram_retention) { -		if (!enable_ret && hwacc_ret_enabled[hwacc_dev]) { -			r = regulator_disable(hwacc_ret_regulator[hwacc_dev]); -			if (r < 0) { -				pr_err("prcmu_set_hwacc: ret disable failed\n"); -				goto out; -			} -			hwacc_ret_enabled[hwacc_dev] = false; -		} -	} - -out: -	return r; -} -EXPORT_SYMBOL(prcmu_set_hwacc); - -/**   * db8500_prcmu_set_epod - set the state of a EPOD (power domain)   * @epod_id: The EPOD to set   * @epod_state: The new EPOD state @@ -1375,7 +1434,7 @@ static int request_timclk(bool enable)  	return 0;  } -static int request_reg_clock(u8 clock, bool enable) +static int request_clock(u8 clock, bool enable)  {  	u32 val;  	unsigned long flags; @@ -1386,14 +1445,14 @@ static int request_reg_clock(u8 clock, bool enable)  	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0)  		cpu_relax(); -	val = readl(_PRCMU_BASE + clk_mgt[clock].offset); +	val = readl(clk_mgt[clock].reg);  	if (enable) {  		val |= (PRCM_CLK_MGT_CLKEN | clk_mgt[clock].pllsw);  	} else {  		clk_mgt[clock].pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK);  		val &= ~(PRCM_CLK_MGT_CLKEN | PRCM_CLK_MGT_CLKPLLSW_MASK);  	} -	writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); +	writel(val, clk_mgt[clock].reg);  	/* Release the HW semaphore. */  	writel(0, PRCM_SEM); @@ -1413,7 +1472,7 @@ static int request_sga_clock(u8 clock, bool enable)  		writel(val | PRCM_CGATING_BYPASS_ICN2, PRCM_CGATING_BYPASS);  	} -	ret = request_reg_clock(clock, enable); +	ret = request_clock(clock, enable);  	if (!ret && !enable) {  		val = readl(PRCM_CGATING_BYPASS); @@ -1423,6 +1482,78 @@ static int request_sga_clock(u8 clock, bool enable)  	return ret;  } +static inline bool plldsi_locked(void) +{ +	return (readl(PRCM_PLLDSI_LOCKP) & +		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | +		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3)) == +		(PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10 | +		 PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3); +} + +static int request_plldsi(bool enable) +{ +	int r = 0; +	u32 val; + +	writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | +		PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), (enable ? +		PRCM_MMIP_LS_CLAMP_CLR : PRCM_MMIP_LS_CLAMP_SET)); + +	val = readl(PRCM_PLLDSI_ENABLE); +	if (enable) +		val |= PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; +	else +		val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; +	writel(val, PRCM_PLLDSI_ENABLE); + +	if (enable) { +		unsigned int i; +		bool locked = plldsi_locked(); + +		for (i = 10; !locked && (i > 0); --i) { +			udelay(100); +			locked = plldsi_locked(); +		} +		if (locked) { +			writel(PRCM_APE_RESETN_DSIPLL_RESETN, +				PRCM_APE_RESETN_SET); +		} else { +			writel((PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP | +				PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI), +				PRCM_MMIP_LS_CLAMP_SET); +			val &= ~PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE; +			writel(val, PRCM_PLLDSI_ENABLE); +			r = -EAGAIN; +		} +	} else { +		writel(PRCM_APE_RESETN_DSIPLL_RESETN, PRCM_APE_RESETN_CLR); +	} +	return r; +} + +static int request_dsiclk(u8 n, bool enable) +{ +	u32 val; + +	val = readl(PRCM_DSI_PLLOUT_SEL); +	val &= ~dsiclk[n].divsel_mask; +	val |= ((enable ? dsiclk[n].divsel : PRCM_DSI_PLLOUT_SEL_OFF) << +		dsiclk[n].divsel_shift); +	writel(val, PRCM_DSI_PLLOUT_SEL); +	return 0; +} + +static int request_dsiescclk(u8 n, bool enable) +{ +	u32 val; + +	val = readl(PRCM_DSITVCLK_DIV); +	enable ? (val |= dsiescclk[n].en) : (val &= ~dsiescclk[n].en); +	writel(val, PRCM_DSITVCLK_DIV); +	return 0; +} +  /**   * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled.   * @clock:      The clock for which the request is made. @@ -1433,21 +1564,435 @@ static int request_sga_clock(u8 clock, bool enable)   */  int db8500_prcmu_request_clock(u8 clock, bool enable)  { -	switch(clock) { -	case PRCMU_SGACLK: +	if (clock == PRCMU_SGACLK)  		return request_sga_clock(clock, enable); -	case PRCMU_TIMCLK: +	else if (clock < PRCMU_NUM_REG_CLOCKS) +		return request_clock(clock, enable); +	else if (clock == PRCMU_TIMCLK)  		return request_timclk(enable); -	case PRCMU_SYSCLK: +	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) +		return request_dsiclk((clock - PRCMU_DSI0CLK), enable); +	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) +		return request_dsiescclk((clock - PRCMU_DSI0ESCCLK), enable); +	else if (clock == PRCMU_PLLDSI) +		return request_plldsi(enable); +	else if (clock == PRCMU_SYSCLK)  		return request_sysclk(enable); -	case PRCMU_PLLSOC1: +	else if ((clock == PRCMU_PLLSOC0) || (clock == PRCMU_PLLSOC1))  		return request_pll(clock, enable); +	else +		return -EINVAL; +} + +static unsigned long pll_rate(void __iomem *reg, unsigned long src_rate, +	int branch) +{ +	u64 rate; +	u32 val; +	u32 d; +	u32 div = 1; + +	val = readl(reg); + +	rate = src_rate; +	rate *= ((val & PRCM_PLL_FREQ_D_MASK) >> PRCM_PLL_FREQ_D_SHIFT); + +	d = ((val & PRCM_PLL_FREQ_N_MASK) >> PRCM_PLL_FREQ_N_SHIFT); +	if (d > 1) +		div *= d; + +	d = ((val & PRCM_PLL_FREQ_R_MASK) >> PRCM_PLL_FREQ_R_SHIFT); +	if (d > 1) +		div *= d; + +	if (val & PRCM_PLL_FREQ_SELDIV2) +		div *= 2; + +	if ((branch == PLL_FIX) || ((branch == PLL_DIV) && +		(val & PRCM_PLL_FREQ_DIV2EN) && +		((reg == PRCM_PLLSOC0_FREQ) || +		 (reg == PRCM_PLLDDR_FREQ)))) +		div *= 2; + +	(void)do_div(rate, div); + +	return (unsigned long)rate; +} + +#define ROOT_CLOCK_RATE 38400000 + +static unsigned long clock_rate(u8 clock) +{ +	u32 val; +	u32 pllsw; +	unsigned long rate = ROOT_CLOCK_RATE; + +	val = readl(clk_mgt[clock].reg); + +	if (val & PRCM_CLK_MGT_CLK38) { +		if (clk_mgt[clock].clk38div && (val & PRCM_CLK_MGT_CLK38DIV)) +			rate /= 2; +		return rate; +	} + +	val |= clk_mgt[clock].pllsw; +	pllsw = (val & PRCM_CLK_MGT_CLKPLLSW_MASK); + +	if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC0) +		rate = pll_rate(PRCM_PLLSOC0_FREQ, rate, clk_mgt[clock].branch); +	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_SOC1) +		rate = pll_rate(PRCM_PLLSOC1_FREQ, rate, clk_mgt[clock].branch); +	else if (pllsw == PRCM_CLK_MGT_CLKPLLSW_DDR) +		rate = pll_rate(PRCM_PLLDDR_FREQ, rate, clk_mgt[clock].branch); +	else +		return 0; + +	if ((clock == PRCMU_SGACLK) && +		(val & PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN)) { +		u64 r = (rate * 10); + +		(void)do_div(r, 25); +		return (unsigned long)r; +	} +	val &= PRCM_CLK_MGT_CLKPLLDIV_MASK; +	if (val) +		return rate / val; +	else +		return 0; +} + +static unsigned long dsiclk_rate(u8 n) +{ +	u32 divsel; +	u32 div = 1; + +	divsel = readl(PRCM_DSI_PLLOUT_SEL); +	divsel = ((divsel & dsiclk[n].divsel_mask) >> dsiclk[n].divsel_shift); + +	if (divsel == PRCM_DSI_PLLOUT_SEL_OFF) +		divsel = dsiclk[n].divsel; + +	switch (divsel) { +	case PRCM_DSI_PLLOUT_SEL_PHI_4: +		div *= 2; +	case PRCM_DSI_PLLOUT_SEL_PHI_2: +		div *= 2; +	case PRCM_DSI_PLLOUT_SEL_PHI: +		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), +			PLL_RAW) / div;  	default: -		break; +		return 0;  	} +} + +static unsigned long dsiescclk_rate(u8 n) +{ +	u32 div; + +	div = readl(PRCM_DSITVCLK_DIV); +	div = ((div & dsiescclk[n].div_mask) >> (dsiescclk[n].div_shift)); +	return clock_rate(PRCMU_TVCLK) / max((u32)1, div); +} + +unsigned long prcmu_clock_rate(u8 clock) +{  	if (clock < PRCMU_NUM_REG_CLOCKS) -		return request_reg_clock(clock, enable); -	return -EINVAL; +		return clock_rate(clock); +	else if (clock == PRCMU_TIMCLK) +		return ROOT_CLOCK_RATE / 16; +	else if (clock == PRCMU_SYSCLK) +		return ROOT_CLOCK_RATE; +	else if (clock == PRCMU_PLLSOC0) +		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, PLL_RAW); +	else if (clock == PRCMU_PLLSOC1) +		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, PLL_RAW); +	else if (clock == PRCMU_PLLDDR) +		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, PLL_RAW); +	else if (clock == PRCMU_PLLDSI) +		return pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), +			PLL_RAW); +	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) +		return dsiclk_rate(clock - PRCMU_DSI0CLK); +	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) +		return dsiescclk_rate(clock - PRCMU_DSI0ESCCLK); +	else +		return 0; +} + +static unsigned long clock_source_rate(u32 clk_mgt_val, int branch) +{ +	if (clk_mgt_val & PRCM_CLK_MGT_CLK38) +		return ROOT_CLOCK_RATE; +	clk_mgt_val &= PRCM_CLK_MGT_CLKPLLSW_MASK; +	if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC0) +		return pll_rate(PRCM_PLLSOC0_FREQ, ROOT_CLOCK_RATE, branch); +	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_SOC1) +		return pll_rate(PRCM_PLLSOC1_FREQ, ROOT_CLOCK_RATE, branch); +	else if (clk_mgt_val == PRCM_CLK_MGT_CLKPLLSW_DDR) +		return pll_rate(PRCM_PLLDDR_FREQ, ROOT_CLOCK_RATE, branch); +	else +		return 0; +} + +static u32 clock_divider(unsigned long src_rate, unsigned long rate) +{ +	u32 div; + +	div = (src_rate / rate); +	if (div == 0) +		return 1; +	if (rate < (src_rate / div)) +		div++; +	return div; +} + +static long round_clock_rate(u8 clock, unsigned long rate) +{ +	u32 val; +	u32 div; +	unsigned long src_rate; +	long rounded_rate; + +	val = readl(clk_mgt[clock].reg); +	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), +		clk_mgt[clock].branch); +	div = clock_divider(src_rate, rate); +	if (val & PRCM_CLK_MGT_CLK38) { +		if (clk_mgt[clock].clk38div) { +			if (div > 2) +				div = 2; +		} else { +			div = 1; +		} +	} else if ((clock == PRCMU_SGACLK) && (div == 3)) { +		u64 r = (src_rate * 10); + +		(void)do_div(r, 25); +		if (r <= rate) +			return (unsigned long)r; +	} +	rounded_rate = (src_rate / min(div, (u32)31)); + +	return rounded_rate; +} + +#define MIN_PLL_VCO_RATE 600000000ULL +#define MAX_PLL_VCO_RATE 1680640000ULL + +static long round_plldsi_rate(unsigned long rate) +{ +	long rounded_rate = 0; +	unsigned long src_rate; +	unsigned long rem; +	u32 r; + +	src_rate = clock_rate(PRCMU_HDMICLK); +	rem = rate; + +	for (r = 7; (rem > 0) && (r > 0); r--) { +		u64 d; + +		d = (r * rate); +		(void)do_div(d, src_rate); +		if (d < 6) +			d = 6; +		else if (d > 255) +			d = 255; +		d *= src_rate; +		if (((2 * d) < (r * MIN_PLL_VCO_RATE)) || +			((r * MAX_PLL_VCO_RATE) < (2 * d))) +			continue; +		(void)do_div(d, r); +		if (rate < d) { +			if (rounded_rate == 0) +				rounded_rate = (long)d; +			break; +		} +		if ((rate - d) < rem) { +			rem = (rate - d); +			rounded_rate = (long)d; +		} +	} +	return rounded_rate; +} + +static long round_dsiclk_rate(unsigned long rate) +{ +	u32 div; +	unsigned long src_rate; +	long rounded_rate; + +	src_rate = pll_rate(PRCM_PLLDSI_FREQ, clock_rate(PRCMU_HDMICLK), +		PLL_RAW); +	div = clock_divider(src_rate, rate); +	rounded_rate = (src_rate / ((div > 2) ? 4 : div)); + +	return rounded_rate; +} + +static long round_dsiescclk_rate(unsigned long rate) +{ +	u32 div; +	unsigned long src_rate; +	long rounded_rate; + +	src_rate = clock_rate(PRCMU_TVCLK); +	div = clock_divider(src_rate, rate); +	rounded_rate = (src_rate / min(div, (u32)255)); + +	return rounded_rate; +} + +long prcmu_round_clock_rate(u8 clock, unsigned long rate) +{ +	if (clock < PRCMU_NUM_REG_CLOCKS) +		return round_clock_rate(clock, rate); +	else if (clock == PRCMU_PLLDSI) +		return round_plldsi_rate(rate); +	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) +		return round_dsiclk_rate(rate); +	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) +		return round_dsiescclk_rate(rate); +	else +		return (long)prcmu_clock_rate(clock); +} + +static void set_clock_rate(u8 clock, unsigned long rate) +{ +	u32 val; +	u32 div; +	unsigned long src_rate; +	unsigned long flags; + +	spin_lock_irqsave(&clk_mgt_lock, flags); + +	/* Grab the HW semaphore. */ +	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) +		cpu_relax(); + +	val = readl(clk_mgt[clock].reg); +	src_rate = clock_source_rate((val | clk_mgt[clock].pllsw), +		clk_mgt[clock].branch); +	div = clock_divider(src_rate, rate); +	if (val & PRCM_CLK_MGT_CLK38) { +		if (clk_mgt[clock].clk38div) { +			if (div > 1) +				val |= PRCM_CLK_MGT_CLK38DIV; +			else +				val &= ~PRCM_CLK_MGT_CLK38DIV; +		} +	} else if (clock == PRCMU_SGACLK) { +		val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK | +			PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN); +		if (div == 3) { +			u64 r = (src_rate * 10); + +			(void)do_div(r, 25); +			if (r <= rate) { +				val |= PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN; +				div = 0; +			} +		} +		val |= min(div, (u32)31); +	} else { +		val &= ~PRCM_CLK_MGT_CLKPLLDIV_MASK; +		val |= min(div, (u32)31); +	} +	writel(val, clk_mgt[clock].reg); + +	/* Release the HW semaphore. */ +	writel(0, PRCM_SEM); + +	spin_unlock_irqrestore(&clk_mgt_lock, flags); +} + +static int set_plldsi_rate(unsigned long rate) +{ +	unsigned long src_rate; +	unsigned long rem; +	u32 pll_freq = 0; +	u32 r; + +	src_rate = clock_rate(PRCMU_HDMICLK); +	rem = rate; + +	for (r = 7; (rem > 0) && (r > 0); r--) { +		u64 d; +		u64 hwrate; + +		d = (r * rate); +		(void)do_div(d, src_rate); +		if (d < 6) +			d = 6; +		else if (d > 255) +			d = 255; +		hwrate = (d * src_rate); +		if (((2 * hwrate) < (r * MIN_PLL_VCO_RATE)) || +			((r * MAX_PLL_VCO_RATE) < (2 * hwrate))) +			continue; +		(void)do_div(hwrate, r); +		if (rate < hwrate) { +			if (pll_freq == 0) +				pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | +					(r << PRCM_PLL_FREQ_R_SHIFT)); +			break; +		} +		if ((rate - hwrate) < rem) { +			rem = (rate - hwrate); +			pll_freq = (((u32)d << PRCM_PLL_FREQ_D_SHIFT) | +				(r << PRCM_PLL_FREQ_R_SHIFT)); +		} +	} +	if (pll_freq == 0) +		return -EINVAL; + +	pll_freq |= (1 << PRCM_PLL_FREQ_N_SHIFT); +	writel(pll_freq, PRCM_PLLDSI_FREQ); + +	return 0; +} + +static void set_dsiclk_rate(u8 n, unsigned long rate) +{ +	u32 val; +	u32 div; + +	div = clock_divider(pll_rate(PRCM_PLLDSI_FREQ, +			clock_rate(PRCMU_HDMICLK), PLL_RAW), rate); + +	dsiclk[n].divsel = (div == 1) ? PRCM_DSI_PLLOUT_SEL_PHI : +			   (div == 2) ? PRCM_DSI_PLLOUT_SEL_PHI_2 : +			   /* else */	PRCM_DSI_PLLOUT_SEL_PHI_4; + +	val = readl(PRCM_DSI_PLLOUT_SEL); +	val &= ~dsiclk[n].divsel_mask; +	val |= (dsiclk[n].divsel << dsiclk[n].divsel_shift); +	writel(val, PRCM_DSI_PLLOUT_SEL); +} + +static void set_dsiescclk_rate(u8 n, unsigned long rate) +{ +	u32 val; +	u32 div; + +	div = clock_divider(clock_rate(PRCMU_TVCLK), rate); +	val = readl(PRCM_DSITVCLK_DIV); +	val &= ~dsiescclk[n].div_mask; +	val |= (min(div, (u32)255) << dsiescclk[n].div_shift); +	writel(val, PRCM_DSITVCLK_DIV); +} + +int prcmu_set_clock_rate(u8 clock, unsigned long rate) +{ +	if (clock < PRCMU_NUM_REG_CLOCKS) +		set_clock_rate(clock, rate); +	else if (clock == PRCMU_PLLDSI) +		return set_plldsi_rate(rate); +	else if ((clock == PRCMU_DSI0CLK) || (clock == PRCMU_DSI1CLK)) +		set_dsiclk_rate((clock - PRCMU_DSI0CLK), rate); +	else if ((PRCMU_DSI0ESCCLK <= clock) && (clock <= PRCMU_DSI2ESCCLK)) +		set_dsiescclk_rate((clock - PRCMU_DSI0ESCCLK), rate); +	return 0;  }  int db8500_prcmu_config_esram0_deep_sleep(u8 state) @@ -1476,7 +2021,7 @@ int db8500_prcmu_config_esram0_deep_sleep(u8 state)  	return 0;  } -int prcmu_config_hotdog(u8 threshold) +int db8500_prcmu_config_hotdog(u8 threshold)  {  	mutex_lock(&mb4_transfer.lock); @@ -1494,7 +2039,7 @@ int prcmu_config_hotdog(u8 threshold)  	return 0;  } -int prcmu_config_hotmon(u8 low, u8 high) +int db8500_prcmu_config_hotmon(u8 low, u8 high)  {  	mutex_lock(&mb4_transfer.lock); @@ -1533,7 +2078,7 @@ static int config_hot_period(u16 val)  	return 0;  } -int prcmu_start_temp_sense(u16 cycles32k) +int db8500_prcmu_start_temp_sense(u16 cycles32k)  {  	if (cycles32k == 0xFFFF)  		return -EINVAL; @@ -1541,7 +2086,7 @@ int prcmu_start_temp_sense(u16 cycles32k)  	return config_hot_period(cycles32k);  } -int prcmu_stop_temp_sense(void) +int db8500_prcmu_stop_temp_sense(void)  {  	return config_hot_period(0xFFFF);  } @@ -1570,7 +2115,7 @@ static int prcmu_a9wdog(u8 cmd, u8 d0, u8 d1, u8 d2, u8 d3)  } -int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) +int db8500_prcmu_config_a9wdog(u8 num, bool sleep_auto_off)  {  	BUG_ON(num == 0 || num > 0xf);  	return prcmu_a9wdog(MB4H_A9WDOG_CONF, num, 0, 0, @@ -1578,17 +2123,17 @@ int prcmu_config_a9wdog(u8 num, bool sleep_auto_off)  			    A9WDOG_AUTO_OFF_DIS);  } -int prcmu_enable_a9wdog(u8 id) +int db8500_prcmu_enable_a9wdog(u8 id)  {  	return prcmu_a9wdog(MB4H_A9WDOG_EN, id, 0, 0, 0);  } -int prcmu_disable_a9wdog(u8 id) +int db8500_prcmu_disable_a9wdog(u8 id)  {  	return prcmu_a9wdog(MB4H_A9WDOG_DIS, id, 0, 0, 0);  } -int prcmu_kick_a9wdog(u8 id) +int db8500_prcmu_kick_a9wdog(u8 id)  {  	return prcmu_a9wdog(MB4H_A9WDOG_KICK, id, 0, 0, 0);  } @@ -1596,16 +2141,8 @@ int prcmu_kick_a9wdog(u8 id)  /*   * timeout is 28 bit, in ms.   */ -#define MAX_WATCHDOG_TIMEOUT 131000 -int prcmu_load_a9wdog(u8 id, u32 timeout) +int db8500_prcmu_load_a9wdog(u8 id, u32 timeout)  { -	if (timeout > MAX_WATCHDOG_TIMEOUT) -		/* -		 * Due to calculation bug in prcmu fw, timeouts -		 * can't be bigger than 131 seconds. -		 */ -		return -EINVAL; -  	return prcmu_a9wdog(MB4H_A9WDOG_LOAD,  			    (id & A9WDOG_ID_MASK) |  			    /* @@ -1619,41 +2156,6 @@ int prcmu_load_a9wdog(u8 id, u32 timeout)  }  /** - * prcmu_set_clock_divider() - Configure the clock divider. - * @clock:	The clock for which the request is made. - * @divider:	The clock divider. (< 32) - * - * This function should only be used by the clock implementation. - * Do not use it from any other place! - */ -int prcmu_set_clock_divider(u8 clock, u8 divider) -{ -	u32 val; -	unsigned long flags; - -	if ((clock >= PRCMU_NUM_REG_CLOCKS) || (divider < 1) || (31 < divider)) -		return -EINVAL; - -	spin_lock_irqsave(&clk_mgt_lock, flags); - -	/* Grab the HW semaphore. */ -	while ((readl(PRCM_SEM) & PRCM_SEM_PRCM_SEM) != 0) -		cpu_relax(); - -	val = readl(_PRCMU_BASE + clk_mgt[clock].offset); -	val &= ~(PRCM_CLK_MGT_CLKPLLDIV_MASK); -	val |= (u32)divider; -	writel(val, (_PRCMU_BASE + clk_mgt[clock].offset)); - -	/* Release the HW semaphore. */ -	writel(0, PRCM_SEM); - -	spin_unlock_irqrestore(&clk_mgt_lock, flags); - -	return 0; -} - -/**   * prcmu_abb_read() - Read register value(s) from the ABB.   * @slave:	The I2C slave address.   * @reg:	The (start) register address. @@ -1675,6 +2177,7 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)  	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))  		cpu_relax(); +	writeb(0, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));  	writeb(PRCMU_I2C_READ(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));  	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));  	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); @@ -1700,16 +2203,19 @@ int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size)  }  /** - * prcmu_abb_write() - Write register value(s) to the ABB. + * prcmu_abb_write_masked() - Write masked register value(s) to the ABB.   * @slave:	The I2C slave address.   * @reg:	The (start) register address.   * @value:	The value(s) to write. + * @mask:	The mask(s) to use.   * @size:	The number of registers to write.   * - * Reads register value(s) from the ABB. + * Writes masked register value(s) to the ABB. + * For each @value, only the bits set to 1 in the corresponding @mask + * will be written. The other bits are not changed.   * @size has to be 1 for the current firmware version.   */ -int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +int prcmu_abb_write_masked(u8 slave, u8 reg, u8 *value, u8 *mask, u8 size)  {  	int r; @@ -1721,6 +2227,7 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)  	while (readl(PRCM_MBOX_CPU_VAL) & MBOX_BIT(5))  		cpu_relax(); +	writeb(~*mask, (tcdm_base + PRCM_MBOX_HEADER_REQ_MB5));  	writeb(PRCMU_I2C_WRITE(slave), (tcdm_base + PRCM_REQ_MB5_I2C_SLAVE_OP));  	writeb(PRCMU_I2C_STOP_EN, (tcdm_base + PRCM_REQ_MB5_I2C_HW_BITS));  	writeb(reg, (tcdm_base + PRCM_REQ_MB5_I2C_REG)); @@ -1743,6 +2250,23 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size)  }  /** + * prcmu_abb_write() - Write register value(s) to the ABB. + * @slave:	The I2C slave address. + * @reg:	The (start) register address. + * @value:	The value(s) to write. + * @size:	The number of registers to write. + * + * Writes register value(s) to the ABB. + * @size has to be 1 for the current firmware version. + */ +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +{ +	u8 mask = ~0; + +	return prcmu_abb_write_masked(slave, reg, value, &mask, size); +} + +/**   * prcmu_ac_wake_req - should be called whenever ARM wants to wakeup Modem   */  void prcmu_ac_wake_req(void) @@ -1850,9 +2374,9 @@ u16 db8500_prcmu_get_reset_code(void)  }  /** - * prcmu_reset_modem - ask the PRCMU to reset modem + * db8500_prcmu_reset_modem - ask the PRCMU to reset modem   */ -void prcmu_modem_reset(void) +void db8500_prcmu_modem_reset(void)  {  	mutex_lock(&mb1_transfer.lock); @@ -2099,6 +2623,26 @@ static struct irq_chip prcmu_irq_chip = {  	.irq_unmask	= prcmu_irq_unmask,  }; +static char *fw_project_name(u8 project) +{ +	switch (project) { +	case PRCMU_FW_PROJECT_U8500: +		return "U8500"; +	case PRCMU_FW_PROJECT_U8500_C2: +		return "U8500 C2"; +	case PRCMU_FW_PROJECT_U9500: +		return "U9500"; +	case PRCMU_FW_PROJECT_U9500_C2: +		return "U9500 C2"; +	case PRCMU_FW_PROJECT_U8520: +		return "U8520"; +	case PRCMU_FW_PROJECT_U8420: +		return "U8420"; +	default: +		return "Unknown"; +	} +} +  void __init db8500_prcmu_early_init(void)  {  	unsigned int i; @@ -2108,11 +2652,13 @@ void __init db8500_prcmu_early_init(void)  		if (tcpm_base != NULL) {  			u32 version;  			version = readl(tcpm_base + PRCMU_FW_VERSION_OFFSET); -			prcmu_version.project_number = version & 0xFF; -			prcmu_version.api_version = (version >> 8) & 0xFF; -			prcmu_version.func_version = (version >> 16) & 0xFF; -			prcmu_version.errata = (version >> 24) & 0xFF; -			pr_info("PRCMU firmware version %d.%d.%d\n", +			fw_info.version.project = version & 0xFF; +			fw_info.version.api_version = (version >> 8) & 0xFF; +			fw_info.version.func_version = (version >> 16) & 0xFF; +			fw_info.version.errata = (version >> 24) & 0xFF; +			fw_info.valid = true; +			pr_info("PRCMU firmware: %s, version %d.%d.%d\n", +				fw_project_name(fw_info.version.project),  				(version >> 8) & 0xFF, (version >> 16) & 0xFF,  				(version >> 24) & 0xFF);  			iounmap(tcpm_base); @@ -2130,6 +2676,7 @@ void __init db8500_prcmu_early_init(void)  	init_completion(&mb0_transfer.ac_wake_work);  	mutex_init(&mb1_transfer.lock);  	init_completion(&mb1_transfer.work); +	mb1_transfer.ape_opp = APE_NO_CHANGE;  	mutex_init(&mb2_transfer.lock);  	init_completion(&mb2_transfer.work);  	spin_lock_init(&mb2_transfer.auto_pm_lock); @@ -2154,7 +2701,7 @@ void __init db8500_prcmu_early_init(void)  	}  } -static void __init db8500_prcmu_init_clkforce(void) +static void __init init_prcm_registers(void)  {  	u32 val; @@ -2186,19 +2733,17 @@ static struct regulator_consumer_supply db8500_vape_consumers[] = {  	REGULATOR_SUPPLY("vcore", "uart1"),  	REGULATOR_SUPPLY("vcore", "uart2"),  	REGULATOR_SUPPLY("v-ape", "nmk-ske-keypad.0"), +	REGULATOR_SUPPLY("v-hsi", "ste_hsi.0"),  };  static struct regulator_consumer_supply db8500_vsmps2_consumers[] = { -	/* CG2900 and CW1200 power to off-chip peripherals */ -	REGULATOR_SUPPLY("gbf_1v8", "cg2900-uart.0"), -	REGULATOR_SUPPLY("wlan_1v8", "cw1200.0"),  	REGULATOR_SUPPLY("musb_1v8", "ab8500-usb.0"),  	/* AV8100 regulator */  	REGULATOR_SUPPLY("hdmi_1v8", "0-0070"),  };  static struct regulator_consumer_supply db8500_b2r2_mcde_consumers[] = { -	REGULATOR_SUPPLY("vsupply", "b2r2.0"), +	REGULATOR_SUPPLY("vsupply", "b2r2_bus"),  	REGULATOR_SUPPLY("vsupply", "mcde"),  }; @@ -2235,6 +2780,7 @@ static struct regulator_consumer_supply db8500_esram12_consumers[] = {  static struct regulator_consumer_supply db8500_esram34_consumers[] = {  	REGULATOR_SUPPLY("v-esram34", "mcde"),  	REGULATOR_SUPPLY("esram34", "cm_control"), +	REGULATOR_SUPPLY("lcla_esram", "dma40.0"),  };  static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = { @@ -2291,7 +2837,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		},  	},  	[DB8500_REGULATOR_SWITCH_SVAMMDSP] = { -		.supply_regulator = "db8500-vape", +		/* dependency to u8500-vape is handled outside regulator framework */  		.constraints = {  			.name = "db8500-sva-mmdsp",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2307,7 +2853,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		},  	},  	[DB8500_REGULATOR_SWITCH_SVAPIPE] = { -		.supply_regulator = "db8500-vape", +		/* dependency to u8500-vape is handled outside regulator framework */  		.constraints = {  			.name = "db8500-sva-pipe",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2316,7 +2862,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		.num_consumer_supplies = ARRAY_SIZE(db8500_svapipe_consumers),  	},  	[DB8500_REGULATOR_SWITCH_SIAMMDSP] = { -		.supply_regulator = "db8500-vape", +		/* dependency to u8500-vape is handled outside regulator framework */  		.constraints = {  			.name = "db8500-sia-mmdsp",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2331,7 +2877,7 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		},  	},  	[DB8500_REGULATOR_SWITCH_SIAPIPE] = { -		.supply_regulator = "db8500-vape", +		/* dependency to u8500-vape is handled outside regulator framework */  		.constraints = {  			.name = "db8500-sia-pipe",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2359,7 +2905,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		.num_consumer_supplies = ARRAY_SIZE(db8500_b2r2_mcde_consumers),  	},  	[DB8500_REGULATOR_SWITCH_ESRAM12] = { -		.supply_regulator = "db8500-vape", +		/* +		 * esram12 is set in retention and supplied by Vsafe when Vape is off, +		 * no need to hold Vape +		 */  		.constraints = {  			.name = "db8500-esram12",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2374,7 +2923,10 @@ static struct regulator_init_data db8500_regulators[DB8500_NUM_REGULATORS] = {  		},  	},  	[DB8500_REGULATOR_SWITCH_ESRAM34] = { -		.supply_regulator = "db8500-vape", +		/* +		 * esram34 is set in retention and supplied by Vsafe when Vape is off, +		 * no need to hold Vape +		 */  		.constraints = {  			.name = "db8500-esram34",  			.valid_ops_mask = REGULATOR_CHANGE_STATUS, @@ -2412,7 +2964,7 @@ static int __init db8500_prcmu_probe(struct platform_device *pdev)  	if (ux500_is_svp())  		return -ENODEV; -	db8500_prcmu_init_clkforce(); +	init_prcm_registers();  	/* Clean up the mailbox interrupts after pre-kernel code. */  	writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h index ec22e9f15d3..3a0bf91d778 100644 --- a/drivers/mfd/dbx500-prcmu-regs.h +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -17,41 +17,41 @@  #define BITS(_start, _end) ((BIT(_end) - BIT(_start)) + BIT(_end)) -#define PRCM_SVACLK_MGT_OFF		0x008 -#define PRCM_SIACLK_MGT_OFF		0x00C -#define PRCM_SGACLK_MGT_OFF		0x014 -#define PRCM_UARTCLK_MGT_OFF		0x018 -#define PRCM_MSP02CLK_MGT_OFF		0x01C -#define PRCM_I2CCLK_MGT_OFF		0x020 -#define PRCM_SDMMCCLK_MGT_OFF		0x024 -#define PRCM_SLIMCLK_MGT_OFF		0x028 -#define PRCM_PER1CLK_MGT_OFF		0x02C -#define PRCM_PER2CLK_MGT_OFF		0x030 -#define PRCM_PER3CLK_MGT_OFF		0x034 -#define PRCM_PER5CLK_MGT_OFF		0x038 -#define PRCM_PER6CLK_MGT_OFF		0x03C -#define PRCM_PER7CLK_MGT_OFF		0x040 -#define PRCM_PWMCLK_MGT_OFF		0x044 /* for DB5500 */ -#define PRCM_IRDACLK_MGT_OFF		0x048 /* for DB5500 */ -#define PRCM_IRRCCLK_MGT_OFF		0x04C /* for DB5500 */ -#define PRCM_LCDCLK_MGT_OFF		0x044 -#define PRCM_BMLCLK_MGT_OFF		0x04C -#define PRCM_HSITXCLK_MGT_OFF		0x050 -#define PRCM_HSIRXCLK_MGT_OFF		0x054 -#define PRCM_HDMICLK_MGT_OFF		0x058 -#define PRCM_APEATCLK_MGT_OFF		0x05C -#define PRCM_APETRACECLK_MGT_OFF	0x060 -#define PRCM_MCDECLK_MGT_OFF		0x064 -#define PRCM_IPI2CCLK_MGT_OFF		0x068 -#define PRCM_DSIALTCLK_MGT_OFF		0x06C -#define PRCM_DMACLK_MGT_OFF		0x074 -#define PRCM_B2R2CLK_MGT_OFF		0x078 -#define PRCM_TVCLK_MGT_OFF		0x07C -#define PRCM_UNIPROCLK_MGT_OFF		0x278 -#define PRCM_SSPCLK_MGT_OFF		0x280 -#define PRCM_RNGCLK_MGT_OFF		0x284 -#define PRCM_UICCCLK_MGT_OFF		0x27C -#define PRCM_MSP1CLK_MGT_OFF		0x288 +#define PRCM_CLK_MGT(_offset) (void __iomem *)(IO_ADDRESS(U8500_PRCMU_BASE) \ +	+ _offset) +#define PRCM_ACLK_MGT		PRCM_CLK_MGT(0x004) +#define PRCM_SVACLK_MGT		PRCM_CLK_MGT(0x008) +#define PRCM_SIACLK_MGT		PRCM_CLK_MGT(0x00C) +#define PRCM_SGACLK_MGT		PRCM_CLK_MGT(0x014) +#define PRCM_UARTCLK_MGT	PRCM_CLK_MGT(0x018) +#define PRCM_MSP02CLK_MGT	PRCM_CLK_MGT(0x01C) +#define PRCM_I2CCLK_MGT		PRCM_CLK_MGT(0x020) +#define PRCM_SDMMCCLK_MGT	PRCM_CLK_MGT(0x024) +#define PRCM_SLIMCLK_MGT	PRCM_CLK_MGT(0x028) +#define PRCM_PER1CLK_MGT	PRCM_CLK_MGT(0x02C) +#define PRCM_PER2CLK_MGT	PRCM_CLK_MGT(0x030) +#define PRCM_PER3CLK_MGT	PRCM_CLK_MGT(0x034) +#define PRCM_PER5CLK_MGT	PRCM_CLK_MGT(0x038) +#define PRCM_PER6CLK_MGT	PRCM_CLK_MGT(0x03C) +#define PRCM_PER7CLK_MGT	PRCM_CLK_MGT(0x040) +#define PRCM_LCDCLK_MGT		PRCM_CLK_MGT(0x044) +#define PRCM_BMLCLK_MGT		PRCM_CLK_MGT(0x04C) +#define PRCM_HSITXCLK_MGT	PRCM_CLK_MGT(0x050) +#define PRCM_HSIRXCLK_MGT	PRCM_CLK_MGT(0x054) +#define PRCM_HDMICLK_MGT	PRCM_CLK_MGT(0x058) +#define PRCM_APEATCLK_MGT	PRCM_CLK_MGT(0x05C) +#define PRCM_APETRACECLK_MGT	PRCM_CLK_MGT(0x060) +#define PRCM_MCDECLK_MGT	PRCM_CLK_MGT(0x064) +#define PRCM_IPI2CCLK_MGT	PRCM_CLK_MGT(0x068) +#define PRCM_DSIALTCLK_MGT	PRCM_CLK_MGT(0x06C) +#define PRCM_DMACLK_MGT		PRCM_CLK_MGT(0x074) +#define PRCM_B2R2CLK_MGT	PRCM_CLK_MGT(0x078) +#define PRCM_TVCLK_MGT		PRCM_CLK_MGT(0x07C) +#define PRCM_UNIPROCLK_MGT	PRCM_CLK_MGT(0x278) +#define PRCM_SSPCLK_MGT		PRCM_CLK_MGT(0x280) +#define PRCM_RNGCLK_MGT		PRCM_CLK_MGT(0x284) +#define PRCM_UICCCLK_MGT	PRCM_CLK_MGT(0x27C) +#define PRCM_MSP1CLK_MGT	PRCM_CLK_MGT(0x288)  #define PRCM_ARM_PLLDIVPS	(_PRCMU_BASE + 0x118)  #define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE		0x3f @@ -79,6 +79,8 @@  /* ARM WFI Standby signal register */  #define PRCM_ARM_WFI_STANDBY    (_PRCMU_BASE + 0x130) +#define PRCM_ARM_WFI_STANDBY_WFI0               0x08 +#define PRCM_ARM_WFI_STANDBY_WFI1               0x10  #define PRCM_IOCR		(_PRCMU_BASE + 0x310)  #define PRCM_IOCR_IOFORCE			0x1 @@ -131,20 +133,58 @@  #define PRCM_MMIP_LS_CLAMP_SET     (_PRCMU_BASE + 0x420)  #define PRCM_MMIP_LS_CLAMP_CLR     (_PRCMU_BASE + 0x424) +#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMP		BIT(11) +#define PRCM_MMIP_LS_CLAMP_DSIPLL_CLAMPI	BIT(22) +  /* PRCMU clock/PLL/reset registers */ +#define PRCM_PLLSOC0_FREQ	   (_PRCMU_BASE + 0x080) +#define PRCM_PLLSOC1_FREQ	   (_PRCMU_BASE + 0x084) +#define PRCM_PLLDDR_FREQ	   (_PRCMU_BASE + 0x08C) +#define PRCM_PLL_FREQ_D_SHIFT	0 +#define PRCM_PLL_FREQ_D_MASK	BITS(0, 7) +#define PRCM_PLL_FREQ_N_SHIFT	8 +#define PRCM_PLL_FREQ_N_MASK	BITS(8, 13) +#define PRCM_PLL_FREQ_R_SHIFT	16 +#define PRCM_PLL_FREQ_R_MASK	BITS(16, 18) +#define PRCM_PLL_FREQ_SELDIV2	BIT(24) +#define PRCM_PLL_FREQ_DIV2EN	BIT(25) +  #define PRCM_PLLDSI_FREQ           (_PRCMU_BASE + 0x500)  #define PRCM_PLLDSI_ENABLE         (_PRCMU_BASE + 0x504)  #define PRCM_PLLDSI_LOCKP          (_PRCMU_BASE + 0x508) -#define PRCM_LCDCLK_MGT            (_PRCMU_BASE + PRCM_LCDCLK_MGT_OFF) -#define PRCM_MCDECLK_MGT           (_PRCMU_BASE + PRCM_MCDECLK_MGT_OFF) -#define PRCM_HDMICLK_MGT           (_PRCMU_BASE + PRCM_HDMICLK_MGT_OFF) -#define PRCM_TVCLK_MGT             (_PRCMU_BASE + PRCM_TVCLK_MGT_OFF)  #define PRCM_DSI_PLLOUT_SEL        (_PRCMU_BASE + 0x530)  #define PRCM_DSITVCLK_DIV          (_PRCMU_BASE + 0x52C)  #define PRCM_PLLDSI_LOCKP          (_PRCMU_BASE + 0x508)  #define PRCM_APE_RESETN_SET        (_PRCMU_BASE + 0x1E4)  #define PRCM_APE_RESETN_CLR        (_PRCMU_BASE + 0x1E8) +#define PRCM_PLLDSI_ENABLE_PRCM_PLLDSI_ENABLE BIT(0) + +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP10	BIT(0) +#define PRCM_PLLDSI_LOCKP_PRCM_PLLDSI_LOCKP3	BIT(1) + +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_SHIFT	0 +#define PRCM_DSI_PLLOUT_SEL_DSI0_PLLOUT_DIVSEL_MASK	BITS(0, 2) +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_SHIFT	8 +#define PRCM_DSI_PLLOUT_SEL_DSI1_PLLOUT_DIVSEL_MASK	BITS(8, 10) + +#define PRCM_DSI_PLLOUT_SEL_OFF		0 +#define PRCM_DSI_PLLOUT_SEL_PHI		1 +#define PRCM_DSI_PLLOUT_SEL_PHI_2	2 +#define PRCM_DSI_PLLOUT_SEL_PHI_4	3 + +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_SHIFT	0 +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_DIV_MASK		BITS(0, 7) +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_SHIFT	8 +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_DIV_MASK		BITS(8, 15) +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_SHIFT	16 +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_DIV_MASK		BITS(16, 23) +#define PRCM_DSITVCLK_DIV_DSI0_ESC_CLK_EN		BIT(24) +#define PRCM_DSITVCLK_DIV_DSI1_ESC_CLK_EN		BIT(25) +#define PRCM_DSITVCLK_DIV_DSI2_ESC_CLK_EN		BIT(26) + +#define PRCM_APE_RESETN_DSIPLL_RESETN BIT(14) +  #define PRCM_CLKOCR		   (_PRCMU_BASE + 0x1CC)  #define PRCM_CLKOCR_CLKOUT0_REF_CLK	(1 << 0)  #define PRCM_CLKOCR_CLKOUT0_MASK	BITS(0, 13) @@ -183,9 +223,15 @@  #define PRCM_CLKOCR_CLKOSEL1_MASK	BITS(22, 24)  #define PRCM_CLKOCR_CLK1TYPE		BIT(28) -#define PRCM_CLK_MGT_CLKPLLDIV_MASK	BITS(0, 4) -#define PRCM_CLK_MGT_CLKPLLSW_MASK	BITS(5, 7) -#define PRCM_CLK_MGT_CLKEN		BIT(8) +#define PRCM_CLK_MGT_CLKPLLDIV_MASK		BITS(0, 4) +#define PRCM_CLK_MGT_CLKPLLSW_SOC0		BIT(5) +#define PRCM_CLK_MGT_CLKPLLSW_SOC1		BIT(6) +#define PRCM_CLK_MGT_CLKPLLSW_DDR		BIT(7) +#define PRCM_CLK_MGT_CLKPLLSW_MASK		BITS(5, 7) +#define PRCM_CLK_MGT_CLKEN			BIT(8) +#define PRCM_CLK_MGT_CLK38			BIT(9) +#define PRCM_CLK_MGT_CLK38DIV			BIT(11) +#define PRCM_SGACLK_MGT_SGACLKDIV_BY_2_5_EN	BIT(12)  /* GPIOCR register */  #define PRCM_GPIOCR_SPI2_SELECT BIT(23) diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 7122386b4e3..9fd4f63c45c 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -560,6 +560,8 @@ EXPORT_SYMBOL(mc13xxx_get_flags);  #define MC13XXX_ADC1_CHAN0_SHIFT	5  #define MC13XXX_ADC1_CHAN1_SHIFT	8 +#define MC13783_ADC1_ATO_SHIFT		11 +#define MC13783_ADC1_ATOX		(1 << 19)  struct mc13xxx_adcdone_data {  	struct mc13xxx *mc13xxx; @@ -580,7 +582,8 @@ static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data)  #define MC13XXX_ADC_WORKING (1 << 0)  int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, -		unsigned int channel, unsigned int *sample) +		unsigned int channel, u8 ato, bool atox, +		unsigned int *sample)  {  	u32 adc0, adc1, old_adc0;  	int i, ret; @@ -631,6 +634,9 @@ int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode,  		return -EINVAL;  	} +	adc1 |= ato << MC13783_ADC1_ATO_SHIFT; +	if (atox) +		adc1 |= MC13783_ADC1_ATOX;  	dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__);  	mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE,  			mc13xxx_handler_adcdone, __func__, &adcdone_data); @@ -813,7 +819,8 @@ err_revision:  		mc13xxx_add_subdevice(mc13xxx, "%s-rtc");  	if (mc13xxx->flags & MC13XXX_USE_TOUCHSCREEN) -		mc13xxx_add_subdevice(mc13xxx, "%s-ts"); +		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-ts", +				&pdata->touch, sizeof(pdata->touch));  	if (pdata) {  		mc13xxx_add_subdevice_pdata(mc13xxx, "%s-regulator", diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 411f523d487..ffc3d48676a 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -162,7 +162,7 @@ int mfd_add_devices(struct device *parent, int id,  	atomic_t *cnts;  	/* initialize reference counting for all cells */ -	cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL); +	cnts = kcalloc(n_devs, sizeof(*cnts), GFP_KERNEL);  	if (!cnts)  		return -ENOMEM; diff --git a/drivers/mfd/omap-usb-host.c b/drivers/mfd/omap-usb-host.c index 68ac2c55d5a..95a2e546a48 100644 --- a/drivers/mfd/omap-usb-host.c +++ b/drivers/mfd/omap-usb-host.c @@ -170,7 +170,7 @@ struct usbhs_hcd_omap {  /*-------------------------------------------------------------------------*/  const char usbhs_driver_name[] = USBHS_DRIVER_NAME; -static u64 usbhs_dmamask = ~(u32)0; +static u64 usbhs_dmamask = DMA_BIT_MASK(32);  /*-------------------------------------------------------------------------*/ @@ -223,7 +223,7 @@ static struct platform_device *omap_usbhs_alloc_child(const char *name,  	}  	child->dev.dma_mask		= &usbhs_dmamask; -	child->dev.coherent_dma_mask	= 0xffffffff; +	dma_set_coherent_mask(&child->dev, DMA_BIT_MASK(32));  	child->dev.parent		= dev;  	ret = platform_device_add(child); @@ -799,14 +799,13 @@ static int __devinit usbhs_omap_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, omap); +	omap_usbhs_init(dev);  	ret = omap_usbhs_alloc_children(pdev);  	if (ret) {  		dev_err(dev, "omap_usbhs_alloc_children failed\n");  		goto err_alloc;  	} -	omap_usbhs_init(dev); -  	goto end_probe;  err_alloc: diff --git a/drivers/mfd/pcf50633-core.c b/drivers/mfd/pcf50633-core.c index ff1a7e741ec..189c2f07b83 100644 --- a/drivers/mfd/pcf50633-core.c +++ b/drivers/mfd/pcf50633-core.c @@ -46,13 +46,7 @@ EXPORT_SYMBOL_GPL(pcf50633_read_block);  int pcf50633_write_block(struct pcf50633 *pcf , u8 reg,  					int nr_regs, u8 *data)  { -	int ret; - -	ret = regmap_raw_write(pcf->regmap, reg, data, nr_regs); -	if (ret != 0) -		return ret; - -	return nr_regs; +	return regmap_raw_write(pcf->regmap, reg, data, nr_regs);  }  EXPORT_SYMBOL_GPL(pcf50633_write_block); diff --git a/drivers/mfd/pcf50633-gpio.c b/drivers/mfd/pcf50633-gpio.c index 9ab19a8f669..d02ddf2ebd6 100644 --- a/drivers/mfd/pcf50633-gpio.c +++ b/drivers/mfd/pcf50633-gpio.c @@ -19,32 +19,7 @@  #include <linux/mfd/pcf50633/core.h>  #include <linux/mfd/pcf50633/gpio.h> - -enum pcf50633_regulator_id { -	PCF50633_REGULATOR_AUTO, -	PCF50633_REGULATOR_DOWN1, -	PCF50633_REGULATOR_DOWN2, -	PCF50633_REGULATOR_LDO1, -	PCF50633_REGULATOR_LDO2, -	PCF50633_REGULATOR_LDO3, -	PCF50633_REGULATOR_LDO4, -	PCF50633_REGULATOR_LDO5, -	PCF50633_REGULATOR_LDO6, -	PCF50633_REGULATOR_HCLDO, -	PCF50633_REGULATOR_MEMLDO, -}; - -#define PCF50633_REG_AUTOOUT	0x1a -#define PCF50633_REG_DOWN1OUT	0x1e -#define PCF50633_REG_DOWN2OUT	0x22 -#define PCF50633_REG_MEMLDOOUT	0x26 -#define PCF50633_REG_LDO1OUT	0x2d -#define PCF50633_REG_LDO2OUT	0x2f -#define PCF50633_REG_LDO3OUT	0x31 -#define PCF50633_REG_LDO4OUT	0x33 -#define PCF50633_REG_LDO5OUT	0x35 -#define PCF50633_REG_LDO6OUT	0x37 -#define PCF50633_REG_HCLDOOUT	0x39 +#include <linux/mfd/pcf50633/pmic.h>  static const u8 pcf50633_regulator_registers[PCF50633_NUM_REGULATORS] = {  	[PCF50633_REGULATOR_AUTO]	= PCF50633_REG_AUTOOUT, diff --git a/drivers/mfd/pcf50633-irq.c b/drivers/mfd/pcf50633-irq.c index 048a3b903b0..498286cbb53 100644 --- a/drivers/mfd/pcf50633-irq.c +++ b/drivers/mfd/pcf50633-irq.c @@ -19,12 +19,7 @@  #include <linux/slab.h>  #include <linux/mfd/pcf50633/core.h> - -/* Two MBCS registers used during cold start */ -#define PCF50633_REG_MBCS1		0x4b -#define PCF50633_REG_MBCS2		0x4c -#define PCF50633_MBCS1_USBPRES 		0x01 -#define PCF50633_MBCS1_ADAPTPRES	0x01 +#include <linux/mfd/pcf50633/mbc.h>  int pcf50633_register_irq(struct pcf50633 *pcf, int irq,  			void (*handler) (int, void *), void *data) diff --git a/drivers/mfd/rc5t583-irq.c b/drivers/mfd/rc5t583-irq.c new file mode 100644 index 00000000000..fa6f80fad5f --- /dev/null +++ b/drivers/mfd/rc5t583-irq.c @@ -0,0 +1,408 @@ +/* + * Interrupt driver for RICOH583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * based on code + *      Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/mfd/rc5t583.h> + +enum int_type { +	SYS_INT  = 0x1, +	DCDC_INT = 0x2, +	RTC_INT  = 0x4, +	ADC_INT  = 0x8, +	GPIO_INT = 0x10, +}; + +static int gpedge_add[] = { +	RC5T583_GPIO_GPEDGE2, +	RC5T583_GPIO_GPEDGE2 +}; + +static int irq_en_add[] = { +	RC5T583_INT_EN_SYS1, +	RC5T583_INT_EN_SYS2, +	RC5T583_INT_EN_DCDC, +	RC5T583_INT_EN_RTC, +	RC5T583_INT_EN_ADC1, +	RC5T583_INT_EN_ADC2, +	RC5T583_INT_EN_ADC3, +	RC5T583_GPIO_EN_INT +}; + +static int irq_mon_add[] = { +	RC5T583_INT_MON_SYS1, +	RC5T583_INT_MON_SYS2, +	RC5T583_INT_MON_DCDC, +	RC5T583_INT_MON_RTC, +	RC5T583_INT_IR_ADCL, +	RC5T583_INT_IR_ADCH, +	RC5T583_INT_IR_ADCEND, +	RC5T583_INT_IR_GPIOF, +	RC5T583_INT_IR_GPIOR +}; + +static int irq_clr_add[] = { +	RC5T583_INT_IR_SYS1, +	RC5T583_INT_IR_SYS2, +	RC5T583_INT_IR_DCDC, +	RC5T583_INT_IR_RTC, +	RC5T583_INT_IR_ADCL, +	RC5T583_INT_IR_ADCH, +	RC5T583_INT_IR_ADCEND, +	RC5T583_INT_IR_GPIOF, +	RC5T583_INT_IR_GPIOR +}; + +static int main_int_type[] = { +	SYS_INT, +	SYS_INT, +	DCDC_INT, +	RTC_INT, +	ADC_INT, +	ADC_INT, +	ADC_INT, +	GPIO_INT, +	GPIO_INT, +}; + +struct rc5t583_irq_data { +	u8	int_type; +	u8	master_bit; +	u8	int_en_bit; +	u8	mask_reg_index; +	int	grp_index; +}; + +#define RC5T583_IRQ(_int_type, _master_bit, _grp_index, \ +			_int_bit, _mask_ind)		\ +	{						\ +		.int_type	= _int_type,		\ +		.master_bit	= _master_bit,		\ +		.grp_index	= _grp_index,		\ +		.int_en_bit	= _int_bit,		\ +		.mask_reg_index	= _mask_ind,		\ +	} + +static const struct rc5t583_irq_data rc5t583_irqs[RC5T583_MAX_IRQS] = { +	[RC5T583_IRQ_ONKEY]		= RC5T583_IRQ(SYS_INT,  0, 0, 0, 0), +	[RC5T583_IRQ_ACOK]		= RC5T583_IRQ(SYS_INT,  0, 1, 1, 0), +	[RC5T583_IRQ_LIDOPEN]		= RC5T583_IRQ(SYS_INT,  0, 2, 2, 0), +	[RC5T583_IRQ_PREOT]		= RC5T583_IRQ(SYS_INT,  0, 3, 3, 0), +	[RC5T583_IRQ_CLKSTP]		= RC5T583_IRQ(SYS_INT,  0, 4, 4, 0), +	[RC5T583_IRQ_ONKEY_OFF]		= RC5T583_IRQ(SYS_INT,  0, 5, 5, 0), +	[RC5T583_IRQ_WD]		= RC5T583_IRQ(SYS_INT,  0, 7, 7, 0), +	[RC5T583_IRQ_EN_PWRREQ1]	= RC5T583_IRQ(SYS_INT,  0, 8, 0, 1), +	[RC5T583_IRQ_EN_PWRREQ2]	= RC5T583_IRQ(SYS_INT,  0, 9, 1, 1), +	[RC5T583_IRQ_PRE_VINDET]	= RC5T583_IRQ(SYS_INT,  0, 10, 2, 1), + +	[RC5T583_IRQ_DC0LIM]		= RC5T583_IRQ(DCDC_INT, 1, 0, 0, 2), +	[RC5T583_IRQ_DC1LIM]		= RC5T583_IRQ(DCDC_INT, 1, 1, 1, 2), +	[RC5T583_IRQ_DC2LIM]		= RC5T583_IRQ(DCDC_INT, 1, 2, 2, 2), +	[RC5T583_IRQ_DC3LIM]		= RC5T583_IRQ(DCDC_INT, 1, 3, 3, 2), + +	[RC5T583_IRQ_CTC]		= RC5T583_IRQ(RTC_INT,  2, 0, 0, 3), +	[RC5T583_IRQ_YALE]		= RC5T583_IRQ(RTC_INT,  2, 5, 5, 3), +	[RC5T583_IRQ_DALE]		= RC5T583_IRQ(RTC_INT,  2, 6, 6, 3), +	[RC5T583_IRQ_WALE]		= RC5T583_IRQ(RTC_INT,  2, 7, 7, 3), + +	[RC5T583_IRQ_AIN1L]		= RC5T583_IRQ(ADC_INT,  3, 0, 0, 4), +	[RC5T583_IRQ_AIN2L]		= RC5T583_IRQ(ADC_INT,  3, 1, 1, 4), +	[RC5T583_IRQ_AIN3L]		= RC5T583_IRQ(ADC_INT,  3, 2, 2, 4), +	[RC5T583_IRQ_VBATL]		= RC5T583_IRQ(ADC_INT,  3, 3, 3, 4), +	[RC5T583_IRQ_VIN3L]		= RC5T583_IRQ(ADC_INT,  3, 4, 4, 4), +	[RC5T583_IRQ_VIN8L]		= RC5T583_IRQ(ADC_INT,  3, 5, 5, 4), +	[RC5T583_IRQ_AIN1H]		= RC5T583_IRQ(ADC_INT,  3, 6, 0, 5), +	[RC5T583_IRQ_AIN2H]		= RC5T583_IRQ(ADC_INT,  3, 7, 1, 5), +	[RC5T583_IRQ_AIN3H]		= RC5T583_IRQ(ADC_INT,  3, 8, 2, 5), +	[RC5T583_IRQ_VBATH]		= RC5T583_IRQ(ADC_INT,  3, 9, 3, 5), +	[RC5T583_IRQ_VIN3H]		= RC5T583_IRQ(ADC_INT,  3, 10, 4, 5), +	[RC5T583_IRQ_VIN8H]		= RC5T583_IRQ(ADC_INT,  3, 11, 5, 5), +	[RC5T583_IRQ_ADCEND]		= RC5T583_IRQ(ADC_INT,  3, 12, 0, 6), + +	[RC5T583_IRQ_GPIO0]		= RC5T583_IRQ(GPIO_INT, 4, 0, 0, 7), +	[RC5T583_IRQ_GPIO1]		= RC5T583_IRQ(GPIO_INT, 4, 1, 1, 7), +	[RC5T583_IRQ_GPIO2]		= RC5T583_IRQ(GPIO_INT, 4, 2, 2, 7), +	[RC5T583_IRQ_GPIO3]		= RC5T583_IRQ(GPIO_INT, 4, 3, 3, 7), +	[RC5T583_IRQ_GPIO4]		= RC5T583_IRQ(GPIO_INT, 4, 4, 4, 7), +	[RC5T583_IRQ_GPIO5]		= RC5T583_IRQ(GPIO_INT, 4, 5, 5, 7), +	[RC5T583_IRQ_GPIO6]		= RC5T583_IRQ(GPIO_INT, 4, 6, 6, 7), +	[RC5T583_IRQ_GPIO7]		= RC5T583_IRQ(GPIO_INT, 4, 7, 7, 7), +}; + +static void rc5t583_irq_lock(struct irq_data *irq_data) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	mutex_lock(&rc5t583->irq_lock); +} + +static void rc5t583_irq_unmask(struct irq_data *irq_data) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	unsigned int __irq = irq_data->irq - rc5t583->irq_base; +	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; + +	rc5t583->group_irq_en[data->grp_index] |= 1 << data->grp_index; +	rc5t583->intc_inten_reg |= 1 << data->master_bit; +	rc5t583->irq_en_reg[data->mask_reg_index] |= 1 << data->int_en_bit; +} + +static void rc5t583_irq_mask(struct irq_data *irq_data) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	unsigned int __irq = irq_data->irq - rc5t583->irq_base; +	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; + +	rc5t583->group_irq_en[data->grp_index] &= ~(1 << data->grp_index); +	if (!rc5t583->group_irq_en[data->grp_index]) +		rc5t583->intc_inten_reg &= ~(1 << data->master_bit); + +	rc5t583->irq_en_reg[data->mask_reg_index] &= ~(1 << data->int_en_bit); +} + +static int rc5t583_irq_set_type(struct irq_data *irq_data, unsigned int type) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	unsigned int __irq = irq_data->irq - rc5t583->irq_base; +	const struct rc5t583_irq_data *data = &rc5t583_irqs[__irq]; +	int val = 0; +	int gpedge_index; +	int gpedge_bit_pos; + +	/* Supporting only trigger level inetrrupt */ +	if ((data->int_type & GPIO_INT) && (type & IRQ_TYPE_EDGE_BOTH)) { +		gpedge_index = data->int_en_bit / 4; +		gpedge_bit_pos = data->int_en_bit % 4; + +		if (type & IRQ_TYPE_EDGE_FALLING) +			val |= 0x2; + +		if (type & IRQ_TYPE_EDGE_RISING) +			val |= 0x1; + +		rc5t583->gpedge_reg[gpedge_index] &= ~(3 << gpedge_bit_pos); +		rc5t583->gpedge_reg[gpedge_index] |= (val << gpedge_bit_pos); +		rc5t583_irq_unmask(irq_data); +		return 0; +	} +	return -EINVAL; +} + +static void rc5t583_irq_sync_unlock(struct irq_data *irq_data) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	int i; +	int ret; + +	for (i = 0; i < ARRAY_SIZE(rc5t583->gpedge_reg); i++) { +		ret = rc5t583_write(rc5t583->dev, gpedge_add[i], +				rc5t583->gpedge_reg[i]); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				gpedge_add[i], ret); +	} + +	for (i = 0; i < ARRAY_SIZE(rc5t583->irq_en_reg); i++) { +		ret = rc5t583_write(rc5t583->dev, irq_en_add[i], +					rc5t583->irq_en_reg[i]); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				irq_en_add[i], ret); +	} + +	ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, +				rc5t583->intc_inten_reg); +	if (ret < 0) +		dev_warn(rc5t583->dev, +			"Error in writing reg 0x%02x error: %d\n", +			RC5T583_INTC_INTEN, ret); + +	mutex_unlock(&rc5t583->irq_lock); +} +#ifdef CONFIG_PM_SLEEP +static int rc5t583_irq_set_wake(struct irq_data *irq_data, unsigned int on) +{ +	struct rc5t583 *rc5t583 = irq_data_get_irq_chip_data(irq_data); +	return irq_set_irq_wake(rc5t583->chip_irq, on); +} +#else +#define rc5t583_irq_set_wake NULL +#endif + +static irqreturn_t rc5t583_irq(int irq, void *data) +{ +	struct rc5t583 *rc5t583 = data; +	uint8_t int_sts[RC5T583_MAX_INTERRUPT_MASK_REGS]; +	uint8_t master_int; +	int i; +	int ret; +	unsigned int rtc_int_sts = 0; + +	/* Clear the status */ +	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++) +		int_sts[i] = 0; + +	ret  = rc5t583_read(rc5t583->dev, RC5T583_INTC_INTMON, &master_int); +	if (ret < 0) { +		dev_err(rc5t583->dev, +			"Error in reading reg 0x%02x error: %d\n", +			RC5T583_INTC_INTMON, ret); +		return IRQ_HANDLED; +	} + +	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; ++i) { +		if (!(master_int & main_int_type[i])) +			continue; + +		ret = rc5t583_read(rc5t583->dev, irq_mon_add[i], &int_sts[i]); +		if (ret < 0) { +			dev_warn(rc5t583->dev, +				"Error in reading reg 0x%02x error: %d\n", +				irq_mon_add[i], ret); +			int_sts[i] = 0; +			continue; +		} + +		if (main_int_type[i] & RTC_INT) { +			rtc_int_sts = 0; +			if (int_sts[i] & 0x1) +				rtc_int_sts |= BIT(6); +			if (int_sts[i] & 0x2) +				rtc_int_sts |= BIT(7); +			if (int_sts[i] & 0x4) +				rtc_int_sts |= BIT(0); +			if (int_sts[i] & 0x8) +				rtc_int_sts |= BIT(5); +		} + +		ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], +				~int_sts[i]); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in reading reg 0x%02x error: %d\n", +				irq_clr_add[i], ret); + +		if (main_int_type[i] & RTC_INT) +			int_sts[i] = rtc_int_sts; +	} + +	/* Merge gpio interrupts for rising and falling case*/ +	int_sts[7] |= int_sts[8]; + +	/* Call interrupt handler if enabled */ +	for (i = 0; i < RC5T583_MAX_IRQS; ++i) { +		const struct rc5t583_irq_data *data = &rc5t583_irqs[i]; +		if ((int_sts[data->mask_reg_index] & (1 << data->int_en_bit)) && +			(rc5t583->group_irq_en[data->master_bit] & +					(1 << data->grp_index))) +			handle_nested_irq(rc5t583->irq_base + i); +	} + +	return IRQ_HANDLED; +} + +static struct irq_chip rc5t583_irq_chip = { +	.name = "rc5t583-irq", +	.irq_mask = rc5t583_irq_mask, +	.irq_unmask = rc5t583_irq_unmask, +	.irq_bus_lock = rc5t583_irq_lock, +	.irq_bus_sync_unlock = rc5t583_irq_sync_unlock, +	.irq_set_type = rc5t583_irq_set_type, +	.irq_set_wake = rc5t583_irq_set_wake, +}; + +int rc5t583_irq_init(struct rc5t583 *rc5t583, int irq, int irq_base) +{ +	int i, ret; + +	if (!irq_base) { +		dev_warn(rc5t583->dev, "No interrupt support on IRQ base\n"); +		return -EINVAL; +	} + +	mutex_init(&rc5t583->irq_lock); + +	/* Initailize all int register to 0 */ +	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)  { +		ret = rc5t583_write(rc5t583->dev, irq_en_add[i], +				rc5t583->irq_en_reg[i]); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				irq_en_add[i], ret); +	} + +	for (i = 0; i < RC5T583_MAX_GPEDGE_REG; i++)  { +		ret = rc5t583_write(rc5t583->dev, gpedge_add[i], +				rc5t583->gpedge_reg[i]); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				gpedge_add[i], ret); +	} + +	ret = rc5t583_write(rc5t583->dev, RC5T583_INTC_INTEN, 0x0); +	if (ret < 0) +		dev_warn(rc5t583->dev, +			"Error in writing reg 0x%02x error: %d\n", +			RC5T583_INTC_INTEN, ret); + +	/* Clear all interrupts in case they woke up active. */ +	for (i = 0; i < RC5T583_MAX_INTERRUPT_MASK_REGS; i++)  { +		ret = rc5t583_write(rc5t583->dev, irq_clr_add[i], 0); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				irq_clr_add[i], ret); +	} + +	rc5t583->irq_base = irq_base; +	rc5t583->chip_irq = irq; + +	for (i = 0; i < RC5T583_MAX_IRQS; i++) { +		int __irq = i + rc5t583->irq_base; +		irq_set_chip_data(__irq, rc5t583); +		irq_set_chip_and_handler(__irq, &rc5t583_irq_chip, +					 handle_simple_irq); +		irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM +		set_irq_flags(__irq, IRQF_VALID); +#endif +	} + +	ret = request_threaded_irq(irq, NULL, rc5t583_irq, IRQF_ONESHOT, +				"rc5t583", rc5t583); +	if (ret < 0) +		dev_err(rc5t583->dev, +			"Error in registering interrupt error: %d\n", ret); +	return ret; +} + +int rc5t583_irq_exit(struct rc5t583 *rc5t583) +{ +	if (rc5t583->chip_irq) +		free_irq(rc5t583->chip_irq, rc5t583); +	return 0; +} diff --git a/drivers/mfd/rc5t583.c b/drivers/mfd/rc5t583.c new file mode 100644 index 00000000000..99ef944c621 --- /dev/null +++ b/drivers/mfd/rc5t583.c @@ -0,0 +1,386 @@ +/* + * Core driver access RC5T583 power management chip. + * + * Copyright (c) 2011-2012, NVIDIA CORPORATION.  All rights reserved. + * Author: Laxman dewangan <ldewangan@nvidia.com> + * + * Based on code + *	Copyright (C) 2011 RICOH COMPANY,LTD + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + * + */ +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/err.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/rc5t583.h> +#include <linux/regmap.h> + +#define RICOH_ONOFFSEL_REG	0x10 +#define RICOH_SWCTL_REG		0x5E + +struct deepsleep_control_data { +	u8 reg_add; +	u8 ds_pos_bit; +}; + +#define DEEPSLEEP_INIT(_id, _reg, _pos)		\ +	{					\ +		.reg_add = RC5T583_##_reg,	\ +		.ds_pos_bit = _pos,		\ +	} + +static struct deepsleep_control_data deepsleep_data[] = { +	DEEPSLEEP_INIT(DC0, SLPSEQ1, 0), +	DEEPSLEEP_INIT(DC1, SLPSEQ1, 4), +	DEEPSLEEP_INIT(DC2, SLPSEQ2, 0), +	DEEPSLEEP_INIT(DC3, SLPSEQ2, 4), +	DEEPSLEEP_INIT(LDO0, SLPSEQ3, 0), +	DEEPSLEEP_INIT(LDO1, SLPSEQ3, 4), +	DEEPSLEEP_INIT(LDO2, SLPSEQ4, 0), +	DEEPSLEEP_INIT(LDO3, SLPSEQ4, 4), +	DEEPSLEEP_INIT(LDO4, SLPSEQ5, 0), +	DEEPSLEEP_INIT(LDO5, SLPSEQ5, 4), +	DEEPSLEEP_INIT(LDO6, SLPSEQ6, 0), +	DEEPSLEEP_INIT(LDO7, SLPSEQ6, 4), +	DEEPSLEEP_INIT(LDO8, SLPSEQ7, 0), +	DEEPSLEEP_INIT(LDO9, SLPSEQ7, 4), +	DEEPSLEEP_INIT(PSO0, SLPSEQ8, 0), +	DEEPSLEEP_INIT(PSO1, SLPSEQ8, 4), +	DEEPSLEEP_INIT(PSO2, SLPSEQ9, 0), +	DEEPSLEEP_INIT(PSO3, SLPSEQ9, 4), +	DEEPSLEEP_INIT(PSO4, SLPSEQ10, 0), +	DEEPSLEEP_INIT(PSO5, SLPSEQ10, 4), +	DEEPSLEEP_INIT(PSO6, SLPSEQ11, 0), +	DEEPSLEEP_INIT(PSO7, SLPSEQ11, 4), +}; + +#define EXT_PWR_REQ		\ +	(RC5T583_EXT_PWRREQ1_CONTROL | RC5T583_EXT_PWRREQ2_CONTROL) + +static struct mfd_cell rc5t583_subdevs[] = { +	{.name = "rc5t583-regulator",}, +	{.name = "rc5t583-rtc",      }, +	{.name = "rc5t583-key",      } +}; + +int rc5t583_write(struct device *dev, uint8_t reg, uint8_t val) +{ +	struct rc5t583 *rc5t583 = dev_get_drvdata(dev); +	return regmap_write(rc5t583->regmap, reg, val); +} + +int rc5t583_read(struct device *dev, uint8_t reg, uint8_t *val) +{ +	struct rc5t583 *rc5t583 = dev_get_drvdata(dev); +	unsigned int ival; +	int ret; +	ret = regmap_read(rc5t583->regmap, reg, &ival); +	if (!ret) +		*val = (uint8_t)ival; +	return ret; +} + +int rc5t583_set_bits(struct device *dev, unsigned int reg, +			unsigned int bit_mask) +{ +	struct rc5t583 *rc5t583 = dev_get_drvdata(dev); +	return regmap_update_bits(rc5t583->regmap, reg, bit_mask, bit_mask); +} + +int rc5t583_clear_bits(struct device *dev, unsigned int reg, +			unsigned int bit_mask) +{ +	struct rc5t583 *rc5t583 = dev_get_drvdata(dev); +	return regmap_update_bits(rc5t583->regmap, reg, bit_mask, 0); +} + +int rc5t583_update(struct device *dev, unsigned int reg, +		unsigned int val, unsigned int mask) +{ +	struct rc5t583 *rc5t583 = dev_get_drvdata(dev); +	return regmap_update_bits(rc5t583->regmap, reg, mask, val); +} + +static int __rc5t583_set_ext_pwrreq1_control(struct device *dev, +	int id, int ext_pwr, int slots) +{ +	int ret; +	uint8_t sleepseq_val; +	unsigned int en_bit; +	unsigned int slot_bit; + +	if (id == RC5T583_DS_DC0) { +		dev_err(dev, "PWRREQ1 is invalid control for rail %d\n", id); +		return -EINVAL; +	} + +	en_bit = deepsleep_data[id].ds_pos_bit; +	slot_bit = en_bit + 1; +	ret = rc5t583_read(dev, deepsleep_data[id].reg_add, &sleepseq_val); +	if (ret < 0) { +		dev_err(dev, "Error in reading reg 0x%x\n", +				deepsleep_data[id].reg_add); +		return ret; +	} + +	sleepseq_val &= ~(0xF << en_bit); +	sleepseq_val |= BIT(en_bit); +	sleepseq_val |= ((slots & 0x7) << slot_bit); +	ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(1)); +	if (ret < 0) { +		dev_err(dev, "Error in updating the 0x%02x register\n", +				RICOH_ONOFFSEL_REG); +		return ret; +	} + +	ret = rc5t583_write(dev, deepsleep_data[id].reg_add, sleepseq_val); +	if (ret < 0) { +		dev_err(dev, "Error in writing reg 0x%x\n", +				deepsleep_data[id].reg_add); +		return ret; +	} + +	if (id == RC5T583_DS_LDO4) { +		ret = rc5t583_write(dev, RICOH_SWCTL_REG, 0x1); +		if (ret < 0) +			dev_err(dev, "Error in writing reg 0x%x\n", +				RICOH_SWCTL_REG); +	} +	return ret; +} + +static int __rc5t583_set_ext_pwrreq2_control(struct device *dev, +	int id, int ext_pwr) +{ +	int ret; + +	if (id != RC5T583_DS_DC0) { +		dev_err(dev, "PWRREQ2 is invalid control for rail %d\n", id); +		return -EINVAL; +	} + +	ret = rc5t583_set_bits(dev, RICOH_ONOFFSEL_REG, BIT(2)); +	if (ret < 0) +		dev_err(dev, "Error in updating the ONOFFSEL 0x10 register\n"); +	return ret; +} + +int rc5t583_ext_power_req_config(struct device *dev, int ds_id, +	int ext_pwr_req, int deepsleep_slot_nr) +{ +	if ((ext_pwr_req & EXT_PWR_REQ) == EXT_PWR_REQ) +		return -EINVAL; + +	if (ext_pwr_req & RC5T583_EXT_PWRREQ1_CONTROL) +		return __rc5t583_set_ext_pwrreq1_control(dev, ds_id, +				ext_pwr_req, deepsleep_slot_nr); + +	if (ext_pwr_req & RC5T583_EXT_PWRREQ2_CONTROL) +		return __rc5t583_set_ext_pwrreq2_control(dev, +			ds_id, ext_pwr_req); +	return 0; +} + +static int rc5t583_clear_ext_power_req(struct rc5t583 *rc5t583, +	struct rc5t583_platform_data *pdata) +{ +	int ret; +	int i; +	uint8_t on_off_val = 0; + +	/*  Clear ONOFFSEL register */ +	if (pdata->enable_shutdown) +		on_off_val = 0x1; + +	ret = rc5t583_write(rc5t583->dev, RICOH_ONOFFSEL_REG, on_off_val); +	if (ret < 0) +		dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n", +					RICOH_ONOFFSEL_REG, ret); + +	ret = rc5t583_write(rc5t583->dev, RICOH_SWCTL_REG, 0x0); +	if (ret < 0) +		dev_warn(rc5t583->dev, "Error in writing reg %d error: %d\n", +					RICOH_SWCTL_REG, ret); + +	/* Clear sleep sequence register */ +	for (i = RC5T583_SLPSEQ1; i <= RC5T583_SLPSEQ11; ++i) { +		ret = rc5t583_write(rc5t583->dev, i, 0x0); +		if (ret < 0) +			dev_warn(rc5t583->dev, +				"Error in writing reg 0x%02x error: %d\n", +				i, ret); +	} +	return 0; +} + +static bool volatile_reg(struct device *dev, unsigned int reg) +{ +	/* Enable caching in interrupt registers */ +	switch (reg) { +	case RC5T583_INT_EN_SYS1: +	case RC5T583_INT_EN_SYS2: +	case RC5T583_INT_EN_DCDC: +	case RC5T583_INT_EN_RTC: +	case RC5T583_INT_EN_ADC1: +	case RC5T583_INT_EN_ADC2: +	case RC5T583_INT_EN_ADC3: +	case RC5T583_GPIO_GPEDGE1: +	case RC5T583_GPIO_GPEDGE2: +	case RC5T583_GPIO_EN_INT: +		return false; + +	case RC5T583_GPIO_MON_IOIN: +		/* This is gpio input register */ +		return true; + +	default: +		/* Enable caching in gpio registers */ +		if ((reg >= RC5T583_GPIO_IOSEL) && +				(reg <= RC5T583_GPIO_GPOFUNC)) +			return false; + +		/* Enable caching in sleep seq registers */ +		if ((reg >= RC5T583_SLPSEQ1) && (reg <= RC5T583_SLPSEQ11)) +			return false; + +		/* Enable caching of regulator registers */ +		if ((reg >= RC5T583_REG_DC0CTL) && (reg <= RC5T583_REG_SR3CTL)) +			return false; +		if ((reg >= RC5T583_REG_LDOEN1) && +					(reg <= RC5T583_REG_LDO9DAC_DS)) +			return false; + +		break; +	} + +	return true; +} + +static const struct regmap_config rc5t583_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.volatile_reg = volatile_reg, +	.max_register = RC5T583_MAX_REGS, +	.num_reg_defaults_raw = RC5T583_MAX_REGS, +	.cache_type = REGCACHE_RBTREE, +}; + +static int __devinit rc5t583_i2c_probe(struct i2c_client *i2c, +			      const struct i2c_device_id *id) +{ +	struct rc5t583 *rc5t583; +	struct rc5t583_platform_data *pdata = i2c->dev.platform_data; +	int ret; +	bool irq_init_success = false; + +	if (!pdata) { +		dev_err(&i2c->dev, "Err: Platform data not found\n"); +		return -EINVAL; +	} + +	rc5t583 = devm_kzalloc(&i2c->dev, sizeof(struct rc5t583), GFP_KERNEL); +	if (!rc5t583) { +		dev_err(&i2c->dev, "Memory allocation failed\n"); +		return -ENOMEM; +	} + +	rc5t583->dev = &i2c->dev; +	i2c_set_clientdata(i2c, rc5t583); + +	rc5t583->regmap = regmap_init_i2c(i2c, &rc5t583_regmap_config); +	if (IS_ERR(rc5t583->regmap)) { +		ret = PTR_ERR(rc5t583->regmap); +		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); +		return ret; +	} + +	ret = rc5t583_clear_ext_power_req(rc5t583, pdata); +	if (ret < 0) +		goto err_irq_init; + +	if (i2c->irq) { +		ret = rc5t583_irq_init(rc5t583, i2c->irq, pdata->irq_base); +		/* Still continue with waring if irq init fails */ +		if (ret) +			dev_warn(&i2c->dev, "IRQ init failed: %d\n", ret); +		else +			irq_init_success = true; +	} + +	ret = mfd_add_devices(rc5t583->dev, -1, rc5t583_subdevs, +			ARRAY_SIZE(rc5t583_subdevs), NULL, 0); +	if (ret) { +		dev_err(&i2c->dev, "add mfd devices failed: %d\n", ret); +		goto err_add_devs; +	} + +	return 0; + +err_add_devs: +	if (irq_init_success) +		rc5t583_irq_exit(rc5t583); +err_irq_init: +	regmap_exit(rc5t583->regmap); +	return ret; +} + +static int  __devexit rc5t583_i2c_remove(struct i2c_client *i2c) +{ +	struct rc5t583 *rc5t583 = i2c_get_clientdata(i2c); + +	mfd_remove_devices(rc5t583->dev); +	rc5t583_irq_exit(rc5t583); +	regmap_exit(rc5t583->regmap); +	return 0; +} + +static const struct i2c_device_id rc5t583_i2c_id[] = { +	{.name = "rc5t583", .driver_data = 0}, +	{} +}; + +MODULE_DEVICE_TABLE(i2c, rc5t583_i2c_id); + +static struct i2c_driver rc5t583_i2c_driver = { +	.driver = { +		   .name = "rc5t583", +		   .owner = THIS_MODULE, +		   }, +	.probe = rc5t583_i2c_probe, +	.remove = __devexit_p(rc5t583_i2c_remove), +	.id_table = rc5t583_i2c_id, +}; + +static int __init rc5t583_i2c_init(void) +{ +	return i2c_add_driver(&rc5t583_i2c_driver); +} +subsys_initcall(rc5t583_i2c_init); + +static void __exit rc5t583_i2c_exit(void) +{ +	i2c_del_driver(&rc5t583_i2c_driver); +} + +module_exit(rc5t583_i2c_exit); + +MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>"); +MODULE_DESCRIPTION("RICOH RC5T583 power management system device driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/s5m-core.c b/drivers/mfd/s5m-core.c index caadabeed8e..48949d998d1 100644 --- a/drivers/mfd/s5m-core.c +++ b/drivers/mfd/s5m-core.c @@ -26,7 +26,27 @@  #include <linux/mfd/s5m87xx/s5m-rtc.h>  #include <linux/regmap.h> -static struct mfd_cell s5m87xx_devs[] = { +static struct mfd_cell s5m8751_devs[] = { +	{ +		.name = "s5m8751-pmic", +	}, { +		.name = "s5m-charger", +	}, { +		.name = "s5m8751-codec", +	}, +}; + +static struct mfd_cell s5m8763_devs[] = { +	{ +		.name = "s5m8763-pmic", +	}, { +		.name = "s5m-rtc", +	}, { +		.name = "s5m-charger", +	}, +}; + +static struct mfd_cell s5m8767_devs[] = {  	{  		.name = "s5m8767-pmic",  	}, { @@ -42,7 +62,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_read);  int s5m_bulk_read(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)  { -	return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);; +	return regmap_bulk_read(s5m87xx->regmap, reg, buf, count);  }  EXPORT_SYMBOL_GPL(s5m_bulk_read); @@ -54,7 +74,7 @@ EXPORT_SYMBOL_GPL(s5m_reg_write);  int s5m_bulk_write(struct s5m87xx_dev *s5m87xx, u8 reg, int count, u8 *buf)  { -	return regmap_raw_write(s5m87xx->regmap, reg, buf, count * sizeof(u16)); +	return regmap_raw_write(s5m87xx->regmap, reg, buf, count);  }  EXPORT_SYMBOL_GPL(s5m_bulk_write); @@ -74,10 +94,10 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,  {  	struct s5m_platform_data *pdata = i2c->dev.platform_data;  	struct s5m87xx_dev *s5m87xx; -	int ret = 0; -	int error; +	int ret; -	s5m87xx = kzalloc(sizeof(struct s5m87xx_dev), GFP_KERNEL); +	s5m87xx = devm_kzalloc(&i2c->dev, sizeof(struct s5m87xx_dev), +				GFP_KERNEL);  	if (s5m87xx == NULL)  		return -ENOMEM; @@ -96,9 +116,9 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,  	s5m87xx->regmap = regmap_init_i2c(i2c, &s5m_regmap_config);  	if (IS_ERR(s5m87xx->regmap)) { -		error = PTR_ERR(s5m87xx->regmap); +		ret = PTR_ERR(s5m87xx->regmap);  		dev_err(&i2c->dev, "Failed to allocate register map: %d\n", -			error); +			ret);  		goto err;  	} @@ -112,9 +132,23 @@ static int s5m87xx_i2c_probe(struct i2c_client *i2c,  	pm_runtime_set_active(s5m87xx->dev); -	ret = mfd_add_devices(s5m87xx->dev, -1, -				s5m87xx_devs, ARRAY_SIZE(s5m87xx_devs), -				NULL, 0); +	switch (s5m87xx->device_type) { +	case S5M8751X: +		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8751_devs, +					ARRAY_SIZE(s5m8751_devs), NULL, 0); +		break; +	case S5M8763X: +		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8763_devs, +					ARRAY_SIZE(s5m8763_devs), NULL, 0); +		break; +	case S5M8767X: +		ret = mfd_add_devices(s5m87xx->dev, -1, s5m8767_devs, +					ARRAY_SIZE(s5m8767_devs), NULL, 0); +		break; +	default: +		/* If this happens the probe function is problem */ +		BUG(); +	}  	if (ret < 0)  		goto err; @@ -126,7 +160,6 @@ err:  	s5m_irq_exit(s5m87xx);  	i2c_unregister_device(s5m87xx->rtc);  	regmap_exit(s5m87xx->regmap); -	kfree(s5m87xx);  	return ret;  } @@ -138,7 +171,6 @@ static int s5m87xx_i2c_remove(struct i2c_client *i2c)  	s5m_irq_exit(s5m87xx);  	i2c_unregister_device(s5m87xx->rtc);  	regmap_exit(s5m87xx->regmap); -	kfree(s5m87xx);  	return 0;  } diff --git a/drivers/mfd/s5m-irq.c b/drivers/mfd/s5m-irq.c index de76dfb6f0a..0236676085c 100644 --- a/drivers/mfd/s5m-irq.c +++ b/drivers/mfd/s5m-irq.c @@ -342,7 +342,10 @@ int s5m_irq_resume(struct s5m87xx_dev *s5m87xx)  			s5m8767_irq_thread(s5m87xx->irq_base, s5m87xx);  			break;  		default: -			break; +			dev_err(s5m87xx->dev, +				"Unknown device type %d\n", +				s5m87xx->device_type); +			return -EINVAL;  		}  	} @@ -444,7 +447,9 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx)  		}  		break;  	default: -		break; +		dev_err(s5m87xx->dev, +			"Unknown device type %d\n", s5m87xx->device_type); +		return -EINVAL;  	}  	if (!s5m87xx->ono) @@ -467,12 +472,15 @@ int s5m_irq_init(struct s5m87xx_dev *s5m87xx)  					IRQF_ONESHOT, "s5m87xx-ono", s5m87xx);  		break;  	default: +		ret = -EINVAL;  		break;  	} -	if (ret) +	if (ret) {  		dev_err(s5m87xx->dev, "Failed to request IRQ %d: %d\n",  			s5m87xx->ono, ret); +		return ret; +	}  	return 0;  } diff --git a/drivers/mfd/sm501.c b/drivers/mfd/sm501.c index f4d86117f44..d927dd49acb 100644 --- a/drivers/mfd/sm501.c +++ b/drivers/mfd/sm501.c @@ -387,14 +387,6 @@ int sm501_unit_power(struct device *dev, unsigned int unit, unsigned int to)  EXPORT_SYMBOL_GPL(sm501_unit_power); - -/* Perform a rounded division. */ -static long sm501fb_round_div(long num, long denom) -{ -        /* n / d + 1 / 2 = (2n + d) / 2d */ -        return (2 * num + denom) / (2 * denom); -} -  /* clock value structure. */  struct sm501_clock {  	unsigned long mclk; @@ -428,7 +420,7 @@ static int sm501_calc_clock(unsigned long freq,  		/* try all 8 shift values.*/  		for (shift = 0; shift < 8; shift++) {  			/* Calculate difference to requested clock */ -			diff = sm501fb_round_div(mclk, divider << shift) - freq; +			diff = DIV_ROUND_CLOSEST(mclk, divider << shift) - freq;  			if (diff < 0)  				diff = -diff; diff --git a/drivers/mfd/stmpe.c b/drivers/mfd/stmpe.c index e07947e56b2..2dd8d49cb30 100644 --- a/drivers/mfd/stmpe.c +++ b/drivers/mfd/stmpe.c @@ -298,6 +298,11 @@ static struct mfd_cell stmpe_gpio_cell = {  	.num_resources	= ARRAY_SIZE(stmpe_gpio_resources),  }; +static struct mfd_cell stmpe_gpio_cell_noirq = { +	.name		= "stmpe-gpio", +	/* gpio cell resources consist of an irq only so no resources here */ +}; +  /*   * Keypad (1601, 2401, 2403)   */ @@ -346,6 +351,13 @@ static struct stmpe_variant_block stmpe801_blocks[] = {  	},  }; +static struct stmpe_variant_block stmpe801_blocks_noirq[] = { +	{ +		.cell	= &stmpe_gpio_cell_noirq, +		.block	= STMPE_BLOCK_GPIO, +	}, +}; +  static int stmpe801_enable(struct stmpe *stmpe, unsigned int blocks,  			   bool enable)  { @@ -367,6 +379,17 @@ static struct stmpe_variant_info stmpe801 = {  	.enable		= stmpe801_enable,  }; +static struct stmpe_variant_info stmpe801_noirq = { +	.name		= "stmpe801", +	.id_val		= STMPE801_ID, +	.id_mask	= 0xffff, +	.num_gpios	= 8, +	.regs		= stmpe801_regs, +	.blocks		= stmpe801_blocks_noirq, +	.num_blocks	= ARRAY_SIZE(stmpe801_blocks_noirq), +	.enable		= stmpe801_enable, +}; +  /*   * Touchscreen (STMPE811 or STMPE610)   */ @@ -712,7 +735,7 @@ static struct stmpe_variant_info stmpe2403 = {  	.enable_autosleep	= stmpe1601_autosleep, /* same as stmpe1601 */  }; -static struct stmpe_variant_info *stmpe_variant_info[] = { +static struct stmpe_variant_info *stmpe_variant_info[STMPE_NBR_PARTS] = {  	[STMPE610]	= &stmpe610,  	[STMPE801]	= &stmpe801,  	[STMPE811]	= &stmpe811, @@ -721,6 +744,16 @@ static struct stmpe_variant_info *stmpe_variant_info[] = {  	[STMPE2403]	= &stmpe2403,  }; +/* + * These devices can be connected in a 'no-irq' configuration - the irq pin + * is not used and the device cannot interrupt the CPU. Here we only list + * devices which support this configuration - the driver will fail probing + * for any devices not listed here which are configured in this way. + */ +static struct stmpe_variant_info *stmpe_noirq_variant_info[STMPE_NBR_PARTS] = { +	[STMPE801]	= &stmpe801_noirq, +}; +  static irqreturn_t stmpe_irq(int irq, void *data)  {  	struct stmpe *stmpe = data; @@ -864,7 +897,7 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)  	unsigned int irq_trigger = stmpe->pdata->irq_trigger;  	int autosleep_timeout = stmpe->pdata->autosleep_timeout;  	struct stmpe_variant_info *variant = stmpe->variant; -	u8 icr; +	u8 icr = 0;  	unsigned int id;  	u8 data[2];  	int ret; @@ -887,31 +920,33 @@ static int __devinit stmpe_chip_init(struct stmpe *stmpe)  	if (ret)  		return ret; -	if (id == STMPE801_ID) -		icr = STMPE801_REG_SYS_CTRL_INT_EN; -	else -		icr = STMPE_ICR_LSB_GIM; - -	/* STMPE801 doesn't support Edge interrupts */ -	if (id != STMPE801_ID) { -		if (irq_trigger == IRQF_TRIGGER_FALLING || -				irq_trigger == IRQF_TRIGGER_RISING) -			icr |= STMPE_ICR_LSB_EDGE; -	} - -	if (irq_trigger == IRQF_TRIGGER_RISING || -			irq_trigger == IRQF_TRIGGER_HIGH) { +	if (stmpe->irq >= 0) {  		if (id == STMPE801_ID) -			icr |= STMPE801_REG_SYS_CTRL_INT_HI; +			icr = STMPE801_REG_SYS_CTRL_INT_EN;  		else -			icr |= STMPE_ICR_LSB_HIGH; -	} +			icr = STMPE_ICR_LSB_GIM; -	if (stmpe->pdata->irq_invert_polarity) { -		if (id == STMPE801_ID) -			icr ^= STMPE801_REG_SYS_CTRL_INT_HI; -		else -			icr ^= STMPE_ICR_LSB_HIGH; +		/* STMPE801 doesn't support Edge interrupts */ +		if (id != STMPE801_ID) { +			if (irq_trigger == IRQF_TRIGGER_FALLING || +					irq_trigger == IRQF_TRIGGER_RISING) +				icr |= STMPE_ICR_LSB_EDGE; +		} + +		if (irq_trigger == IRQF_TRIGGER_RISING || +				irq_trigger == IRQF_TRIGGER_HIGH) { +			if (id == STMPE801_ID) +				icr |= STMPE801_REG_SYS_CTRL_INT_HI; +			else +				icr |= STMPE_ICR_LSB_HIGH; +		} + +		if (stmpe->pdata->irq_invert_polarity) { +			if (id == STMPE801_ID) +				icr ^= STMPE801_REG_SYS_CTRL_INT_HI; +			else +				icr ^= STMPE_ICR_LSB_HIGH; +		}  	}  	if (stmpe->pdata->autosleep) { @@ -1001,19 +1036,38 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)  		stmpe->irq = ci->irq;  	} +	if (stmpe->irq < 0) { +		/* use alternate variant info for no-irq mode, if supported */ +		dev_info(stmpe->dev, +			"%s configured in no-irq mode by platform data\n", +			stmpe->variant->name); +		if (!stmpe_noirq_variant_info[stmpe->partnum]) { +			dev_err(stmpe->dev, +				"%s does not support no-irq mode!\n", +				stmpe->variant->name); +			ret = -ENODEV; +			goto free_gpio; +		} +		stmpe->variant = stmpe_noirq_variant_info[stmpe->partnum]; +	} +  	ret = stmpe_chip_init(stmpe);  	if (ret)  		goto free_gpio; -	ret = stmpe_irq_init(stmpe); -	if (ret) -		goto free_gpio; +	if (stmpe->irq >= 0) { +		ret = stmpe_irq_init(stmpe); +		if (ret) +			goto free_gpio; -	ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, -			pdata->irq_trigger | IRQF_ONESHOT, "stmpe", stmpe); -	if (ret) { -		dev_err(stmpe->dev, "failed to request IRQ: %d\n", ret); -		goto out_removeirq; +		ret = request_threaded_irq(stmpe->irq, NULL, stmpe_irq, +				pdata->irq_trigger | IRQF_ONESHOT, +				"stmpe", stmpe); +		if (ret) { +			dev_err(stmpe->dev, "failed to request IRQ: %d\n", +					ret); +			goto out_removeirq; +		}  	}  	ret = stmpe_devices_init(stmpe); @@ -1026,9 +1080,11 @@ int __devinit stmpe_probe(struct stmpe_client_info *ci, int partnum)  out_removedevs:  	mfd_remove_devices(stmpe->dev); -	free_irq(stmpe->irq, stmpe); +	if (stmpe->irq >= 0) +		free_irq(stmpe->irq, stmpe);  out_removeirq: -	stmpe_irq_remove(stmpe); +	if (stmpe->irq >= 0) +		stmpe_irq_remove(stmpe);  free_gpio:  	if (pdata->irq_over_gpio)  		gpio_free(pdata->irq_gpio); @@ -1041,8 +1097,10 @@ int stmpe_remove(struct stmpe *stmpe)  {  	mfd_remove_devices(stmpe->dev); -	free_irq(stmpe->irq, stmpe); -	stmpe_irq_remove(stmpe); +	if (stmpe->irq >= 0) { +		free_irq(stmpe->irq, stmpe); +		stmpe_irq_remove(stmpe); +	}  	if (stmpe->pdata->irq_over_gpio)  		gpio_free(stmpe->pdata->irq_gpio); @@ -1057,7 +1115,7 @@ static int stmpe_suspend(struct device *dev)  {  	struct stmpe *stmpe = dev_get_drvdata(dev); -	if (device_may_wakeup(dev)) +	if (stmpe->irq >= 0 && device_may_wakeup(dev))  		enable_irq_wake(stmpe->irq);  	return 0; @@ -1067,7 +1125,7 @@ static int stmpe_resume(struct device *dev)  {  	struct stmpe *stmpe = dev_get_drvdata(dev); -	if (device_may_wakeup(dev)) +	if (stmpe->irq >= 0 && device_may_wakeup(dev))  		disable_irq_wake(stmpe->irq);  	return 0; diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c new file mode 100644 index 00000000000..a66d4df5129 --- /dev/null +++ b/drivers/mfd/tps65090.c @@ -0,0 +1,387 @@ +/* + * Core driver for TI TPS65090 PMIC family + * + * Copyright (c) 2012, NVIDIA CORPORATION.  All rights reserved. + + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for + * more details. + + * You should have received a copy of the GNU General Public License + * along with this program.  If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/slab.h> +#include <linux/i2c.h> +#include <linux/mfd/core.h> +#include <linux/mfd/tps65090.h> +#include <linux/regmap.h> +#include <linux/err.h> + +#define NUM_INT_REG 2 +#define TOTAL_NUM_REG 0x18 + +/* interrupt status registers */ +#define TPS65090_INT_STS	0x0 +#define TPS65090_INT_STS2	0x1 + +/* interrupt mask registers */ +#define TPS65090_INT_MSK	0x2 +#define TPS65090_INT_MSK2	0x3 + +struct tps65090_irq_data { +	u8		mask_reg; +	u8		mask_pos; +}; + +#define TPS65090_IRQ(_reg, _mask_pos)		\ +	{					\ +		.mask_reg	= (_reg),	\ +		.mask_pos	= (_mask_pos),	\ +	} + +static const struct tps65090_irq_data tps65090_irqs[] = { +	[0]		= TPS65090_IRQ(0, 0), +	[1]		= TPS65090_IRQ(0, 1), +	[2]		= TPS65090_IRQ(0, 2), +	[3]		= TPS65090_IRQ(0, 3), +	[4]		= TPS65090_IRQ(0, 4), +	[5]		= TPS65090_IRQ(0, 5), +	[6]		= TPS65090_IRQ(0, 6), +	[7]		= TPS65090_IRQ(0, 7), +	[8]		= TPS65090_IRQ(1, 0), +	[9]		= TPS65090_IRQ(1, 1), +	[10]		= TPS65090_IRQ(1, 2), +	[11]		= TPS65090_IRQ(1, 3), +	[12]		= TPS65090_IRQ(1, 4), +	[13]		= TPS65090_IRQ(1, 5), +	[14]		= TPS65090_IRQ(1, 6), +	[15]		= TPS65090_IRQ(1, 7), +}; + +static struct mfd_cell tps65090s[] = { +	{ +		.name = "tps65910-pmic", +	}, +	{ +		.name = "tps65910-regulator", +	}, +}; + +struct tps65090 { +	struct mutex		lock; +	struct device		*dev; +	struct i2c_client	*client; +	struct regmap		*rmap; +	struct irq_chip		irq_chip; +	struct mutex		irq_lock; +	int			irq_base; +	unsigned int		id; +}; + +int tps65090_write(struct device *dev, int reg, uint8_t val) +{ +	struct tps65090 *tps = dev_get_drvdata(dev); +	return regmap_write(tps->rmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65090_write); + +int tps65090_read(struct device *dev, int reg, uint8_t *val) +{ +	struct tps65090 *tps = dev_get_drvdata(dev); +	unsigned int temp_val; +	int ret; +	ret = regmap_read(tps->rmap, reg, &temp_val); +	if (!ret) +		*val = temp_val; +	return ret; +} +EXPORT_SYMBOL_GPL(tps65090_read); + +int tps65090_set_bits(struct device *dev, int reg, uint8_t bit_num) +{ +	struct tps65090 *tps = dev_get_drvdata(dev); +	return regmap_update_bits(tps->rmap, reg, BIT(bit_num), ~0u); +} +EXPORT_SYMBOL_GPL(tps65090_set_bits); + +int tps65090_clr_bits(struct device *dev, int reg, uint8_t bit_num) +{ +	struct tps65090 *tps = dev_get_drvdata(dev); +	return regmap_update_bits(tps->rmap, reg, BIT(bit_num), 0u); +} +EXPORT_SYMBOL_GPL(tps65090_clr_bits); + +static void tps65090_irq_lock(struct irq_data *data) +{ +	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); + +	mutex_lock(&tps65090->irq_lock); +} + +static void tps65090_irq_mask(struct irq_data *irq_data) +{ +	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); +	unsigned int __irq = irq_data->hwirq; +	const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; + +	tps65090_set_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), +		data->mask_pos); +} + +static void tps65090_irq_unmask(struct irq_data *irq_data) +{ +	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(irq_data); +	unsigned int __irq = irq_data->irq - tps65090->irq_base; +	const struct tps65090_irq_data *data = &tps65090_irqs[__irq]; + +	tps65090_clr_bits(tps65090->dev, (TPS65090_INT_MSK + data->mask_reg), +		data->mask_pos); +} + +static void tps65090_irq_sync_unlock(struct irq_data *data) +{ +	struct tps65090 *tps65090 = irq_data_get_irq_chip_data(data); + +	mutex_unlock(&tps65090->irq_lock); +} + +static irqreturn_t tps65090_irq(int irq, void *data) +{ +	struct tps65090 *tps65090 = data; +	int ret = 0; +	u8 status, mask; +	unsigned long int acks = 0; +	int i; + +	for (i = 0; i < NUM_INT_REG; i++) { +		ret = tps65090_read(tps65090->dev, TPS65090_INT_MSK + i, &mask); +		if (ret < 0) { +			dev_err(tps65090->dev, +				"failed to read mask reg [addr:%d]\n", +				TPS65090_INT_MSK + i); +			return IRQ_NONE; +		} +		ret = tps65090_read(tps65090->dev, TPS65090_INT_STS + i, +			&status); +		if (ret < 0) { +			dev_err(tps65090->dev, +				"failed to read status reg [addr:%d]\n", +				 TPS65090_INT_STS + i); +			return IRQ_NONE; +		} +		if (status) { +			/* Ack only those interrupts which are not masked */ +			status &= (~mask); +			ret = tps65090_write(tps65090->dev, +					TPS65090_INT_STS + i, status); +			if (ret < 0) { +				dev_err(tps65090->dev, +					"failed to write interrupt status\n"); +				return IRQ_NONE; +			} +			acks |= (status << (i * 8)); +		} +	} + +	for_each_set_bit(i, &acks, ARRAY_SIZE(tps65090_irqs)) +		handle_nested_irq(tps65090->irq_base + i); +	return acks ? IRQ_HANDLED : IRQ_NONE; +} + +static int __devinit tps65090_irq_init(struct tps65090 *tps65090, int irq, +	int irq_base) +{ +	int i, ret; + +	if (!irq_base) { +		dev_err(tps65090->dev, "IRQ base not set\n"); +		return -EINVAL; +	} + +	mutex_init(&tps65090->irq_lock); + +	for (i = 0; i < NUM_INT_REG; i++) +		tps65090_write(tps65090->dev, TPS65090_INT_MSK + i, 0xFF); + +	for (i = 0; i < NUM_INT_REG; i++) +		tps65090_write(tps65090->dev, TPS65090_INT_STS + i, 0xff); + +	tps65090->irq_base = irq_base; +	tps65090->irq_chip.name = "tps65090"; +	tps65090->irq_chip.irq_mask = tps65090_irq_mask; +	tps65090->irq_chip.irq_unmask = tps65090_irq_unmask; +	tps65090->irq_chip.irq_bus_lock = tps65090_irq_lock; +	tps65090->irq_chip.irq_bus_sync_unlock = tps65090_irq_sync_unlock; + +	for (i = 0; i < ARRAY_SIZE(tps65090_irqs); i++) { +		int __irq = i + tps65090->irq_base; +		irq_set_chip_data(__irq, tps65090); +		irq_set_chip_and_handler(__irq, &tps65090->irq_chip, +					 handle_simple_irq); +		irq_set_nested_thread(__irq, 1); +#ifdef CONFIG_ARM +		set_irq_flags(__irq, IRQF_VALID); +#endif +	} + +	ret = request_threaded_irq(irq, NULL, tps65090_irq, IRQF_ONESHOT, +				"tps65090", tps65090); +	if (!ret) { +		device_init_wakeup(tps65090->dev, 1); +		enable_irq_wake(irq); +	} + +	return ret; +} + +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ +	if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS)) +		return true; +	else +		return false; +} + +static const struct regmap_config tps65090_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.max_register = TOTAL_NUM_REG, +	.num_reg_defaults_raw = TOTAL_NUM_REG, +	.cache_type = REGCACHE_RBTREE, +	.volatile_reg = is_volatile_reg, +}; + +static int __devinit tps65090_i2c_probe(struct i2c_client *client, +					const struct i2c_device_id *id) +{ +	struct tps65090_platform_data *pdata = client->dev.platform_data; +	struct tps65090 *tps65090; +	int ret; + +	if (!pdata) { +		dev_err(&client->dev, "tps65090 requires platform data\n"); +		return -EINVAL; +	} + +	tps65090 = devm_kzalloc(&client->dev, sizeof(struct tps65090), +		GFP_KERNEL); +	if (tps65090 == NULL) +		return -ENOMEM; + +	tps65090->client = client; +	tps65090->dev = &client->dev; +	i2c_set_clientdata(client, tps65090); + +	mutex_init(&tps65090->lock); + +	if (client->irq) { +		ret = tps65090_irq_init(tps65090, client->irq, pdata->irq_base); +		if (ret) { +			dev_err(&client->dev, "IRQ init failed with err: %d\n", +				ret); +			goto err_exit; +		} +	} + +	tps65090->rmap = regmap_init_i2c(tps65090->client, +		&tps65090_regmap_config); +	if (IS_ERR(tps65090->rmap)) { +		dev_err(&client->dev, "regmap_init failed with err: %ld\n", +			PTR_ERR(tps65090->rmap)); +		goto err_irq_exit; +	}; + +	ret = mfd_add_devices(tps65090->dev, -1, tps65090s, +		ARRAY_SIZE(tps65090s), NULL, 0); +	if (ret) { +		dev_err(&client->dev, "add mfd devices failed with err: %d\n", +			ret); +		goto err_regmap_exit; +	} + +	return 0; + +err_regmap_exit: +	regmap_exit(tps65090->rmap); + +err_irq_exit: +	if (client->irq) +		free_irq(client->irq, tps65090); +err_exit: +	return ret; +} + +static int __devexit tps65090_i2c_remove(struct i2c_client *client) +{ +	struct tps65090 *tps65090 = i2c_get_clientdata(client); + +	mfd_remove_devices(tps65090->dev); +	regmap_exit(tps65090->rmap); +	if (client->irq) +		free_irq(client->irq, tps65090); + +	return 0; +} + +#ifdef CONFIG_PM +static int tps65090_i2c_suspend(struct i2c_client *client, pm_message_t state) +{ +	if (client->irq) +		disable_irq(client->irq); +	return 0; +} + +static int tps65090_i2c_resume(struct i2c_client *client) +{ +	if (client->irq) +		enable_irq(client->irq); +	return 0; +} +#endif + +static const struct i2c_device_id tps65090_id_table[] = { +	{ "tps65090", 0 }, +	{ }, +}; +MODULE_DEVICE_TABLE(i2c, tps65090_id_table); + +static struct i2c_driver tps65090_driver = { +	.driver	= { +		.name	= "tps65090", +		.owner	= THIS_MODULE, +	}, +	.probe		= tps65090_i2c_probe, +	.remove		= __devexit_p(tps65090_i2c_remove), +#ifdef CONFIG_PM +	.suspend	= tps65090_i2c_suspend, +	.resume		= tps65090_i2c_resume, +#endif +	.id_table	= tps65090_id_table, +}; + +static int __init tps65090_init(void) +{ +	return i2c_add_driver(&tps65090_driver); +} +subsys_initcall(tps65090_init); + +static void __exit tps65090_exit(void) +{ +	i2c_del_driver(&tps65090_driver); +} +module_exit(tps65090_exit); + +MODULE_DESCRIPTION("TPS65090 core driver"); +MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c new file mode 100644 index 00000000000..f7d854e4cc6 --- /dev/null +++ b/drivers/mfd/tps65217.c @@ -0,0 +1,242 @@ +/* + * tps65217.c + * + * TPS65217 chip family multi-function driver + * + * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation version 2. + * + * This program is distributed "as is" WITHOUT ANY WARRANTY of any + * kind, whether express or implied; without even the implied warranty + * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/device.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/err.h> + +#include <linux/mfd/core.h> +#include <linux/mfd/tps65217.h> + +/** + * tps65217_reg_read: Read a single tps65217 register. + * + * @tps: Device to read from. + * @reg: Register to read. + * @val: Contians the value + */ +int tps65217_reg_read(struct tps65217 *tps, unsigned int reg, +			unsigned int *val) +{ +	return regmap_read(tps->regmap, reg, val); +} +EXPORT_SYMBOL_GPL(tps65217_reg_read); + +/** + * tps65217_reg_write: Write a single tps65217 register. + * + * @tps65217: Device to write to. + * @reg: Register to write to. + * @val: Value to write. + * @level: Password protected level + */ +int tps65217_reg_write(struct tps65217 *tps, unsigned int reg, +			unsigned int val, unsigned int level) +{ +	int ret; +	unsigned int xor_reg_val; + +	switch (level) { +	case TPS65217_PROTECT_NONE: +		return regmap_write(tps->regmap, reg, val); +	case TPS65217_PROTECT_L1: +		xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK; +		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, +							xor_reg_val); +		if (ret < 0) +			return ret; + +		return regmap_write(tps->regmap, reg, val); +	case TPS65217_PROTECT_L2: +		xor_reg_val = reg ^ TPS65217_PASSWORD_REGS_UNLOCK; +		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, +							xor_reg_val); +		if (ret < 0) +			return ret; +		ret = regmap_write(tps->regmap, reg, val); +		if (ret < 0) +			return ret; +		ret = regmap_write(tps->regmap, TPS65217_REG_PASSWORD, +							xor_reg_val); +		if (ret < 0) +			return ret; +		return regmap_write(tps->regmap, reg, val); +	default: +		return -EINVAL; +	} +} +EXPORT_SYMBOL_GPL(tps65217_reg_write); + +/** + * tps65217_update_bits: Modify bits w.r.t mask, val and level. + * + * @tps65217: Device to write to. + * @reg: Register to read-write to. + * @mask: Mask. + * @val: Value to write. + * @level: Password protected level + */ +int tps65217_update_bits(struct tps65217 *tps, unsigned int reg, +		unsigned int mask, unsigned int val, unsigned int level) +{ +	int ret; +	unsigned int data; + +	ret = tps65217_reg_read(tps, reg, &data); +	if (ret) { +		dev_err(tps->dev, "Read from reg 0x%x failed\n", reg); +		return ret; +	} + +	data &= ~mask; +	data |= val & mask; + +	ret = tps65217_reg_write(tps, reg, data, level); +	if (ret) +		dev_err(tps->dev, "Write for reg 0x%x failed\n", reg); + +	return ret; +} + +int tps65217_set_bits(struct tps65217 *tps, unsigned int reg, +		unsigned int mask, unsigned int val, unsigned int level) +{ +	return tps65217_update_bits(tps, reg, mask, val, level); +} +EXPORT_SYMBOL_GPL(tps65217_set_bits); + +int tps65217_clear_bits(struct tps65217 *tps, unsigned int reg, +		unsigned int mask, unsigned int level) +{ +	return tps65217_update_bits(tps, reg, mask, 0, level); +} +EXPORT_SYMBOL_GPL(tps65217_clear_bits); + +static struct regmap_config tps65217_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +}; + +static int __devinit tps65217_probe(struct i2c_client *client, +				const struct i2c_device_id *ids) +{ +	struct tps65217 *tps; +	struct tps65217_board *pdata = client->dev.platform_data; +	int i, ret; +	unsigned int version; + +	tps = devm_kzalloc(&client->dev, sizeof(*tps), GFP_KERNEL); +	if (!tps) +		return -ENOMEM; + +	tps->pdata = pdata; +	tps->regmap = regmap_init_i2c(client, &tps65217_regmap_config); +	if (IS_ERR(tps->regmap)) { +		ret = PTR_ERR(tps->regmap); +		dev_err(tps->dev, "Failed to allocate register map: %d\n", +			ret); +		return ret; +	} + +	i2c_set_clientdata(client, tps); +	tps->dev = &client->dev; + +	ret = tps65217_reg_read(tps, TPS65217_REG_CHIPID, &version); +	if (ret < 0) { +		dev_err(tps->dev, "Failed to read revision" +					" register: %d\n", ret); +		goto err_regmap; +	} + +	dev_info(tps->dev, "TPS65217 ID %#x version 1.%d\n", +			(version & TPS65217_CHIPID_CHIP_MASK) >> 4, +			version & TPS65217_CHIPID_REV_MASK); + +	for (i = 0; i < TPS65217_NUM_REGULATOR; i++) { +		struct platform_device *pdev; + +		pdev = platform_device_alloc("tps65217-pmic", i); +		if (!pdev) { +			dev_err(tps->dev, "Cannot create regulator %d\n", i); +			continue; +		} + +		pdev->dev.parent = tps->dev; +		platform_device_add_data(pdev, &pdata->tps65217_init_data[i], +					sizeof(pdata->tps65217_init_data[i])); +		tps->regulator_pdev[i] = pdev; + +		platform_device_add(pdev); +	} + +	return 0; + +err_regmap: +	regmap_exit(tps->regmap); + +	return ret; +} + +static int __devexit tps65217_remove(struct i2c_client *client) +{ +	struct tps65217 *tps = i2c_get_clientdata(client); +	int i; + +	for (i = 0; i < TPS65217_NUM_REGULATOR; i++) +		platform_device_unregister(tps->regulator_pdev[i]); + +	regmap_exit(tps->regmap); + +	return 0; +} + +static const struct i2c_device_id tps65217_id_table[] = { +	{"tps65217", 0xF0}, +	{/* end of list */} +}; +MODULE_DEVICE_TABLE(i2c, tps65217_id_table); + +static struct i2c_driver tps65217_driver = { +	.driver		= { +		.name	= "tps65217", +	}, +	.id_table	= tps65217_id_table, +	.probe		= tps65217_probe, +	.remove		= __devexit_p(tps65217_remove), +}; + +static int __init tps65217_init(void) +{ +	return i2c_add_driver(&tps65217_driver); +} +subsys_initcall(tps65217_init); + +static void __exit tps65217_exit(void) +{ +	i2c_del_driver(&tps65217_driver); +} +module_exit(tps65217_exit); + +MODULE_AUTHOR("AnilKumar Ch <anilkumar@ti.com>"); +MODULE_DESCRIPTION("TPS65217 chip family multi-function driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/mfd/tps65910-irq.c b/drivers/mfd/tps65910-irq.c index 95c0d7978be..c9ed5c00a62 100644 --- a/drivers/mfd/tps65910-irq.c +++ b/drivers/mfd/tps65910-irq.c @@ -145,12 +145,23 @@ static void tps65910_irq_disable(struct irq_data *data)  	tps65910->irq_mask |= ( 1 << irq_to_tps65910_irq(tps65910, data->irq));  } +#ifdef CONFIG_PM_SLEEP +static int tps65910_irq_set_wake(struct irq_data *data, unsigned int enable) +{ +	struct tps65910 *tps65910 = irq_data_get_irq_chip_data(data); +	return irq_set_irq_wake(tps65910->chip_irq, enable); +} +#else +#define tps65910_irq_set_wake NULL +#endif +  static struct irq_chip tps65910_irq_chip = {  	.name = "tps65910",  	.irq_bus_lock = tps65910_irq_lock,  	.irq_bus_sync_unlock = tps65910_irq_sync_unlock,  	.irq_disable = tps65910_irq_disable,  	.irq_enable = tps65910_irq_enable, +	.irq_set_wake = tps65910_irq_set_wake,  };  int tps65910_irq_init(struct tps65910 *tps65910, int irq, diff --git a/drivers/mfd/tps65910.c b/drivers/mfd/tps65910.c index 4392f6bca15..bf2b25ebf2c 100644 --- a/drivers/mfd/tps65910.c +++ b/drivers/mfd/tps65910.c @@ -16,10 +16,12 @@  #include <linux/module.h>  #include <linux/moduleparam.h>  #include <linux/init.h> +#include <linux/err.h>  #include <linux/slab.h>  #include <linux/i2c.h>  #include <linux/gpio.h>  #include <linux/mfd/core.h> +#include <linux/regmap.h>  #include <linux/mfd/tps65910.h>  static struct mfd_cell tps65910s[] = { @@ -38,98 +40,55 @@ static struct mfd_cell tps65910s[] = {  static int tps65910_i2c_read(struct tps65910 *tps65910, u8 reg,  				  int bytes, void *dest)  { -	struct i2c_client *i2c = tps65910->i2c_client; -	struct i2c_msg xfer[2]; -	int ret; - -	/* Write register */ -	xfer[0].addr = i2c->addr; -	xfer[0].flags = 0; -	xfer[0].len = 1; -	xfer[0].buf = ® - -	/* Read data */ -	xfer[1].addr = i2c->addr; -	xfer[1].flags = I2C_M_RD; -	xfer[1].len = bytes; -	xfer[1].buf = dest; - -	ret = i2c_transfer(i2c->adapter, xfer, 2); -	if (ret == 2) -		ret = 0; -	else if (ret >= 0) -		ret = -EIO; - -	return ret; +	return regmap_bulk_read(tps65910->regmap, reg, dest, bytes);  }  static int tps65910_i2c_write(struct tps65910 *tps65910, u8 reg, -				   int bytes, void *src) +				  int bytes, void *src)  { -	struct i2c_client *i2c = tps65910->i2c_client; -	/* we add 1 byte for device register */ -	u8 msg[TPS65910_MAX_REGISTER + 1]; -	int ret; - -	if (bytes > TPS65910_MAX_REGISTER) -		return -EINVAL; - -	msg[0] = reg; -	memcpy(&msg[1], src, bytes); - -	ret = i2c_master_send(i2c, msg, bytes + 1); -	if (ret < 0) -		return ret; -	if (ret != bytes + 1) -		return -EIO; -	return 0; +	return regmap_bulk_write(tps65910->regmap, reg, src, bytes);  }  int tps65910_set_bits(struct tps65910 *tps65910, u8 reg, u8 mask)  { -	u8 data; -	int err; - -	mutex_lock(&tps65910->io_mutex); -	err = tps65910_i2c_read(tps65910, reg, 1, &data); -	if (err) { -		dev_err(tps65910->dev, "read from reg %x failed\n", reg); -		goto out; -	} - -	data |= mask; -	err = tps65910_i2c_write(tps65910, reg, 1, &data); -	if (err) -		dev_err(tps65910->dev, "write to reg %x failed\n", reg); - -out: -	mutex_unlock(&tps65910->io_mutex); -	return err; +	return regmap_update_bits(tps65910->regmap, reg, mask, mask);  }  EXPORT_SYMBOL_GPL(tps65910_set_bits);  int tps65910_clear_bits(struct tps65910 *tps65910, u8 reg, u8 mask)  { -	u8 data; -	int err; - -	mutex_lock(&tps65910->io_mutex); -	err = tps65910_i2c_read(tps65910, reg, 1, &data); -	if (err) { -		dev_err(tps65910->dev, "read from reg %x failed\n", reg); -		goto out; -	} +	return regmap_update_bits(tps65910->regmap, reg, mask, 0); +} +EXPORT_SYMBOL_GPL(tps65910_clear_bits); -	data &= ~mask; -	err = tps65910_i2c_write(tps65910, reg, 1, &data); -	if (err) -		dev_err(tps65910->dev, "write to reg %x failed\n", reg); +static bool is_volatile_reg(struct device *dev, unsigned int reg) +{ +	struct tps65910 *tps65910 = dev_get_drvdata(dev); -out: -	mutex_unlock(&tps65910->io_mutex); -	return err; +	/* +	 * Caching all regulator registers. +	 * All regualator register address range is same for +	 * TPS65910 and TPS65911 +	 */ +	if ((reg >= TPS65910_VIO) && (reg <= TPS65910_VDAC)) { +		/* Check for non-existing register */ +		if (tps65910_chip_id(tps65910) == TPS65910) +			if ((reg == TPS65911_VDDCTRL_OP) || +				(reg == TPS65911_VDDCTRL_SR)) +				return true; +		return false; +	} +	return true;  } -EXPORT_SYMBOL_GPL(tps65910_clear_bits); + +static const struct regmap_config tps65910_regmap_config = { +	.reg_bits = 8, +	.val_bits = 8, +	.volatile_reg = is_volatile_reg, +	.max_register = TPS65910_MAX_REGISTER, +	.num_reg_defaults_raw = TPS65910_MAX_REGISTER, +	.cache_type = REGCACHE_RBTREE, +};  static int tps65910_i2c_probe(struct i2c_client *i2c,  			    const struct i2c_device_id *id) @@ -161,6 +120,13 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,  	tps65910->write = tps65910_i2c_write;  	mutex_init(&tps65910->io_mutex); +	tps65910->regmap = regmap_init_i2c(i2c, &tps65910_regmap_config); +	if (IS_ERR(tps65910->regmap)) { +		ret = PTR_ERR(tps65910->regmap); +		dev_err(&i2c->dev, "regmap initialization failed: %d\n", ret); +		goto regmap_err; +	} +  	ret = mfd_add_devices(tps65910->dev, -1,  			      tps65910s, ARRAY_SIZE(tps65910s),  			      NULL, 0); @@ -178,6 +144,8 @@ static int tps65910_i2c_probe(struct i2c_client *i2c,  	return ret;  err: +	regmap_exit(tps65910->regmap); +regmap_err:  	kfree(tps65910);  	kfree(init_data);  	return ret; @@ -189,6 +157,7 @@ static int tps65910_i2c_remove(struct i2c_client *i2c)  	tps65910_irq_exit(tps65910);  	mfd_remove_devices(tps65910->dev); +	regmap_exit(tps65910->regmap);  	kfree(tps65910);  	return 0; diff --git a/drivers/mfd/twl-core.c b/drivers/mfd/twl-core.c index 806680d1bbb..7c2267e71f8 100644 --- a/drivers/mfd/twl-core.c +++ b/drivers/mfd/twl-core.c @@ -46,9 +46,7 @@  #include <linux/i2c.h>  #include <linux/i2c/twl.h> -#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3) -#include <plat/cpu.h> -#endif +#include "twl-core.h"  /*   * The TWL4030 "Triton 2" is one of a family of a multi-function "Power @@ -116,8 +114,8 @@  #define twl_has_watchdog()        false  #endif -#if defined(CONFIG_MFD_TWL4030_AUDIO) || defined(CONFIG_MFD_TWL4030_AUDIO_MODULE) ||\ -	defined(CONFIG_TWL6040_CORE) || defined(CONFIG_TWL6040_CORE_MODULE) +#if defined(CONFIG_MFD_TWL4030_AUDIO) || \ +	defined(CONFIG_MFD_TWL4030_AUDIO_MODULE)  #define twl_has_codec()	true  #else  #define twl_has_codec()	false @@ -147,12 +145,10 @@  #define SUB_CHIP_ID1 1  #define SUB_CHIP_ID2 2  #define SUB_CHIP_ID3 3 +#define SUB_CHIP_ID_INVAL 0xff  #define TWL_MODULE_LAST TWL4030_MODULE_LAST -#define TWL4030_NR_IRQS    34 /* core:8, power:8, gpio: 18 */ -#define TWL6030_NR_IRQS    20 -  /* Base Address defns for twl4030_map[] */  /* subchip/slave 0 - USB ID */ @@ -314,7 +310,7 @@ static struct twl_mapping twl6030_map[] = {  	 * so they continue to match the order in this table.  	 */  	{ SUB_CHIP_ID1, TWL6030_BASEADD_USB }, -	{ SUB_CHIP_ID3, TWL6030_BASEADD_AUDIO }, +	{ SUB_CHIP_ID_INVAL, TWL6030_BASEADD_AUDIO },  	{ SUB_CHIP_ID2, TWL6030_BASEADD_DIEID },  	{ SUB_CHIP_ID2, TWL6030_BASEADD_RSV },  	{ SUB_CHIP_ID1, TWL6030_BASEADD_PIH }, @@ -376,6 +372,11 @@ int twl_i2c_write(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)  		return -EPERM;  	}  	sid = twl_map[mod_no].sid; +	if (unlikely(sid == SUB_CHIP_ID_INVAL)) { +		pr_err("%s: module %d is not part of the pmic\n", +		       DRIVER_NAME, mod_no); +		return -EINVAL; +	}  	twl = &twl_modules[sid];  	mutex_lock(&twl->xfer_lock); @@ -433,6 +434,11 @@ int twl_i2c_read(u8 mod_no, u8 *value, u8 reg, unsigned num_bytes)  		return -EPERM;  	}  	sid = twl_map[mod_no].sid; +	if (unlikely(sid == SUB_CHIP_ID_INVAL)) { +		pr_err("%s: module %d is not part of the pmic\n", +		       DRIVER_NAME, mod_no); +		return -EINVAL; +	}  	twl = &twl_modules[sid];  	mutex_lock(&twl->xfer_lock); @@ -663,7 +669,8 @@ add_regulator(int num, struct regulator_init_data *pdata,   */  static int -add_children(struct twl4030_platform_data *pdata, unsigned long features) +add_children(struct twl4030_platform_data *pdata, unsigned irq_base, +		unsigned long features)  {  	struct device	*child;  	unsigned sub_chip_id; @@ -671,7 +678,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  	if (twl_has_gpio() && pdata->gpio) {  		child = add_child(SUB_CHIP_ID1, "twl4030_gpio",  				pdata->gpio, sizeof(*pdata->gpio), -				false, pdata->irq_base + GPIO_INTR_OFFSET, 0); +				false, irq_base + GPIO_INTR_OFFSET, 0);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -679,7 +686,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  	if (twl_has_keypad() && pdata->keypad) {  		child = add_child(SUB_CHIP_ID2, "twl4030_keypad",  				pdata->keypad, sizeof(*pdata->keypad), -				true, pdata->irq_base + KEYPAD_INTR_OFFSET, 0); +				true, irq_base + KEYPAD_INTR_OFFSET, 0);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -687,7 +694,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  	if (twl_has_madc() && pdata->madc) {  		child = add_child(2, "twl4030_madc",  				pdata->madc, sizeof(*pdata->madc), -				true, pdata->irq_base + MADC_INTR_OFFSET, 0); +				true, irq_base + MADC_INTR_OFFSET, 0);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -703,7 +710,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  		sub_chip_id = twl_map[TWL_MODULE_RTC].sid;  		child = add_child(sub_chip_id, "twl_rtc",  				NULL, 0, -				true, pdata->irq_base + RTC_INTR_OFFSET, 0); +				true, irq_base + RTC_INTR_OFFSET, 0);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -756,8 +763,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  				pdata->usb, sizeof(*pdata->usb),  				true,  				/* irq0 = USB_PRES, irq1 = USB */ -				pdata->irq_base + USB_PRES_INTR_OFFSET, -				pdata->irq_base + USB_INTR_OFFSET); +				irq_base + USB_PRES_INTR_OFFSET, +				irq_base + USB_INTR_OFFSET);  		if (IS_ERR(child))  			return PTR_ERR(child); @@ -805,8 +812,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  			pdata->usb, sizeof(*pdata->usb),  			true,  			/* irq1 = VBUS_PRES, irq0 = USB ID */ -			pdata->irq_base + USBOTG_INTR_OFFSET, -			pdata->irq_base + USB_PRES_INTR_OFFSET); +			irq_base + USBOTG_INTR_OFFSET, +			irq_base + USB_PRES_INTR_OFFSET);  		if (IS_ERR(child))  			return PTR_ERR(child); @@ -833,7 +840,7 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  	if (twl_has_pwrbutton() && twl_class_is_4030()) {  		child = add_child(1, "twl4030_pwrbutton", -				NULL, 0, true, pdata->irq_base + 8 + 0, 0); +				NULL, 0, true, irq_base + 8 + 0, 0);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -847,15 +854,6 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  			return PTR_ERR(child);  	} -	if (twl_has_codec() && pdata->audio && twl_class_is_6030()) { -		sub_chip_id = twl_map[TWL_MODULE_AUDIO_VOICE].sid; -		child = add_child(sub_chip_id, "twl6040", -				pdata->audio, sizeof(*pdata->audio), -				false, 0, 0); -		if (IS_ERR(child)) -			return PTR_ERR(child); -	} -  	/* twl4030 regulators */  	if (twl_has_regulator() && twl_class_is_4030()) {  		child = add_regulator(TWL4030_REG_VPLL1, pdata->vpll1, @@ -1092,8 +1090,8 @@ add_children(struct twl4030_platform_data *pdata, unsigned long features)  		child = add_child(3, "twl4030_bci",  				pdata->bci, sizeof(*pdata->bci), false,  				/* irq0 = CHG_PRES, irq1 = BCI */ -				pdata->irq_base + BCI_PRES_INTR_OFFSET, -				pdata->irq_base + BCI_INTR_OFFSET); +				irq_base + BCI_PRES_INTR_OFFSET, +				irq_base + BCI_INTR_OFFSET);  		if (IS_ERR(child))  			return PTR_ERR(child);  	} @@ -1193,26 +1191,24 @@ static void clocks_init(struct device *dev,  /*----------------------------------------------------------------------*/ -int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl4030_exit_irq(void); -int twl4030_init_chip_irq(const char *chip); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); -int twl6030_exit_irq(void);  static int twl_remove(struct i2c_client *client)  { -	unsigned i; +	unsigned i, num_slaves;  	int status; -	if (twl_class_is_4030()) +	if (twl_class_is_4030()) {  		status = twl4030_exit_irq(); -	else +		num_slaves = TWL_NUM_SLAVES; +	} else {  		status = twl6030_exit_irq(); +		num_slaves = TWL_NUM_SLAVES - 1; +	}  	if (status < 0)  		return status; -	for (i = 0; i < TWL_NUM_SLAVES; i++) { +	for (i = 0; i < num_slaves; i++) {  		struct twl_client	*twl = &twl_modules[i];  		if (twl->client && twl->client != client) @@ -1223,20 +1219,15 @@ static int twl_remove(struct i2c_client *client)  	return 0;  } -/* NOTE:  this driver only handles a single twl4030/tps659x0 chip */ +/* NOTE: This driver only handles a single twl4030/tps659x0 chip */  static int __devinit  twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  { -	int				status; -	unsigned			i;  	struct twl4030_platform_data	*pdata = client->dev.platform_data;  	struct device_node		*node = client->dev.of_node; -	u8 temp; -	int ret = 0; -	int nr_irqs = TWL4030_NR_IRQS; - -	if ((id->driver_data) & TWL6030_CLASS) -		nr_irqs = TWL6030_NR_IRQS; +	int				irq_base = 0; +	int				status; +	unsigned			i, num_slaves;  	if (node && !pdata) {  		/* @@ -1255,17 +1246,6 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		return -EINVAL;  	} -	status = irq_alloc_descs(-1, pdata->irq_base, nr_irqs, 0); -	if (IS_ERR_VALUE(status)) { -		dev_err(&client->dev, "Fail to allocate IRQ descs\n"); -		return status; -	} - -	pdata->irq_base = status; -	pdata->irq_end = pdata->irq_base + nr_irqs; -	irq_domain_add_legacy(node, nr_irqs, pdata->irq_base, 0, -			      &irq_domain_simple_ops, NULL); -  	if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C) == 0) {  		dev_dbg(&client->dev, "can't talk I2C?\n");  		return -EIO; @@ -1276,13 +1256,23 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		return -EBUSY;  	} -	for (i = 0; i < TWL_NUM_SLAVES; i++) { -		struct twl_client	*twl = &twl_modules[i]; +	if ((id->driver_data) & TWL6030_CLASS) { +		twl_id = TWL6030_CLASS_ID; +		twl_map = &twl6030_map[0]; +		num_slaves = TWL_NUM_SLAVES - 1; +	} else { +		twl_id = TWL4030_CLASS_ID; +		twl_map = &twl4030_map[0]; +		num_slaves = TWL_NUM_SLAVES; +	} + +	for (i = 0; i < num_slaves; i++) { +		struct twl_client *twl = &twl_modules[i];  		twl->address = client->addr + i; -		if (i == 0) +		if (i == 0) {  			twl->client = client; -		else { +		} else {  			twl->client = i2c_new_dummy(client->adapter,  					twl->address);  			if (!twl->client) { @@ -1294,22 +1284,16 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		}  		mutex_init(&twl->xfer_lock);  	} +  	inuse = true; -	if ((id->driver_data) & TWL6030_CLASS) { -		twl_id = TWL6030_CLASS_ID; -		twl_map = &twl6030_map[0]; -	} else { -		twl_id = TWL4030_CLASS_ID; -		twl_map = &twl4030_map[0]; -	}  	/* setup clock framework */  	clocks_init(&client->dev, pdata->clock);  	/* read TWL IDCODE Register */  	if (twl_id == TWL4030_CLASS_ID) { -		ret = twl_read_idcode_register(); -		WARN(ret < 0, "Error: reading twl_idcode register value\n"); +		status = twl_read_idcode_register(); +		WARN(status < 0, "Error: reading twl_idcode register value\n");  	}  	/* load power event scripts */ @@ -1317,31 +1301,31 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  		twl4030_power_init(pdata->power);  	/* Maybe init the T2 Interrupt subsystem */ -	if (client->irq -			&& pdata->irq_base -			&& pdata->irq_end > pdata->irq_base) { +	if (client->irq) {  		if (twl_class_is_4030()) {  			twl4030_init_chip_irq(id->name); -			status = twl4030_init_irq(client->irq, pdata->irq_base, -			pdata->irq_end); +			irq_base = twl4030_init_irq(&client->dev, client->irq);  		} else { -			status = twl6030_init_irq(client->irq, pdata->irq_base, -			pdata->irq_end); +			irq_base = twl6030_init_irq(&client->dev, client->irq);  		} -		if (status < 0) +		if (irq_base < 0) { +			status = irq_base;  			goto fail; +		}  	} -	/* Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface. +	/* +	 * Disable TWL4030/TWL5030 I2C Pull-up on I2C1 and I2C4(SR) interface.  	 * Program I2C_SCL_CTRL_PU(bit 0)=0, I2C_SDA_CTRL_PU (bit 2)=0,  	 * SR_I2C_SCL_CTRL_PU(bit 4)=0 and SR_I2C_SDA_CTRL_PU(bit 6)=0.  	 */ -  	if (twl_class_is_4030()) { +		u8 temp; +  		twl_i2c_read_u8(TWL4030_MODULE_INTBR, &temp, REG_GPPUPDCTR1);  		temp &= ~(SR_I2C_SDA_CTRL_PU | SR_I2C_SCL_CTRL_PU | \ -		I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU); +			I2C_SDA_CTRL_PU | I2C_SCL_CTRL_PU);  		twl_i2c_write_u8(TWL4030_MODULE_INTBR, temp, REG_GPPUPDCTR1);  	} @@ -1349,11 +1333,12 @@ twl_probe(struct i2c_client *client, const struct i2c_device_id *id)  	if (node)  		status = of_platform_populate(node, NULL, NULL, &client->dev);  	if (status) -		status = add_children(pdata, id->driver_data); +		status = add_children(pdata, irq_base, id->driver_data);  fail:  	if (status < 0)  		twl_remove(client); +  	return status;  } diff --git a/drivers/mfd/twl-core.h b/drivers/mfd/twl-core.h index 8c50a556e98..6ff99dce714 100644 --- a/drivers/mfd/twl-core.h +++ b/drivers/mfd/twl-core.h @@ -1,9 +1,9 @@  #ifndef __TWL_CORE_H__  #define __TWL_CORE_H__ -extern int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); +extern int twl6030_init_irq(struct device *dev, int irq_num);  extern int twl6030_exit_irq(void); -extern int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end); +extern int twl4030_init_irq(struct device *dev, int irq_num);  extern int twl4030_exit_irq(void);  extern int twl4030_init_chip_irq(const char *chip); diff --git a/drivers/mfd/twl4030-irq.c b/drivers/mfd/twl4030-irq.c index b69bb517b10..5d656e81435 100644 --- a/drivers/mfd/twl4030-irq.c +++ b/drivers/mfd/twl4030-irq.c @@ -28,10 +28,12 @@   */  #include <linux/init.h> +#include <linux/export.h>  #include <linux/interrupt.h>  #include <linux/irq.h>  #include <linux/slab.h> - +#include <linux/of.h> +#include <linux/irqdomain.h>  #include <linux/i2c/twl.h>  #include "twl-core.h" @@ -53,13 +55,14 @@   *	base + 8  .. base + 15	SIH for PWR_INT   *	base + 16 .. base + 33	SIH for GPIO   */ +#define TWL4030_CORE_NR_IRQS	8 +#define TWL4030_PWR_NR_IRQS	8  /* PIH register offsets */  #define REG_PIH_ISR_P1			0x01  #define REG_PIH_ISR_P2			0x02  #define REG_PIH_SIR			0x03	/* for testing */ -  /* Linux could (eventually) use either IRQ line */  static int irq_line; @@ -111,7 +114,8 @@ static int nr_sih_modules;  #define TWL4030_MODULE_INT_PWR		TWL4030_MODULE_INT -/* Order in this table matches order in PIH_ISR.  That is, +/* + * Order in this table matches order in PIH_ISR.  That is,   * BIT(n) in PIH_ISR is sih_modules[n].   */  /* sih_modules_twl4030 is used both in twl4030 and twl5030 */ @@ -288,7 +292,6 @@ static unsigned twl4030_irq_base;   */  static irqreturn_t handle_twl4030_pih(int irq, void *devid)  { -	int		module_irq;  	irqreturn_t	ret;  	u8		pih_isr; @@ -299,16 +302,18 @@ static irqreturn_t handle_twl4030_pih(int irq, void *devid)  		return IRQ_NONE;  	} -	/* these handlers deal with the relevant SIH irq status */ -	for (module_irq = twl4030_irq_base; -			pih_isr; -			pih_isr >>= 1, module_irq++) { -		if (pih_isr & 0x1) -			handle_nested_irq(module_irq); +	while (pih_isr) { +		unsigned long	pending = __ffs(pih_isr); +		unsigned int	irq; + +		pih_isr &= ~BIT(pending); +		irq = pending + twl4030_irq_base; +		handle_nested_irq(irq);  	}  	return IRQ_HANDLED;  } +  /*----------------------------------------------------------------------*/  /* @@ -337,7 +342,6 @@ static int twl4030_init_sih_modules(unsigned line)  	memset(buf, 0xff, sizeof buf);  	sih = sih_modules;  	for (i = 0; i < nr_sih_modules; i++, sih++) { -  		/* skip USB -- it's funky */  		if (!sih->bytes_ixr)  			continue; @@ -352,7 +356,8 @@ static int twl4030_init_sih_modules(unsigned line)  			pr_err("twl4030: err %d initializing %s %s\n",  					status, sih->name, "IMR"); -		/* Maybe disable "exclusive" mode; buffer second pending irq; +		/* +		 * Maybe disable "exclusive" mode; buffer second pending irq;  		 * set Clear-On-Read (COR) bit.  		 *  		 * NOTE that sometimes COR polarity is documented as being @@ -382,7 +387,8 @@ static int twl4030_init_sih_modules(unsigned line)  		if (sih->irq_lines <= line)  			continue; -		/* Clear pending interrupt status.  Either the read was +		/* +		 * Clear pending interrupt status.  Either the read was  		 * enough, or we need to write those bits.  Repeat, in  		 * case an IRQ is pending (PENDDIS=0) ... that's not  		 * uncommon with PWR_INT.PWRON. @@ -398,7 +404,8 @@ static int twl4030_init_sih_modules(unsigned line)  				status = twl_i2c_write(sih->module, buf,  					sih->mask[line].isr_offset,  					sih->bytes_ixr); -			/* else COR=1 means read sufficed. +			/* +			 * else COR=1 means read sufficed.  			 * (for most SIH modules...)  			 */  		} @@ -410,7 +417,8 @@ static int twl4030_init_sih_modules(unsigned line)  static inline void activate_irq(int irq)  {  #ifdef CONFIG_ARM -	/* ARM requires an extra step to clear IRQ_NOREQUEST, which it +	/* +	 * ARM requires an extra step to clear IRQ_NOREQUEST, which it  	 * sets on behalf of every irq_chip.  Also sets IRQ_NOPROBE.  	 */  	set_irq_flags(irq, IRQF_VALID); @@ -620,33 +628,24 @@ static irqreturn_t handle_twl4030_sih(int irq, void *data)  	return IRQ_HANDLED;  } -static unsigned twl4030_irq_next; - -/* returns the first IRQ used by this SIH bank, - * or negative errno - */ -int twl4030_sih_setup(int module) +/* returns the first IRQ used by this SIH bank, or negative errno */ +int twl4030_sih_setup(struct device *dev, int module, int irq_base)  {  	int			sih_mod;  	const struct sih	*sih = NULL;  	struct sih_agent	*agent;  	int			i, irq;  	int			status = -EINVAL; -	unsigned		irq_base = twl4030_irq_next;  	/* only support modules with standard clear-on-read for now */ -	for (sih_mod = 0, sih = sih_modules; -			sih_mod < nr_sih_modules; +	for (sih_mod = 0, sih = sih_modules; sih_mod < nr_sih_modules;  			sih_mod++, sih++) {  		if (sih->module == module && sih->set_cor) { -			if (!WARN((irq_base + sih->bits) > NR_IRQS, -					"irq %d for %s too big\n", -					irq_base + sih->bits, -					sih->name)) -				status = 0; +			status = 0;  			break;  		}  	} +  	if (status < 0)  		return status; @@ -654,8 +653,6 @@ int twl4030_sih_setup(int module)  	if (!agent)  		return -ENOMEM; -	status = 0; -  	agent->irq_base = irq_base;  	agent->sih = sih;  	agent->imr = ~0; @@ -671,8 +668,6 @@ int twl4030_sih_setup(int module)  		activate_irq(irq);  	} -	twl4030_irq_next += i; -  	/* replace generic PIH handler (handle_simple_irq) */  	irq = sih_mod + twl4030_irq_base;  	irq_set_handler_data(irq, agent); @@ -680,26 +675,43 @@ int twl4030_sih_setup(int module)  	status = request_threaded_irq(irq, NULL, handle_twl4030_sih, 0,  				      agent->irq_name ?: sih->name, NULL); -	pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", sih->name, -			irq, irq_base, twl4030_irq_next - 1); +	dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", sih->name, +			irq, irq_base, irq_base + i - 1);  	return status < 0 ? status : irq_base;  }  /* FIXME need a call to reverse twl4030_sih_setup() ... */ -  /*----------------------------------------------------------------------*/  /* FIXME pass in which interrupt line we'll use ... */  #define twl_irq_line	0 -int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) +int twl4030_init_irq(struct device *dev, int irq_num)  {  	static struct irq_chip	twl4030_irq_chip; +	int			status, i; +	int			irq_base, irq_end, nr_irqs; +	struct			device_node *node = dev->of_node; -	int			status; -	int			i; +	/* +	 * TWL core and pwr interrupts must be contiguous because +	 * the hwirqs numbers are defined contiguously from 1 to 15. +	 * Create only one domain for both. +	 */ +	nr_irqs = TWL4030_PWR_NR_IRQS + TWL4030_CORE_NR_IRQS; + +	irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); +	if (IS_ERR_VALUE(irq_base)) { +		dev_err(dev, "Fail to allocate IRQ descs\n"); +		return irq_base; +	} + +	irq_domain_add_legacy(node, nr_irqs, irq_base, 0, +			      &irq_domain_simple_ops, NULL); + +	irq_end = irq_base + TWL4030_CORE_NR_IRQS;  	/*  	 * Mask and clear all TWL4030 interrupts since initially we do @@ -711,7 +723,8 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)  	twl4030_irq_base = irq_base; -	/* install an irq handler for each of the SIH modules; +	/* +	 * Install an irq handler for each of the SIH modules;  	 * clone dummy irq_chip since PIH can't *do* anything  	 */  	twl4030_irq_chip = dummy_irq_chip; @@ -725,14 +738,14 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)  		irq_set_nested_thread(i, 1);  		activate_irq(i);  	} -	twl4030_irq_next = i; -	pr_info("twl4030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", -			irq_num, irq_base, twl4030_irq_next - 1); + +	dev_info(dev, "%s (irq %d) chaining IRQs %d..%d\n", "PIH", +			irq_num, irq_base, irq_end);  	/* ... and the PWR_INT module ... */ -	status = twl4030_sih_setup(TWL4030_MODULE_INT); +	status = twl4030_sih_setup(dev, TWL4030_MODULE_INT, irq_end);  	if (status < 0) { -		pr_err("twl4030: sih_setup PWR INT --> %d\n", status); +		dev_err(dev, "sih_setup PWR INT --> %d\n", status);  		goto fail;  	} @@ -741,11 +754,11 @@ int twl4030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)  				      IRQF_ONESHOT,  				      "TWL4030-PIH", NULL);  	if (status < 0) { -		pr_err("twl4030: could not claim irq%d: %d\n", irq_num, status); +		dev_err(dev, "could not claim irq%d: %d\n", irq_num, status);  		goto fail_rqirq;  	} -	return status; +	return irq_base;  fail_rqirq:  	/* clean up twl4030_sih_setup */  fail: diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c index c6b456ad734..b76902f1e44 100644 --- a/drivers/mfd/twl6030-irq.c +++ b/drivers/mfd/twl6030-irq.c @@ -39,6 +39,8 @@  #include <linux/i2c/twl.h>  #include <linux/platform_device.h>  #include <linux/suspend.h> +#include <linux/of.h> +#include <linux/irqdomain.h>  #include "twl-core.h" @@ -51,8 +53,8 @@   *   * We set up IRQs starting at a platform-specified base. An interrupt map table,   * specifies mapping between interrupt number and the associated module. - *   */ +#define TWL6030_NR_IRQS    20  static int twl6030_interrupt_mapping[24] = {  	PWR_INTR_OFFSET,	/* Bit 0	PWRON			*/ @@ -185,8 +187,17 @@ static int twl6030_irq_thread(void *data)  			}  		local_irq_enable();  		} -		ret = twl_i2c_write(TWL_MODULE_PIH, sts.bytes, -				REG_INT_STS_A, 3); /* clear INT_STS_A */ + +		/* +		 * NOTE: +		 * Simulation confirms that documentation is wrong w.r.t the +		 * interrupt status clear operation. A single *byte* write to +		 * any one of STS_A to STS_C register results in all three +		 * STS registers being reset. Since it does not matter which +		 * value is written, all three registers are cleared on a +		 * single byte write, so we just use 0x0 to clear. +		 */ +		ret = twl_i2c_write_u8(TWL_MODULE_PIH, 0x00, REG_INT_STS_A);  		if (ret)  			pr_warning("twl6030: I2C error in clearing PIH ISR\n"); @@ -227,7 +238,7 @@ static inline void activate_irq(int irq)  #endif  } -int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) +static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)  {  	if (on)  		atomic_inc(&twl6030_wakeirqs); @@ -237,11 +248,6 @@ int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)  	return 0;  } -/*----------------------------------------------------------------------*/ - -static unsigned twl6030_irq_next; - -/*----------------------------------------------------------------------*/  int twl6030_interrupt_unmask(u8 bit_mask, u8 offset)  {  	int ret; @@ -311,7 +317,8 @@ int twl6030_mmc_card_detect_config(void)  									ret);  		return ret;  	} -	return 0; + +	return twl6030_irq_base + MMCDETECT_INTR_OFFSET;  }  EXPORT_SYMBOL(twl6030_mmc_card_detect_config); @@ -340,29 +347,44 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)  }  EXPORT_SYMBOL(twl6030_mmc_card_detect); -int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end) +int twl6030_init_irq(struct device *dev, int irq_num)  { - -	int	status = 0; -	int	i; +	struct			device_node *node = dev->of_node; +	int			nr_irqs, irq_base, irq_end;  	struct task_struct	*task; -	int ret; -	u8 mask[4]; +	static struct irq_chip  twl6030_irq_chip; +	int			status = 0; +	int			i; +	u8			mask[4]; + +	nr_irqs = TWL6030_NR_IRQS; + +	irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0); +	if (IS_ERR_VALUE(irq_base)) { +		dev_err(dev, "Fail to allocate IRQ descs\n"); +		return irq_base; +	} + +	irq_domain_add_legacy(node, nr_irqs, irq_base, 0, +			      &irq_domain_simple_ops, NULL); + +	irq_end = irq_base + nr_irqs; -	static struct irq_chip	twl6030_irq_chip;  	mask[1] = 0xFF;  	mask[2] = 0xFF;  	mask[3] = 0xFF; -	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], -			REG_INT_MSK_LINE_A, 3); /* MASK ALL INT LINES */ -	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], -			REG_INT_MSK_STS_A, 3); /* MASK ALL INT STS */ -	ret = twl_i2c_write(TWL_MODULE_PIH, &mask[0], -			REG_INT_STS_A, 3); /* clear INT_STS_A,B,C */ + +	/* mask all int lines */ +	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_LINE_A, 3); +	/* mask all int sts */ +	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_MSK_STS_A, 3); +	/* clear INT_STS_A,B,C */ +	twl_i2c_write(TWL_MODULE_PIH, &mask[0], REG_INT_STS_A, 3);  	twl6030_irq_base = irq_base; -	/* install an irq handler for each of the modules; +	/* +	 * install an irq handler for each of the modules;  	 * clone dummy irq_chip since PIH can't *do* anything  	 */  	twl6030_irq_chip = dummy_irq_chip; @@ -377,30 +399,29 @@ int twl6030_init_irq(int irq_num, unsigned irq_base, unsigned irq_end)  		activate_irq(i);  	} -	twl6030_irq_next = i; -	pr_info("twl6030: %s (irq %d) chaining IRQs %d..%d\n", "PIH", -			irq_num, irq_base, twl6030_irq_next - 1); +	dev_info(dev, "PIH (irq %d) chaining IRQs %d..%d\n", +			irq_num, irq_base, irq_end);  	/* install an irq handler to demultiplex the TWL6030 interrupt */  	init_completion(&irq_event); -	status = request_irq(irq_num, handle_twl6030_pih, 0, -				"TWL6030-PIH", &irq_event); +	status = request_irq(irq_num, handle_twl6030_pih, 0, "TWL6030-PIH", +			     &irq_event);  	if (status < 0) { -		pr_err("twl6030: could not claim irq%d: %d\n", irq_num, status); +		dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);  		goto fail_irq;  	}  	task = kthread_run(twl6030_irq_thread, (void *)irq_num, "twl6030-irq");  	if (IS_ERR(task)) { -		pr_err("twl6030: could not create irq %d thread!\n", irq_num); +		dev_err(dev, "could not create irq %d thread!\n", irq_num);  		status = PTR_ERR(task);  		goto fail_kthread;  	}  	twl_irq = irq_num;  	register_pm_notifier(&twl6030_irq_pm_notifier_block); -	return status; +	return irq_base;  fail_kthread:  	free_irq(irq_num, &irq_event); @@ -408,6 +429,7 @@ fail_kthread:  fail_irq:  	for (i = irq_base; i < irq_end; i++)  		irq_set_chip_and_handler(i, NULL, NULL); +  	return status;  } diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index 745c8794566..4bceee98f0a 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -89,7 +89,7 @@ static const struct spi_device_id wm831x_spi_ids[] = {  	{ "wm8326", WM8326 },  	{ },  }; -MODULE_DEVICE_TABLE(spi, wm831x_spi_id); +MODULE_DEVICE_TABLE(spi, wm831x_spi_ids);  static struct spi_driver wm831x_spi_driver = {  	.driver = { diff --git a/drivers/mfd/wm8400-core.c b/drivers/mfd/wm8400-core.c index 237764ae5f9..1189a17f0f2 100644 --- a/drivers/mfd/wm8400-core.c +++ b/drivers/mfd/wm8400-core.c @@ -271,8 +271,7 @@ static int wm8400_init(struct wm8400 *wm8400,  		return -EIO;  	}  	if (i != reg_data[WM8400_RESET_ID].default_val) { -		dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", -			reg); +		dev_err(wm8400->dev, "Device is not a WM8400, ID is %x\n", i);  		return -ENODEV;  	} diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 98733d408fe..9d7ca1e978f 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -639,7 +639,7 @@ static __devinit int wm8994_device_init(struct wm8994 *wm8994, int irq)  	}  	pm_runtime_enable(wm8994->dev); -	pm_runtime_resume(wm8994->dev); +	pm_runtime_idle(wm8994->dev);  	return 0; diff --git a/drivers/mfd/wm8994-regmap.c b/drivers/mfd/wm8994-regmap.c index 7605b609545..bfd25af6ecb 100644 --- a/drivers/mfd/wm8994-regmap.c +++ b/drivers/mfd/wm8994-regmap.c @@ -20,7 +20,6 @@  #include "wm8994.h"  static struct reg_default wm1811_defaults[] = { -	{ 0x0000, 0x1811 },    /* R0    - Software Reset */  	{ 0x0001, 0x0000 },    /* R1    - Power Management (1) */  	{ 0x0002, 0x6000 },    /* R2    - Power Management (2) */  	{ 0x0003, 0x0000 },    /* R3    - Power Management (3) */ @@ -61,7 +60,7 @@ static struct reg_default wm1811_defaults[] = {  	{ 0x0036, 0x0000 },    /* R54   - Speaker Mixer */  	{ 0x0037, 0x0000 },    /* R55   - Additional Control */  	{ 0x0038, 0x0000 },    /* R56   - AntiPOP (1) */ -	{ 0x0039, 0x0180 },    /* R57   - AntiPOP (2) */ +	{ 0x0039, 0x0000 },    /* R57   - AntiPOP (2) */  	{ 0x003B, 0x000D },    /* R59   - LDO 1 */  	{ 0x003C, 0x0003 },    /* R60   - LDO 2 */  	{ 0x003D, 0x0039 },    /* R61   - MICBIAS1 */ @@ -69,16 +68,12 @@ static struct reg_default wm1811_defaults[] = {  	{ 0x004C, 0x1F25 },    /* R76   - Charge Pump (1) */  	{ 0x004D, 0xAB19 },    /* R77   - Charge Pump (2) */  	{ 0x0051, 0x0004 },    /* R81   - Class W (1) */ -	{ 0x0054, 0x0000 },    /* R84   - DC Servo (1) */  	{ 0x0055, 0x054A },    /* R85   - DC Servo (2) */ -	{ 0x0058, 0x0000 },    /* R88   - DC Servo Readback */  	{ 0x0059, 0x0000 },    /* R89   - DC Servo (4) */  	{ 0x0060, 0x0000 },    /* R96   - Analogue HP (1) */  	{ 0x00C5, 0x0000 },    /* R197  - Class D Test (5) */  	{ 0x00D0, 0x7600 },    /* R208  - Mic Detect 1 */  	{ 0x00D1, 0x007F },    /* R209  - Mic Detect 2 */ -	{ 0x00D2, 0x0000 },    /* R210  - Mic Detect 3 */ -	{ 0x0100, 0x0100 },    /* R256  - Chip Revision */  	{ 0x0101, 0x8004 },    /* R257  - Control Interface */  	{ 0x0200, 0x0000 },    /* R512  - AIF1 Clocking (1) */  	{ 0x0201, 0x0000 },    /* R513  - AIF1 Clocking (2) */ @@ -88,7 +83,6 @@ static struct reg_default wm1811_defaults[] = {  	{ 0x0209, 0x0000 },    /* R521  - Clocking (2) */  	{ 0x0210, 0x0083 },    /* R528  - AIF1 Rate */  	{ 0x0211, 0x0083 },    /* R529  - AIF2 Rate */ -	{ 0x0212, 0x0000 },    /* R530  - Rate Status */  	{ 0x0220, 0x0000 },    /* R544  - FLL1 Control (1) */  	{ 0x0221, 0x0000 },    /* R545  - FLL1 Control (2) */  	{ 0x0222, 0x0000 },    /* R546  - FLL1 Control (3) */ @@ -218,8 +212,6 @@ static struct reg_default wm1811_defaults[] = {  	{ 0x070A, 0xA101 },    /* R1802 - GPIO 11 */  	{ 0x0720, 0x0000 },    /* R1824 - Pull Control (1) */  	{ 0x0721, 0x0156 },    /* R1825 - Pull Control (2) */ -	{ 0x0730, 0x0000 },    /* R1840 - Interrupt Status 1 */ -	{ 0x0731, 0x0000 },    /* R1841 - Interrupt Status 2 */  	{ 0x0732, 0x0000 },    /* R1842 - Interrupt Raw Status 2 */  	{ 0x0738, 0x07FF },    /* R1848 - Interrupt Status 1 Mask */  	{ 0x0739, 0xDFEF },    /* R1849 - Interrupt Status 2 Mask */ @@ -228,7 +220,6 @@ static struct reg_default wm1811_defaults[] = {  };  static struct reg_default wm8994_defaults[] = { -	{ 0x0000, 0x8994 },    /* R0     - Software Reset */   	{ 0x0001, 0x0000 },    /* R1     - Power Management (1) */   	{ 0x0002, 0x6000 },    /* R2     - Power Management (2) */   	{ 0x0003, 0x0000 },    /* R3     - Power Management (3) */  @@ -275,12 +266,9 @@ static struct reg_default wm8994_defaults[] = {  	{ 0x003C, 0x0003 },    /* R60    - LDO 2 */   	{ 0x004C, 0x1F25 },    /* R76    - Charge Pump (1) */   	{ 0x0051, 0x0004 },    /* R81    - Class W (1) */  -	{ 0x0054, 0x0000 },    /* R84    - DC Servo (1) */   	{ 0x0055, 0x054A },    /* R85    - DC Servo (2) */   	{ 0x0057, 0x0000 },    /* R87    - DC Servo (4) */  -	{ 0x0058, 0x0000 },    /* R88    - DC Servo Readback */   	{ 0x0060, 0x0000 },    /* R96    - Analogue HP (1) */  -	{ 0x0100, 0x0003 },    /* R256   - Chip Revision */   	{ 0x0101, 0x8004 },    /* R257   - Control Interface */   	{ 0x0110, 0x0000 },    /* R272   - Write Sequencer Ctrl (1) */   	{ 0x0111, 0x0000 },    /* R273   - Write Sequencer Ctrl (2) */  @@ -292,7 +280,6 @@ static struct reg_default wm8994_defaults[] = {  	{ 0x0209, 0x0000 },    /* R521   - Clocking (2) */   	{ 0x0210, 0x0083 },    /* R528   - AIF1 Rate */   	{ 0x0211, 0x0083 },    /* R529   - AIF2 Rate */  -	{ 0x0212, 0x0000 },    /* R530   - Rate Status */   	{ 0x0220, 0x0000 },    /* R544   - FLL1 Control (1) */   	{ 0x0221, 0x0000 },    /* R545   - FLL1 Control (2) */   	{ 0x0222, 0x0000 },    /* R546   - FLL1 Control (3) */  @@ -445,9 +432,6 @@ static struct reg_default wm8994_defaults[] = {  	{ 0x070A, 0xA101 },    /* R1802  - GPIO 11 */   	{ 0x0720, 0x0000 },    /* R1824  - Pull Control (1) */   	{ 0x0721, 0x0156 },    /* R1825  - Pull Control (2) */  -	{ 0x0730, 0x0000 },    /* R1840  - Interrupt Status 1 */  -	{ 0x0731, 0x0000 },    /* R1841  - Interrupt Status 2 */  -	{ 0x0732, 0x0000 },    /* R1842  - Interrupt Raw Status 2 */   	{ 0x0738, 0x07FF },    /* R1848  - Interrupt Status 1 Mask */   	{ 0x0739, 0xFFFF },    /* R1849  - Interrupt Status 2 Mask */   	{ 0x0740, 0x0000 },    /* R1856  - Interrupt Control */  @@ -455,7 +439,6 @@ static struct reg_default wm8994_defaults[] = {  };  static struct reg_default wm8958_defaults[] = { -	{ 0x0000, 0x8958 },    /* R0     - Software Reset */   	{ 0x0001, 0x0000 },    /* R1     - Power Management (1) */  	{ 0x0002, 0x6000 },    /* R2     - Power Management (2) */  	{ 0x0003, 0x0000 },    /* R3     - Power Management (3) */ @@ -970,6 +953,7 @@ static bool wm8994_readable_register(struct device *dev, unsigned int reg)  {  	switch (reg) {  	case WM8994_DC_SERVO_READBACK: +	case WM8994_MICBIAS:  	case WM8994_WRITE_SEQUENCER_CTRL_1:  	case WM8994_WRITE_SEQUENCER_CTRL_2:  	case WM8994_AIF1_ADC2_LEFT_VOLUME: diff --git a/drivers/rtc/rtc-88pm860x.c b/drivers/rtc/rtc-88pm860x.c index f04761e6622..afee0e8ae71 100644 --- a/drivers/rtc/rtc-88pm860x.c +++ b/drivers/rtc/rtc-88pm860x.c @@ -376,6 +376,9 @@ static int __devinit pm860x_rtc_probe(struct platform_device *pdev)  	INIT_DELAYED_WORK(&info->calib_work, calibrate_vrtc_work);  	schedule_delayed_work(&info->calib_work, VRTC_CALIB_INTERVAL);  #endif	/* VRTC_CALIBRATION */ + +	device_init_wakeup(&pdev->dev, 1); +  	return 0;  out_rtc:  	free_irq(info->irq, info); @@ -401,10 +404,34 @@ static int __devexit pm860x_rtc_remove(struct platform_device *pdev)  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int pm860x_rtc_suspend(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + +	if (device_may_wakeup(dev)) +		chip->wakeup_flag |= 1 << PM8607_IRQ_RTC; +	return 0; +} +static int pm860x_rtc_resume(struct device *dev) +{ +	struct platform_device *pdev = to_platform_device(dev); +	struct pm860x_chip *chip = dev_get_drvdata(pdev->dev.parent); + +	if (device_may_wakeup(dev)) +		chip->wakeup_flag &= ~(1 << PM8607_IRQ_RTC); +	return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(pm860x_rtc_pm_ops, pm860x_rtc_suspend, pm860x_rtc_resume); +  static struct platform_driver pm860x_rtc_driver = {  	.driver		= {  		.name	= "88pm860x-rtc",  		.owner	= THIS_MODULE, +		.pm	= &pm860x_rtc_pm_ops,  	},  	.probe		= pm860x_rtc_probe,  	.remove		= __devexit_p(pm860x_rtc_remove), diff --git a/drivers/video/backlight/88pm860x_bl.c b/drivers/video/backlight/88pm860x_bl.c index 915943af3f2..f49181c7311 100644 --- a/drivers/video/backlight/88pm860x_bl.c +++ b/drivers/video/backlight/88pm860x_bl.c @@ -67,6 +67,28 @@ static inline int wled_idc(int port)  	return ret;  } +static int backlight_power_set(struct pm860x_chip *chip, int port, +		int on) +{ +	int ret = -EINVAL; + +	switch (port) { +	case PM8606_BACKLIGHT1: +		ret = on ? pm8606_osc_enable(chip, WLED1_DUTY) : +			pm8606_osc_disable(chip, WLED1_DUTY); +		break; +	case PM8606_BACKLIGHT2: +		ret = on ? pm8606_osc_enable(chip, WLED2_DUTY) : +			pm8606_osc_disable(chip, WLED2_DUTY); +		break; +	case PM8606_BACKLIGHT3: +		ret = on ? pm8606_osc_enable(chip, WLED3_DUTY) : +			pm8606_osc_disable(chip, WLED3_DUTY); +		break; +	} +	return ret; +} +  static int pm860x_backlight_set(struct backlight_device *bl, int brightness)  {  	struct pm860x_backlight_data *data = bl_get_data(bl); @@ -79,6 +101,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)  	else  		value = brightness; +	if (brightness) +		backlight_power_set(chip, data->port, 1); +  	ret = pm860x_reg_write(data->i2c, wled_a(data->port), value);  	if (ret < 0)  		goto out; @@ -115,6 +140,9 @@ static int pm860x_backlight_set(struct backlight_device *bl, int brightness)  	if (ret < 0)  		goto out; +	if (brightness == 0) +		backlight_power_set(chip, data->port, 0); +  	dev_dbg(chip->dev, "set brightness %d\n", value);  	data->current_brightness = value;  	return 0; @@ -170,7 +198,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)  	struct backlight_device *bl;  	struct resource *res;  	struct backlight_properties props; -	unsigned char value;  	char name[MFD_NAME_SIZE];  	int ret; @@ -217,26 +244,6 @@ static int pm860x_backlight_probe(struct platform_device *pdev)  	platform_set_drvdata(pdev, bl); -	/* Enable reference VSYS */ -	ret = pm860x_reg_read(data->i2c, PM8606_VSYS); -	if (ret < 0) -		goto out; -	if ((ret & PM8606_VSYS_EN) == 0) { -		value = ret | PM8606_VSYS_EN; -		ret = pm860x_reg_write(data->i2c, PM8606_VSYS, value); -		if (ret < 0) -			goto out; -	} -	/* Enable reference OSC */ -	ret = pm860x_reg_read(data->i2c, PM8606_MISC); -	if (ret < 0) -		goto out; -	if ((ret & PM8606_MISC_OSC_EN) == 0) { -		value = ret | PM8606_MISC_OSC_EN; -		ret = pm860x_reg_write(data->i2c, PM8606_MISC, value); -		if (ret < 0) -			goto out; -	}  	/* read current backlight */  	ret = pm860x_backlight_get_brightness(bl);  	if (ret < 0)  |