From 6e3ad118041f56db752a5eb2b557517d14592af7 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 8 Aug 2011 17:04:40 +0900 Subject: mfd: Convert pcf50633 to use new register map API Signed-off-by: Mark Brown Tested-by: Lars-Peter Clausen Signed-off-by: Samuel Ortiz --- include/linux/mfd/pcf50633/core.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mfd/pcf50633/core.h b/include/linux/mfd/pcf50633/core.h index 50d4a047118..a80840752b4 100644 --- a/include/linux/mfd/pcf50633/core.h +++ b/include/linux/mfd/pcf50633/core.h @@ -21,6 +21,7 @@ #include struct pcf50633; +struct regmap; #define PCF50633_NUM_REGULATORS 11 @@ -134,7 +135,7 @@ enum { struct pcf50633 { struct device *dev; - struct i2c_client *i2c_client; + struct regmap *regmap; struct pcf50633_platform_data *pdata; int irq; -- cgit v1.2.3-70-g09d2 From bd4a40b57b13907b4fe01b6c605be56d8f3733fe Mon Sep 17 00:00:00 2001 From: Karl Komierowski Date: Wed, 10 Aug 2011 15:09:43 +0200 Subject: mfd: Refactor ab8500 GPADC API, add raw access Refactor the GPADC interface to avoid bugs in calling code: - ab8500_gpadc_[convert|read_raw|ad_to_voltage] clarifies each functions use case, *convert wraps *read_raw, and we can access raw ADC values properly. - Renamed gpadc function arguments from "input" to "channel" to clarify use, so we don't get confused again. Signed-off-by: Kalle Komierowski Reviewed-by: Mattias Wallin Reviewed-by: John Beckett Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab8500-gpadc.c | 56 ++++++++++++++++++++++++++++++---------- include/linux/mfd/ab8500/gpadc.h | 5 +++- 2 files changed, 47 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/ab8500-gpadc.c b/drivers/mfd/ab8500-gpadc.c index f16afb234ff..e985d1701a8 100644 --- a/drivers/mfd/ab8500-gpadc.c +++ b/drivers/mfd/ab8500-gpadc.c @@ -143,12 +143,15 @@ struct ab8500_gpadc *ab8500_gpadc_get(char *name) } EXPORT_SYMBOL(ab8500_gpadc_get); -static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, +/** + * ab8500_gpadc_ad_to_voltage() - Convert a raw ADC value to a voltage + */ +int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 channel, int ad_value) { int res; - switch (input) { + switch (channel) { case MAIN_CHARGER_V: /* For some reason we don't have calibrated data */ if (!gpadc->cal_data[ADC_INPUT_VMAIN].gain) { @@ -232,18 +235,46 @@ static int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, u8 input, } return res; } +EXPORT_SYMBOL(ab8500_gpadc_ad_to_voltage); /** * ab8500_gpadc_convert() - gpadc conversion - * @input: analog input to be converted to digital data + * @channel: analog channel to be converted to digital data * * This function converts the selected analog i/p to digital * data. */ -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel) +{ + int ad_value; + int voltage; + + ad_value = ab8500_gpadc_read_raw(gpadc, channel); + if (ad_value < 0) { + dev_err(gpadc->dev, "GPADC raw value failed ch: %d\n", channel); + return ad_value; + } + + voltage = ab8500_gpadc_ad_to_voltage(gpadc, channel, ad_value); + + if (voltage < 0) + dev_err(gpadc->dev, "GPADC to voltage conversion failed ch:" + " %d AD: 0x%x\n", channel, ad_value); + + return voltage; +} +EXPORT_SYMBOL(ab8500_gpadc_convert); + +/** + * ab8500_gpadc_read_raw() - gpadc read + * @channel: analog channel to be read + * + * This function obtains the raw ADC value, this then needs + * to be converted by calling ab8500_gpadc_ad_to_voltage() + */ +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel) { int ret; - u16 data = 0; int looplimit = 0; u8 val, low_data, high_data; @@ -278,9 +309,9 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) goto out; } - /* Select the input source and set average samples to 16 */ + /* Select the channel source and set average samples to 16 */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, - AB8500_GPADC_CTRL2_REG, (input | SW_AVG_16)); + AB8500_GPADC_CTRL2_REG, (channel | SW_AVG_16)); if (ret < 0) { dev_err(gpadc->dev, "gpadc_conversion: set avg samples failed\n"); @@ -292,7 +323,7 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) * charging current sense if it needed, ABB 3.0 needs some special * treatment too. */ - switch (input) { + switch (channel) { case MAIN_CHARGER_C: case USB_CHARGER_C: ret = abx500_mask_and_set_register_interruptible(gpadc->dev, @@ -359,7 +390,6 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) goto out; } - data = (high_data << 8) | low_data; /* Disable GPADC */ ret = abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC, AB8500_GPADC_CTRL1_REG, DIS_GPADC); @@ -370,8 +400,8 @@ int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input) /* Disable VTVout LDO this is required for GPADC */ regulator_disable(gpadc->regu); mutex_unlock(&gpadc->ab8500_gpadc_lock); - ret = ab8500_gpadc_ad_to_voltage(gpadc, input, data); - return ret; + + return (high_data << 8) | low_data; out: /* @@ -385,10 +415,10 @@ out: regulator_disable(gpadc->regu); mutex_unlock(&gpadc->ab8500_gpadc_lock); dev_err(gpadc->dev, - "gpadc_conversion: Failed to AD convert channel %d\n", input); + "gpadc_conversion: Failed to AD convert channel %d\n", channel); return ret; } -EXPORT_SYMBOL(ab8500_gpadc_convert); +EXPORT_SYMBOL(ab8500_gpadc_read_raw); /** * ab8500_bm_gpswadcconvend_handler() - isr for s/w gpadc conversion completion diff --git a/include/linux/mfd/ab8500/gpadc.h b/include/linux/mfd/ab8500/gpadc.h index 46b954011f1..252966769d9 100644 --- a/include/linux/mfd/ab8500/gpadc.h +++ b/include/linux/mfd/ab8500/gpadc.h @@ -27,6 +27,9 @@ struct ab8500_gpadc; struct ab8500_gpadc *ab8500_gpadc_get(char *name); -int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 input); +int ab8500_gpadc_convert(struct ab8500_gpadc *gpadc, u8 channel); +int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel); +int ab8500_gpadc_ad_to_voltage(struct ab8500_gpadc *gpadc, + u8 channel, int ad_value); #endif /* _AB8500_GPADC_H */ -- cgit v1.2.3-70-g09d2 From 881de67046f424fc3a6e05b1c681c12afd94e802 Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Mon, 22 Aug 2011 15:43:55 +0200 Subject: mfd: Allow WM8994 LDO enable pulls to be disabled In systems where the LDO enables are always driven (for example, being connected to an always on supply rail or a GPIO which is driven by the CPU even in suspend) then we can disable the pull downs on the LDO for a small power savings. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm8994-core.c | 19 +++++++++++++++++++ include/linux/mfd/wm8994/core.h | 2 ++ include/linux/mfd/wm8994/pdata.h | 7 +++++++ 3 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/wm8994-core.c b/drivers/mfd/wm8994-core.c index 96479c9b172..1f15743460a 100644 --- a/drivers/mfd/wm8994-core.c +++ b/drivers/mfd/wm8994-core.c @@ -281,6 +281,13 @@ static int wm8994_suspend(struct device *dev) return 0; } + /* Disable LDO pulldowns while the device is suspended if we + * don't know that something will be driving them. */ + if (!wm8994->ldo_ena_always_driven) + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD); + /* GPIO configuration state is saved here since we may be configuring * the GPIO alternate functions even if we're not using the gpiolib * driver for them. @@ -350,6 +357,11 @@ static int wm8994_resume(struct device *dev) if (ret < 0) dev_err(dev, "Failed to restore GPIO registers: %d\n", ret); + /* Disable LDO pulldowns while the device is active */ + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + 0); + wm8994->suspended = false; return 0; @@ -513,8 +525,15 @@ static int wm8994_device_init(struct wm8994 *wm8994, int irq) pdata->gpio_defaults[i]); } } + + wm8994->ldo_ena_always_driven = pdata->ldo_ena_always_driven; } + /* Disable LDO pulldowns while the device is active */ + wm8994_set_bits(wm8994, WM8994_PULL_CONTROL_2, + WM8994_LDO1ENA_PD | WM8994_LDO2ENA_PD, + 0); + /* In some system designs where the regulators are not in use, * we can achieve a small reduction in leakage currents by * floating LDO outputs. This bit makes no difference if the diff --git a/include/linux/mfd/wm8994/core.h b/include/linux/mfd/wm8994/core.h index f0b69cdae41..5ab71bd76f9 100644 --- a/include/linux/mfd/wm8994/core.h +++ b/include/linux/mfd/wm8994/core.h @@ -63,6 +63,8 @@ struct wm8994 { void *control_data; + bool ldo_ena_always_driven; + int gpio_base; int irq_base; diff --git a/include/linux/mfd/wm8994/pdata.h b/include/linux/mfd/wm8994/pdata.h index 97cf4f27d64..ea32f306dca 100644 --- a/include/linux/mfd/wm8994/pdata.h +++ b/include/linux/mfd/wm8994/pdata.h @@ -167,6 +167,13 @@ struct wm8994_pdata { /* WM8958 microphone bias configuration */ int micbias[2]; + + /* Disable the internal pull downs on the LDOs if they are + * always driven (eg, connected to an always on supply or + * GPIO that always drives an output. If they float power + * consumption will rise. + */ + bool ldo_ena_always_driven; }; #endif -- cgit v1.2.3-70-g09d2 From 3d6271f92e98094584fd1e609a9969cd33e61122 Mon Sep 17 00:00:00 2001 From: Kyle Manna Date: Thu, 11 Aug 2011 22:33:13 -0500 Subject: mfd: Turn on the twl4030-madc MADC clock Without turning the MADC clock on, no MADC conversions occur. $ cat /sys/class/hwmon/hwmon0/device/in8_input [ 53.428436] twl4030_madc twl4030_madc: conversion timeout! cat: read error: Resource temporarily unavailable Signed-off-by: Kyle Manna Signed-off-by: Samuel Ortiz --- drivers/mfd/twl4030-madc.c | 22 ++++++++++++++++++++++ include/linux/i2c/twl4030-madc.h | 4 ++++ 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/twl4030-madc.c b/drivers/mfd/twl4030-madc.c index 7cbf2aa9e64..834f824d3c1 100644 --- a/drivers/mfd/twl4030-madc.c +++ b/drivers/mfd/twl4030-madc.c @@ -740,6 +740,28 @@ static int __devinit twl4030_madc_probe(struct platform_device *pdev) TWL4030_BCI_BCICTL1); goto err_i2c; } + + /* Check that MADC clock is on */ + ret = twl_i2c_read_u8(TWL4030_MODULE_INTBR, ®val, TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to read reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + + /* If MADC clk is not on, turn it on */ + if (!(regval & TWL4030_GPBR1_MADC_HFCLK_EN)) { + dev_info(&pdev->dev, "clk disabled, enabling\n"); + regval |= TWL4030_GPBR1_MADC_HFCLK_EN; + ret = twl_i2c_write_u8(TWL4030_MODULE_INTBR, regval, + TWL4030_REG_GPBR1); + if (ret) { + dev_err(&pdev->dev, "unable to write reg GPBR1 0x%X\n", + TWL4030_REG_GPBR1); + goto err_i2c; + } + } + platform_set_drvdata(pdev, madc); mutex_init(&madc->lock); ret = request_threaded_irq(platform_get_irq(pdev, 0), NULL, diff --git a/include/linux/i2c/twl4030-madc.h b/include/linux/i2c/twl4030-madc.h index 6427d298fbf..530e11ba073 100644 --- a/include/linux/i2c/twl4030-madc.h +++ b/include/linux/i2c/twl4030-madc.h @@ -129,6 +129,10 @@ enum sample_type { #define REG_BCICTL2 0x024 #define TWL4030_BCI_ITHSENS 0x007 +/* Register and bits for GPBR1 register */ +#define TWL4030_REG_GPBR1 0x0c +#define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7) + struct twl4030_madc_user_parms { int channel; int average; -- cgit v1.2.3-70-g09d2 From 01fdaab8ffced1deeee14d9c7d2745f37349484e Mon Sep 17 00:00:00 2001 From: MyungJoo Ham Date: Fri, 19 Aug 2011 14:39:40 +0900 Subject: mfd: Wake-up from Suspend MAX8997 support - Support wake-up from suspend-to-ram. - Handle pending interrupt after a resume. - If pdata->wakeup is enabled, by default, the device is assumed to be capable of wakeup (the interrupt pin is connected to a wakeup-source GPIO) and may wakeup the system (MAX8997 has a power button input pin). Signed-off-by: MyungJoo Ham Signed-off-by: Kyungmin Park Signed-off-by: Samuel Ortiz --- drivers/mfd/max8997.c | 27 ++++++++++++++++++++++++++- include/linux/mfd/max8997-private.h | 1 - 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/max8997.c b/drivers/mfd/max8997.c index f83103b8970..dc58750bb71 100644 --- a/drivers/mfd/max8997.c +++ b/drivers/mfd/max8997.c @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -142,7 +143,6 @@ static int max8997_i2c_probe(struct i2c_client *i2c, max8997->irq_base = pdata->irq_base; max8997->ono = pdata->ono; - max8997->wakeup = pdata->wakeup; mutex_init(&max8997->iolock); @@ -169,6 +169,9 @@ static int max8997_i2c_probe(struct i2c_client *i2c, if (ret < 0) goto err_mfd; + /* MAX8997 has a power button input. */ + device_init_wakeup(max8997->dev, pdata->wakeup); + return ret; err_mfd: @@ -398,7 +401,29 @@ static int max8997_restore(struct device *dev) return 0; } +static int max8997_suspend(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max8997->irq, 1); + return 0; +} + +static int max8997_resume(struct device *dev) +{ + struct i2c_client *i2c = container_of(dev, struct i2c_client, dev); + struct max8997_dev *max8997 = i2c_get_clientdata(i2c); + + if (device_may_wakeup(dev)) + irq_set_irq_wake(max8997->irq, 0); + return max8997_irq_resume(max8997); +} + const struct dev_pm_ops max8997_pm = { + .suspend = max8997_suspend, + .resume = max8997_resume, .freeze = max8997_freeze, .restore = max8997_restore, }; diff --git a/include/linux/mfd/max8997-private.h b/include/linux/mfd/max8997-private.h index 5ff2400ad46..3f4deb62d6b 100644 --- a/include/linux/mfd/max8997-private.h +++ b/include/linux/mfd/max8997-private.h @@ -326,7 +326,6 @@ struct max8997_dev { int irq; int ono; int irq_base; - bool wakeup; struct mutex irqlock; int irq_masks_cur[MAX8997_IRQ_GROUP_NR]; int irq_masks_cache[MAX8997_IRQ_GROUP_NR]; -- cgit v1.2.3-70-g09d2 From fec316d63219f610e5385f5e54e6c3ea459e58e9 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 24 Aug 2011 15:28:21 +0200 Subject: mfd: Provide a generic version of mc13xxx adc_do_conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This is needed to convert the touch driver away from using struct mc13783. Note this patch drops MC13783_ADC0_ADREFMODE. This is unused and doesn't exist on mc13892. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 89 ++++++++++++++++++++++----------------------- include/linux/mfd/mc13783.h | 35 +++++++++--------- include/linux/mfd/mc13xxx.h | 19 ++++++++++ 3 files changed, 81 insertions(+), 62 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 7e4d44bf92a..5ee5e64d586 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -26,12 +26,12 @@ struct mc13xxx { irq_handler_t irqhandler[MC13XXX_NUM_IRQ]; void *irqdata[MC13XXX_NUM_IRQ]; + + int adcflags; }; struct mc13783 { struct mc13xxx mc13xxx; - - int adcflags; }; struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783) @@ -136,14 +136,14 @@ EXPORT_SYMBOL(mc13783_to_mc13xxx); #define MC13XXX_REVISION_FAB (0x03 << 11) #define MC13XXX_REVISION_ICIDCODE (0x3f << 13) -#define MC13783_ADC1 44 -#define MC13783_ADC1_ADEN (1 << 0) -#define MC13783_ADC1_RAND (1 << 1) -#define MC13783_ADC1_ADSEL (1 << 3) -#define MC13783_ADC1_ASC (1 << 20) -#define MC13783_ADC1_ADTRIGIGN (1 << 21) +#define MC13XXX_ADC1 44 +#define MC13XXX_ADC1_ADEN (1 << 0) +#define MC13XXX_ADC1_RAND (1 << 1) +#define MC13XXX_ADC1_ADSEL (1 << 3) +#define MC13XXX_ADC1_ASC (1 << 20) +#define MC13XXX_ADC1_ADTRIGIGN (1 << 21) -#define MC13783_ADC2 45 +#define MC13XXX_ADC2 45 #define MC13XXX_NUMREGS 0x3f @@ -569,15 +569,15 @@ int mc13xxx_get_flags(struct mc13xxx *mc13xxx) } EXPORT_SYMBOL(mc13xxx_get_flags); -#define MC13783_ADC1_CHAN0_SHIFT 5 -#define MC13783_ADC1_CHAN1_SHIFT 8 +#define MC13XXX_ADC1_CHAN0_SHIFT 5 +#define MC13XXX_ADC1_CHAN1_SHIFT 8 struct mc13xxx_adcdone_data { struct mc13xxx *mc13xxx; struct completion done; }; -static irqreturn_t mc13783_handler_adcdone(int irq, void *data) +static irqreturn_t mc13xxx_handler_adcdone(int irq, void *data) { struct mc13xxx_adcdone_data *adcdone_data = data; @@ -588,12 +588,11 @@ static irqreturn_t mc13783_handler_adcdone(int irq, void *data) return IRQ_HANDLED; } -#define MC13783_ADC_WORKING (1 << 0) +#define MC13XXX_ADC_WORKING (1 << 0) -int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, +int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, unsigned int mode, unsigned int channel, unsigned int *sample) { - struct mc13xxx *mc13xxx = &mc13783->mc13xxx; u32 adc0, adc1, old_adc0; int i, ret; struct mc13xxx_adcdone_data adcdone_data = { @@ -605,51 +604,51 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, mc13xxx_lock(mc13xxx); - if (mc13783->adcflags & MC13783_ADC_WORKING) { + if (mc13xxx->adcflags & MC13XXX_ADC_WORKING) { ret = -EBUSY; goto out; } - mc13783->adcflags |= MC13783_ADC_WORKING; + mc13xxx->adcflags |= MC13XXX_ADC_WORKING; - mc13xxx_reg_read(mc13xxx, MC13783_ADC0, &old_adc0); + mc13xxx_reg_read(mc13xxx, MC13XXX_ADC0, &old_adc0); - adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2; - adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC; + adc0 = MC13XXX_ADC0_ADINC1 | MC13XXX_ADC0_ADINC2; + adc1 = MC13XXX_ADC1_ADEN | MC13XXX_ADC1_ADTRIGIGN | MC13XXX_ADC1_ASC; if (channel > 7) - adc1 |= MC13783_ADC1_ADSEL; + adc1 |= MC13XXX_ADC1_ADSEL; switch (mode) { - case MC13783_ADC_MODE_TS: - adc0 |= MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 | - MC13783_ADC0_TSMOD1; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + case MC13XXX_ADC_MODE_TS: + adc0 |= MC13XXX_ADC0_ADREFEN | MC13XXX_ADC0_TSMOD0 | + MC13XXX_ADC0_TSMOD1; + adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT; break; - case MC13783_ADC_MODE_SINGLE_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= (channel & 0x7) << MC13783_ADC1_CHAN0_SHIFT; - adc1 |= MC13783_ADC1_RAND; + case MC13XXX_ADC_MODE_SINGLE_CHAN: + adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK; + adc1 |= (channel & 0x7) << MC13XXX_ADC1_CHAN0_SHIFT; + adc1 |= MC13XXX_ADC1_RAND; break; - case MC13783_ADC_MODE_MULT_CHAN: - adc0 |= old_adc0 & MC13783_ADC0_TSMOD_MASK; - adc1 |= 4 << MC13783_ADC1_CHAN1_SHIFT; + case MC13XXX_ADC_MODE_MULT_CHAN: + adc0 |= old_adc0 & MC13XXX_ADC0_TSMOD_MASK; + adc1 |= 4 << MC13XXX_ADC1_CHAN1_SHIFT; break; default: - mc13783_unlock(mc13783); + mc13xxx_unlock(mc13xxx); return -EINVAL; } - dev_dbg(&mc13783->mc13xxx.spidev->dev, "%s: request irq\n", __func__); - mc13xxx_irq_request(mc13xxx, MC13783_IRQ_ADCDONE, - mc13783_handler_adcdone, __func__, &adcdone_data); - mc13xxx_irq_ack(mc13xxx, MC13783_IRQ_ADCDONE); + dev_dbg(&mc13xxx->spidev->dev, "%s: request irq\n", __func__); + mc13xxx_irq_request(mc13xxx, MC13XXX_IRQ_ADCDONE, + mc13xxx_handler_adcdone, __func__, &adcdone_data); + mc13xxx_irq_ack(mc13xxx, MC13XXX_IRQ_ADCDONE); - mc13xxx_reg_write(mc13xxx, MC13783_ADC0, adc0); - mc13xxx_reg_write(mc13xxx, MC13783_ADC1, adc1); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, adc0); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC1, adc1); mc13xxx_unlock(mc13xxx); @@ -660,27 +659,27 @@ int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, mc13xxx_lock(mc13xxx); - mc13xxx_irq_free(mc13xxx, MC13783_IRQ_ADCDONE, &adcdone_data); + mc13xxx_irq_free(mc13xxx, MC13XXX_IRQ_ADCDONE, &adcdone_data); if (ret > 0) for (i = 0; i < 4; ++i) { ret = mc13xxx_reg_read(mc13xxx, - MC13783_ADC2, &sample[i]); + MC13XXX_ADC2, &sample[i]); if (ret) break; } - if (mode == MC13783_ADC_MODE_TS) + if (mode == MC13XXX_ADC_MODE_TS) /* restore TSMOD */ - mc13xxx_reg_write(mc13xxx, MC13783_ADC0, old_adc0); + mc13xxx_reg_write(mc13xxx, MC13XXX_ADC0, old_adc0); - mc13783->adcflags &= ~MC13783_ADC_WORKING; + mc13xxx->adcflags &= ~MC13XXX_ADC_WORKING; out: mc13xxx_unlock(mc13xxx); return ret; } -EXPORT_SYMBOL_GPL(mc13783_adc_do_conversion); +EXPORT_SYMBOL_GPL(mc13xxx_adc_do_conversion); static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, const char *format, void *pdata, size_t pdata_size) diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index 7d0f3d6a000..e7a3c0169f6 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -89,18 +89,15 @@ static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq); } -#define MC13783_ADC0 43 -#define MC13783_ADC0_ADREFEN (1 << 10) -#define MC13783_ADC0_ADREFMODE (1 << 11) -#define MC13783_ADC0_TSMOD0 (1 << 12) -#define MC13783_ADC0_TSMOD1 (1 << 13) -#define MC13783_ADC0_TSMOD2 (1 << 14) -#define MC13783_ADC0_ADINC1 (1 << 16) -#define MC13783_ADC0_ADINC2 (1 << 17) - -#define MC13783_ADC0_TSMOD_MASK (MC13783_ADC0_TSMOD0 | \ - MC13783_ADC0_TSMOD1 | \ - MC13783_ADC0_TSMOD2) +#define MC13783_ADC0 MC13XXX_ADC0 +#define MC13783_ADC0_ADREFEN MC13XXX_ADC0_ADREFEN +#define MC13783_ADC0_TSMOD0 MC13XXX_ADC0_TSMOD0 +#define MC13783_ADC0_TSMOD1 MC13XXX_ADC0_TSMOD1 +#define MC13783_ADC0_TSMOD2 MC13XXX_ADC0_TSMOD2 +#define MC13783_ADC0_ADINC1 MC13XXX_ADC0_ADINC1 +#define MC13783_ADC0_ADINC2 MC13XXX_ADC0_ADINC2 + +#define MC13783_ADC0_TSMOD_MASK MC13XXX_ADC0_TSMOD_MASK #define mc13783_regulator_init_data mc13xxx_regulator_init_data #define mc13783_regulator_platform_data mc13xxx_regulator_platform_data @@ -115,12 +112,16 @@ static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) #define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR #define MC13783_USE_LED MC13XXX_USE_LED -#define MC13783_ADC_MODE_TS 1 -#define MC13783_ADC_MODE_SINGLE_CHAN 2 -#define MC13783_ADC_MODE_MULT_CHAN 3 +#define MC13783_ADC_MODE_TS MC13XXX_ADC_MODE_TS +#define MC13783_ADC_MODE_SINGLE_CHAN MC13XXX_ADC_MODE_SINGLE_CHAN +#define MC13783_ADC_MODE_MULT_CHAN MC13XXX_ADC_MODE_MULT_CHAN -int mc13783_adc_do_conversion(struct mc13783 *mc13783, unsigned int mode, - unsigned int channel, unsigned int *sample); +static inline int mc13783_adc_do_conversion(struct mc13783 *mc13783, + unsigned int mode, unsigned int channel, unsigned int *sample) +{ + return mc13xxx_adc_do_conversion(mc13783_to_mc13xxx(mc13783), mode, + channel, sample); +} #define MC13783_REG_SW1A 0 diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index c064beaaccb..6e7c0ac36d0 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -37,6 +37,9 @@ int mc13xxx_irq_ack(struct mc13xxx *mc13xxx, int irq); int mc13xxx_get_flags(struct mc13xxx *mc13xxx); +int mc13xxx_adc_do_conversion(struct mc13xxx *mc13xxx, + unsigned int mode, unsigned int channel, unsigned int *sample); + #define MC13XXX_IRQ_ADCDONE 0 #define MC13XXX_IRQ_ADCBISDONE 1 #define MC13XXX_IRQ_TS 2 @@ -150,4 +153,20 @@ struct mc13xxx_platform_data { struct mc13xxx_leds_platform_data *leds; }; +#define MC13XXX_ADC_MODE_TS 1 +#define MC13XXX_ADC_MODE_SINGLE_CHAN 2 +#define MC13XXX_ADC_MODE_MULT_CHAN 3 + +#define MC13XXX_ADC0 43 +#define MC13XXX_ADC0_ADREFEN (1 << 10) +#define MC13XXX_ADC0_TSMOD0 (1 << 12) +#define MC13XXX_ADC0_TSMOD1 (1 << 13) +#define MC13XXX_ADC0_TSMOD2 (1 << 14) +#define MC13XXX_ADC0_ADINC1 (1 << 16) +#define MC13XXX_ADC0_ADINC2 (1 << 17) + +#define MC13XXX_ADC0_TSMOD_MASK (MC13XXX_ADC0_TSMOD0 | \ + MC13XXX_ADC0_TSMOD1 | \ + MC13XXX_ADC0_TSMOD2) + #endif /* ifndef __LINUX_MFD_MC13XXX_H */ -- cgit v1.2.3-70-g09d2 From b46880e57b4c513adeb2608c3700b352860b5662 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Wed, 24 Aug 2011 15:28:25 +0200 Subject: mfd: Remove mc13783 API functions and symbols MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that all in-tree users are fixed to use the more general mc13xxx API the obsolete stuff can go away. Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/mfd/mc13xxx-core.c | 12 ----- include/linux/mfd/mc13783.h | 112 -------------------------------------------- 2 files changed, 124 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 5ee5e64d586..5f782adffc1 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -30,16 +30,6 @@ struct mc13xxx { int adcflags; }; -struct mc13783 { - struct mc13xxx mc13xxx; -}; - -struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783) -{ - return &mc13783->mc13xxx; -} -EXPORT_SYMBOL(mc13783_to_mc13xxx); - #define MC13XXX_IRQSTAT0 0 #define MC13XXX_IRQSTAT0_ADCDONEI (1 << 0) #define MC13XXX_IRQSTAT0_ADCBISDONEI (1 << 1) @@ -558,8 +548,6 @@ static const char *mc13xxx_get_chipname(struct mc13xxx *mc13xxx) return mc13xxx_chipname[devid->driver_data]; } -#include - int mc13xxx_get_flags(struct mc13xxx *mc13xxx) { struct mc13xxx_platform_data *pdata = diff --git a/include/linux/mfd/mc13783.h b/include/linux/mfd/mc13783.h index e7a3c0169f6..a8eeda773a7 100644 --- a/include/linux/mfd/mc13783.h +++ b/include/linux/mfd/mc13783.h @@ -12,118 +12,6 @@ #include -struct mc13783; - -struct mc13xxx *mc13783_to_mc13xxx(struct mc13783 *mc13783); - -static inline void mc13783_lock(struct mc13783 *mc13783) -{ - mc13xxx_lock(mc13783_to_mc13xxx(mc13783)); -} - -static inline void mc13783_unlock(struct mc13783 *mc13783) -{ - mc13xxx_unlock(mc13783_to_mc13xxx(mc13783)); -} - -static inline int mc13783_reg_read(struct mc13783 *mc13783, - unsigned int offset, u32 *val) -{ - return mc13xxx_reg_read(mc13783_to_mc13xxx(mc13783), offset, val); -} - -static inline int mc13783_reg_write(struct mc13783 *mc13783, - unsigned int offset, u32 val) -{ - return mc13xxx_reg_write(mc13783_to_mc13xxx(mc13783), offset, val); -} - -static inline int mc13783_reg_rmw(struct mc13783 *mc13783, - unsigned int offset, u32 mask, u32 val) -{ - return mc13xxx_reg_rmw(mc13783_to_mc13xxx(mc13783), offset, mask, val); -} - -static inline int mc13783_get_flags(struct mc13783 *mc13783) -{ - return mc13xxx_get_flags(mc13783_to_mc13xxx(mc13783)); -} - -static inline int mc13783_irq_request(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - return mc13xxx_irq_request(mc13783_to_mc13xxx(mc13783), irq, - handler, name, dev); -} - -static inline int mc13783_irq_request_nounmask(struct mc13783 *mc13783, int irq, - irq_handler_t handler, const char *name, void *dev) -{ - return mc13xxx_irq_request_nounmask(mc13783_to_mc13xxx(mc13783), irq, - handler, name, dev); -} - -static inline int mc13783_irq_free(struct mc13783 *mc13783, int irq, void *dev) -{ - return mc13xxx_irq_free(mc13783_to_mc13xxx(mc13783), irq, dev); -} - -static inline int mc13783_irq_mask(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_mask(mc13783_to_mc13xxx(mc13783), irq); -} - -static inline int mc13783_irq_unmask(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_unmask(mc13783_to_mc13xxx(mc13783), irq); -} -static inline int mc13783_irq_status(struct mc13783 *mc13783, int irq, - int *enabled, int *pending) -{ - return mc13xxx_irq_status(mc13783_to_mc13xxx(mc13783), - irq, enabled, pending); -} - -static inline int mc13783_irq_ack(struct mc13783 *mc13783, int irq) -{ - return mc13xxx_irq_ack(mc13783_to_mc13xxx(mc13783), irq); -} - -#define MC13783_ADC0 MC13XXX_ADC0 -#define MC13783_ADC0_ADREFEN MC13XXX_ADC0_ADREFEN -#define MC13783_ADC0_TSMOD0 MC13XXX_ADC0_TSMOD0 -#define MC13783_ADC0_TSMOD1 MC13XXX_ADC0_TSMOD1 -#define MC13783_ADC0_TSMOD2 MC13XXX_ADC0_TSMOD2 -#define MC13783_ADC0_ADINC1 MC13XXX_ADC0_ADINC1 -#define MC13783_ADC0_ADINC2 MC13XXX_ADC0_ADINC2 - -#define MC13783_ADC0_TSMOD_MASK MC13XXX_ADC0_TSMOD_MASK - -#define mc13783_regulator_init_data mc13xxx_regulator_init_data -#define mc13783_regulator_platform_data mc13xxx_regulator_platform_data -#define mc13783_led_platform_data mc13xxx_led_platform_data -#define mc13783_leds_platform_data mc13xxx_leds_platform_data - -#define mc13783_platform_data mc13xxx_platform_data -#define MC13783_USE_TOUCHSCREEN MC13XXX_USE_TOUCHSCREEN -#define MC13783_USE_CODEC MC13XXX_USE_CODEC -#define MC13783_USE_ADC MC13XXX_USE_ADC -#define MC13783_USE_RTC MC13XXX_USE_RTC -#define MC13783_USE_REGULATOR MC13XXX_USE_REGULATOR -#define MC13783_USE_LED MC13XXX_USE_LED - -#define MC13783_ADC_MODE_TS MC13XXX_ADC_MODE_TS -#define MC13783_ADC_MODE_SINGLE_CHAN MC13XXX_ADC_MODE_SINGLE_CHAN -#define MC13783_ADC_MODE_MULT_CHAN MC13XXX_ADC_MODE_MULT_CHAN - -static inline int mc13783_adc_do_conversion(struct mc13783 *mc13783, - unsigned int mode, unsigned int channel, unsigned int *sample) -{ - return mc13xxx_adc_do_conversion(mc13783_to_mc13xxx(mc13783), mode, - channel, sample); -} - - #define MC13783_REG_SW1A 0 #define MC13783_REG_SW1B 1 #define MC13783_REG_SW2A 2 -- cgit v1.2.3-70-g09d2 From 5da721c87aee3d94cfc48384073c2ec51a0b9a3b Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 15 Sep 2011 18:54:53 +0200 Subject: mfd: Support software initiated shutdown of WM831x PMICs In systems where there is no hardware signal from the processor to the PMIC to initiate the final power off sequence we must initiate the shutdown with a register write to the PMIC. Support such systems in the driver. Since this may prevent a full shutdown of the system platform data is used to enable the feature. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-core.c | 11 +++++++++++ drivers/mfd/wm831x-i2c.c | 8 ++++++++ drivers/mfd/wm831x-spi.c | 8 ++++++++ include/linux/mfd/wm831x/core.h | 3 +++ include/linux/mfd/wm831x/pdata.h | 3 +++ 5 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/wm831x-core.c b/drivers/mfd/wm831x-core.c index 282e76ab678..099b6104d15 100644 --- a/drivers/mfd/wm831x-core.c +++ b/drivers/mfd/wm831x-core.c @@ -24,6 +24,7 @@ #include #include #include +#include #include /* Current settings - values are 2*2^(reg_val/4) microamps. These are @@ -1305,6 +1306,7 @@ int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq) mutex_init(&wm831x->io_lock); mutex_init(&wm831x->key_lock); dev_set_drvdata(wm831x->dev, wm831x); + wm831x->soft_shutdown = pdata->soft_shutdown; ret = wm831x_reg_read(wm831x, WM831X_PARENT_ID); if (ret < 0) { @@ -1604,6 +1606,15 @@ int wm831x_device_suspend(struct wm831x *wm831x) return 0; } +void wm831x_device_shutdown(struct wm831x *wm831x) +{ + if (wm831x->soft_shutdown) { + dev_info(wm831x->dev, "Initiating shutdown...\n"); + wm831x_set_bits(wm831x, WM831X_POWER_STATE, WM831X_CHIP_ON, 0); + } +} +EXPORT_SYMBOL_GPL(wm831x_device_shutdown); + MODULE_DESCRIPTION("Core support for the WM831X AudioPlus PMIC"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Mark Brown"); diff --git a/drivers/mfd/wm831x-i2c.c b/drivers/mfd/wm831x-i2c.c index a06cbc73971..3ff8c13db2a 100644 --- a/drivers/mfd/wm831x-i2c.c +++ b/drivers/mfd/wm831x-i2c.c @@ -109,6 +109,13 @@ static int wm831x_i2c_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_i2c_shutdown(struct i2c_client *i2c) +{ + struct wm831x *wm831x = i2c_get_clientdata(i2c); + + wm831x_device_shutdown(wm831x); +} + static const struct i2c_device_id wm831x_i2c_id[] = { { "wm8310", WM8310 }, { "wm8311", WM8311 }, @@ -133,6 +140,7 @@ static struct i2c_driver wm831x_i2c_driver = { }, .probe = wm831x_i2c_probe, .remove = wm831x_i2c_remove, + .shutdown = wm831x_i2c_shutdown, .id_table = wm831x_i2c_id, }; diff --git a/drivers/mfd/wm831x-spi.c b/drivers/mfd/wm831x-spi.c index eed8e4f7a5a..8e8138ba026 100644 --- a/drivers/mfd/wm831x-spi.c +++ b/drivers/mfd/wm831x-spi.c @@ -121,6 +121,13 @@ static int wm831x_spi_suspend(struct device *dev) return wm831x_device_suspend(wm831x); } +static void wm831x_spi_shutdown(struct spi_device *spi) +{ + struct wm831x *wm831x = dev_get_drvdata(&spi->dev); + + wm831x_device_shutdown(wm831x); +} + static const struct dev_pm_ops wm831x_spi_pm = { .freeze = wm831x_spi_suspend, .suspend = wm831x_spi_suspend, @@ -146,6 +153,7 @@ static struct spi_driver wm8311_spi_driver = { }, .probe = wm831x_spi_probe, .remove = __devexit_p(wm831x_spi_remove), + .shutdown = wm831x_spi_shutdown, }; static struct spi_driver wm8312_spi_driver = { diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index 8dda8ded5cd..fb3e84f92e9 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -374,6 +374,8 @@ struct wm831x { int irq_masks_cur[WM831X_NUM_IRQ_REGS]; /* Currently active value */ int irq_masks_cache[WM831X_NUM_IRQ_REGS]; /* Cached hardware value */ + bool soft_shutdown; + /* Chip revision based flags */ unsigned has_gpio_ena:1; /* Has GPIO enable bit */ unsigned has_cs_sts:1; /* Has current sink status bit */ @@ -412,6 +414,7 @@ int wm831x_bulk_read(struct wm831x *wm831x, unsigned short reg, int wm831x_device_init(struct wm831x *wm831x, unsigned long id, int irq); void wm831x_device_exit(struct wm831x *wm831x); int wm831x_device_suspend(struct wm831x *wm831x); +void wm831x_device_shutdown(struct wm831x *wm831x); int wm831x_irq_init(struct wm831x *wm831x, int irq); void wm831x_irq_exit(struct wm831x *wm831x); void wm831x_auxadc_init(struct wm831x *wm831x); diff --git a/include/linux/mfd/wm831x/pdata.h b/include/linux/mfd/wm831x/pdata.h index 0ba24599fe5..1d7a3f7b3b5 100644 --- a/include/linux/mfd/wm831x/pdata.h +++ b/include/linux/mfd/wm831x/pdata.h @@ -123,6 +123,9 @@ struct wm831x_pdata { /** Disable the touchscreen */ bool disable_touch; + /** The driver should initiate a power off sequence during shutdown */ + bool soft_shutdown; + int irq_base; int gpio_base; int gpio_defaults[WM831X_GPIO_NUM]; -- cgit v1.2.3-70-g09d2 From 5ab9059d7f2055f434140046e74d3d811e4cbb15 Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Sun, 18 Sep 2011 17:57:35 +0200 Subject: mfd: Remove unused mc13xxx defines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Philippe Rétornaz Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- include/linux/mfd/mc13xxx.h | 2 -- 1 file changed, 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 6e7c0ac36d0..1acf9cbf5f2 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -145,8 +145,6 @@ struct mc13xxx_platform_data { #define MC13XXX_USE_CODEC (1 << 1) #define MC13XXX_USE_ADC (1 << 2) #define MC13XXX_USE_RTC (1 << 3) -#define MC13XXX_USE_REGULATOR (1 << 4) -#define MC13XXX_USE_LED (1 << 5) unsigned int flags; struct mc13xxx_regulator_platform_data regulators; -- cgit v1.2.3-70-g09d2 From 30fc7ac3f62945a714d9842edae313a757efb49d Mon Sep 17 00:00:00 2001 From: Philippe Rétornaz Date: Sun, 18 Sep 2011 18:10:53 +0200 Subject: input: Add power button support for mc13783 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds support for the power-on buttons of MC13783 PMIC. Signed-off-by: Philippe Rétornaz Acked-by: Dmitry Torokhov Signed-off-by: Uwe Kleine-König Signed-off-by: Samuel Ortiz --- drivers/input/misc/Kconfig | 10 ++ drivers/input/misc/Makefile | 1 + drivers/input/misc/mc13783-pwrbutton.c | 282 +++++++++++++++++++++++++++++++++ drivers/mfd/mc13xxx-core.c | 9 ++ include/linux/mfd/mc13xxx.h | 17 ++ 5 files changed, 319 insertions(+) create mode 100644 drivers/input/misc/mc13783-pwrbutton.c (limited to 'include/linux') diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index c9104bb4db0..7331229e234 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -100,6 +100,16 @@ config INPUT_MAX8925_ONKEY To compile this driver as a module, choose M here: the module will be called max8925_onkey. +config INPUT_MC13783_PWRBUTTON + tristate "MC13783 ON buttons" + depends on MFD_MC13783 + help + Support the ON buttons of MC13783 PMIC as an input device + reporting power button status. + + To compile this driver as a module, choose M here: the module + will be called mc13783-pwrbutton. + config INPUT_MMA8450 tristate "MMA8450 - Freescale's 3-Axis, 8/12-bit Digital Accelerometer" depends on I2C diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 299ad5edba8..f54ccc2d9cb 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o obj-$(CONFIG_INPUT_KXTJ9) += kxtj9.o obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o obj-$(CONFIG_INPUT_MAX8925_ONKEY) += max8925_onkey.o +obj-$(CONFIG_INPUT_MC13783_PWRBUTTON) += mc13783-pwrbutton.o obj-$(CONFIG_INPUT_MMA8450) += mma8450.o obj-$(CONFIG_INPUT_MPU3050) += mpu3050.o obj-$(CONFIG_INPUT_PCAP) += pcap_keys.o diff --git a/drivers/input/misc/mc13783-pwrbutton.c b/drivers/input/misc/mc13783-pwrbutton.c new file mode 100644 index 00000000000..09b05228865 --- /dev/null +++ b/drivers/input/misc/mc13783-pwrbutton.c @@ -0,0 +1,282 @@ +/** + * Copyright (C) 2011 Philippe Rétornaz + * + * Based on twl4030-pwrbutton driver by: + * Peter De Schrijver + * Felipe Balbi + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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, Suite 500, Boston, MA 02110-1335 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mc13783_pwrb { + struct input_dev *pwr; + struct mc13xxx *mc13783; +#define MC13783_PWRB_B1_POL_INVERT (1 << 0) +#define MC13783_PWRB_B2_POL_INVERT (1 << 1) +#define MC13783_PWRB_B3_POL_INVERT (1 << 2) + int flags; + unsigned short keymap[3]; +}; + +#define MC13783_REG_INTERRUPT_SENSE_1 5 +#define MC13783_IRQSENSE1_ONOFD1S (1 << 3) +#define MC13783_IRQSENSE1_ONOFD2S (1 << 4) +#define MC13783_IRQSENSE1_ONOFD3S (1 << 5) + +#define MC13783_REG_POWER_CONTROL_2 15 +#define MC13783_POWER_CONTROL_2_ON1BDBNC 4 +#define MC13783_POWER_CONTROL_2_ON2BDBNC 6 +#define MC13783_POWER_CONTROL_2_ON3BDBNC 8 +#define MC13783_POWER_CONTROL_2_ON1BRSTEN (1 << 1) +#define MC13783_POWER_CONTROL_2_ON2BRSTEN (1 << 2) +#define MC13783_POWER_CONTROL_2_ON3BRSTEN (1 << 3) + +static irqreturn_t button_irq(int irq, void *_priv) +{ + struct mc13783_pwrb *priv = _priv; + int val; + + mc13xxx_irq_ack(priv->mc13783, irq); + mc13xxx_reg_read(priv->mc13783, MC13783_REG_INTERRUPT_SENSE_1, &val); + + switch (irq) { + case MC13783_IRQ_ONOFD1: + val = val & MC13783_IRQSENSE1_ONOFD1S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B1_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[0], val); + break; + + case MC13783_IRQ_ONOFD2: + val = val & MC13783_IRQSENSE1_ONOFD2S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B2_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[1], val); + break; + + case MC13783_IRQ_ONOFD3: + val = val & MC13783_IRQSENSE1_ONOFD3S ? 1 : 0; + if (priv->flags & MC13783_PWRB_B3_POL_INVERT) + val ^= 1; + input_report_key(priv->pwr, priv->keymap[2], val); + break; + } + + input_sync(priv->pwr); + + return IRQ_HANDLED; +} + +static int __devinit mc13783_pwrbutton_probe(struct platform_device *pdev) +{ + const struct mc13xxx_buttons_platform_data *pdata; + struct mc13xxx *mc13783 = dev_get_drvdata(pdev->dev.parent); + struct input_dev *pwr; + struct mc13783_pwrb *priv; + int err = 0; + int reg = 0; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) { + dev_err(&pdev->dev, "missing platform data\n"); + return -ENODEV; + } + + pwr = input_allocate_device(); + if (!pwr) { + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + return -ENOMEM; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) { + err = -ENOMEM; + dev_dbg(&pdev->dev, "Can't allocate power button\n"); + goto free_input_dev; + } + + reg |= (pdata->b1on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON1BDBNC; + reg |= (pdata->b2on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON2BDBNC; + reg |= (pdata->b3on_flags & 0x3) << MC13783_POWER_CONTROL_2_ON3BDBNC; + + priv->pwr = pwr; + priv->mc13783 = mc13783; + + mc13xxx_lock(mc13783); + + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[0] = pdata->b1on_key; + if (pdata->b1on_key != KEY_RESERVED) + __set_bit(pdata->b1on_key, pwr->keybit); + + if (pdata->b1on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B1_POL_INVERT; + + if (pdata->b1on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON1BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD1, + button_irq, "b1on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_priv; + } + } + + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[1] = pdata->b2on_key; + if (pdata->b2on_key != KEY_RESERVED) + __set_bit(pdata->b2on_key, pwr->keybit); + + if (pdata->b2on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B2_POL_INVERT; + + if (pdata->b2on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON2BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD2, + button_irq, "b2on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq\n"); + goto free_irq_b1; + } + } + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) { + priv->keymap[2] = pdata->b3on_key; + if (pdata->b3on_key != KEY_RESERVED) + __set_bit(pdata->b3on_key, pwr->keybit); + + if (pdata->b3on_flags & MC13783_BUTTON_POL_INVERT) + priv->flags |= MC13783_PWRB_B3_POL_INVERT; + + if (pdata->b3on_flags & MC13783_BUTTON_RESET_EN) + reg |= MC13783_POWER_CONTROL_2_ON3BRSTEN; + + err = mc13xxx_irq_request(mc13783, MC13783_IRQ_ONOFD3, + button_irq, "b3on", priv); + if (err) { + dev_dbg(&pdev->dev, "Can't request irq: %d\n", err); + goto free_irq_b2; + } + } + + mc13xxx_reg_rmw(mc13783, MC13783_REG_POWER_CONTROL_2, 0x3FE, reg); + + mc13xxx_unlock(mc13783); + + pwr->name = "mc13783_pwrbutton"; + pwr->phys = "mc13783_pwrbutton/input0"; + pwr->dev.parent = &pdev->dev; + + pwr->keycode = priv->keymap; + pwr->keycodemax = ARRAY_SIZE(priv->keymap); + pwr->keycodesize = sizeof(priv->keymap[0]); + __set_bit(EV_KEY, pwr->evbit); + + err = input_register_device(pwr); + if (err) { + dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); + goto free_irq; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +free_irq: + mc13xxx_lock(mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD3, priv); + +free_irq_b2: + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD2, priv); + +free_irq_b1: + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(mc13783, MC13783_IRQ_ONOFD1, priv); + +free_priv: + mc13xxx_unlock(mc13783); + kfree(priv); + +free_input_dev: + input_free_device(pwr); + + return err; +} + +static int __devexit mc13783_pwrbutton_remove(struct platform_device *pdev) +{ + struct mc13783_pwrb *priv = platform_get_drvdata(pdev); + const struct mc13xxx_buttons_platform_data *pdata; + + pdata = dev_get_platdata(&pdev->dev); + + mc13xxx_lock(priv->mc13783); + + if (pdata->b3on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD3, priv); + if (pdata->b2on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD2, priv); + if (pdata->b1on_flags & MC13783_BUTTON_ENABLE) + mc13xxx_irq_free(priv->mc13783, MC13783_IRQ_ONOFD1, priv); + + mc13xxx_unlock(priv->mc13783); + + input_unregister_device(priv->pwr); + kfree(priv); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +struct platform_driver mc13783_pwrbutton_driver = { + .probe = mc13783_pwrbutton_probe, + .remove = __devexit_p(mc13783_pwrbutton_remove), + .driver = { + .name = "mc13783-pwrbutton", + .owner = THIS_MODULE, + }, +}; + +static int __init mc13783_pwrbutton_init(void) +{ + return platform_driver_register(&mc13783_pwrbutton_driver); +} +module_init(mc13783_pwrbutton_init); + +static void __exit mc13783_pwrbutton_exit(void) +{ + platform_driver_unregister(&mc13783_pwrbutton_driver); +} +module_exit(mc13783_pwrbutton_exit); + +MODULE_ALIAS("platform:mc13783-pwrbutton"); +MODULE_DESCRIPTION("MC13783 Power Button"); +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Philippe Retornaz"); diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index e55b2213623..edcd397cae1 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -703,6 +703,11 @@ static int mc13xxx_probe(struct spi_device *spi) enum mc13xxx_id id; int ret; + if (!pdata) { + dev_err(&spi->dev, "invalid platform data\n"); + return -EINVAL; + } + mc13xxx = kzalloc(sizeof(*mc13xxx), GFP_KERNEL); if (!mc13xxx) return -ENOMEM; @@ -763,6 +768,10 @@ err_revision: mc13xxx_add_subdevice_pdata(mc13xxx, "%s-led", pdata->leds, sizeof(*pdata->leds)); + if (pdata->buttons) + mc13xxx_add_subdevice_pdata(mc13xxx, "%s-pwrbutton", + pdata->buttons, sizeof(*pdata->buttons)); + return 0; } diff --git a/include/linux/mfd/mc13xxx.h b/include/linux/mfd/mc13xxx.h index 1acf9cbf5f2..3816c2fac0a 100644 --- a/include/linux/mfd/mc13xxx.h +++ b/include/linux/mfd/mc13xxx.h @@ -140,6 +140,22 @@ struct mc13xxx_leds_platform_data { char tc3_period; }; +struct mc13xxx_buttons_platform_data { +#define MC13783_BUTTON_DBNC_0MS 0 +#define MC13783_BUTTON_DBNC_30MS 1 +#define MC13783_BUTTON_DBNC_150MS 2 +#define MC13783_BUTTON_DBNC_750MS 3 +#define MC13783_BUTTON_ENABLE (1 << 2) +#define MC13783_BUTTON_POL_INVERT (1 << 3) +#define MC13783_BUTTON_RESET_EN (1 << 4) + int b1on_flags; + unsigned short b1on_key; + int b2on_flags; + unsigned short b2on_key; + int b3on_flags; + unsigned short b3on_key; +}; + struct mc13xxx_platform_data { #define MC13XXX_USE_TOUCHSCREEN (1 << 0) #define MC13XXX_USE_CODEC (1 << 1) @@ -149,6 +165,7 @@ struct mc13xxx_platform_data { struct mc13xxx_regulator_platform_data regulators; struct mc13xxx_leds_platform_data *leds; + struct mc13xxx_buttons_platform_data *buttons; }; #define MC13XXX_ADC_MODE_TS 1 -- cgit v1.2.3-70-g09d2 From 7583a213ec3bde3082547ee37ad96214513bc1cb Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Fri, 16 Sep 2011 13:21:47 +0100 Subject: mfd: Simulate active high IRQs with wm831x In order to ease system integration provide a simulation of active high IRQs on the GPIOs by polling the GPIO status when an IRQ is generated. This isn't ideal on several fronts and will miss initially active IRQs in the current implementation but it should work well for most cases. Signed-off-by: Mark Brown Signed-off-by: Samuel Ortiz --- drivers/mfd/wm831x-irq.c | 22 +++++++++++++++++++++- include/linux/mfd/wm831x/core.h | 1 + 2 files changed, 22 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/mfd/wm831x-irq.c b/drivers/mfd/wm831x-irq.c index a10937cfff4..f4747a4a9a9 100644 --- a/drivers/mfd/wm831x-irq.c +++ b/drivers/mfd/wm831x-irq.c @@ -420,12 +420,19 @@ static int wm831x_irq_set_type(struct irq_data *data, unsigned int type) switch (type) { case IRQ_TYPE_EDGE_BOTH: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_INT_MODE; + wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_RISING: wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; + wm831x->gpio_level[irq] = false; break; case IRQ_TYPE_EDGE_FALLING: wm831x->gpio_update[irq] = 0x10000; + wm831x->gpio_level[irq] = false; + break; + case IRQ_TYPE_LEVEL_HIGH: + wm831x->gpio_update[irq] = 0x10000 | WM831X_GPN_POL; + wm831x->gpio_level[irq] = true; break; default: return -EINVAL; @@ -449,7 +456,7 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) { struct wm831x *wm831x = data; unsigned int i; - int primary, status_addr; + int primary, status_addr, ret; int status_regs[WM831X_NUM_IRQ_REGS] = { 0 }; int read[WM831X_NUM_IRQ_REGS] = { 0 }; int *status; @@ -507,6 +514,19 @@ static irqreturn_t wm831x_irq_thread(int irq, void *data) if (*status & wm831x_irqs[i].mask) handle_nested_irq(wm831x->irq_base + i); + + /* Simulate an edge triggered IRQ by polling the input + * status. This is sucky but improves interoperability. + */ + if (primary == WM831X_GP_INT && + wm831x->gpio_level[i - WM831X_IRQ_GPIO_1]) { + ret = wm831x_reg_read(wm831x, WM831X_GPIO_LEVEL); + while (ret & 1 << (i - WM831X_IRQ_GPIO_1)) { + handle_nested_irq(wm831x->irq_base + i); + ret = wm831x_reg_read(wm831x, + WM831X_GPIO_LEVEL); + } + } } out: diff --git a/include/linux/mfd/wm831x/core.h b/include/linux/mfd/wm831x/core.h index fb3e84f92e9..272f7fb5f8b 100644 --- a/include/linux/mfd/wm831x/core.h +++ b/include/linux/mfd/wm831x/core.h @@ -385,6 +385,7 @@ struct wm831x { /* Used by the interrupt controller code to post writes */ int gpio_update[WM831X_NUM_GPIO_REGS]; + bool gpio_level[WM831X_NUM_GPIO_REGS]; struct mutex auxadc_lock; struct list_head auxadc_pending; -- cgit v1.2.3-70-g09d2 From 1f5a371c075a7101fe75a75cde5aad928460a42e Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 21 Sep 2011 13:03:07 +0200 Subject: mfd: Add Intel MSIC driver Add support for Intel MSIC chip found on Intel Medfield platforms. This chip embeds several subdevices: audio, ADC, GPIO, power button, etc. The driver creates platform device for each subdevice. We also provide an MSIC register access API which should replace the more generic SCU IPC interface currently used. Existing drivers can choose whether they convert to this new API or stick with the SCU IPC interface. Signed-off-by: Mika Westerberg Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/intel_msic.c | 501 +++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/intel_msic.h | 456 +++++++++++++++++++++++++++++++++++++ 4 files changed, 967 insertions(+) create mode 100644 drivers/mfd/intel_msic.c create mode 100644 include/linux/mfd/intel_msic.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index ac8bd4feb04..b01fbe27822 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -757,6 +757,15 @@ config MFD_AAT2870_CORE additional drivers must be enabled in order to use the functionality of the device. +config MFD_INTEL_MSIC + bool "Support for Intel MSIC" + depends on INTEL_SCU_IPC + select MFD_CORE + help + Select this option to enable access to Intel MSIC (Avatele + Passage) chip. This chip embeds audio, battery, GPIO, etc. + devices used in Intel Medfield platforms. + endmenu endif diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index c58020303d1..7d53a7c530c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -102,3 +102,4 @@ 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_AAT2870_CORE) += aat2870-core.o +obj-$(CONFIG_MFD_INTEL_MSIC) += intel_msic.o diff --git a/drivers/mfd/intel_msic.c b/drivers/mfd/intel_msic.c new file mode 100644 index 00000000000..bd086b9e852 --- /dev/null +++ b/drivers/mfd/intel_msic.c @@ -0,0 +1,501 @@ +/* + * Driver for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#define MSIC_VENDOR(id) ((id >> 6) & 3) +#define MSIC_VERSION(id) (id & 0x3f) +#define MSIC_MAJOR(id) ('A' + ((id >> 3) & 7)) +#define MSIC_MINOR(id) (id & 7) + +/* + * MSIC interrupt tree is readable from SRAM at INTEL_MSIC_IRQ_PHYS_BASE. + * Since IRQ block starts from address 0x002 we need to substract that from + * the actual IRQ status register address. + */ +#define MSIC_IRQ_STATUS(x) (INTEL_MSIC_IRQ_PHYS_BASE + ((x) - 2)) +#define MSIC_IRQ_STATUS_ACCDET MSIC_IRQ_STATUS(INTEL_MSIC_ACCDET) + +/* + * The SCU hardware has limitation of 16 bytes per read/write buffer on + * Medfield. + */ +#define SCU_IPC_RWBUF_LIMIT 16 + +/** + * struct intel_msic - an MSIC MFD instance + * @pdev: pointer to the platform device + * @vendor: vendor ID + * @version: chip version + * @irq_base: base address of the mapped MSIC SRAM interrupt tree + */ +struct intel_msic { + struct platform_device *pdev; + unsigned vendor; + unsigned version; + void __iomem *irq_base; +}; + +static struct resource msic_touch_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_adc_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_battery_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_gpio_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_audio_resources[] = { + { + .name = "IRQ", + .flags = IORESOURCE_IRQ, + }, + /* + * We will pass IRQ_BASE to the driver now but this can be removed + * when/if the driver starts to use intel_msic_irq_read(). + */ + { + .name = "IRQ_BASE", + .flags = IORESOURCE_MEM, + .start = MSIC_IRQ_STATUS_ACCDET, + .end = MSIC_IRQ_STATUS_ACCDET, + }, +}; + +static struct resource msic_hdmi_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_thermal_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_power_btn_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +static struct resource msic_ocd_resources[] = { + { + .flags = IORESOURCE_IRQ, + }, +}; + +/* + * Devices that are part of the MSIC and are available via firmware + * populated SFI DEVS table. + */ +static struct mfd_cell msic_devs[] = { + [INTEL_MSIC_BLOCK_TOUCH] = { + .name = "msic_touch", + .num_resources = ARRAY_SIZE(msic_touch_resources), + .resources = msic_touch_resources, + }, + [INTEL_MSIC_BLOCK_ADC] = { + .name = "msic_adc", + .num_resources = ARRAY_SIZE(msic_adc_resources), + .resources = msic_adc_resources, + }, + [INTEL_MSIC_BLOCK_BATTERY] = { + .name = "msic_battery", + .num_resources = ARRAY_SIZE(msic_battery_resources), + .resources = msic_battery_resources, + }, + [INTEL_MSIC_BLOCK_GPIO] = { + .name = "msic_gpio", + .num_resources = ARRAY_SIZE(msic_gpio_resources), + .resources = msic_gpio_resources, + }, + [INTEL_MSIC_BLOCK_AUDIO] = { + .name = "msic_audio", + .num_resources = ARRAY_SIZE(msic_audio_resources), + .resources = msic_audio_resources, + }, + [INTEL_MSIC_BLOCK_HDMI] = { + .name = "msic_hdmi", + .num_resources = ARRAY_SIZE(msic_hdmi_resources), + .resources = msic_hdmi_resources, + }, + [INTEL_MSIC_BLOCK_THERMAL] = { + .name = "msic_thermal", + .num_resources = ARRAY_SIZE(msic_thermal_resources), + .resources = msic_thermal_resources, + }, + [INTEL_MSIC_BLOCK_POWER_BTN] = { + .name = "msic_power_btn", + .num_resources = ARRAY_SIZE(msic_power_btn_resources), + .resources = msic_power_btn_resources, + }, + [INTEL_MSIC_BLOCK_OCD] = { + .name = "msic_ocd", + .num_resources = ARRAY_SIZE(msic_ocd_resources), + .resources = msic_ocd_resources, + }, +}; + +/* + * Other MSIC related devices which are not directly available via SFI DEVS + * table. These can be pseudo devices, regulators etc. which are needed for + * different purposes. + * + * These devices appear only after the MSIC driver itself is initialized so + * we can guarantee that the SCU IPC interface is ready. + */ +static struct mfd_cell msic_other_devs[] = { + /* Audio codec in the MSIC */ + { + .id = -1, + .name = "sn95031", + }, +}; + +/** + * intel_msic_reg_read - read a single MSIC register + * @reg: register to read + * @val: register value is placed here + * + * Read a single register from MSIC. Returns %0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_read(unsigned short reg, u8 *val) +{ + return intel_scu_ipc_ioread8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_read); + +/** + * intel_msic_reg_write - write a single MSIC register + * @reg: register to write + * @val: value to write to that register + * + * Write a single MSIC register. Returns 0 on success and negative + * errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_write(unsigned short reg, u8 val) +{ + return intel_scu_ipc_iowrite8(reg, val); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_write); + +/** + * intel_msic_reg_update - update a single MSIC register + * @reg: register to update + * @val: value to write to the register + * @mask: specifies which of the bits are updated (%0 = don't update, + * %1 = update) + * + * Perform an update to a register @reg. @mask is used to specify which + * bits are updated. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask) +{ + return intel_scu_ipc_update_register(reg, val, mask); +} +EXPORT_SYMBOL_GPL(intel_msic_reg_update); + +/** + * intel_msic_bulk_read - read an array of registers + * @reg: array of register addresses to read + * @buf: array where the read values are placed + * @count: number of registers to read + * + * Function reads @count registers from the MSIC using addresses passed in + * @reg. Read values are placed in @buf. Reads are performed atomically + * wrt. MSIC. + * + * Returns %0 in case of success and negative errno in case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_readv(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_read); + +/** + * intel_msic_bulk_write - write an array of values to the MSIC registers + * @reg: array of registers to write + * @buf: values to write to each register + * @count: number of registers to write + * + * Function writes @count registers in @buf to MSIC. Writes are performed + * atomically wrt MSIC. Returns %0 in case of success and negative errno in + * case of failure. + * + * Function may sleep. + */ +int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count) +{ + if (WARN_ON(count > SCU_IPC_RWBUF_LIMIT)) + return -EINVAL; + + return intel_scu_ipc_writev(reg, buf, count); +} +EXPORT_SYMBOL_GPL(intel_msic_bulk_write); + +/** + * intel_msic_irq_read - read a register from an MSIC interrupt tree + * @msic: MSIC instance + * @reg: interrupt register (between %INTEL_MSIC_IRQLVL1 and + * %INTEL_MSIC_RESETIRQ2) + * @val: value of the register is placed here + * + * This function can be used by an MSIC subdevice interrupt handler to read + * a register value from the MSIC interrupt tree. In this way subdevice + * drivers don't have to map in the interrupt tree themselves but can just + * call this function instead. + * + * Function doesn't sleep and is callable from interrupt context. + * + * Returns %-EINVAL if @reg is outside of the allowed register region. + */ +int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, u8 *val) +{ + if (WARN_ON(reg < INTEL_MSIC_IRQLVL1 || reg > INTEL_MSIC_RESETIRQ2)) + return -EINVAL; + + *val = readb(msic->irq_base + (reg - INTEL_MSIC_IRQLVL1)); + return 0; +} +EXPORT_SYMBOL_GPL(intel_msic_irq_read); + +static int __devinit intel_msic_init_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (pdata->gpio) { + struct mfd_cell *cell = &msic_devs[INTEL_MSIC_BLOCK_GPIO]; + + cell->platform_data = pdata->gpio; + cell->pdata_size = sizeof(*pdata->gpio); + } + + if (pdata->ocd) { + unsigned gpio = pdata->ocd->gpio; + + ret = gpio_request_one(gpio, GPIOF_IN, "ocd_gpio"); + if (ret) { + dev_err(&pdev->dev, "failed to register OCD GPIO\n"); + return ret; + } + + ret = gpio_to_irq(gpio); + if (ret < 0) { + dev_err(&pdev->dev, "no IRQ number for OCD GPIO\n"); + gpio_free(gpio); + return ret; + } + + /* Update the IRQ number for the OCD */ + pdata->irq[INTEL_MSIC_BLOCK_OCD] = ret; + } + + for (i = 0; i < ARRAY_SIZE(msic_devs); i++) { + if (!pdata->irq[i]) + continue; + + ret = mfd_add_devices(&pdev->dev, -1, &msic_devs[i], 1, NULL, + pdata->irq[i]); + if (ret) + goto fail; + } + + ret = mfd_add_devices(&pdev->dev, 0, msic_other_devs, + ARRAY_SIZE(msic_other_devs), NULL, 0); + if (ret) + goto fail; + + return 0; + +fail: + mfd_remove_devices(&pdev->dev); + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); + + return ret; +} + +static void __devexit intel_msic_remove_devices(struct intel_msic *msic) +{ + struct platform_device *pdev = msic->pdev; + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + + mfd_remove_devices(&pdev->dev); + + if (pdata->ocd) + gpio_free(pdata->ocd->gpio); +} + +static int __devinit intel_msic_probe(struct platform_device *pdev) +{ + struct intel_msic_platform_data *pdata = pdev->dev.platform_data; + struct intel_msic *msic; + struct resource *res; + u8 id0, id1; + int ret; + + if (!pdata) { + dev_err(&pdev->dev, "no platform data passed\n"); + return -EINVAL; + } + + /* First validate that we have an MSIC in place */ + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID0, &id0); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID0)\n"); + return -ENXIO; + } + + ret = intel_scu_ipc_ioread8(INTEL_MSIC_ID1, &id1); + if (ret) { + dev_err(&pdev->dev, "failed to identify the MSIC chip (ID1)\n"); + return -ENXIO; + } + + if (MSIC_VENDOR(id0) != MSIC_VENDOR(id1)) { + dev_err(&pdev->dev, "invalid vendor ID: %x, %x\n", id0, id1); + return -ENXIO; + } + + msic = kzalloc(sizeof(*msic), GFP_KERNEL); + if (!msic) + return -ENOMEM; + + msic->vendor = MSIC_VENDOR(id0); + msic->version = MSIC_VERSION(id0); + msic->pdev = pdev; + + /* + * Map in the MSIC interrupt tree area in SRAM. This is exposed to + * the clients via intel_msic_irq_read(). + */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "failed to get SRAM iomem resource\n"); + ret = -ENODEV; + goto fail_free_msic; + } + + res = request_mem_region(res->start, resource_size(res), pdev->name); + if (!res) { + ret = -EBUSY; + goto fail_free_msic; + } + + msic->irq_base = ioremap_nocache(res->start, resource_size(res)); + if (!msic->irq_base) { + dev_err(&pdev->dev, "failed to map SRAM memory\n"); + ret = -ENOMEM; + goto fail_release_region; + } + + platform_set_drvdata(pdev, msic); + + ret = intel_msic_init_devices(msic); + if (ret) { + dev_err(&pdev->dev, "failed to initialize MSIC devices\n"); + goto fail_unmap_mem; + } + + dev_info(&pdev->dev, "Intel MSIC version %c%d (vendor %#x)\n", + MSIC_MAJOR(msic->version), MSIC_MINOR(msic->version), + msic->vendor); + + return 0; + +fail_unmap_mem: + iounmap(msic->irq_base); +fail_release_region: + release_mem_region(res->start, resource_size(res)); +fail_free_msic: + kfree(msic); + + return ret; +} + +static int __devexit intel_msic_remove(struct platform_device *pdev) +{ + struct intel_msic *msic = platform_get_drvdata(pdev); + struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + intel_msic_remove_devices(msic); + platform_set_drvdata(pdev, NULL); + iounmap(msic->irq_base); + release_mem_region(res->start, resource_size(res)); + kfree(msic); + + return 0; +} + +static struct platform_driver intel_msic_driver = { + .probe = intel_msic_probe, + .remove = __devexit_p(intel_msic_remove), + .driver = { + .name = "intel_msic", + .owner = THIS_MODULE, + }, +}; + +static int __init intel_msic_init(void) +{ + return platform_driver_register(&intel_msic_driver); +} +module_init(intel_msic_init); + +static void __exit intel_msic_exit(void) +{ + platform_driver_unregister(&intel_msic_driver); +} +module_exit(intel_msic_exit); + +MODULE_DESCRIPTION("Driver for Intel MSIC"); +MODULE_AUTHOR("Mika Westerberg "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/intel_msic.h b/include/linux/mfd/intel_msic.h new file mode 100644 index 00000000000..439a7a617bc --- /dev/null +++ b/include/linux/mfd/intel_msic.h @@ -0,0 +1,456 @@ +/* + * include/linux/mfd/intel_msic.h - Core interface for Intel MSIC + * + * Copyright (C) 2011, Intel Corporation + * Author: Mika Westerberg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __LINUX_MFD_INTEL_MSIC_H__ +#define __LINUX_MFD_INTEL_MSIC_H__ + +/* ID */ +#define INTEL_MSIC_ID0 0x000 /* RO */ +#define INTEL_MSIC_ID1 0x001 /* RO */ + +/* IRQ */ +#define INTEL_MSIC_IRQLVL1 0x002 +#define INTEL_MSIC_ADC1INT 0x003 +#define INTEL_MSIC_CCINT 0x004 +#define INTEL_MSIC_PWRSRCINT 0x005 +#define INTEL_MSIC_PWRSRCINT1 0x006 +#define INTEL_MSIC_CHRINT 0x007 +#define INTEL_MSIC_CHRINT1 0x008 +#define INTEL_MSIC_RTCIRQ 0x009 +#define INTEL_MSIC_GPIO0LVIRQ 0x00a +#define INTEL_MSIC_GPIO1LVIRQ 0x00b +#define INTEL_MSIC_GPIOHVIRQ 0x00c +#define INTEL_MSIC_VRINT 0x00d +#define INTEL_MSIC_OCAUDIO 0x00e +#define INTEL_MSIC_ACCDET 0x00f +#define INTEL_MSIC_RESETIRQ1 0x010 +#define INTEL_MSIC_RESETIRQ2 0x011 +#define INTEL_MSIC_MADC1INT 0x012 +#define INTEL_MSIC_MCCINT 0x013 +#define INTEL_MSIC_MPWRSRCINT 0x014 +#define INTEL_MSIC_MPWRSRCINT1 0x015 +#define INTEL_MSIC_MCHRINT 0x016 +#define INTEL_MSIC_MCHRINT1 0x017 +#define INTEL_MSIC_RTCIRQMASK 0x018 +#define INTEL_MSIC_GPIO0LVIRQMASK 0x019 +#define INTEL_MSIC_GPIO1LVIRQMASK 0x01a +#define INTEL_MSIC_GPIOHVIRQMASK 0x01b +#define INTEL_MSIC_VRINTMASK 0x01c +#define INTEL_MSIC_OCAUDIOMASK 0x01d +#define INTEL_MSIC_ACCDETMASK 0x01e +#define INTEL_MSIC_RESETIRQ1MASK 0x01f +#define INTEL_MSIC_RESETIRQ2MASK 0x020 +#define INTEL_MSIC_IRQLVL1MSK 0x021 +#define INTEL_MSIC_PBCONFIG 0x03e +#define INTEL_MSIC_PBSTATUS 0x03f /* RO */ + +/* GPIO */ +#define INTEL_MSIC_GPIO0LV7CTLO 0x040 +#define INTEL_MSIC_GPIO0LV6CTLO 0x041 +#define INTEL_MSIC_GPIO0LV5CTLO 0x042 +#define INTEL_MSIC_GPIO0LV4CTLO 0x043 +#define INTEL_MSIC_GPIO0LV3CTLO 0x044 +#define INTEL_MSIC_GPIO0LV2CTLO 0x045 +#define INTEL_MSIC_GPIO0LV1CTLO 0x046 +#define INTEL_MSIC_GPIO0LV0CTLO 0x047 +#define INTEL_MSIC_GPIO1LV7CTLOS 0x048 +#define INTEL_MSIC_GPIO1LV6CTLO 0x049 +#define INTEL_MSIC_GPIO1LV5CTLO 0x04a +#define INTEL_MSIC_GPIO1LV4CTLO 0x04b +#define INTEL_MSIC_GPIO1LV3CTLO 0x04c +#define INTEL_MSIC_GPIO1LV2CTLO 0x04d +#define INTEL_MSIC_GPIO1LV1CTLO 0x04e +#define INTEL_MSIC_GPIO1LV0CTLO 0x04f +#define INTEL_MSIC_GPIO0LV7CTLI 0x050 +#define INTEL_MSIC_GPIO0LV6CTLI 0x051 +#define INTEL_MSIC_GPIO0LV5CTLI 0x052 +#define INTEL_MSIC_GPIO0LV4CTLI 0x053 +#define INTEL_MSIC_GPIO0LV3CTLI 0x054 +#define INTEL_MSIC_GPIO0LV2CTLI 0x055 +#define INTEL_MSIC_GPIO0LV1CTLI 0x056 +#define INTEL_MSIC_GPIO0LV0CTLI 0x057 +#define INTEL_MSIC_GPIO1LV7CTLIS 0x058 +#define INTEL_MSIC_GPIO1LV6CTLI 0x059 +#define INTEL_MSIC_GPIO1LV5CTLI 0x05a +#define INTEL_MSIC_GPIO1LV4CTLI 0x05b +#define INTEL_MSIC_GPIO1LV3CTLI 0x05c +#define INTEL_MSIC_GPIO1LV2CTLI 0x05d +#define INTEL_MSIC_GPIO1LV1CTLI 0x05e +#define INTEL_MSIC_GPIO1LV0CTLI 0x05f +#define INTEL_MSIC_PWM0CLKDIV1 0x061 +#define INTEL_MSIC_PWM0CLKDIV0 0x062 +#define INTEL_MSIC_PWM1CLKDIV1 0x063 +#define INTEL_MSIC_PWM1CLKDIV0 0x064 +#define INTEL_MSIC_PWM2CLKDIV1 0x065 +#define INTEL_MSIC_PWM2CLKDIV0 0x066 +#define INTEL_MSIC_PWM0DUTYCYCLE 0x067 +#define INTEL_MSIC_PWM1DUTYCYCLE 0x068 +#define INTEL_MSIC_PWM2DUTYCYCLE 0x069 +#define INTEL_MSIC_GPIO0HV3CTLO 0x06d +#define INTEL_MSIC_GPIO0HV2CTLO 0x06e +#define INTEL_MSIC_GPIO0HV1CTLO 0x06f +#define INTEL_MSIC_GPIO0HV0CTLO 0x070 +#define INTEL_MSIC_GPIO1HV3CTLO 0x071 +#define INTEL_MSIC_GPIO1HV2CTLO 0x072 +#define INTEL_MSIC_GPIO1HV1CTLO 0x073 +#define INTEL_MSIC_GPIO1HV0CTLO 0x074 +#define INTEL_MSIC_GPIO0HV3CTLI 0x075 +#define INTEL_MSIC_GPIO0HV2CTLI 0x076 +#define INTEL_MSIC_GPIO0HV1CTLI 0x077 +#define INTEL_MSIC_GPIO0HV0CTLI 0x078 +#define INTEL_MSIC_GPIO1HV3CTLI 0x079 +#define INTEL_MSIC_GPIO1HV2CTLI 0x07a +#define INTEL_MSIC_GPIO1HV1CTLI 0x07b +#define INTEL_MSIC_GPIO1HV0CTLI 0x07c + +/* SVID */ +#define INTEL_MSIC_SVIDCTRL0 0x080 +#define INTEL_MSIC_SVIDCTRL1 0x081 +#define INTEL_MSIC_SVIDCTRL2 0x082 +#define INTEL_MSIC_SVIDTXLASTPKT3 0x083 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT2 0x084 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT1 0x085 /* RO */ +#define INTEL_MSIC_SVIDTXLASTPKT0 0x086 /* RO */ +#define INTEL_MSIC_SVIDPKTOUTBYTE3 0x087 +#define INTEL_MSIC_SVIDPKTOUTBYTE2 0x088 +#define INTEL_MSIC_SVIDPKTOUTBYTE1 0x089 +#define INTEL_MSIC_SVIDPKTOUTBYTE0 0x08a +#define INTEL_MSIC_SVIDRXVPDEBUG1 0x08b +#define INTEL_MSIC_SVIDRXVPDEBUG0 0x08c +#define INTEL_MSIC_SVIDRXLASTPKT3 0x08d /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT2 0x08e /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT1 0x08f /* RO */ +#define INTEL_MSIC_SVIDRXLASTPKT0 0x090 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS3 0x091 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS2 0x092 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS1 0x093 /* RO */ +#define INTEL_MSIC_SVIDRXCHKSTATUS0 0x094 /* RO */ + +/* VREG */ +#define INTEL_MSIC_VCCLATCH 0x0c0 +#define INTEL_MSIC_VNNLATCH 0x0c1 +#define INTEL_MSIC_VCCCNT 0x0c2 +#define INTEL_MSIC_SMPSRAMP 0x0c3 +#define INTEL_MSIC_VNNCNT 0x0c4 +#define INTEL_MSIC_VNNAONCNT 0x0c5 +#define INTEL_MSIC_VCC122AONCNT 0x0c6 +#define INTEL_MSIC_V180AONCNT 0x0c7 +#define INTEL_MSIC_V500CNT 0x0c8 +#define INTEL_MSIC_VIHFCNT 0x0c9 +#define INTEL_MSIC_LDORAMP1 0x0ca +#define INTEL_MSIC_LDORAMP2 0x0cb +#define INTEL_MSIC_VCC108AONCNT 0x0cc +#define INTEL_MSIC_VCC108ASCNT 0x0cd +#define INTEL_MSIC_VCC108CNT 0x0ce +#define INTEL_MSIC_VCCA100ASCNT 0x0cf +#define INTEL_MSIC_VCCA100CNT 0x0d0 +#define INTEL_MSIC_VCC180AONCNT 0x0d1 +#define INTEL_MSIC_VCC180CNT 0x0d2 +#define INTEL_MSIC_VCC330CNT 0x0d3 +#define INTEL_MSIC_VUSB330CNT 0x0d4 +#define INTEL_MSIC_VCCSDIOCNT 0x0d5 +#define INTEL_MSIC_VPROG1CNT 0x0d6 +#define INTEL_MSIC_VPROG2CNT 0x0d7 +#define INTEL_MSIC_VEMMCSCNT 0x0d8 +#define INTEL_MSIC_VEMMC1CNT 0x0d9 +#define INTEL_MSIC_VEMMC2CNT 0x0da +#define INTEL_MSIC_VAUDACNT 0x0db +#define INTEL_MSIC_VHSPCNT 0x0dc +#define INTEL_MSIC_VHSNCNT 0x0dd +#define INTEL_MSIC_VHDMICNT 0x0de +#define INTEL_MSIC_VOTGCNT 0x0df +#define INTEL_MSIC_V1P35CNT 0x0e0 +#define INTEL_MSIC_V330AONCNT 0x0e1 + +/* RESET */ +#define INTEL_MSIC_CHIPCNTRL 0x100 /* WO */ +#define INTEL_MSIC_ERCONFIG 0x101 + +/* BURST */ +#define INTEL_MSIC_BATCURRENTLIMIT12 0x102 +#define INTEL_MSIC_BATTIMELIMIT12 0x103 +#define INTEL_MSIC_BATTIMELIMIT3 0x104 +#define INTEL_MSIC_BATTIMEDB 0x105 +#define INTEL_MSIC_BRSTCONFIGOUTPUTS 0x106 +#define INTEL_MSIC_BRSTCONFIGACTIONS 0x107 +#define INTEL_MSIC_BURSTCONTROLSTATUS 0x108 + +/* RTC */ +#define INTEL_MSIC_RTCB1 0x140 /* RO */ +#define INTEL_MSIC_RTCB2 0x141 /* RO */ +#define INTEL_MSIC_RTCB3 0x142 /* RO */ +#define INTEL_MSIC_RTCB4 0x143 /* RO */ +#define INTEL_MSIC_RTCOB1 0x144 +#define INTEL_MSIC_RTCOB2 0x145 +#define INTEL_MSIC_RTCOB3 0x146 +#define INTEL_MSIC_RTCOB4 0x147 +#define INTEL_MSIC_RTCAB1 0x148 +#define INTEL_MSIC_RTCAB2 0x149 +#define INTEL_MSIC_RTCAB3 0x14a +#define INTEL_MSIC_RTCAB4 0x14b +#define INTEL_MSIC_RTCWAB1 0x14c +#define INTEL_MSIC_RTCWAB2 0x14d +#define INTEL_MSIC_RTCWAB3 0x14e +#define INTEL_MSIC_RTCWAB4 0x14f +#define INTEL_MSIC_RTCSC1 0x150 +#define INTEL_MSIC_RTCSC2 0x151 +#define INTEL_MSIC_RTCSC3 0x152 +#define INTEL_MSIC_RTCSC4 0x153 +#define INTEL_MSIC_RTCSTATUS 0x154 /* RO */ +#define INTEL_MSIC_RTCCONFIG1 0x155 +#define INTEL_MSIC_RTCCONFIG2 0x156 + +/* CHARGER */ +#define INTEL_MSIC_BDTIMER 0x180 +#define INTEL_MSIC_BATTRMV 0x181 +#define INTEL_MSIC_VBUSDET 0x182 +#define INTEL_MSIC_VBUSDET1 0x183 +#define INTEL_MSIC_ADPHVDET 0x184 +#define INTEL_MSIC_ADPLVDET 0x185 +#define INTEL_MSIC_ADPDETDBDM 0x186 +#define INTEL_MSIC_LOWBATTDET 0x187 +#define INTEL_MSIC_CHRCTRL 0x188 +#define INTEL_MSIC_CHRCVOLTAGE 0x189 +#define INTEL_MSIC_CHRCCURRENT 0x18a +#define INTEL_MSIC_SPCHARGER 0x18b +#define INTEL_MSIC_CHRTTIME 0x18c +#define INTEL_MSIC_CHRCTRL1 0x18d +#define INTEL_MSIC_PWRSRCLMT 0x18e +#define INTEL_MSIC_CHRSTWDT 0x18f +#define INTEL_MSIC_WDTWRITE 0x190 /* WO */ +#define INTEL_MSIC_CHRSAFELMT 0x191 +#define INTEL_MSIC_SPWRSRCINT 0x192 /* RO */ +#define INTEL_MSIC_SPWRSRCINT1 0x193 /* RO */ +#define INTEL_MSIC_CHRLEDPWM 0x194 +#define INTEL_MSIC_CHRLEDCTRL 0x195 + +/* ADC */ +#define INTEL_MSIC_ADC1CNTL1 0x1c0 +#define INTEL_MSIC_ADC1CNTL2 0x1c1 +#define INTEL_MSIC_ADC1CNTL3 0x1c2 +#define INTEL_MSIC_ADC1OFFSETH 0x1c3 /* RO */ +#define INTEL_MSIC_ADC1OFFSETL 0x1c4 /* RO */ +#define INTEL_MSIC_ADC1ADDR0 0x1c5 +#define INTEL_MSIC_ADC1ADDR1 0x1c6 +#define INTEL_MSIC_ADC1ADDR2 0x1c7 +#define INTEL_MSIC_ADC1ADDR3 0x1c8 +#define INTEL_MSIC_ADC1ADDR4 0x1c9 +#define INTEL_MSIC_ADC1ADDR5 0x1ca +#define INTEL_MSIC_ADC1ADDR6 0x1cb +#define INTEL_MSIC_ADC1ADDR7 0x1cc +#define INTEL_MSIC_ADC1ADDR8 0x1cd +#define INTEL_MSIC_ADC1ADDR9 0x1ce +#define INTEL_MSIC_ADC1ADDR10 0x1cf +#define INTEL_MSIC_ADC1ADDR11 0x1d0 +#define INTEL_MSIC_ADC1ADDR12 0x1d1 +#define INTEL_MSIC_ADC1ADDR13 0x1d2 +#define INTEL_MSIC_ADC1ADDR14 0x1d3 +#define INTEL_MSIC_ADC1SNS0H 0x1d4 /* RO */ +#define INTEL_MSIC_ADC1SNS0L 0x1d5 /* RO */ +#define INTEL_MSIC_ADC1SNS1H 0x1d6 /* RO */ +#define INTEL_MSIC_ADC1SNS1L 0x1d7 /* RO */ +#define INTEL_MSIC_ADC1SNS2H 0x1d8 /* RO */ +#define INTEL_MSIC_ADC1SNS2L 0x1d9 /* RO */ +#define INTEL_MSIC_ADC1SNS3H 0x1da /* RO */ +#define INTEL_MSIC_ADC1SNS3L 0x1db /* RO */ +#define INTEL_MSIC_ADC1SNS4H 0x1dc /* RO */ +#define INTEL_MSIC_ADC1SNS4L 0x1dd /* RO */ +#define INTEL_MSIC_ADC1SNS5H 0x1de /* RO */ +#define INTEL_MSIC_ADC1SNS5L 0x1df /* RO */ +#define INTEL_MSIC_ADC1SNS6H 0x1e0 /* RO */ +#define INTEL_MSIC_ADC1SNS6L 0x1e1 /* RO */ +#define INTEL_MSIC_ADC1SNS7H 0x1e2 /* RO */ +#define INTEL_MSIC_ADC1SNS7L 0x1e3 /* RO */ +#define INTEL_MSIC_ADC1SNS8H 0x1e4 /* RO */ +#define INTEL_MSIC_ADC1SNS8L 0x1e5 /* RO */ +#define INTEL_MSIC_ADC1SNS9H 0x1e6 /* RO */ +#define INTEL_MSIC_ADC1SNS9L 0x1e7 /* RO */ +#define INTEL_MSIC_ADC1SNS10H 0x1e8 /* RO */ +#define INTEL_MSIC_ADC1SNS10L 0x1e9 /* RO */ +#define INTEL_MSIC_ADC1SNS11H 0x1ea /* RO */ +#define INTEL_MSIC_ADC1SNS11L 0x1eb /* RO */ +#define INTEL_MSIC_ADC1SNS12H 0x1ec /* RO */ +#define INTEL_MSIC_ADC1SNS12L 0x1ed /* RO */ +#define INTEL_MSIC_ADC1SNS13H 0x1ee /* RO */ +#define INTEL_MSIC_ADC1SNS13L 0x1ef /* RO */ +#define INTEL_MSIC_ADC1SNS14H 0x1f0 /* RO */ +#define INTEL_MSIC_ADC1SNS14L 0x1f1 /* RO */ +#define INTEL_MSIC_ADC1BV0H 0x1f2 /* RO */ +#define INTEL_MSIC_ADC1BV0L 0x1f3 /* RO */ +#define INTEL_MSIC_ADC1BV1H 0x1f4 /* RO */ +#define INTEL_MSIC_ADC1BV1L 0x1f5 /* RO */ +#define INTEL_MSIC_ADC1BV2H 0x1f6 /* RO */ +#define INTEL_MSIC_ADC1BV2L 0x1f7 /* RO */ +#define INTEL_MSIC_ADC1BV3H 0x1f8 /* RO */ +#define INTEL_MSIC_ADC1BV3L 0x1f9 /* RO */ +#define INTEL_MSIC_ADC1BI0H 0x1fa /* RO */ +#define INTEL_MSIC_ADC1BI0L 0x1fb /* RO */ +#define INTEL_MSIC_ADC1BI1H 0x1fc /* RO */ +#define INTEL_MSIC_ADC1BI1L 0x1fd /* RO */ +#define INTEL_MSIC_ADC1BI2H 0x1fe /* RO */ +#define INTEL_MSIC_ADC1BI2L 0x1ff /* RO */ +#define INTEL_MSIC_ADC1BI3H 0x200 /* RO */ +#define INTEL_MSIC_ADC1BI3L 0x201 /* RO */ +#define INTEL_MSIC_CCCNTL 0x202 +#define INTEL_MSIC_CCOFFSETH 0x203 /* RO */ +#define INTEL_MSIC_CCOFFSETL 0x204 /* RO */ +#define INTEL_MSIC_CCADCHA 0x205 /* RO */ +#define INTEL_MSIC_CCADCLA 0x206 /* RO */ + +/* AUDIO */ +#define INTEL_MSIC_AUDPLLCTRL 0x240 +#define INTEL_MSIC_DMICBUF0123 0x241 +#define INTEL_MSIC_DMICBUF45 0x242 +#define INTEL_MSIC_DMICGPO 0x244 +#define INTEL_MSIC_DMICMUX 0x245 +#define INTEL_MSIC_DMICCLK 0x246 +#define INTEL_MSIC_MICBIAS 0x247 +#define INTEL_MSIC_ADCCONFIG 0x248 +#define INTEL_MSIC_MICAMP1 0x249 +#define INTEL_MSIC_MICAMP2 0x24a +#define INTEL_MSIC_NOISEMUX 0x24b +#define INTEL_MSIC_AUDIOMUX12 0x24c +#define INTEL_MSIC_AUDIOMUX34 0x24d +#define INTEL_MSIC_AUDIOSINC 0x24e +#define INTEL_MSIC_AUDIOTXEN 0x24f +#define INTEL_MSIC_HSEPRXCTRL 0x250 +#define INTEL_MSIC_IHFRXCTRL 0x251 +#define INTEL_MSIC_VOICETXVOL 0x252 +#define INTEL_MSIC_SIDETONEVOL 0x253 +#define INTEL_MSIC_MUSICSHARVOL 0x254 +#define INTEL_MSIC_VOICETXCTRL 0x255 +#define INTEL_MSIC_HSMIXER 0x256 +#define INTEL_MSIC_DACCONFIG 0x257 +#define INTEL_MSIC_SOFTMUTE 0x258 +#define INTEL_MSIC_HSLVOLCTRL 0x259 +#define INTEL_MSIC_HSRVOLCTRL 0x25a +#define INTEL_MSIC_IHFLVOLCTRL 0x25b +#define INTEL_MSIC_IHFRVOLCTRL 0x25c +#define INTEL_MSIC_DRIVEREN 0x25d +#define INTEL_MSIC_LINEOUTCTRL 0x25e +#define INTEL_MSIC_VIB1CTRL1 0x25f +#define INTEL_MSIC_VIB1CTRL2 0x260 +#define INTEL_MSIC_VIB1CTRL3 0x261 +#define INTEL_MSIC_VIB1SPIPCM_1 0x262 +#define INTEL_MSIC_VIB1SPIPCM_2 0x263 +#define INTEL_MSIC_VIB1CTRL5 0x264 +#define INTEL_MSIC_VIB2CTRL1 0x265 +#define INTEL_MSIC_VIB2CTRL2 0x266 +#define INTEL_MSIC_VIB2CTRL3 0x267 +#define INTEL_MSIC_VIB2SPIPCM_1 0x268 +#define INTEL_MSIC_VIB2SPIPCM_2 0x269 +#define INTEL_MSIC_VIB2CTRL5 0x26a +#define INTEL_MSIC_BTNCTRL1 0x26b +#define INTEL_MSIC_BTNCTRL2 0x26c +#define INTEL_MSIC_PCM1TXSLOT01 0x26d +#define INTEL_MSIC_PCM1TXSLOT23 0x26e +#define INTEL_MSIC_PCM1TXSLOT45 0x26f +#define INTEL_MSIC_PCM1RXSLOT0123 0x270 +#define INTEL_MSIC_PCM1RXSLOT045 0x271 +#define INTEL_MSIC_PCM2TXSLOT01 0x272 +#define INTEL_MSIC_PCM2TXSLOT23 0x273 +#define INTEL_MSIC_PCM2TXSLOT45 0x274 +#define INTEL_MSIC_PCM2RXSLOT01 0x275 +#define INTEL_MSIC_PCM2RXSLOT23 0x276 +#define INTEL_MSIC_PCM2RXSLOT45 0x277 +#define INTEL_MSIC_PCM1CTRL1 0x278 +#define INTEL_MSIC_PCM1CTRL2 0x279 +#define INTEL_MSIC_PCM1CTRL3 0x27a +#define INTEL_MSIC_PCM2CTRL1 0x27b +#define INTEL_MSIC_PCM2CTRL2 0x27c + +/* HDMI */ +#define INTEL_MSIC_HDMIPUEN 0x280 +#define INTEL_MSIC_HDMISTATUS 0x281 /* RO */ + +/* Physical address of the start of the MSIC interrupt tree in SRAM */ +#define INTEL_MSIC_IRQ_PHYS_BASE 0xffff7fc0 + +/** + * struct intel_msic_gpio_pdata - platform data for the MSIC GPIO driver + * @gpio_base: base number for the GPIOs + */ +struct intel_msic_gpio_pdata { + unsigned gpio_base; +}; + +/** + * struct intel_msic_ocd_pdata - platform data for the MSIC OCD driver + * @gpio: GPIO number used for OCD interrupts + * + * The MSIC MFD driver converts @gpio into an IRQ number and passes it to + * the OCD driver as %IORESOURCE_IRQ. + */ +struct intel_msic_ocd_pdata { + unsigned gpio; +}; + +/* MSIC embedded blocks (subdevices) */ +enum intel_msic_block { + INTEL_MSIC_BLOCK_TOUCH, + INTEL_MSIC_BLOCK_ADC, + INTEL_MSIC_BLOCK_BATTERY, + INTEL_MSIC_BLOCK_GPIO, + INTEL_MSIC_BLOCK_AUDIO, + INTEL_MSIC_BLOCK_HDMI, + INTEL_MSIC_BLOCK_THERMAL, + INTEL_MSIC_BLOCK_POWER_BTN, + INTEL_MSIC_BLOCK_OCD, + + INTEL_MSIC_BLOCK_LAST, +}; + +/** + * struct intel_msic_platform_data - platform data for the MSIC driver + * @irq: array of interrupt numbers, one per device. If @irq is set to %0 + * for a given block, the corresponding platform device is not + * created. For devices which don't have an interrupt, use %0xff + * (this is same as in SFI spec). + * @gpio: platform data for the MSIC GPIO driver + * @ocd: platform data for the MSIC OCD driver + * + * Once the MSIC driver is initialized, the register interface is ready to + * use. All the platform devices for subdevices are created after the + * register interface is ready so that we can guarantee its availability to + * the subdevice drivers. + * + * Interrupt numbers are passed to the subdevices via %IORESOURCE_IRQ + * resources of the created platform device. + */ +struct intel_msic_platform_data { + int irq[INTEL_MSIC_BLOCK_LAST]; + struct intel_msic_gpio_pdata *gpio; + struct intel_msic_ocd_pdata *ocd; +}; + +struct intel_msic; + +extern int intel_msic_reg_read(unsigned short reg, u8 *val); +extern int intel_msic_reg_write(unsigned short reg, u8 val); +extern int intel_msic_reg_update(unsigned short reg, u8 val, u8 mask); +extern int intel_msic_bulk_read(unsigned short *reg, u8 *buf, size_t count); +extern int intel_msic_bulk_write(unsigned short *reg, u8 *buf, size_t count); + +/* + * pdev_to_intel_msic - gets an MSIC instance from the platform device + * @pdev: platform device pointer + * + * The client drivers need to have pointer to the MSIC instance if they + * want to call intel_msic_irq_read(). This macro can be used for + * convenience to get the MSIC pointer from @pdev where needed. This is + * _only_ valid for devices which are managed by the MSIC. + */ +#define pdev_to_intel_msic(pdev) (dev_get_drvdata(pdev->dev.parent)) + +extern int intel_msic_irq_read(struct intel_msic *msic, unsigned short reg, + u8 *val); + +#endif /* __LINUX_MFD_INTEL_MSIC_H__ */ -- cgit v1.2.3-70-g09d2 From 3d5e2cabf11a65685e5067382ba4c4a76f18fcb7 Mon Sep 17 00:00:00 2001 From: Mattias Wallin Date: Thu, 22 Sep 2011 08:22:18 +0200 Subject: mfd: ab5500 chip register access The analog baseband chip ab5500 is a multi functional chip containing regulators, charging, gpio, USB and accessory detect. It also contain various multimedia functionalities like digital encoder and audio codec. The core driver added with this patch provides register access via i2c via PRCMU. Event handling implemented as irq_chip will come in future patches since it depends on PRCMU functionality not yet implemented. Signed-off-by: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/Kconfig | 9 + drivers/mfd/Makefile | 1 + drivers/mfd/ab5500-core.c | 2312 +++++++++++++++++++++++++++++++++++++ include/linux/mfd/ab5500/ab5500.h | 140 +++ include/linux/mfd/abx500.h | 4 +- 5 files changed, 2464 insertions(+), 2 deletions(-) create mode 100644 drivers/mfd/ab5500-core.c create mode 100644 include/linux/mfd/ab5500/ab5500.h (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index b01fbe27822..8594de8b86a 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -563,6 +563,15 @@ config EZX_PCAP This enables the PCAP ASIC present on EZX Phones. This is needed for MMC, TouchScreen, Sound, USB, etc.. +config AB5500_CORE + bool "ST-Ericsson AB5500 Mixed Signal Power Management chip" + depends on ABX500_CORE && MFD_DB5500_PRCMU + select MFD_CORE + help + Select this option to enable access to AB5500 power management + chip. This connects to the db5500 chip via the I2C bus via PRCMU. + This chip embeds various other multimedia funtionalities as well. + config AB8500_CORE bool "ST-Ericsson AB8500 Mixed Signal Power Management chip" depends on GENERIC_HARDIRQS && ABX500_CORE diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7d53a7c530c..457fed8474c 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -80,6 +80,7 @@ obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o obj-$(CONFIG_AB3550_CORE) += ab3550-core.o +obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o obj-$(CONFIG_AB8500_DEBUG) += ab8500-debugfs.o obj-$(CONFIG_AB8500_GPADC) += ab8500-gpadc.o diff --git a/drivers/mfd/ab5500-core.c b/drivers/mfd/ab5500-core.c new file mode 100644 index 00000000000..afec0f2ede4 --- /dev/null +++ b/drivers/mfd/ab5500-core.c @@ -0,0 +1,2312 @@ +/* + * Copyright (C) 2007-2011 ST-Ericsson + * License terms: GNU General Public License (GPL) version 2 + * Low-level core for exclusive access to the AB5500 IC on the I2C bus + * and some basic chip-configuration. + * Author: Bengt Jonsson + * Author: Mattias Nilsson + * Author: Mattias Wallin + * Author: Rickard Andersson + * Author: Karl Komierowski + * Author: Bibek Basu + * + * TODO: Event handling with irq_chip. Waiting for PRCMU fw support. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AB5500_NUM_EVENT_REG 23 +#define AB5500_IT_LATCH0_REG 0x40 +#define AB5500_IT_MASK0_REG 0x60 + +/* Read/write operation values. */ +#define AB5500_PERM_RD (0x01) +#define AB5500_PERM_WR (0x02) + +/* Read/write permissions. */ +#define AB5500_PERM_RO (AB5500_PERM_RD) +#define AB5500_PERM_RW (AB5500_PERM_RD | AB5500_PERM_WR) + +#define AB5500_MASK_BASE (0x60) +#define AB5500_MASK_END (0x79) +#define AB5500_CHIP_ID (0x20) + +/** + * struct ab5500_bank + * @slave_addr: I2C slave_addr found in AB5500 specification + * @name: Documentation name of the bank. For reference + */ +struct ab5500_bank { + u8 slave_addr; + const char *name; +}; + +static const struct ab5500_bank bankinfo[AB5500_NUM_BANKS] = { + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP, "VIT_IO_I2C_CLK_TST_OTP"}, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST, "VDDDIG_IO_I2C_CLK_TST"}, + [AB5500_BANK_VDENC] = {AB5500_ADDR_VDENC, "VDENC"}, + [AB5500_BANK_SIM_USBSIM] = {AB5500_ADDR_SIM_USBSIM, "SIM_USBSIM"}, + [AB5500_BANK_LED] = {AB5500_ADDR_LED, "LED"}, + [AB5500_BANK_ADC] = {AB5500_ADDR_ADC, "ADC"}, + [AB5500_BANK_RTC] = {AB5500_ADDR_RTC, "RTC"}, + [AB5500_BANK_STARTUP] = {AB5500_ADDR_STARTUP, "STARTUP"}, + [AB5500_BANK_DBI_ECI] = {AB5500_ADDR_DBI_ECI, "DBI-ECI"}, + [AB5500_BANK_CHG] = {AB5500_ADDR_CHG, "CHG"}, + [AB5500_BANK_FG_BATTCOM_ACC] = { + AB5500_ADDR_FG_BATTCOM_ACC, "FG_BATCOM_ACC"}, + [AB5500_BANK_USB] = {AB5500_ADDR_USB, "USB"}, + [AB5500_BANK_IT] = {AB5500_ADDR_IT, "IT"}, + [AB5500_BANK_VIBRA] = {AB5500_ADDR_VIBRA, "VIBRA"}, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + AB5500_ADDR_AUDIO_HEADSETUSB, "AUDIO_HEADSETUSB"}, +}; + +/** + * struct ab5500_reg_range + * @first: the first address of the range + * @last: the last address of the range + * @perm: access permissions for the range + */ +struct ab5500_reg_range { + u8 first; + u8 last; + u8 perm; +}; + +/** + * struct ab5500_i2c_ranges + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_ranges { + u8 nranges; + u8 bankid; + const struct ab5500_reg_range *range; +}; + +/** + * struct ab5500_i2c_banks + * @count: the number of ranges in the list + * @range: the list of register ranges + */ +struct ab5500_i2c_banks { + u8 nbanks; + const struct ab5500_i2c_ranges *bank; +}; + +/* + * Permissible register ranges for reading and writing per device and bank. + * + * The ranges must be listed in increasing address order, and no overlaps are + * allowed. It is assumed that write permission implies read permission + * (i.e. only RO and RW permissions should be used). Ranges with write + * permission must not be split up. + */ + +#define NO_RANGE {.count = 0, .range = NULL,} +static struct ab5500_i2c_banks ab5500_bank_ranges[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_USB] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_ADC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + }, + }, + [AB5500_DEVID_LEDS] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIDEO] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_REGULATORS] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_SIM] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_RTC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CHARGER] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_CODEC] = { + .nbanks = 1, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, + [AB5500_DEVID_POWER] = { + .nbanks = 2, + .bank = (struct ab5500_i2c_ranges []) { + { + .bankid = AB5500_BANK_STARTUP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x30, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + }, + }, + { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + }, + }, + }, + }, +}; + +#define AB5500_IRQ(bank, bit) ((bank) * 8 + (bit)) + +/* I appologize for the resource names beeing a mix of upper case + * and lower case but I want them to be exact as the documentation */ +static struct mfd_cell ab5500_devs[AB5500_NUM_DEVICES] = { + [AB5500_DEVID_LEDS] = { + .name = "ab5500-leds", + .id = AB5500_DEVID_LEDS, + }, + [AB5500_DEVID_POWER] = { + .name = "ab5500-power", + .id = AB5500_DEVID_POWER, + }, + [AB5500_DEVID_REGULATORS] = { + .name = "ab5500-regulator", + .id = AB5500_DEVID_REGULATORS, + }, + [AB5500_DEVID_SIM] = { + .name = "ab5500-sim", + .id = AB5500_DEVID_SIM, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "SIMOFF", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(2, 0), /*rising*/ + .end = AB5500_IRQ(2, 1), /*falling*/ + }, + }, + }, + [AB5500_DEVID_RTC] = { + .name = "ab5500-rtc", + .id = AB5500_DEVID_RTC, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "RTC_Alarm", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 7), + .end = AB5500_IRQ(1, 7), + } + }, + }, + [AB5500_DEVID_CHARGER] = { + .name = "ab5500-charger", + .id = AB5500_DEVID_CHARGER, + }, + [AB5500_DEVID_ADC] = { + .name = "ab5500-adc", + .id = AB5500_DEVID_ADC, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "TRIGGER-0", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 0), + .end = AB5500_IRQ(0, 0), + }, + { + .name = "TRIGGER-1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 1), + .end = AB5500_IRQ(0, 1), + }, + { + .name = "TRIGGER-2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 2), + .end = AB5500_IRQ(0, 2), + }, + { + .name = "TRIGGER-3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 3), + .end = AB5500_IRQ(0, 3), + }, + { + .name = "TRIGGER-4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 4), + .end = AB5500_IRQ(0, 4), + }, + { + .name = "TRIGGER-5", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 5), + .end = AB5500_IRQ(0, 5), + }, + { + .name = "TRIGGER-6", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 6), + .end = AB5500_IRQ(0, 6), + }, + { + .name = "TRIGGER-7", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 7), + .end = AB5500_IRQ(0, 7), + }, + { + .name = "TRIGGER-VBAT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 8), + .end = AB5500_IRQ(0, 8), + }, + { + .name = "TRIGGER-VBAT-TXON", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(0, 9), + .end = AB5500_IRQ(0, 9), + }, + }, + }, + [AB5500_DEVID_FUELGAUGE] = { + .name = "ab5500-fuelgauge", + .id = AB5500_DEVID_FUELGAUGE, + .num_resources = 6, + .resources = (struct resource[]) { + { + .name = "Batt_attach", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 5), + .end = AB5500_IRQ(7, 5), + }, + { + .name = "Batt_removal", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 6), + .end = AB5500_IRQ(7, 6), + }, + { + .name = "UART_framing", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(7, 7), + .end = AB5500_IRQ(7, 7), + }, + { + .name = "UART_overrun", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 0), + .end = AB5500_IRQ(8, 0), + }, + { + .name = "UART_Rdy_RX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 1), + .end = AB5500_IRQ(8, 1), + }, + { + .name = "UART_Rdy_TX", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 2), + .end = AB5500_IRQ(8, 2), + }, + }, + }, + [AB5500_DEVID_VIBRATOR] = { + .name = "ab5500-vibrator", + .id = AB5500_DEVID_VIBRATOR, + }, + [AB5500_DEVID_CODEC] = { + .name = "ab5500-codec", + .id = AB5500_DEVID_CODEC, + .num_resources = 3, + .resources = (struct resource[]) { + { + .name = "audio_spkr1_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 5), + }, + { + .name = "audio_plllocked", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 6), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "audio_spkr2_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 4), + .end = AB5500_IRQ(17, 4), + }, + }, + }, + [AB5500_DEVID_USB] = { + .name = "ab5500-usb", + .id = AB5500_DEVID_USB, + .num_resources = 36, + .resources = (struct resource[]) { + { + .name = "Link_Update", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 1), + .end = AB5500_IRQ(22, 1), + }, + { + .name = "DCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 3), + .end = AB5500_IRQ(8, 4), + }, + { + .name = "VBUS_R", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 5), + .end = AB5500_IRQ(8, 5), + }, + { + .name = "VBUS_F", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 6), + .end = AB5500_IRQ(8, 6), + }, + { + .name = "CHGstate_10_PCVBUSchg", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(8, 7), + .end = AB5500_IRQ(8, 7), + }, + { + .name = "DCIOreverse_ovc", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 0), + .end = AB5500_IRQ(9, 0), + }, + { + .name = "USBCharDetDone", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 1), + .end = AB5500_IRQ(9, 1), + }, + { + .name = "DCIO_no_limit", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 2), + .end = AB5500_IRQ(9, 2), + }, + { + .name = "USB_suspend", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 3), + .end = AB5500_IRQ(9, 3), + }, + { + .name = "DCIOreverse_fwdcurrent", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 4), + .end = AB5500_IRQ(9, 4), + }, + { + .name = "Vbus_Imeasmax_change", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(9, 5), + .end = AB5500_IRQ(9, 6), + }, + { + .name = "OVV", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 5), + .end = AB5500_IRQ(14, 5), + }, + { + .name = "USBcharging_NOTok", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 3), + .end = AB5500_IRQ(15, 3), + }, + { + .name = "usb_adp_sensoroff", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 6), + .end = AB5500_IRQ(15, 6), + }, + { + .name = "usb_adp_probeplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 7), + .end = AB5500_IRQ(15, 7), + }, + { + .name = "usb_adp_sinkerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 0), + .end = AB5500_IRQ(16, 6), + }, + { + .name = "usb_adp_sourceerror", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 1), + .end = AB5500_IRQ(16, 1), + }, + { + .name = "usb_idgnd_r", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 2), + .end = AB5500_IRQ(16, 2), + }, + { + .name = "usb_idgnd_f", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 3), + .end = AB5500_IRQ(16, 3), + }, + { + .name = "usb_iddetR1", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 4), + .end = AB5500_IRQ(16, 5), + }, + { + .name = "usb_iddetR2", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(16, 6), + .end = AB5500_IRQ(16, 7), + }, + { + .name = "usb_iddetR3", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 0), + .end = AB5500_IRQ(17, 1), + }, + { + .name = "usb_iddetR4", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 2), + .end = AB5500_IRQ(17, 3), + }, + { + .name = "CharTempWindowOk", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(17, 7), + .end = AB5500_IRQ(18, 0), + }, + { + .name = "USB_SprDetect", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 1), + .end = AB5500_IRQ(18, 1), + }, + { + .name = "usb_adp_probe_unplug", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 2), + .end = AB5500_IRQ(18, 2), + }, + { + .name = "VBUSChDrop", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 3), + .end = AB5500_IRQ(18, 4), + }, + { + .name = "dcio_char_rec_done", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 5), + .end = AB5500_IRQ(18, 5), + }, + { + .name = "Charging_stopped_by_temp", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(18, 6), + .end = AB5500_IRQ(18, 6), + }, + { + .name = "CHGstate_11_SafeModeVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 1), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_12_comletedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 2), + .end = AB5500_IRQ(21, 2), + }, + { + .name = "CHGstate_13_completedVBUS", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 3), + .end = AB5500_IRQ(21, 3), + }, + { + .name = "CHGstate_14_FullChgDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 4), + .end = AB5500_IRQ(21, 4), + }, + { + .name = "CHGstate_15_SafeModeDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 5), + .end = AB5500_IRQ(21, 5), + }, + { + .name = "CHGstate_16_OFFsuspendDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 6), + .end = AB5500_IRQ(21, 6), + }, + { + .name = "CHGstate_17_completedDCIO", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(21, 7), + .end = AB5500_IRQ(21, 7), + }, + }, + }, + [AB5500_DEVID_OTP] = { + .name = "ab5500-otp", + .id = AB5500_DEVID_OTP, + }, + [AB5500_DEVID_VIDEO] = { + .name = "ab5500-video", + .id = AB5500_DEVID_VIDEO, + .num_resources = 1, + .resources = (struct resource[]) { + { + .name = "plugTVdet", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(22, 2), + .end = AB5500_IRQ(22, 2), + }, + }, + }, + [AB5500_DEVID_DBIECI] = { + .name = "ab5500-dbieci", + .id = AB5500_DEVID_DBIECI, + .num_resources = 10, + .resources = (struct resource[]) { + { + .name = "COLL", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 0), + .end = AB5500_IRQ(14, 0), + }, + { + .name = "RESERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 1), + .end = AB5500_IRQ(14, 1), + }, + { + .name = "FRAERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 2), + .end = AB5500_IRQ(14, 2), + }, + { + .name = "COMERR", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 3), + .end = AB5500_IRQ(14, 3), + }, + { + .name = "BSI_indicator", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 4), + .end = AB5500_IRQ(14, 4), + }, + { + .name = "SPDSET", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 6), + .end = AB5500_IRQ(14, 6), + }, + { + .name = "DSENT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(14, 7), + .end = AB5500_IRQ(14, 7), + }, + { + .name = "DREC", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 0), + .end = AB5500_IRQ(15, 0), + }, + { + .name = "ACCINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 1), + .end = AB5500_IRQ(15, 1), + }, + { + .name = "NOPINT", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(15, 2), + .end = AB5500_IRQ(15, 2), + }, + }, + }, + [AB5500_DEVID_ONSWA] = { + .name = "ab5500-onswa", + .id = AB5500_DEVID_ONSWA, + .num_resources = 2, + .resources = (struct resource[]) { + { + .name = "ONSWAn_rising", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 3), + .end = AB5500_IRQ(1, 3), + }, + { + .name = "ONSWAn_falling", + .flags = IORESOURCE_IRQ, + .start = AB5500_IRQ(1, 4), + .end = AB5500_IRQ(1, 4), + }, + }, + }, +}; + +/* + * Functionality for getting/setting register values. + */ +static int get_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, + u8 *value) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, reg, value, 1); + + mutex_unlock(&ab->access_mutex); + return err; +} + +static int get_register_page_interruptible(struct ab5500 *ab, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + int err; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + while (numregs) { + /* The hardware limit for get page is 4 */ + u8 curnum = min_t(u8, numregs, 4u); + + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + first_reg, regvals, curnum); + if (err) + goto out; + + numregs -= curnum; + first_reg += curnum; + regvals += curnum; + } + +out: + mutex_unlock(&ab->access_mutex); + return err; +} + +static int mask_and_set_register_interruptible(struct ab5500 *ab, u8 bank, + u8 reg, u8 bitmask, u8 bitvalues) +{ + int err = 0; + + if (bank >= AB5500_NUM_BANKS) + return -EINVAL; + + if (bitmask) { + u8 buf; + + err = mutex_lock_interruptible(&ab->access_mutex); + if (err) + return err; + + if (bitmask == 0xFF) /* No need to read in this case. */ + buf = bitvalues; + else { /* Read and modify the register value. */ + err = db5500_prcmu_abb_read(bankinfo[bank].slave_addr, + reg, &buf, 1); + if (err) + return err; + + buf = ((~bitmask & buf) | (bitmask & bitvalues)); + } + /* Write the new value. */ + err = db5500_prcmu_abb_write(bankinfo[bank].slave_addr, reg, + &buf, 1); + + mutex_unlock(&ab->access_mutex); + } + return err; +} + +static int +set_register_interruptible(struct ab5500 *ab, u8 bank, u8 reg, u8 value) +{ + return mask_and_set_register_interruptible(ab, bank, reg, 0xff, value); +} + +/* + * Read/write permission checking functions. + */ +static const struct ab5500_i2c_ranges *get_bankref(u8 devid, u8 bank) +{ + u8 i; + + if (devid < AB5500_NUM_DEVICES) { + for (i = 0; i < ab5500_bank_ranges[devid].nbanks; i++) { + if (ab5500_bank_ranges[devid].bank[i].bankid == bank) + return &ab5500_bank_ranges[devid].bank[i]; + } + } + return NULL; +} + +static bool page_write_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; /* range loop index */ + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + break; + if ((last_reg <= bankref->range[i].last) && + (bankref->range[i].perm & AB5500_PERM_WR)) + return true; + } + return false; +} + +static bool reg_write_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_write_allowed(devid, bank, reg, reg); +} + +static bool page_read_allowed(u8 devid, u8 bank, u8 first_reg, u8 last_reg) +{ + u8 i; + const struct ab5500_i2c_ranges *bankref; + + bankref = get_bankref(devid, bank); + if (bankref == NULL || last_reg < first_reg) + return false; + + + /* Find the range (if it exists in the list) that includes first_reg. */ + for (i = 0; i < bankref->nranges; i++) { + if (first_reg < bankref->range[i].first) + return false; + if (first_reg <= bankref->range[i].last) + break; + } + /* Make sure that the entire range up to and including last_reg is + * readable. This may span several of the ranges in the list. + */ + while ((i < bankref->nranges) && + (bankref->range[i].perm & AB5500_PERM_RD)) { + if (last_reg <= bankref->range[i].last) + return true; + if ((++i >= bankref->nranges) || + (bankref->range[i].first != + (bankref->range[i - 1].last + 1))) { + break; + } + } + return false; +} + +static bool reg_read_allowed(u8 devid, u8 bank, u8 reg) +{ + return page_read_allowed(devid, bank, reg, reg); +} + + +/* + * The exported register access functionality. + */ +static int ab5500_get_chip_id(struct device *dev) +{ + struct ab5500 *ab = dev_get_drvdata(dev->parent); + + return (int)ab->chip_id; +} + +static int ab5500_mask_and_set_register_interruptible(struct device *dev, + u8 bank, u8 reg, u8 bitmask, u8 bitvalues) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_write_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return mask_and_set_register_interruptible(ab, bank, reg, + bitmask, bitvalues); +} + +static int ab5500_set_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 value) +{ + return ab5500_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, + value); +} + +static int ab5500_get_register_interruptible(struct device *dev, u8 bank, + u8 reg, u8 *value) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !reg_read_allowed(pdev->id, bank, reg)) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_interruptible(ab, bank, reg, value); +} + +static int ab5500_get_register_page_interruptible(struct device *dev, u8 bank, + u8 first_reg, u8 *regvals, u8 numregs) +{ + struct ab5500 *ab; + struct platform_device *pdev = to_platform_device(dev); + + if ((AB5500_NUM_BANKS <= bank) || + !page_read_allowed(pdev->id, bank, + first_reg, (first_reg + numregs - 1))) + return -EINVAL; + + ab = dev_get_drvdata(dev->parent); + return get_register_page_interruptible(ab, bank, first_reg, regvals, + numregs); +} + +static int +ab5500_event_registers_startup_state_get(struct device *dev, u8 *event) +{ + struct ab5500 *ab; + + ab = dev_get_drvdata(dev->parent); + if (!ab->startup_events_read) + return -EAGAIN; /* Try again later */ + + memcpy(event, ab->startup_events, AB5500_NUM_EVENT_REG); + return 0; +} + +static struct abx500_ops ab5500_ops = { + .get_chip_id = ab5500_get_chip_id, + .get_register = ab5500_get_register_interruptible, + .set_register = ab5500_set_register_interruptible, + .get_register_page = ab5500_get_register_page_interruptible, + .set_register_page = NULL, + .mask_and_set_register = ab5500_mask_and_set_register_interruptible, + .event_registers_startup_state_get = + ab5500_event_registers_startup_state_get, + .startup_irq_enabled = NULL, +}; + +#ifdef CONFIG_DEBUG_FS +static struct ab5500_i2c_ranges ab5500_reg_ranges[AB5500_NUM_BANKS] = { + [AB5500_BANK_LED] = { + .bankid = AB5500_BANK_LED, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_ADC] = { + .bankid = AB5500_BANK_ADC, + .nranges = 6, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x1F, + .last = 0x22, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x23, + .last = 0x24, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x26, + .last = 0x2D, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x37, + .last = 0x57, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x58, + .last = 0x58, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_RTC] = { + .bankid = AB5500_BANK_RTC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x04, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x06, + .last = 0x0C, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_STARTUP] = { + .bankid = AB5500_BANK_STARTUP, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1F, + .last = 0x1F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x2E, + .last = 0x2E, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x2F, + .last = 0x30, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x51, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x61, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x66, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8C, + .last = 0x96, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xAA, + .last = 0xB4, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xB7, + .last = 0xBF, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xC1, + .last = 0xCA, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xD3, + .last = 0xE0, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_DBI_ECI] = { + .bankid = AB5500_BANK_DBI_ECI, + .nranges = 3, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x07, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x10, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x13, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_CHG] = { + .bankid = AB5500_BANK_CHG, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x11, + .last = 0x11, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x12, + .last = 0x1B, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_FG_BATTCOM_ACC] = { + .bankid = AB5500_BANK_FG_BATTCOM_ACC, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x0B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0C, + .last = 0x10, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_USB] = { + .bankid = AB5500_BANK_USB, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x83, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x87, + .last = 0x8A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x8B, + .last = 0x8B, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x91, + .last = 0x92, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x93, + .last = 0x93, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x94, + .last = 0x94, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xA8, + .last = 0xB0, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB2, + .last = 0xB2, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xB4, + .last = 0xBC, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xBF, + .last = 0xBF, + .perm = AB5500_PERM_RO, + }, + { + .first = 0xC1, + .last = 0xC5, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_IT] = { + .bankid = AB5500_BANK_IT, + .nranges = 4, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x20, + .last = 0x36, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x40, + .last = 0x56, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x60, + .last = 0x76, + .perm = AB5500_PERM_RO, + }, + }, + }, + [AB5500_BANK_VDDDIG_IO_I2C_CLK_TST] = { + .bankid = AB5500_BANK_VDDDIG_IO_I2C_CLK_TST, + .nranges = 7, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x12, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x34, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x44, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x54, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x64, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x74, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP] = { + .bankid = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + .nranges = 13, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x01, + .last = 0x01, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x02, + .last = 0x02, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0D, + .last = 0x0F, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1C, + .last = 0x1C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1E, + .last = 0x1E, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x20, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x25, + .last = 0x25, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x28, + .last = 0x2A, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x30, + .last = 0x33, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x40, + .last = 0x43, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x50, + .last = 0x53, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x60, + .last = 0x63, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x70, + .last = 0x73, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VIBRA] = { + .bankid = AB5500_BANK_VIBRA, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x10, + .last = 0x13, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xFE, + .last = 0xFE, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_AUDIO_HEADSETUSB] = { + .bankid = AB5500_BANK_AUDIO_HEADSETUSB, + .nranges = 2, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x48, + .perm = AB5500_PERM_RW, + }, + { + .first = 0xEB, + .last = 0xFB, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_SIM_USBSIM] = { + .bankid = AB5500_BANK_SIM_USBSIM, + .nranges = 1, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x13, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + }, + }, + [AB5500_BANK_VDENC] = { + .bankid = AB5500_BANK_VDENC, + .nranges = 12, + .range = (struct ab5500_reg_range[]) { + { + .first = 0x00, + .last = 0x08, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x09, + .last = 0x09, + .perm = AB5500_PERM_RO, + }, + { + .first = 0x0A, + .last = 0x12, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x15, + .last = 0x19, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x1B, + .last = 0x21, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x27, + .last = 0x2C, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x41, + .last = 0x41, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x45, + .last = 0x5B, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x5D, + .last = 0x5D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x69, + .last = 0x69, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x6C, + .last = 0x6D, + .perm = AB5500_PERM_RW, + }, + { + .first = 0x80, + .last = 0x81, + .perm = AB5500_PERM_RW, + }, + }, + }, +}; +static int ab5500_registers_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + unsigned int i; + u8 bank = (u8)ab->debug_bank; + + seq_printf(s, "ab5500 register values:\n"); + for (bank = 0; bank < AB5500_NUM_BANKS; bank++) { + seq_printf(s, " bank %u, %s (0x%x):\n", bank, + bankinfo[bank].name, + bankinfo[bank].slave_addr); + for (i = 0; i < ab5500_reg_ranges[bank].nranges; i++) { + u8 reg; + int err; + + for (reg = ab5500_reg_ranges[bank].range[i].first; + reg <= ab5500_reg_ranges[bank].range[i].last; + reg++) { + u8 value; + + err = get_register_interruptible(ab, bank, reg, + &value); + if (err < 0) { + dev_err(ab->dev, "get_reg failed %d" + "bank 0x%x reg 0x%x\n", + err, bank, reg); + return err; + } + + err = seq_printf(s, "[%d/0x%02X]: 0x%02X\n", + bank, reg, value); + if (err < 0) { + dev_err(ab->dev, + "seq_printf overflow\n"); + /* + * Error is not returned here since + * the output is wanted in any case + */ + return 0; + } + } + } + } + return 0; +} + +static int ab5500_registers_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_registers_print, inode->i_private); +} + +static const struct file_operations ab5500_registers_fops = { + .open = ab5500_registers_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static int ab5500_bank_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "%d\n", ab->debug_bank); + return 0; +} + +static int ab5500_bank_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_bank_print, inode->i_private); +} + +static ssize_t ab5500_bank_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_bank; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_bank); + if (err) + return -EINVAL; + + if (user_bank >= AB5500_NUM_BANKS) { + dev_err(ab->dev, + "debugfs error input > number of banks\n"); + return -EINVAL; + } + + ab->debug_bank = user_bank; + + return buf_size; +} + +static int ab5500_address_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + + seq_printf(s, "0x%02X\n", ab->debug_address); + return 0; +} + +static int ab5500_address_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_address_print, inode->i_private); +} + +static ssize_t ab5500_address_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_address; + int err; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf) - 1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_address); + if (err) + return -EINVAL; + if (user_address > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + ab->debug_address = user_address; + return buf_size; +} + +static int ab5500_val_print(struct seq_file *s, void *p) +{ + struct ab5500 *ab = s->private; + int err; + u8 regvalue; + + err = get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) { + dev_err(ab->dev, "get_reg failed %d, bank 0x%x" + ", reg 0x%x\n", err, ab->debug_bank, + ab->debug_address); + return -EINVAL; + } + seq_printf(s, "0x%02X\n", regvalue); + + return 0; +} + +static int ab5500_val_open(struct inode *inode, struct file *file) +{ + return single_open(file, ab5500_val_print, inode->i_private); +} + +static ssize_t ab5500_val_write(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ab5500 *ab = ((struct seq_file *)(file->private_data))->private; + char buf[32]; + int buf_size; + unsigned long user_val; + int err; + u8 regvalue; + + /* Get userspace string and assure termination */ + buf_size = min(count, (sizeof(buf)-1)); + if (copy_from_user(buf, user_buf, buf_size)) + return -EFAULT; + buf[buf_size] = 0; + + err = strict_strtoul(buf, 0, &user_val); + if (err) + return -EINVAL; + if (user_val > 0xff) { + dev_err(ab->dev, + "debugfs error input > 0xff\n"); + return -EINVAL; + } + err = mask_and_set_register_interruptible( + ab, (u8)ab->debug_bank, + (u8)ab->debug_address, 0xFF, (u8)user_val); + if (err) + return -EINVAL; + + get_register_interruptible(ab, (u8)ab->debug_bank, + (u8)ab->debug_address, ®value); + if (err) + return -EINVAL; + + return buf_size; +} + +static const struct file_operations ab5500_bank_fops = { + .open = ab5500_bank_open, + .write = ab5500_bank_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_address_fops = { + .open = ab5500_address_open, + .write = ab5500_address_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static const struct file_operations ab5500_val_fops = { + .open = ab5500_val_open, + .write = ab5500_val_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, + .owner = THIS_MODULE, +}; + +static struct dentry *ab5500_dir; +static struct dentry *ab5500_reg_file; +static struct dentry *ab5500_bank_file; +static struct dentry *ab5500_address_file; +static struct dentry *ab5500_val_file; + +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ + ab->debug_bank = AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP; + ab->debug_address = AB5500_CHIP_ID; + + ab5500_dir = debugfs_create_dir("ab5500", NULL); + if (!ab5500_dir) + goto exit_no_debugfs; + + ab5500_reg_file = debugfs_create_file("all-bank-registers", + S_IRUGO, ab5500_dir, ab, &ab5500_registers_fops); + if (!ab5500_reg_file) + goto exit_destroy_dir; + + ab5500_bank_file = debugfs_create_file("register-bank", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_bank_fops); + if (!ab5500_bank_file) + goto exit_destroy_reg; + + ab5500_address_file = debugfs_create_file("register-address", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_address_fops); + if (!ab5500_address_file) + goto exit_destroy_bank; + + ab5500_val_file = debugfs_create_file("register-value", + (S_IRUGO | S_IWUGO), ab5500_dir, ab, &ab5500_val_fops); + if (!ab5500_val_file) + goto exit_destroy_address; + + return; + +exit_destroy_address: + debugfs_remove(ab5500_address_file); +exit_destroy_bank: + debugfs_remove(ab5500_bank_file); +exit_destroy_reg: + debugfs_remove(ab5500_reg_file); +exit_destroy_dir: + debugfs_remove(ab5500_dir); +exit_no_debugfs: + dev_err(ab->dev, "failed to create debugfs entries.\n"); + return; +} + +static inline void ab5500_remove_debugfs(void) +{ + debugfs_remove(ab5500_val_file); + debugfs_remove(ab5500_address_file); + debugfs_remove(ab5500_bank_file); + debugfs_remove(ab5500_reg_file); + debugfs_remove(ab5500_dir); +} + +#else /* !CONFIG_DEBUG_FS */ +static inline void ab5500_setup_debugfs(struct ab5500 *ab) +{ +} +static inline void ab5500_remove_debugfs(void) +{ +} +#endif + +/* + * ab5500_setup : Basic set-up, datastructure creation/destruction + * and I2C interface.This sets up a default config + * in the AB5500 chip so that it will work as expected. + * @ab : Pointer to ab5500 structure + * @settings : Pointer to struct abx500_init_settings + * @size : Size of init data + */ +static int __init ab5500_setup(struct ab5500 *ab, + struct abx500_init_settings *settings, unsigned int size) +{ + int err = 0; + int i; + + for (i = 0; i < size; i++) { + err = mask_and_set_register_interruptible(ab, + settings[i].bank, + settings[i].reg, + 0xFF, settings[i].setting); + if (err) + goto exit_no_setup; + + /* If event mask register update the event mask in ab5500 */ + if ((settings[i].bank == AB5500_BANK_IT) && + (AB5500_MASK_BASE <= settings[i].reg) && + (settings[i].reg <= AB5500_MASK_END)) { + ab->mask[settings[i].reg - AB5500_MASK_BASE] = + settings[i].setting; + } + } +exit_no_setup: + return err; +} + +struct ab_family_id { + u8 id; + char *name; +}; + +static const struct ab_family_id ids[] __initdata = { + /* AB5500 */ + { + .id = AB5500_1_0, + .name = "1.0" + }, + { + .id = AB5500_1_1, + .name = "1.1" + }, + /* Terminator */ + { + .id = 0x00, + } +}; + +static int __init ab5500_probe(struct platform_device *pdev) +{ + struct ab5500 *ab; + struct ab5500_platform_data *ab5500_plf_data = + pdev->dev.platform_data; + int err; + int i; + + ab = kzalloc(sizeof(struct ab5500), GFP_KERNEL); + if (!ab) { + dev_err(&pdev->dev, + "could not allocate ab5500 device\n"); + return -ENOMEM; + } + + /* Initialize data structure */ + mutex_init(&ab->access_mutex); + mutex_init(&ab->irq_lock); + ab->dev = &pdev->dev; + + platform_set_drvdata(pdev, ab); + + /* Read chip ID register */ + err = get_register_interruptible(ab, AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP, + AB5500_CHIP_ID, &ab->chip_id); + if (err) { + dev_err(&pdev->dev, "could not communicate with the analog " + "baseband chip\n"); + goto exit_no_detect; + } + + for (i = 0; ids[i].id != 0x0; i++) { + if (ids[i].id == ab->chip_id) { + snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, + "AB5500 %s", ids[i].name); + break; + } + } + if (ids[i].id == 0x0) { + dev_err(&pdev->dev, "unknown analog baseband chip id: 0x%x\n", + ab->chip_id); + dev_err(&pdev->dev, "driver not started!\n"); + goto exit_no_detect; + } + + /* Clear and mask all interrupts */ + for (i = 0; i < AB5500_NUM_IRQ_REGS; i++) { + u8 latchreg = AB5500_IT_LATCH0_REG + i; + u8 maskreg = AB5500_IT_MASK0_REG + i; + u8 val; + + get_register_interruptible(ab, AB5500_BANK_IT, latchreg, &val); + set_register_interruptible(ab, AB5500_BANK_IT, maskreg, 0xff); + ab->mask[i] = ab->oldmask[i] = 0xff; + } + + err = abx500_register_ops(&pdev->dev, &ab5500_ops); + if (err) { + dev_err(&pdev->dev, "ab5500_register ops error\n"); + goto exit_no_detect; + } + + /* Set up and register the platform devices. */ + for (i = 0; i < AB5500_NUM_DEVICES; i++) { + ab5500_devs[i].platform_data = ab5500_plf_data->dev_data[i]; + ab5500_devs[i].pdata_size = + sizeof(ab5500_plf_data->dev_data[i]); + } + + err = mfd_add_devices(&pdev->dev, 0, ab5500_devs, + ARRAY_SIZE(ab5500_devs), NULL, + ab5500_plf_data->irq.base); + if (err) { + dev_err(&pdev->dev, "ab5500_mfd_add_device error\n"); + goto exit_no_detect; + } + + err = ab5500_setup(ab, ab5500_plf_data->init_settings, + ab5500_plf_data->init_settings_sz); + if (err) { + dev_err(&pdev->dev, "ab5500_setup error\n"); + goto exit_no_detect; + } + + ab5500_setup_debugfs(ab); + + dev_info(&pdev->dev, "detected AB chip: %s\n", &ab->chip_name[0]); + return 0; + +exit_no_detect: + kfree(ab); + return err; +} + +static int __exit ab5500_remove(struct platform_device *pdev) +{ + struct ab5500 *ab = platform_get_drvdata(pdev); + + ab5500_remove_debugfs(); + mfd_remove_devices(&pdev->dev); + kfree(ab); + return 0; +} + +static struct platform_driver ab5500_driver = { + .driver = { + .name = "ab5500-core", + .owner = THIS_MODULE, + }, + .remove = __exit_p(ab5500_remove), +}; + +static int __init ab5500_core_init(void) +{ + return platform_driver_probe(&ab5500_driver, ab5500_probe); +} + +static void __exit ab5500_core_exit(void) +{ + platform_driver_unregister(&ab5500_driver); +} + +subsys_initcall(ab5500_core_init); +module_exit(ab5500_core_exit); + +MODULE_AUTHOR("Mattias Wallin "); +MODULE_DESCRIPTION("AB5500 core driver"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/ab5500/ab5500.h b/include/linux/mfd/ab5500/ab5500.h new file mode 100644 index 00000000000..a720051ae93 --- /dev/null +++ b/include/linux/mfd/ab5500/ab5500.h @@ -0,0 +1,140 @@ +/* + * Copyright (C) ST-Ericsson 2011 + * + * License Terms: GNU General Public License v2 + */ +#ifndef MFD_AB5500_H +#define MFD_AB5500_H + +#include + +enum ab5500_devid { + AB5500_DEVID_ADC, + AB5500_DEVID_LEDS, + AB5500_DEVID_POWER, + AB5500_DEVID_REGULATORS, + AB5500_DEVID_SIM, + AB5500_DEVID_RTC, + AB5500_DEVID_CHARGER, + AB5500_DEVID_FUELGAUGE, + AB5500_DEVID_VIBRATOR, + AB5500_DEVID_CODEC, + AB5500_DEVID_USB, + AB5500_DEVID_OTP, + AB5500_DEVID_VIDEO, + AB5500_DEVID_DBIECI, + AB5500_DEVID_ONSWA, + AB5500_NUM_DEVICES, +}; + +enum ab5500_banks { + AB5500_BANK_VIT_IO_I2C_CLK_TST_OTP = 0, + AB5500_BANK_VDDDIG_IO_I2C_CLK_TST = 1, + AB5500_BANK_VDENC = 2, + AB5500_BANK_SIM_USBSIM = 3, + AB5500_BANK_LED = 4, + AB5500_BANK_ADC = 5, + AB5500_BANK_RTC = 6, + AB5500_BANK_STARTUP = 7, + AB5500_BANK_DBI_ECI = 8, + AB5500_BANK_CHG = 9, + AB5500_BANK_FG_BATTCOM_ACC = 10, + AB5500_BANK_USB = 11, + AB5500_BANK_IT = 12, + AB5500_BANK_VIBRA = 13, + AB5500_BANK_AUDIO_HEADSETUSB = 14, + AB5500_NUM_BANKS = 15, +}; + +enum ab5500_banks_addr { + AB5500_ADDR_VIT_IO_I2C_CLK_TST_OTP = 0x4A, + AB5500_ADDR_VDDDIG_IO_I2C_CLK_TST = 0x4B, + AB5500_ADDR_VDENC = 0x06, + AB5500_ADDR_SIM_USBSIM = 0x04, + AB5500_ADDR_LED = 0x10, + AB5500_ADDR_ADC = 0x0A, + AB5500_ADDR_RTC = 0x0F, + AB5500_ADDR_STARTUP = 0x03, + AB5500_ADDR_DBI_ECI = 0x07, + AB5500_ADDR_CHG = 0x0B, + AB5500_ADDR_FG_BATTCOM_ACC = 0x0C, + AB5500_ADDR_USB = 0x05, + AB5500_ADDR_IT = 0x0E, + AB5500_ADDR_VIBRA = 0x02, + AB5500_ADDR_AUDIO_HEADSETUSB = 0x0D, +}; + +/* + * Interrupt register offsets + * Bank : 0x0E + */ +#define AB5500_IT_SOURCE0_REG 0x20 +#define AB5500_IT_SOURCE1_REG 0x21 +#define AB5500_IT_SOURCE2_REG 0x22 +#define AB5500_IT_SOURCE3_REG 0x23 +#define AB5500_IT_SOURCE4_REG 0x24 +#define AB5500_IT_SOURCE5_REG 0x25 +#define AB5500_IT_SOURCE6_REG 0x26 +#define AB5500_IT_SOURCE7_REG 0x27 +#define AB5500_IT_SOURCE8_REG 0x28 +#define AB5500_IT_SOURCE9_REG 0x29 +#define AB5500_IT_SOURCE10_REG 0x2A +#define AB5500_IT_SOURCE11_REG 0x2B +#define AB5500_IT_SOURCE12_REG 0x2C +#define AB5500_IT_SOURCE13_REG 0x2D +#define AB5500_IT_SOURCE14_REG 0x2E +#define AB5500_IT_SOURCE15_REG 0x2F +#define AB5500_IT_SOURCE16_REG 0x30 +#define AB5500_IT_SOURCE17_REG 0x31 +#define AB5500_IT_SOURCE18_REG 0x32 +#define AB5500_IT_SOURCE19_REG 0x33 +#define AB5500_IT_SOURCE20_REG 0x34 +#define AB5500_IT_SOURCE21_REG 0x35 +#define AB5500_IT_SOURCE22_REG 0x36 +#define AB5500_IT_SOURCE23_REG 0x37 + +#define AB5500_NUM_IRQ_REGS 23 + +/** + * struct ab5500 + * @access_mutex: lock out concurrent accesses to the AB registers + * @dev: a pointer to the device struct for this chip driver + * @ab5500_irq: the analog baseband irq + * @irq_base: the platform configuration irq base for subdevices + * @chip_name: name of this chip variant + * @chip_id: 8 bit chip ID for this chip variant + * @irq_lock: a lock to protect the mask + * @abb_events: a local bit mask of the prcmu wakeup events + * @event_mask: a local copy of the mask event registers + * @last_event_mask: a copy of the last event_mask written to hardware + * @startup_events: a copy of the first reading of the event registers + * @startup_events_read: whether the first events have been read + */ +struct ab5500 { + struct mutex access_mutex; + struct device *dev; + unsigned int ab5500_irq; + unsigned int irq_base; + char chip_name[32]; + u8 chip_id; + struct mutex irq_lock; + u32 abb_events; + u8 mask[AB5500_NUM_IRQ_REGS]; + u8 oldmask[AB5500_NUM_IRQ_REGS]; + u8 startup_events[AB5500_NUM_IRQ_REGS]; + bool startup_events_read; +#ifdef CONFIG_DEBUG_FS + unsigned int debug_bank; + unsigned int debug_address; +#endif +}; + +struct ab5500_platform_data { + struct {unsigned int base; unsigned int count; } irq; + void *dev_data[AB5500_NUM_DEVICES]; + struct abx500_init_settings *init_settings; + unsigned int init_settings_sz; + bool pm_power_off; +}; + +#endif /* MFD_AB5500_H */ diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 896b5e47f16..79ec2c7b5fa 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -31,8 +31,8 @@ #define AB3100_R2B 0xc8 #define AB3550_P1A 0x10 #define AB5500_1_0 0x20 -#define AB5500_2_0 0x21 -#define AB5500_2_1 0x22 +#define AB5500_1_1 0x21 +#define AB5500_2_0 0x24 /* AB8500 CIDs*/ #define AB8500_CUTEARLY 0x00 -- cgit v1.2.3-70-g09d2 From 8959e74399c798b45c0f5d477972b927c28f8dc9 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Mon, 26 Sep 2011 11:45:30 +0200 Subject: mfd: Delete ab3550 driver The AB3550 never passed the prototype stage. Instead it was used as a precursor to AB5500 for testing basic building blocks used in that chip, since they had large similarities. Since AB3550 will not see the light of day in product form and since the prototypes are no longer used, let's delete the driver and any references to it. Cc: Mattias Wallin Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-u300/i2c.c | 51 +- arch/arm/mach-u300/include/mach/irqs.h | 7 - drivers/mfd/Kconfig | 14 - drivers/mfd/Makefile | 1 - drivers/mfd/ab3550-core.c | 1380 -------------------------------- include/linux/mfd/abx500.h | 48 +- 6 files changed, 2 insertions(+), 1499 deletions(-) delete mode 100644 drivers/mfd/ab3550-core.c (limited to 'include/linux') diff --git a/arch/arm/mach-u300/i2c.c b/arch/arm/mach-u300/i2c.c index f0394baa11f..5140deeddf7 100644 --- a/arch/arm/mach-u300/i2c.c +++ b/arch/arm/mach-u300/i2c.c @@ -256,57 +256,8 @@ static struct ab3100_platform_data ab3100_plf_data = { }; #endif -#ifdef CONFIG_AB3550_CORE -static struct abx500_init_settings ab3550_init_settings[] = { - { - .bank = 0, - .reg = AB3550_IMR1, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR2, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR3, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR4, - .setting = 0xff - }, - { - .bank = 0, - .reg = AB3550_IMR5, - /* The two most significant bits are not used */ - .setting = 0x3f - }, -}; - -static struct ab3550_platform_data ab3550_plf_data = { - .irq = { - .base = IRQ_AB3550_BASE, - .count = (IRQ_AB3550_END - IRQ_AB3550_BASE + 1), - }, - .dev_data = { - }, - .init_settings = ab3550_init_settings, - .init_settings_sz = ARRAY_SIZE(ab3550_init_settings), -}; -#endif - static struct i2c_board_info __initdata bus0_i2c_board_info[] = { -#if defined(CONFIG_AB3550_CORE) - { - .type = "ab3550", - .addr = 0x4A, - .irq = IRQ_U300_IRQ0_EXT, - .platform_data = &ab3550_plf_data, - }, -#elif defined(CONFIG_AB3100_CORE) +#ifdef CONFIG_AB3100_CORE { .type = "ab3100", .addr = 0x48, diff --git a/arch/arm/mach-u300/include/mach/irqs.h b/arch/arm/mach-u300/include/mach/irqs.h index 09b1b28fa8f..a6867b12773 100644 --- a/arch/arm/mach-u300/include/mach/irqs.h +++ b/arch/arm/mach-u300/include/mach/irqs.h @@ -109,13 +109,6 @@ #define U300_NR_IRQS 48 #endif -#ifdef CONFIG_AB3550_CORE -#define IRQ_AB3550_BASE (U300_NR_IRQS) -#define IRQ_AB3550_END (IRQ_AB3550_BASE + 37) - -#define NR_IRQS (IRQ_AB3550_END + 1) -#else #define NR_IRQS U300_NR_IRQS -#endif #endif diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index f22bd2f0ecc..74d4893a8f2 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -616,20 +616,6 @@ config AB8500_GPADC help AB8500 GPADC driver used to convert Acc and battery/ac/usb voltage -config AB3550_CORE - bool "ST-Ericsson AB3550 Mixed Signal Circuit core functions" - select MFD_CORE - depends on I2C=y && GENERIC_HARDIRQS && ABX500_CORE - help - Select this to enable the AB3550 Mixed Signal IC core - functionality. This connects to a AB3550 on the I2C bus - and expose a number of symbols needed for dependent devices - to read and write registers and subscribe to events from - this multi-functional IC. This is needed to use other features - of the AB3550 such as battery-backed RTC, charging control, - LEDs, vibrator, system power and temperature, power management - and ALSA sound. - config MFD_DB8500_PRCMU bool "ST-Ericsson DB8500 Power Reset Control Management Unit" depends on UX500_SOC_DB8500 diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile index 7ed553d8157..b2292eb7524 100644 --- a/drivers/mfd/Makefile +++ b/drivers/mfd/Makefile @@ -79,7 +79,6 @@ obj-$(CONFIG_PCF50633_GPIO) += pcf50633-gpio.o obj-$(CONFIG_ABX500_CORE) += abx500-core.o obj-$(CONFIG_AB3100_CORE) += ab3100-core.o obj-$(CONFIG_AB3100_OTP) += ab3100-otp.o -obj-$(CONFIG_AB3550_CORE) += ab3550-core.o obj-$(CONFIG_AB5500_CORE) += ab5500-core.o obj-$(CONFIG_AB5500_DEBUG) += ab5500-debugfs.o obj-$(CONFIG_AB8500_CORE) += ab8500-core.o ab8500-sysctrl.o diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c deleted file mode 100644 index 882ea7192d8..00000000000 --- a/drivers/mfd/ab3550-core.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Copyright (C) 2007-2010 ST-Ericsson - * License terms: GNU General Public License (GPL) version 2 - * Low-level core for exclusive access to the AB3550 IC on the I2C bus - * and some basic chip-configuration. - * Author: Bengt Jonsson - * Author: Mattias Nilsson - * Author: Mattias Wallin - * Author: Rickard Andersson - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define AB3550_NAME_STRING "ab3550" -#define AB3550_ID_FORMAT_STRING "AB3550 %s" -#define AB3550_NUM_BANKS 2 -#define AB3550_NUM_EVENT_REG 5 - -/* These are the only registers inside AB3550 used in this main file */ - -/* Chip ID register */ -#define AB3550_CID_REG 0x20 - -/* Interrupt event registers */ -#define AB3550_EVENT_BANK 0 -#define AB3550_EVENT_REG 0x22 - -/* Read/write operation values. */ -#define AB3550_PERM_RD (0x01) -#define AB3550_PERM_WR (0x02) - -/* Read/write permissions. */ -#define AB3550_PERM_RO (AB3550_PERM_RD) -#define AB3550_PERM_RW (AB3550_PERM_RD | AB3550_PERM_WR) - -/** - * struct ab3550 - * @access_mutex: lock out concurrent accesses to the AB registers - * @i2c_client: I2C client for this chip - * @chip_name: name of this chip variant - * @chip_id: 8 bit chip ID for this chip variant - * @mask_work: a worker for writing to mask registers - * @event_lock: a lock to protect the event_mask - * @event_mask: a local copy of the mask event registers - * @startup_events: a copy of the first reading of the event registers - * @startup_events_read: whether the first events have been read - */ -struct ab3550 { - struct mutex access_mutex; - struct i2c_client *i2c_client[AB3550_NUM_BANKS]; - char chip_name[32]; - u8 chip_id; - struct work_struct mask_work; - spinlock_t event_lock; - u8 event_mask[AB3550_NUM_EVENT_REG]; - u8 startup_events[AB3550_NUM_EVENT_REG]; - bool startup_events_read; -#ifdef CONFIG_DEBUG_FS - unsigned int debug_bank; - unsigned int debug_address; -#endif -}; - -/** - * struct ab3550_reg_range - * @first: the first address of the range - * @last: the last address of the range - * @perm: access permissions for the range - */ -struct ab3550_reg_range { - u8 first; - u8 last; - u8 perm; -}; - -/** - * struct ab3550_reg_ranges - * @count: the number of ranges in the list - * @range: the list of register ranges - */ -struct ab3550_reg_ranges { - u8 count; - const struct ab3550_reg_range *range; -}; - -/* - * Permissible register ranges for reading and writing per device and bank. - * - * The ranges must be listed in increasing address order, and no overlaps are - * allowed. It is assumed that write permission implies read permission - * (i.e. only RO and RW permissions should be used). Ranges with write - * permission must not be split up. - */ - -#define NO_RANGE {.count = 0, .range = NULL,} - -static struct -ab3550_reg_ranges ab3550_reg_ranges[AB3550_NUM_DEVICES][AB3550_NUM_BANKS] = { - [AB3550_DEVID_DAC] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0xb0, - .last = 0xba, - .perm = AB3550_PERM_RW, - }, - { - .first = 0xbc, - .last = 0xc3, - .perm = AB3550_PERM_RW, - }, - }, - }, - }, - [AB3550_DEVID_LEDS] = { - NO_RANGE, - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x5a, - .last = 0x88, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x8a, - .last = 0xad, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_POWER] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_REGULATORS] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x69, - .last = 0xa3, - .perm = AB3550_PERM_RW, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x16, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_SIM] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x14, - .last = 0x17, - .perm = AB3550_PERM_RW, - }, - } - - }, - }, - [AB3550_DEVID_UART] = { - NO_RANGE, - NO_RANGE, - }, - [AB3550_DEVID_RTC] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0c, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_CHARGER] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x1a, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - NO_RANGE, - }, - [AB3550_DEVID_ADC] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x20, - .last = 0x56, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x21, - .last = 0x21, - .perm = AB3550_PERM_RO, - }, - } - }, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - .perm = AB3550_PERM_RW, - }, - } - }, - }, - [AB3550_DEVID_VIBRATOR] = { - NO_RANGE, - { - .count = 1, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x10, - .last = 0x13, - .perm = AB3550_PERM_RW, - }, - - } - }, - }, - [AB3550_DEVID_CODEC] = { - { - .count = 2, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x31, - .last = 0x63, - .perm = AB3550_PERM_RW, - }, - { - .first = 0x65, - .last = 0x68, - .perm = AB3550_PERM_RW, - }, - } - }, - NO_RANGE, - }, -}; - -static struct mfd_cell ab3550_devs[AB3550_NUM_DEVICES] = { - [AB3550_DEVID_DAC] = { - .name = "ab3550-dac", - .id = AB3550_DEVID_DAC, - .num_resources = 0, - }, - [AB3550_DEVID_LEDS] = { - .name = "ab3550-leds", - .id = AB3550_DEVID_LEDS, - }, - [AB3550_DEVID_POWER] = { - .name = "ab3550-power", - .id = AB3550_DEVID_POWER, - }, - [AB3550_DEVID_REGULATORS] = { - .name = "ab3550-regulators", - .id = AB3550_DEVID_REGULATORS, - }, - [AB3550_DEVID_SIM] = { - .name = "ab3550-sim", - .id = AB3550_DEVID_SIM, - }, - [AB3550_DEVID_UART] = { - .name = "ab3550-uart", - .id = AB3550_DEVID_UART, - }, - [AB3550_DEVID_RTC] = { - .name = "ab3550-rtc", - .id = AB3550_DEVID_RTC, - }, - [AB3550_DEVID_CHARGER] = { - .name = "ab3550-charger", - .id = AB3550_DEVID_CHARGER, - }, - [AB3550_DEVID_ADC] = { - .name = "ab3550-adc", - .id = AB3550_DEVID_ADC, - .num_resources = 10, - .resources = (struct resource[]) { - { - .name = "TRIGGER-0", - .flags = IORESOURCE_IRQ, - .start = 16, - .end = 16, - }, - { - .name = "TRIGGER-1", - .flags = IORESOURCE_IRQ, - .start = 17, - .end = 17, - }, - { - .name = "TRIGGER-2", - .flags = IORESOURCE_IRQ, - .start = 18, - .end = 18, - }, - { - .name = "TRIGGER-3", - .flags = IORESOURCE_IRQ, - .start = 19, - .end = 19, - }, - { - .name = "TRIGGER-4", - .flags = IORESOURCE_IRQ, - .start = 20, - .end = 20, - }, - { - .name = "TRIGGER-5", - .flags = IORESOURCE_IRQ, - .start = 21, - .end = 21, - }, - { - .name = "TRIGGER-6", - .flags = IORESOURCE_IRQ, - .start = 22, - .end = 22, - }, - { - .name = "TRIGGER-7", - .flags = IORESOURCE_IRQ, - .start = 23, - .end = 23, - }, - { - .name = "TRIGGER-VBAT-TXON", - .flags = IORESOURCE_IRQ, - .start = 13, - .end = 13, - }, - { - .name = "TRIGGER-VBAT", - .flags = IORESOURCE_IRQ, - .start = 12, - .end = 12, - }, - }, - }, - [AB3550_DEVID_FUELGAUGE] = { - .name = "ab3550-fuelgauge", - .id = AB3550_DEVID_FUELGAUGE, - }, - [AB3550_DEVID_VIBRATOR] = { - .name = "ab3550-vibrator", - .id = AB3550_DEVID_VIBRATOR, - }, - [AB3550_DEVID_CODEC] = { - .name = "ab3550-codec", - .id = AB3550_DEVID_CODEC, - }, -}; - -/* - * I2C transactions with error messages. - */ -static int ab3550_i2c_master_send(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_send(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "send error: %d\n", err); - return err; - } - return 0; -} - -static int ab3550_i2c_master_recv(struct ab3550 *ab, u8 bank, u8 *data, - u8 count) -{ - int err; - - err = i2c_master_recv(ab->i2c_client[bank], data, count); - if (err < 0) { - dev_err(&ab->i2c_client[0]->dev, "receive error: %d\n", err); - return err; - } - return 0; -} - -/* - * Functionality for getting/setting register values. - */ -static int get_register_interruptible(struct ab3550 *ab, u8 bank, u8 reg, - u8 *value) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, value, 1); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int get_register_page_interruptible(struct ab3550 *ab, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - int err; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - err = ab3550_i2c_master_send(ab, bank, &first_reg, 1); - if (!err) - err = ab3550_i2c_master_recv(ab, bank, regvals, numregs); - - mutex_unlock(&ab->access_mutex); - return err; -} - -static int mask_and_set_register_interruptible(struct ab3550 *ab, u8 bank, - u8 reg, u8 bitmask, u8 bitvalues) -{ - int err = 0; - - if (likely(bitmask)) { - u8 reg_bits[2] = {reg, 0}; - - err = mutex_lock_interruptible(&ab->access_mutex); - if (err) - return err; - - if (bitmask == 0xFF) /* No need to read in this case. */ - reg_bits[1] = bitvalues; - else { /* Read and modify the register value. */ - u8 bits; - - err = ab3550_i2c_master_send(ab, bank, ®, 1); - if (err) - goto unlock_and_return; - err = ab3550_i2c_master_recv(ab, bank, &bits, 1); - if (err) - goto unlock_and_return; - reg_bits[1] = ((~bitmask & bits) | - (bitmask & bitvalues)); - } - /* Write the new value. */ - err = ab3550_i2c_master_send(ab, bank, reg_bits, 2); -unlock_and_return: - mutex_unlock(&ab->access_mutex); - } - return err; -} - -/* - * Read/write permission checking functions. - */ -static bool page_write_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - break; - if ((last_reg <= ranges->range[i].last) && - (ranges->range[i].perm & AB3550_PERM_WR)) - return true; - } - return false; -} - -static bool reg_write_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_write_allowed(ranges, reg, reg); -} - -static bool page_read_allowed(const struct ab3550_reg_ranges *ranges, - u8 first_reg, u8 last_reg) -{ - u8 i; - - if (last_reg < first_reg) - return false; - /* Find the range (if it exists in the list) that includes first_reg. */ - for (i = 0; i < ranges->count; i++) { - if (first_reg < ranges->range[i].first) - return false; - if (first_reg <= ranges->range[i].last) - break; - } - /* Make sure that the entire range up to and including last_reg is - * readable. This may span several of the ranges in the list. - */ - while ((i < ranges->count) && - (ranges->range[i].perm & AB3550_PERM_RD)) { - if (last_reg <= ranges->range[i].last) - return true; - if ((++i >= ranges->count) || - (ranges->range[i].first != - (ranges->range[i - 1].last + 1))) { - break; - } - } - return false; -} - -static bool reg_read_allowed(const struct ab3550_reg_ranges *ranges, u8 reg) -{ - return page_read_allowed(ranges, reg, reg); -} - -/* - * The register access functionality. - */ -static int ab3550_get_chip_id(struct device *dev) -{ - struct ab3550 *ab = dev_get_drvdata(dev->parent); - return (int)ab->chip_id; -} - -static int ab3550_mask_and_set_register_interruptible(struct device *dev, - u8 bank, u8 reg, u8 bitmask, u8 bitvalues) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_write_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return mask_and_set_register_interruptible(ab, bank, reg, - bitmask, bitvalues); -} - -static int ab3550_set_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 value) -{ - return ab3550_mask_and_set_register_interruptible(dev, bank, reg, 0xFF, - value); -} - -static int ab3550_get_register_interruptible(struct device *dev, u8 bank, - u8 reg, u8 *value) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !reg_read_allowed(&ab3550_reg_ranges[pdev->id][bank], reg)) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_interruptible(ab, bank, reg, value); -} - -static int ab3550_get_register_page_interruptible(struct device *dev, u8 bank, - u8 first_reg, u8 *regvals, u8 numregs) -{ - struct ab3550 *ab; - struct platform_device *pdev = to_platform_device(dev); - - if ((AB3550_NUM_BANKS <= bank) || - !page_read_allowed(&ab3550_reg_ranges[pdev->id][bank], - first_reg, (first_reg + numregs - 1))) - return -EINVAL; - - ab = dev_get_drvdata(dev->parent); - return get_register_page_interruptible(ab, bank, first_reg, regvals, - numregs); -} - -static int ab3550_event_registers_startup_state_get(struct device *dev, - u8 *event) -{ - struct ab3550 *ab; - - ab = dev_get_drvdata(dev->parent); - if (!ab->startup_events_read) - return -EAGAIN; /* Try again later */ - - memcpy(event, ab->startup_events, AB3550_NUM_EVENT_REG); - return 0; -} - -static int ab3550_startup_irq_enabled(struct device *dev, unsigned int irq) -{ - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - bool val; - - ab = irq_get_chip_data(irq); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq -= plf_data->irq.base; - val = ((ab->startup_events[irq / 8] & BIT(irq % 8)) != 0); - - return val; -} - -static struct abx500_ops ab3550_ops = { - .get_chip_id = ab3550_get_chip_id, - .get_register = ab3550_get_register_interruptible, - .set_register = ab3550_set_register_interruptible, - .get_register_page = ab3550_get_register_page_interruptible, - .set_register_page = NULL, - .mask_and_set_register = ab3550_mask_and_set_register_interruptible, - .event_registers_startup_state_get = - ab3550_event_registers_startup_state_get, - .startup_irq_enabled = ab3550_startup_irq_enabled, -}; - -static irqreturn_t ab3550_irq_handler(int irq, void *data) -{ - struct ab3550 *ab = data; - int err; - unsigned int i; - u8 e[AB3550_NUM_EVENT_REG]; - u8 *events; - unsigned long flags; - - events = (ab->startup_events_read ? e : ab->startup_events); - - err = get_register_page_interruptible(ab, AB3550_EVENT_BANK, - AB3550_EVENT_REG, events, AB3550_NUM_EVENT_REG); - if (err) - goto err_event_rd; - - if (!ab->startup_events_read) { - dev_info(&ab->i2c_client[0]->dev, - "startup events 0x%x,0x%x,0x%x,0x%x,0x%x\n", - ab->startup_events[0], ab->startup_events[1], - ab->startup_events[2], ab->startup_events[3], - ab->startup_events[4]); - ab->startup_events_read = true; - goto out; - } - - /* The two highest bits in event[4] are not used. */ - events[4] &= 0x3f; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - events[i] &= ~ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - u8 bit; - u8 event_reg; - - dev_dbg(&ab->i2c_client[0]->dev, "IRQ Event[%d]: 0x%2x\n", - i, events[i]); - - event_reg = events[i]; - for (bit = 0; event_reg; bit++, event_reg /= 2) { - if (event_reg % 2) { - unsigned int irq; - struct ab3550_platform_data *plf_data; - - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = plf_data->irq.base + (i * 8) + bit; - handle_nested_irq(irq); - } - } - } -out: - return IRQ_HANDLED; - -err_event_rd: - dev_dbg(&ab->i2c_client[0]->dev, "error reading event registers\n"); - return IRQ_HANDLED; -} - -#ifdef CONFIG_DEBUG_FS -static struct ab3550_reg_ranges debug_ranges[AB3550_NUM_BANKS] = { - { - .count = 6, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x1a, - }, - { - .first = 0x1e, - .last = 0x4f, - }, - { - .first = 0x51, - .last = 0x63, - }, - { - .first = 0x65, - .last = 0xa3, - }, - { - .first = 0xa5, - .last = 0xa8, - }, - } - }, - { - .count = 8, - .range = (struct ab3550_reg_range[]) { - { - .first = 0x00, - .last = 0x0e, - }, - { - .first = 0x10, - .last = 0x17, - }, - { - .first = 0x1a, - .last = 0x1c, - }, - { - .first = 0x20, - .last = 0x56, - }, - { - .first = 0x5a, - .last = 0x88, - }, - { - .first = 0x8a, - .last = 0xad, - }, - { - .first = 0xb0, - .last = 0xba, - }, - { - .first = 0xbc, - .last = 0xc3, - }, - } - }, -}; - -static int ab3550_registers_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int bank; - - seq_printf(s, AB3550_NAME_STRING " register values:\n"); - - for (bank = 0; bank < AB3550_NUM_BANKS; bank++) { - unsigned int i; - - seq_printf(s, " bank %d:\n", bank); - for (i = 0; i < debug_ranges[bank].count; i++) { - u8 reg; - - for (reg = debug_ranges[bank].range[i].first; - reg <= debug_ranges[bank].range[i].last; - reg++) { - u8 value; - - get_register_interruptible(ab, bank, reg, - &value); - seq_printf(s, " [%d/0x%02X]: 0x%02X\n", bank, - reg, value); - } - } - } - return 0; -} - -static int ab3550_registers_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_registers_print, inode->i_private); -} - -static const struct file_operations ab3550_registers_fops = { - .open = ab3550_registers_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static int ab3550_bank_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "%d\n", ab->debug_bank); - return 0; -} - -static int ab3550_bank_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_bank_print, inode->i_private); -} - -static ssize_t ab3550_bank_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_bank; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_bank); - if (err) - return err; - - if (user_bank >= AB3550_NUM_BANKS) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > number of banks\n"); - return -EINVAL; - } - - ab->debug_bank = user_bank; - - return count; -} - -static int ab3550_address_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - - seq_printf(s, "0x%02X\n", ab->debug_address); - return 0; -} - -static int ab3550_address_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_address_print, inode->i_private); -} - -static ssize_t ab3550_address_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_address; - int err; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_address); - if (err) - return err; - - if (user_address > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - ab->debug_address = user_address; - return count; -} - -static int ab3550_val_print(struct seq_file *s, void *p) -{ - struct ab3550 *ab = s->private; - int err; - u8 regvalue; - - err = get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - seq_printf(s, "0x%02X\n", regvalue); - - return 0; -} - -static int ab3550_val_open(struct inode *inode, struct file *file) -{ - return single_open(file, ab3550_val_print, inode->i_private); -} - -static ssize_t ab3550_val_write(struct file *file, - const char __user *user_buf, - size_t count, loff_t *ppos) -{ - struct ab3550 *ab = ((struct seq_file *)(file->private_data))->private; - unsigned long user_val; - int err; - u8 regvalue; - - /* Get userspace string and assure termination */ - err = kstrtoul_from_user(user_buf, count, 0, &user_val); - if (err) - return err; - - if (user_val > 0xff) { - dev_err(&ab->i2c_client[0]->dev, - "debugfs error input > 0xff\n"); - return -EINVAL; - } - err = mask_and_set_register_interruptible( - ab, (u8)ab->debug_bank, - (u8)ab->debug_address, 0xFF, (u8)user_val); - if (err) - return -EINVAL; - - get_register_interruptible(ab, (u8)ab->debug_bank, - (u8)ab->debug_address, ®value); - if (err) - return -EINVAL; - - return count; -} - -static const struct file_operations ab3550_bank_fops = { - .open = ab3550_bank_open, - .write = ab3550_bank_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_address_fops = { - .open = ab3550_address_open, - .write = ab3550_address_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static const struct file_operations ab3550_val_fops = { - .open = ab3550_val_open, - .write = ab3550_val_write, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, - .owner = THIS_MODULE, -}; - -static struct dentry *ab3550_dir; -static struct dentry *ab3550_reg_file; -static struct dentry *ab3550_bank_file; -static struct dentry *ab3550_address_file; -static struct dentry *ab3550_val_file; - -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ - ab->debug_bank = 0; - ab->debug_address = 0x00; - - ab3550_dir = debugfs_create_dir(AB3550_NAME_STRING, NULL); - if (!ab3550_dir) - goto exit_no_debugfs; - - ab3550_reg_file = debugfs_create_file("all-registers", - S_IRUGO, ab3550_dir, ab, &ab3550_registers_fops); - if (!ab3550_reg_file) - goto exit_destroy_dir; - - ab3550_bank_file = debugfs_create_file("register-bank", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_bank_fops); - if (!ab3550_bank_file) - goto exit_destroy_reg; - - ab3550_address_file = debugfs_create_file("register-address", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_address_fops); - if (!ab3550_address_file) - goto exit_destroy_bank; - - ab3550_val_file = debugfs_create_file("register-value", - (S_IRUGO | S_IWUSR), ab3550_dir, ab, &ab3550_val_fops); - if (!ab3550_val_file) - goto exit_destroy_address; - - return; - -exit_destroy_address: - debugfs_remove(ab3550_address_file); -exit_destroy_bank: - debugfs_remove(ab3550_bank_file); -exit_destroy_reg: - debugfs_remove(ab3550_reg_file); -exit_destroy_dir: - debugfs_remove(ab3550_dir); -exit_no_debugfs: - dev_err(&ab->i2c_client[0]->dev, "failed to create debugfs entries.\n"); - return; -} - -static inline void ab3550_remove_debugfs(void) -{ - debugfs_remove(ab3550_val_file); - debugfs_remove(ab3550_address_file); - debugfs_remove(ab3550_bank_file); - debugfs_remove(ab3550_reg_file); - debugfs_remove(ab3550_dir); -} - -#else /* !CONFIG_DEBUG_FS */ -static inline void ab3550_setup_debugfs(struct ab3550 *ab) -{ -} -static inline void ab3550_remove_debugfs(void) -{ -} -#endif - -/* - * Basic set-up, datastructure creation/destruction and I2C interface. - * This sets up a default config in the AB3550 chip so that it - * will work as expected. - */ -static int __devinit ab3550_setup(struct ab3550 *ab) -{ - int err = 0; - int i; - struct ab3550_platform_data *plf_data; - struct abx500_init_settings *settings; - - plf_data = ab->i2c_client[0]->dev.platform_data; - settings = plf_data->init_settings; - - for (i = 0; i < plf_data->init_settings_sz; i++) { - err = mask_and_set_register_interruptible(ab, - settings[i].bank, - settings[i].reg, - 0xFF, settings[i].setting); - if (err) - goto exit_no_setup; - - /* If event mask register update the event mask in ab3550 */ - if ((settings[i].bank == 0) && - (AB3550_IMR1 <= settings[i].reg) && - (settings[i].reg <= AB3550_IMR5)) { - ab->event_mask[settings[i].reg - AB3550_IMR1] = - settings[i].setting; - } - } -exit_no_setup: - return err; -} - -static void ab3550_mask_work(struct work_struct *work) -{ - struct ab3550 *ab = container_of(work, struct ab3550, mask_work); - int i; - unsigned long flags; - u8 mask[AB3550_NUM_EVENT_REG]; - - spin_lock_irqsave(&ab->event_lock, flags); - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) - mask[i] = ab->event_mask[i]; - spin_unlock_irqrestore(&ab->event_lock, flags); - - for (i = 0; i < AB3550_NUM_EVENT_REG; i++) { - int err; - - err = mask_and_set_register_interruptible(ab, 0, - (AB3550_IMR1 + i), ~0, mask[i]); - if (err) - dev_err(&ab->i2c_client[0]->dev, - "ab3550_mask_work failed 0x%x,0x%x\n", - (AB3550_IMR1 + i), mask[i]); - } -} - -static void ab3550_mask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] |= BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void ab3550_unmask(struct irq_data *data) -{ - unsigned long flags; - struct ab3550 *ab; - struct ab3550_platform_data *plf_data; - int irq; - - ab = irq_data_get_irq_chip_data(data); - plf_data = ab->i2c_client[0]->dev.platform_data; - irq = data->irq - plf_data->irq.base; - - spin_lock_irqsave(&ab->event_lock, flags); - ab->event_mask[irq / 8] &= ~BIT(irq % 8); - spin_unlock_irqrestore(&ab->event_lock, flags); - - schedule_work(&ab->mask_work); -} - -static void noop(struct irq_data *data) -{ -} - -static struct irq_chip ab3550_irq_chip = { - .name = "ab3550-core", /* Keep the same name as the request */ - .irq_disable = ab3550_mask, /* No default to mask in chip.c */ - .irq_ack = noop, - .irq_mask = ab3550_mask, - .irq_unmask = ab3550_unmask, -}; - -struct ab_family_id { - u8 id; - char *name; -}; - -static const struct ab_family_id ids[] __devinitconst = { - /* AB3550 */ - { - .id = AB3550_P1A, - .name = "P1A" - }, - /* Terminator */ - { - .id = 0x00, - } -}; - -static int __devinit ab3550_probe(struct i2c_client *client, - const struct i2c_device_id *id) -{ - struct ab3550 *ab; - struct ab3550_platform_data *ab3550_plf_data = - client->dev.platform_data; - int err; - int i; - int num_i2c_clients = 0; - - ab = kzalloc(sizeof(struct ab3550), GFP_KERNEL); - if (!ab) { - dev_err(&client->dev, - "could not allocate " AB3550_NAME_STRING " device\n"); - return -ENOMEM; - } - - /* Initialize data structure */ - mutex_init(&ab->access_mutex); - spin_lock_init(&ab->event_lock); - ab->i2c_client[0] = client; - - i2c_set_clientdata(client, ab); - - /* Read chip ID register */ - err = get_register_interruptible(ab, 0, AB3550_CID_REG, &ab->chip_id); - if (err) { - dev_err(&client->dev, "could not communicate with the analog " - "baseband chip\n"); - goto exit_no_detect; - } - - for (i = 0; ids[i].id != 0x0; i++) { - if (ids[i].id == ab->chip_id) { - snprintf(&ab->chip_name[0], sizeof(ab->chip_name) - 1, - AB3550_ID_FORMAT_STRING, ids[i].name); - break; - } - } - - if (ids[i].id == 0x0) { - dev_err(&client->dev, "unknown analog baseband chip id: 0x%x\n", - ab->chip_id); - dev_err(&client->dev, "driver not started!\n"); - goto exit_no_detect; - } - - dev_info(&client->dev, "detected AB chip: %s\n", &ab->chip_name[0]); - - /* Attach other dummy I2C clients. */ - while (++num_i2c_clients < AB3550_NUM_BANKS) { - ab->i2c_client[num_i2c_clients] = - i2c_new_dummy(client->adapter, - (client->addr + num_i2c_clients)); - if (!ab->i2c_client[num_i2c_clients]) { - err = -ENOMEM; - goto exit_no_dummy_client; - } - strlcpy(ab->i2c_client[num_i2c_clients]->name, id->name, - sizeof(ab->i2c_client[num_i2c_clients]->name)); - } - - err = ab3550_setup(ab); - if (err) - goto exit_no_setup; - - INIT_WORK(&ab->mask_work, ab3550_mask_work); - - for (i = 0; i < ab3550_plf_data->irq.count; i++) { - unsigned int irq; - - irq = ab3550_plf_data->irq.base + i; - irq_set_chip_data(irq, ab); - irq_set_chip_and_handler(irq, &ab3550_irq_chip, - handle_simple_irq); - irq_set_nested_thread(irq, 1); -#ifdef CONFIG_ARM - set_irq_flags(irq, IRQF_VALID); -#else - irq_set_noprobe(irq); -#endif - } - - err = request_threaded_irq(client->irq, NULL, ab3550_irq_handler, - IRQF_ONESHOT, "ab3550-core", ab); - /* This real unpredictable IRQ is of course sampled for entropy */ - rand_initialize_irq(client->irq); - - if (err) - goto exit_no_irq; - - err = abx500_register_ops(&client->dev, &ab3550_ops); - if (err) - goto exit_no_ops; - - /* Set up and register the platform devices. */ - for (i = 0; i < AB3550_NUM_DEVICES; i++) { - ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; - ab3550_devs[i].pdata_size = ab3550_plf_data->dev_data_sz[i]; - } - - err = mfd_add_devices(&client->dev, 0, ab3550_devs, - ARRAY_SIZE(ab3550_devs), NULL, - ab3550_plf_data->irq.base); - - ab3550_setup_debugfs(ab); - - return 0; - -exit_no_ops: -exit_no_irq: -exit_no_setup: -exit_no_dummy_client: - /* Unregister the dummy i2c clients. */ - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); -exit_no_detect: - kfree(ab); - return err; -} - -static int __devexit ab3550_remove(struct i2c_client *client) -{ - struct ab3550 *ab = i2c_get_clientdata(client); - int num_i2c_clients = AB3550_NUM_BANKS; - - mfd_remove_devices(&client->dev); - ab3550_remove_debugfs(); - - while (--num_i2c_clients) - i2c_unregister_device(ab->i2c_client[num_i2c_clients]); - - /* - * At this point, all subscribers should have unregistered - * their notifiers so deactivate IRQ - */ - free_irq(client->irq, ab); - kfree(ab); - return 0; -} - -static const struct i2c_device_id ab3550_id[] = { - {AB3550_NAME_STRING, 0}, - {} -}; -MODULE_DEVICE_TABLE(i2c, ab3550_id); - -static struct i2c_driver ab3550_driver = { - .driver = { - .name = AB3550_NAME_STRING, - .owner = THIS_MODULE, - }, - .id_table = ab3550_id, - .probe = ab3550_probe, - .remove = __devexit_p(ab3550_remove), -}; - -static int __init ab3550_i2c_init(void) -{ - return i2c_add_driver(&ab3550_driver); -} - -static void __exit ab3550_i2c_exit(void) -{ - i2c_del_driver(&ab3550_driver); -} - -subsys_initcall(ab3550_i2c_init); -module_exit(ab3550_i2c_exit); - -MODULE_AUTHOR("Mattias Wallin "); -MODULE_DESCRIPTION("AB3550 core driver"); -MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 79ec2c7b5fa..6d096e8b774 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -6,7 +6,7 @@ * * ABX500 core access functions. * The abx500 interface is used for the Analog Baseband chip - * ab3100, ab3550, ab5500, and ab8500. + * ab3100, ab5500, and ab8500. * * Author: Mattias Wallin * Author: Mattias Nilsson @@ -29,7 +29,6 @@ #define AB3100_P1G 0xc6 #define AB3100_R2A 0xc7 #define AB3100_R2B 0xc8 -#define AB3550_P1A 0x10 #define AB5500_1_0 0x20 #define AB5500_1_1 0x21 #define AB5500_2_0 0x24 @@ -143,39 +142,6 @@ int ab3100_event_register(struct ab3100 *ab3100, int ab3100_event_unregister(struct ab3100 *ab3100, struct notifier_block *nb); -/* AB3550, STR register flags */ -#define AB3550_STR_ONSWA (0x01) -#define AB3550_STR_ONSWB (0x02) -#define AB3550_STR_ONSWC (0x04) -#define AB3550_STR_DCIO (0x08) -#define AB3550_STR_BOOT_MODE (0x10) -#define AB3550_STR_SIM_OFF (0x20) -#define AB3550_STR_BATT_REMOVAL (0x40) -#define AB3550_STR_VBUS (0x80) - -/* Interrupt mask registers */ -#define AB3550_IMR1 0x29 -#define AB3550_IMR2 0x2a -#define AB3550_IMR3 0x2b -#define AB3550_IMR4 0x2c -#define AB3550_IMR5 0x2d - -enum ab3550_devid { - AB3550_DEVID_ADC, - AB3550_DEVID_DAC, - AB3550_DEVID_LEDS, - AB3550_DEVID_POWER, - AB3550_DEVID_REGULATORS, - AB3550_DEVID_SIM, - AB3550_DEVID_UART, - AB3550_DEVID_RTC, - AB3550_DEVID_CHARGER, - AB3550_DEVID_FUELGAUGE, - AB3550_DEVID_VIBRATOR, - AB3550_DEVID_CODEC, - AB3550_NUM_DEVICES, -}; - /** * struct abx500_init_setting * Initial value of the registers for driver to use during setup. @@ -186,18 +152,6 @@ struct abx500_init_settings { u8 setting; }; -/** - * struct ab3550_platform_data - * Data supplied to initialize board connections to the AB3550 - */ -struct ab3550_platform_data { - struct {unsigned int base; unsigned int count; } irq; - void *dev_data[AB3550_NUM_DEVICES]; - size_t dev_data_sz[AB3550_NUM_DEVICES]; - struct abx500_init_settings *init_settings; - unsigned int init_settings_sz; -}; - int abx500_set_register_interruptible(struct device *dev, u8 bank, u8 reg, u8 value); int abx500_get_register_interruptible(struct device *dev, u8 bank, u8 reg, -- cgit v1.2.3-70-g09d2 From fea799e3d3ab84ac675de7e48a13a79fb76b6e63 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:28:02 +0200 Subject: mfd: Create a common interface for dbx500 PRCMU drivers This adds a header file that contains the set of functions and definitions that will be shared between the DB8500 and DB5500 PRCMU drivers. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- include/linux/mfd/dbx500-prcmu.h | 549 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 549 insertions(+) create mode 100644 include/linux/mfd/dbx500-prcmu.h (limited to 'include/linux') diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h new file mode 100644 index 00000000000..6c7584d69d8 --- /dev/null +++ b/include/linux/mfd/dbx500-prcmu.h @@ -0,0 +1,549 @@ +/* + * Copyright (C) ST Ericsson SA 2011 + * + * License Terms: GNU General Public License v2 + * + * STE Ux500 PRCMU API + */ +#ifndef __MACH_PRCMU_H +#define __MACH_PRCMU_H + +#include +#include +#include + +/* PRCMU Wakeup defines */ +enum prcmu_wakeup_index { + PRCMU_WAKEUP_INDEX_RTC, + PRCMU_WAKEUP_INDEX_RTT0, + PRCMU_WAKEUP_INDEX_RTT1, + PRCMU_WAKEUP_INDEX_HSI0, + PRCMU_WAKEUP_INDEX_HSI1, + PRCMU_WAKEUP_INDEX_USB, + PRCMU_WAKEUP_INDEX_ABB, + PRCMU_WAKEUP_INDEX_ABB_FIFO, + PRCMU_WAKEUP_INDEX_ARM, + PRCMU_WAKEUP_INDEX_CD_IRQ, + NUM_PRCMU_WAKEUP_INDICES +}; +#define PRCMU_WAKEUP(_name) (BIT(PRCMU_WAKEUP_INDEX_##_name)) + +/* EPOD (power domain) IDs */ + +/* + * DB8500 EPODs + * - EPOD_ID_SVAMMDSP: power domain for SVA MMDSP + * - EPOD_ID_SVAPIPE: power domain for SVA pipe + * - EPOD_ID_SIAMMDSP: power domain for SIA MMDSP + * - EPOD_ID_SIAPIPE: power domain for SIA pipe + * - EPOD_ID_SGA: power domain for SGA + * - EPOD_ID_B2R2_MCDE: power domain for B2R2 and MCDE + * - EPOD_ID_ESRAM12: power domain for ESRAM 1 and 2 + * - EPOD_ID_ESRAM34: power domain for ESRAM 3 and 4 + * - NUM_EPOD_ID: number of power domains + * + * TODO: These should be prefixed. + */ +#define EPOD_ID_SVAMMDSP 0 +#define EPOD_ID_SVAPIPE 1 +#define EPOD_ID_SIAMMDSP 2 +#define EPOD_ID_SIAPIPE 3 +#define EPOD_ID_SGA 4 +#define EPOD_ID_B2R2_MCDE 5 +#define EPOD_ID_ESRAM12 6 +#define EPOD_ID_ESRAM34 7 +#define NUM_EPOD_ID 8 + +/* + * DB5500 EPODs + */ +#define DB5500_EPOD_ID_BASE 0x0100 +#define DB5500_EPOD_ID_SGA (DB5500_EPOD_ID_BASE + 0) +#define DB5500_EPOD_ID_HVA (DB5500_EPOD_ID_BASE + 1) +#define DB5500_EPOD_ID_SIA (DB5500_EPOD_ID_BASE + 2) +#define DB5500_EPOD_ID_DISP (DB5500_EPOD_ID_BASE + 3) +#define DB5500_EPOD_ID_ESRAM12 (DB5500_EPOD_ID_BASE + 6) +#define DB5500_NUM_EPOD_ID 7 + +/* + * state definition for EPOD (power domain) + * - EPOD_STATE_NO_CHANGE: The EPOD should remain unchanged + * - EPOD_STATE_OFF: The EPOD is switched off + * - EPOD_STATE_RAMRET: The EPOD is switched off with its internal RAM in + * retention + * - EPOD_STATE_ON_CLK_OFF: The EPOD is switched on, clock is still off + * - EPOD_STATE_ON: Same as above, but with clock enabled + */ +#define EPOD_STATE_NO_CHANGE 0x00 +#define EPOD_STATE_OFF 0x01 +#define EPOD_STATE_RAMRET 0x02 +#define EPOD_STATE_ON_CLK_OFF 0x03 +#define EPOD_STATE_ON 0x04 + +/* + * CLKOUT sources + */ +#define PRCMU_CLKSRC_CLK38M 0x00 +#define PRCMU_CLKSRC_ACLK 0x01 +#define PRCMU_CLKSRC_SYSCLK 0x02 +#define PRCMU_CLKSRC_LCDCLK 0x03 +#define PRCMU_CLKSRC_SDMMCCLK 0x04 +#define PRCMU_CLKSRC_TVCLK 0x05 +#define PRCMU_CLKSRC_TIMCLK 0x06 +#define PRCMU_CLKSRC_CLK009 0x07 +/* These are only valid for CLKOUT1: */ +#define PRCMU_CLKSRC_SIAMMDSPCLK 0x40 +#define PRCMU_CLKSRC_I2CCLK 0x41 +#define PRCMU_CLKSRC_MSP02CLK 0x42 +#define PRCMU_CLKSRC_ARMPLL_OBSCLK 0x43 +#define PRCMU_CLKSRC_HSIRXCLK 0x44 +#define PRCMU_CLKSRC_HSITXCLK 0x45 +#define PRCMU_CLKSRC_ARMCLKFIX 0x46 +#define PRCMU_CLKSRC_HDMICLK 0x47 + +/* + * Clock identifiers. + */ +enum prcmu_clock { + PRCMU_SGACLK, + PRCMU_UARTCLK, + PRCMU_MSP02CLK, + PRCMU_MSP1CLK, + PRCMU_I2CCLK, + PRCMU_SDMMCCLK, + PRCMU_SLIMCLK, + PRCMU_PER1CLK, + PRCMU_PER2CLK, + PRCMU_PER3CLK, + PRCMU_PER5CLK, + PRCMU_PER6CLK, + PRCMU_PER7CLK, + PRCMU_LCDCLK, + PRCMU_BMLCLK, + PRCMU_HSITXCLK, + PRCMU_HSIRXCLK, + PRCMU_HDMICLK, + PRCMU_APEATCLK, + PRCMU_APETRACECLK, + PRCMU_MCDECLK, + PRCMU_IPI2CCLK, + PRCMU_DSIALTCLK, + PRCMU_DMACLK, + PRCMU_B2R2CLK, + PRCMU_TVCLK, + PRCMU_SSPCLK, + PRCMU_RNGCLK, + PRCMU_UICCCLK, + PRCMU_PWMCLK, + PRCMU_IRDACLK, + PRCMU_IRRCCLK, + PRCMU_SIACLK, + PRCMU_SVACLK, + PRCMU_NUM_REG_CLOCKS, + PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, + PRCMU_TIMCLK, + PRCMU_PLLSOC0, + PRCMU_PLLSOC1, + PRCMU_PLLDDR, +}; + +/** + * enum ape_opp - APE OPP states definition + * @APE_OPP_INIT: + * @APE_NO_CHANGE: The APE operating point is unchanged + * @APE_100_OPP: The new APE operating point is ape100opp + * @APE_50_OPP: 50% + */ +enum ape_opp { + APE_OPP_INIT = 0x00, + APE_NO_CHANGE = 0x01, + APE_100_OPP = 0x02, + APE_50_OPP = 0x03 +}; + +/** + * enum arm_opp - ARM OPP states definition + * @ARM_OPP_INIT: + * @ARM_NO_CHANGE: The ARM operating point is unchanged + * @ARM_100_OPP: The new ARM operating point is arm100opp + * @ARM_50_OPP: The new ARM operating point is arm50opp + * @ARM_MAX_OPP: Operating point is "max" (more than 100) + * @ARM_MAX_FREQ100OPP: Set max opp if available, else 100 + * @ARM_EXTCLK: The new ARM operating point is armExtClk + */ +enum arm_opp { + ARM_OPP_INIT = 0x00, + ARM_NO_CHANGE = 0x01, + ARM_100_OPP = 0x02, + ARM_50_OPP = 0x03, + ARM_MAX_OPP = 0x04, + ARM_MAX_FREQ100OPP = 0x05, + ARM_EXTCLK = 0x07 +}; + +/** + * enum ddr_opp - DDR OPP states definition + * @DDR_100_OPP: The new DDR operating point is ddr100opp + * @DDR_50_OPP: The new DDR operating point is ddr50opp + * @DDR_25_OPP: The new DDR operating point is ddr25opp + */ +enum ddr_opp { + DDR_100_OPP = 0x00, + DDR_50_OPP = 0x01, + DDR_25_OPP = 0x02, +}; + +/* + * Definitions for controlling ESRAM0 in deep sleep. + */ +#define ESRAM0_DEEP_SLEEP_STATE_OFF 1 +#define ESRAM0_DEEP_SLEEP_STATE_RET 2 + +/** + * enum ddr_pwrst - DDR power states definition + * @DDR_PWR_STATE_UNCHANGED: SDRAM and DDR controller state is unchanged + * @DDR_PWR_STATE_ON: + * @DDR_PWR_STATE_OFFLOWLAT: + * @DDR_PWR_STATE_OFFHIGHLAT: + */ +enum ddr_pwrst { + DDR_PWR_STATE_UNCHANGED = 0x00, + DDR_PWR_STATE_ON = 0x01, + DDR_PWR_STATE_OFFLOWLAT = 0x02, + DDR_PWR_STATE_OFFHIGHLAT = 0x03 +}; + +#include +#include + +#if defined(CONFIG_UX500_SOC_DB8500) || defined(CONFIG_UX500_SOC_DB5500) + +static inline void __init prcmu_early_init(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_early_init(); + else + return db8500_prcmu_early_init(); +} + +static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) +{ + if (machine_is_u5500()) + return db5500_prcmu_set_power_state(state, keep_ulp_clk, + keep_ap_pll); + else + return db8500_prcmu_set_power_state(state, keep_ulp_clk, + keep_ap_pll); +} + +static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) +{ + if (machine_is_u5500()) + return db5500_prcmu_set_epod(epod_id, epod_state); + else + return db8500_prcmu_set_epod(epod_id, epod_state); +} + +static inline void prcmu_enable_wakeups(u32 wakeups) +{ + if (machine_is_u5500()) + db5500_prcmu_enable_wakeups(wakeups); + else + db8500_prcmu_enable_wakeups(wakeups); +} + +static inline void prcmu_disable_wakeups(void) +{ + prcmu_enable_wakeups(0); +} + +static inline void prcmu_config_abb_event_readout(u32 abb_events) +{ + if (machine_is_u5500()) + db5500_prcmu_config_abb_event_readout(abb_events); + else + db8500_prcmu_config_abb_event_readout(abb_events); +} + +static inline void prcmu_get_abb_event_buffer(void __iomem **buf) +{ + if (machine_is_u5500()) + db5500_prcmu_get_abb_event_buffer(buf); + else + db8500_prcmu_get_abb_event_buffer(buf); +} + +int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); +int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); + +int prcmu_config_clkout(u8 clkout, u8 source, u8 div); + +static inline int prcmu_request_clock(u8 clock, bool enable) +{ + if (machine_is_u5500()) + return db5500_prcmu_request_clock(clock, enable); + else + return db8500_prcmu_request_clock(clock, enable); +} + +int prcmu_set_ape_opp(u8 opp); +int prcmu_get_ape_opp(void); +int prcmu_set_ddr_opp(u8 opp); +int prcmu_get_ddr_opp(void); + +static inline int prcmu_set_arm_opp(u8 opp) +{ + if (machine_is_u5500()) + return db5500_prcmu_set_arm_opp(opp); + else + return db8500_prcmu_set_arm_opp(opp); +} + +static inline int prcmu_get_arm_opp(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_get_arm_opp(); + else + return db8500_prcmu_get_arm_opp(); +} + +static inline void prcmu_system_reset(u16 reset_code) +{ + if (machine_is_u5500()) + return db5500_prcmu_system_reset(reset_code); + else + return db8500_prcmu_system_reset(reset_code); +} + +static inline u16 prcmu_get_reset_code(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_get_reset_code(); + else + return db8500_prcmu_get_reset_code(); +} + +void prcmu_ac_wake_req(void); +void prcmu_ac_sleep_req(void); +void prcmu_modem_reset(void); +static inline bool prcmu_is_ac_wake_requested(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_is_ac_wake_requested(); + else + return db8500_prcmu_is_ac_wake_requested(); +} + +static inline int prcmu_set_display_clocks(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_set_display_clocks(); + else + return db8500_prcmu_set_display_clocks(); +} + +static inline int prcmu_disable_dsipll(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_disable_dsipll(); + else + return db8500_prcmu_disable_dsipll(); +} + +static inline int prcmu_enable_dsipll(void) +{ + if (machine_is_u5500()) + return db5500_prcmu_enable_dsipll(); + else + return db8500_prcmu_enable_dsipll(); +} + +static inline int prcmu_config_esram0_deep_sleep(u8 state) +{ + if (machine_is_u5500()) + return db5500_prcmu_config_esram0_deep_sleep(state); + else + return db8500_prcmu_config_esram0_deep_sleep(state); +} +#else + +static inline void __init prcmu_early_init(void) {} + +static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) +{ + return 0; +} + +static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) +{ + return 0; +} + +static inline void prcmu_enable_wakeups(u32 wakeups) {} + +static inline void prcmu_disable_wakeups(void) {} + +static inline int prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) +{ + return -ENOSYS; +} + +static inline int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) +{ + return -ENOSYS; +} + +static inline int prcmu_config_clkout(u8 clkout, u8 source, u8 div) +{ + return 0; +} + +static inline int prcmu_request_clock(u8 clock, bool enable) +{ + return 0; +} + +static inline int prcmu_set_ape_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_ape_opp(void) +{ + return APE_100_OPP; +} + +static inline int prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_arm_opp(void) +{ + return ARM_100_OPP; +} + +static inline int prcmu_set_ddr_opp(u8 opp) +{ + return 0; +} + +static inline int prcmu_get_ddr_opp(void) +{ + return DDR_100_OPP; +} + +static inline void prcmu_system_reset(u16 reset_code) {} + +static inline u16 prcmu_get_reset_code(void) +{ + return 0; +} + +static inline void prcmu_ac_wake_req(void) {} + +static inline void prcmu_ac_sleep_req(void) {} + +static inline void prcmu_modem_reset(void) {} + +static inline bool prcmu_is_ac_wake_requested(void) +{ + return false; +} + +static inline int prcmu_set_display_clocks(void) +{ + return 0; +} + +static inline int prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int prcmu_enable_dsipll(void) +{ + return 0; +} + +static inline int prcmu_config_esram0_deep_sleep(u8 state) +{ + return 0; +} + +static inline void prcmu_config_abb_event_readout(u32 abb_events) {} + +static inline void prcmu_get_abb_event_buffer(void __iomem **buf) +{ + *buf = NULL; +} + +#endif + +/* PRCMU QoS APE OPP class */ +#define PRCMU_QOS_APE_OPP 1 +#define PRCMU_QOS_DDR_OPP 2 +#define PRCMU_QOS_DEFAULT_VALUE -1 + +#ifdef CONFIG_UX500_PRCMU_QOS_POWER + +unsigned long prcmu_qos_get_cpufreq_opp_delay(void); +void prcmu_qos_set_cpufreq_opp_delay(unsigned long); +void prcmu_qos_force_opp(int, s32); +int prcmu_qos_requirement(int pm_qos_class); +int prcmu_qos_add_requirement(int pm_qos_class, char *name, s32 value); +int prcmu_qos_update_requirement(int pm_qos_class, char *name, s32 new_value); +void prcmu_qos_remove_requirement(int pm_qos_class, char *name); +int prcmu_qos_add_notifier(int prcmu_qos_class, + struct notifier_block *notifier); +int prcmu_qos_remove_notifier(int prcmu_qos_class, + struct notifier_block *notifier); + +#else + +static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void) +{ + return 0; +} + +static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {} + +static inline void prcmu_qos_force_opp(int prcmu_qos_class, s32 i) {} + +static inline int prcmu_qos_requirement(int prcmu_qos_class) +{ + return 0; +} + +static inline int prcmu_qos_add_requirement(int prcmu_qos_class, + char *name, s32 value) +{ + return 0; +} + +static inline int prcmu_qos_update_requirement(int prcmu_qos_class, + char *name, s32 new_value) +{ + return 0; +} + +static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name) +{ +} + +static inline int prcmu_qos_add_notifier(int prcmu_qos_class, + struct notifier_block *notifier) +{ + return 0; +} +static inline int prcmu_qos_remove_notifier(int prcmu_qos_class, + struct notifier_block *notifier) +{ + return 0; +} + +#endif + +#endif /* __MACH_PRCMU_H */ -- cgit v1.2.3-70-g09d2 From 73180f85f4ffbb66843f8248811b2ade29b22df2 Mon Sep 17 00:00:00 2001 From: Mattias Nilsson Date: Fri, 12 Aug 2011 10:28:10 +0200 Subject: mfd: Move to the new db500 PRCMU API Now that we have a shared API between the DB8500 and DB5500 PRCMU's, switch to using this neutral API instead. We delete the parts of db8500-prcmu.h that is now PRCMU-neutral, and calls will be diverted to respective driver. Common registers are in dbx500-prcmu-regs.h and common accessors and defines in This way we get a a lot more abstraction and code reuse. Signed-off-by: Mattias Nilsson Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- arch/arm/mach-ux500/cpu.c | 2 +- drivers/cpufreq/db8500-cpufreq.c | 2 +- drivers/mfd/db5500-prcmu-regs.h | 115 ------------ drivers/mfd/db5500-prcmu.c | 22 +-- drivers/mfd/db8500-prcmu-regs.h | 204 ---------------------- drivers/mfd/db8500-prcmu.c | 46 ++--- drivers/mfd/dbx500-prcmu-regs.h | 204 ++++++++++++++++++++++ drivers/regulator/db8500-prcmu.c | 2 +- include/linux/mfd/db5500-prcmu.h | 102 +++++++++-- include/linux/mfd/db8500-prcmu.h | 368 +++++++++------------------------------ include/linux/mfd/dbx500-prcmu.h | 8 +- 11 files changed, 415 insertions(+), 660 deletions(-) delete mode 100644 drivers/mfd/db5500-prcmu-regs.h delete mode 100644 drivers/mfd/db8500-prcmu-regs.h create mode 100644 drivers/mfd/dbx500-prcmu-regs.h (limited to 'include/linux') diff --git a/arch/arm/mach-ux500/cpu.c b/arch/arm/mach-ux500/cpu.c index 1da23bb87c1..bb5653993ca 100644 --- a/arch/arm/mach-ux500/cpu.c +++ b/arch/arm/mach-ux500/cpu.c @@ -53,7 +53,7 @@ void __init ux500_init_irq(void) if (cpu_is_u5500()) db5500_prcmu_early_init(); if (cpu_is_u8500()) - prcmu_early_init(); + db8500_prcmu_early_init(); clk_init(); } diff --git a/drivers/cpufreq/db8500-cpufreq.c b/drivers/cpufreq/db8500-cpufreq.c index d90456a809f..8e89dcf9d94 100644 --- a/drivers/cpufreq/db8500-cpufreq.c +++ b/drivers/cpufreq/db8500-cpufreq.c @@ -12,7 +12,7 @@ #include #include #include -#include +#include #include static struct cpufreq_frequency_table freq_table[] = { diff --git a/drivers/mfd/db5500-prcmu-regs.h b/drivers/mfd/db5500-prcmu-regs.h deleted file mode 100644 index 9a8e9e4ddd3..00000000000 --- a/drivers/mfd/db5500-prcmu-regs.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Kumar Sanghvi - * Author: Sundar Iyer - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit registers - */ - -#ifndef __MACH_PRCMU_REGS_H -#define __MACH_PRCMU_REGS_H - -#include - -#define PRCM_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) -#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f -#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf - -#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) -#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 - -#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 - -#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 - -#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) -#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) -#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) -#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) -#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) - -/* ARM WFI Standby signal register */ -#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) -#define PRCM_IOCR (_PRCMU_BASE + 0x310) -#define PRCM_IOCR_IOFORCE 0x1 - -/* CPU mailbox registers */ -#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) -#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) -#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) - -/* Dual A9 core interrupt management unit registers */ -#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) -#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 - -#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) -#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) -#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) -#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) -#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) -#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) -#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) -#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) -#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) -#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) - -#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) -#define ARM_WAKEUP_MODEM 0x1 - -#define PRCM_ARM_IT1_CLEAR (_PRCMU_BASE + 0x48C) -#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) -#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) - -#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) -#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) -#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) -#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) -#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) -#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) -#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) -#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -/* Level shifter and clamp control registers */ -#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) -#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) - -/* PRCMU clock/PLL/reset registers */ -#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 + 0x044) -#define PRCM_MCDECLK_MGT (_PRCMU_BASE + 0x064) -#define PRCM_HDMICLK_MGT (_PRCMU_BASE + 0x058) -#define PRCM_TVCLK_MGT (_PRCMU_BASE + 0x07c) -#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_CLKOCR (_PRCMU_BASE + 0x1CC) - -/* ePOD and memory power signal control registers */ -#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) -#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) - -/* Debug power control unit registers */ -#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) - -/* Miscellaneous unit registers */ -#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) -#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) -#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 -#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 - - -#endif /* __MACH_PRCMU__REGS_H */ diff --git a/drivers/mfd/db5500-prcmu.c b/drivers/mfd/db5500-prcmu.c index 9dbb3cab4a6..dc215878835 100644 --- a/drivers/mfd/db5500-prcmu.c +++ b/drivers/mfd/db5500-prcmu.c @@ -20,11 +20,11 @@ #include #include #include -#include +#include #include #include #include -#include "db5500-prcmu-regs.h" +#include "dbx500-prcmu-regs.h" #define _PRCM_MB_HEADER (tcdm_base + 0xFE8) #define PRCM_REQ_MB0_HEADER (_PRCM_MB_HEADER + 0x0) @@ -315,31 +315,31 @@ static bool read_mailbox_0(void) r = false; break; } - writel(MBOX_BIT(0), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(0), PRCM_ARM_IT1_CLR); return r; } static bool read_mailbox_1(void) { - writel(MBOX_BIT(1), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(1), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_2(void) { - writel(MBOX_BIT(2), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(2), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_3(void) { - writel(MBOX_BIT(3), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(3), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_4(void) { - writel(MBOX_BIT(4), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(4), PRCM_ARM_IT1_CLR); return false; } @@ -360,19 +360,19 @@ static bool read_mailbox_5(void) print_unknown_header_warning(5, header); break; } - writel(MBOX_BIT(5), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(5), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_6(void) { - writel(MBOX_BIT(6), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(6), PRCM_ARM_IT1_CLR); return false; } static bool read_mailbox_7(void) { - writel(MBOX_BIT(7), PRCM_ARM_IT1_CLEAR); + writel(MBOX_BIT(7), PRCM_ARM_IT1_CLR); return false; } @@ -434,7 +434,7 @@ int __init db5500_prcmu_init(void) return -ENODEV; /* Clean up the mailbox interrupts after pre-kernel code. */ - writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLEAR); + writel(ALL_MBOX_BITS, PRCM_ARM_IT1_CLR); r = request_threaded_irq(IRQ_DB5500_PRCMU1, prcmu_irq_handler, prcmu_irq_thread_fn, 0, "prcmu", NULL); diff --git a/drivers/mfd/db8500-prcmu-regs.h b/drivers/mfd/db8500-prcmu-regs.h deleted file mode 100644 index ec22e9f15d3..00000000000 --- a/drivers/mfd/db8500-prcmu-regs.h +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) STMicroelectronics 2009 - * Copyright (C) ST-Ericsson SA 2010 - * - * Author: Kumar Sanghvi - * Author: Sundar Iyer - * - * License Terms: GNU General Public License v2 - * - * PRCM Unit registers - */ - -#ifndef __DB8500_PRCMU_REGS_H -#define __DB8500_PRCMU_REGS_H - -#include - -#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_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) -#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f -#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf - -#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) -#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 - -#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) -#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 - -#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 -#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 - -#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) -#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C) -#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) -#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) -#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) -#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) - -#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0) -#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1) - -/* ARM WFI Standby signal register */ -#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) -#define PRCM_IOCR (_PRCMU_BASE + 0x310) -#define PRCM_IOCR_IOFORCE 0x1 - -/* CPU mailbox registers */ -#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) -#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) -#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) - -/* Dual A9 core interrupt management unit registers */ -#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) -#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 - -#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) -#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) -#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) -#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) -#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) -#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) -#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) -#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) -#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) -#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) - -#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) -#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 -#define ARM_WAKEUP_MODEM 0x1 - -#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) -#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) -#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) - -#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) -#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) - -#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) -#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) -#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) -#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) -#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) -#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) -#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) -#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -/* Level shifter and clamp control registers */ -#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) -#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) - -/* PRCMU clock/PLL/reset registers */ -#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_CLKOCR (_PRCMU_BASE + 0x1CC) -#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) -#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) -#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16) -#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29) - -/* ePOD and memory power signal control registers */ -#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) -#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) - -/* Debug power control unit registers */ -#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) - -/* Miscellaneous unit registers */ -#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) -#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) -#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 -#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 - -/* PRCMU HW semaphore */ -#define PRCM_SEM (_PRCMU_BASE + 0x400) -#define PRCM_SEM_PRCM_SEM BIT(0) - -#define PRCM_TCR (_PRCMU_BASE + 0x1C8) -#define PRCM_TCR_TENSEL_MASK BITS(0, 7) -#define PRCM_TCR_STOP_TIMERS BIT(16) -#define PRCM_TCR_DOZE_MODE BIT(17) - -#define PRCM_CLKOCR_CLKODIV0_SHIFT 0 -#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5) -#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6 -#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8) -#define PRCM_CLKOCR_CLKODIV1_SHIFT 16 -#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21) -#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22 -#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) - -/* GPIOCR register */ -#define PRCM_GPIOCR_SPI2_SELECT BIT(23) - -#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438) -#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134) -#define PRCM_CGATING_BYPASS_ICN2 BIT(6) - -/* Miscellaneous unit registers */ -#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214) -#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218) - -/* System reset register */ -#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) - -#endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/mfd/db8500-prcmu.c b/drivers/mfd/db8500-prcmu.c index e2c4a26a9eb..cea814509a6 100644 --- a/drivers/mfd/db8500-prcmu.c +++ b/drivers/mfd/db8500-prcmu.c @@ -27,14 +27,14 @@ #include #include #include -#include +#include #include #include #include #include #include #include -#include "db8500-prcmu-regs.h" +#include "dbx500-prcmu-regs.h" /* Offset for the firmware version within the TCPM */ #define PRCMU_FW_VERSION_OFFSET 0xA4 @@ -507,7 +507,7 @@ static struct { } prcmu_version; -int prcmu_enable_dsipll(void) +int db8500_prcmu_enable_dsipll(void) { int i; unsigned int plldsifreq; @@ -542,7 +542,7 @@ int prcmu_enable_dsipll(void) return 0; } -int prcmu_disable_dsipll(void) +int db8500_prcmu_disable_dsipll(void) { /* Disable dsi pll */ writel(PRCMU_DISABLE_PLLDSI, PRCM_PLLDSI_ENABLE); @@ -551,7 +551,7 @@ int prcmu_disable_dsipll(void) return 0; } -int prcmu_set_display_clocks(void) +int db8500_prcmu_set_display_clocks(void) { unsigned long flags; unsigned int dsiclk; @@ -734,7 +734,7 @@ unlock_and_return: return r; } -int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) +int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll) { unsigned long flags; @@ -791,7 +791,7 @@ static void config_wakeups(void) last_abb_events = abb_events; } -void prcmu_enable_wakeups(u32 wakeups) +void db8500_prcmu_enable_wakeups(u32 wakeups) { unsigned long flags; u32 bits; @@ -812,7 +812,7 @@ void prcmu_enable_wakeups(u32 wakeups) spin_unlock_irqrestore(&mb0_transfer.lock, flags); } -void prcmu_config_abb_event_readout(u32 abb_events) +void db8500_prcmu_config_abb_event_readout(u32 abb_events) { unsigned long flags; @@ -824,7 +824,7 @@ void prcmu_config_abb_event_readout(u32 abb_events) spin_unlock_irqrestore(&mb0_transfer.lock, flags); } -void prcmu_get_abb_event_buffer(void __iomem **buf) +void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) { if (readb(tcdm_base + PRCM_ACK_MB0_READ_POINTER) & 1) *buf = (tcdm_base + PRCM_ACK_MB0_WAKEUP_1_4500); @@ -833,13 +833,13 @@ void prcmu_get_abb_event_buffer(void __iomem **buf) } /** - * prcmu_set_arm_opp - set the appropriate ARM OPP + * db8500_prcmu_set_arm_opp - set the appropriate ARM OPP * @opp: The new ARM operating point to which transition is to be made * Returns: 0 on success, non-zero on failure * * This function sets the the operating point of the ARM. */ -int prcmu_set_arm_opp(u8 opp) +int db8500_prcmu_set_arm_opp(u8 opp) { int r; @@ -870,11 +870,11 @@ int prcmu_set_arm_opp(u8 opp) } /** - * prcmu_get_arm_opp - get the current ARM OPP + * db8500_prcmu_get_arm_opp - get the current ARM OPP * * Returns: the current ARM OPP */ -int prcmu_get_arm_opp(void) +int db8500_prcmu_get_arm_opp(void) { return readb(tcdm_base + PRCM_ACK_MB1_CURRENT_ARM_OPP); } @@ -1024,14 +1024,14 @@ int prcmu_release_usb_wakeup_state(void) } /** - * prcmu_set_epod - set the state of a EPOD (power domain) + * db8500_prcmu_set_epod - set the state of a EPOD (power domain) * @epod_id: The EPOD to set * @epod_state: The new EPOD state * * This function sets the state of a EPOD (power domain). It may not be called * from interrupt context. */ -int prcmu_set_epod(u16 epod_id, u8 epod_state) +int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) { int r = 0; bool ram_retention = false; @@ -1221,14 +1221,14 @@ static int request_reg_clock(u8 clock, bool enable) } /** - * prcmu_request_clock() - Request for a clock to be enabled or disabled. + * db8500_prcmu_request_clock() - Request for a clock to be enabled or disabled. * @clock: The clock for which the request is made. * @enable: Whether the clock should be enabled (true) or disabled (false). * * This function should only be used by the clock implementation. * Do not use it from any other place! */ -int prcmu_request_clock(u8 clock, bool enable) +int db8500_prcmu_request_clock(u8 clock, bool enable) { if (clock < PRCMU_NUM_REG_CLOCKS) return request_reg_clock(clock, enable); @@ -1240,7 +1240,7 @@ int prcmu_request_clock(u8 clock, bool enable) return -EINVAL; } -int prcmu_config_esram0_deep_sleep(u8 state) +int db8500_prcmu_config_esram0_deep_sleep(u8 state) { if ((state > ESRAM0_DEEP_SLEEP_STATE_RET) || (state < ESRAM0_DEEP_SLEEP_STATE_OFF)) @@ -1515,18 +1515,18 @@ unlock_and_return: mutex_unlock(&mb0_transfer.ac_wake_lock); } -bool prcmu_is_ac_wake_requested(void) +bool db8500_prcmu_is_ac_wake_requested(void) { return (atomic_read(&ac_wake_req_state) != 0); } /** - * prcmu_system_reset - System reset + * db8500_prcmu_system_reset - System reset * - * Saves the reset reason code and then sets the APE_SOFRST register which + * Saves the reset reason code and then sets the APE_SOFTRST register which * fires interrupt to fw */ -void prcmu_system_reset(u16 reset_code) +void db8500_prcmu_system_reset(u16 reset_code) { writew(reset_code, (tcdm_base + PRCM_SW_RST_REASON)); writel(1, PRCM_APE_SOFTRST); @@ -1782,7 +1782,7 @@ static struct irq_chip prcmu_irq_chip = { .irq_unmask = prcmu_irq_unmask, }; -void __init prcmu_early_init(void) +void __init db8500_prcmu_early_init(void) { unsigned int i; diff --git a/drivers/mfd/dbx500-prcmu-regs.h b/drivers/mfd/dbx500-prcmu-regs.h new file mode 100644 index 00000000000..ec22e9f15d3 --- /dev/null +++ b/drivers/mfd/dbx500-prcmu-regs.h @@ -0,0 +1,204 @@ +/* + * Copyright (C) STMicroelectronics 2009 + * Copyright (C) ST-Ericsson SA 2010 + * + * Author: Kumar Sanghvi + * Author: Sundar Iyer + * + * License Terms: GNU General Public License v2 + * + * PRCM Unit registers + */ + +#ifndef __DB8500_PRCMU_REGS_H +#define __DB8500_PRCMU_REGS_H + +#include + +#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_ARM_PLLDIVPS (_PRCMU_BASE + 0x118) +#define PRCM_ARM_PLLDIVPS_ARM_BRM_RATE 0x3f +#define PRCM_ARM_PLLDIVPS_MAX_MASK 0xf + +#define PRCM_PLLARM_LOCKP (_PRCMU_BASE + 0x0a8) +#define PRCM_PLLARM_LOCKP_PRCM_PLLARM_LOCKP3 0x2 + +#define PRCM_ARM_CHGCLKREQ (_PRCMU_BASE + 0x114) +#define PRCM_ARM_CHGCLKREQ_PRCM_ARM_CHGCLKREQ 0x1 + +#define PRCM_PLLARM_ENABLE (_PRCMU_BASE + 0x98) +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_ENABLE 0x1 +#define PRCM_PLLARM_ENABLE_PRCM_PLLARM_COUNTON 0x100 + +#define PRCM_ARMCLKFIX_MGT (_PRCMU_BASE + 0x0) +#define PRCM_A9PL_FORCE_CLKEN (_PRCMU_BASE + 0x19C) +#define PRCM_A9_RESETN_CLR (_PRCMU_BASE + 0x1f4) +#define PRCM_A9_RESETN_SET (_PRCMU_BASE + 0x1f0) +#define PRCM_ARM_LS_CLAMP (_PRCMU_BASE + 0x30c) +#define PRCM_SRAM_A9 (_PRCMU_BASE + 0x308) + +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9PL_FORCE_CLKEN BIT(0) +#define PRCM_A9PL_FORCE_CLKEN_PRCM_A9AXI_FORCE_CLKEN BIT(1) + +/* ARM WFI Standby signal register */ +#define PRCM_ARM_WFI_STANDBY (_PRCMU_BASE + 0x130) +#define PRCM_IOCR (_PRCMU_BASE + 0x310) +#define PRCM_IOCR_IOFORCE 0x1 + +/* CPU mailbox registers */ +#define PRCM_MBOX_CPU_VAL (_PRCMU_BASE + 0x0fc) +#define PRCM_MBOX_CPU_SET (_PRCMU_BASE + 0x100) +#define PRCM_MBOX_CPU_CLR (_PRCMU_BASE + 0x104) + +/* Dual A9 core interrupt management unit registers */ +#define PRCM_A9_MASK_REQ (_PRCMU_BASE + 0x328) +#define PRCM_A9_MASK_REQ_PRCM_A9_MASK_REQ 0x1 + +#define PRCM_A9_MASK_ACK (_PRCMU_BASE + 0x32c) +#define PRCM_ARMITMSK31TO0 (_PRCMU_BASE + 0x11c) +#define PRCM_ARMITMSK63TO32 (_PRCMU_BASE + 0x120) +#define PRCM_ARMITMSK95TO64 (_PRCMU_BASE + 0x124) +#define PRCM_ARMITMSK127TO96 (_PRCMU_BASE + 0x128) +#define PRCM_POWER_STATE_VAL (_PRCMU_BASE + 0x25C) +#define PRCM_ARMITVAL31TO0 (_PRCMU_BASE + 0x260) +#define PRCM_ARMITVAL63TO32 (_PRCMU_BASE + 0x264) +#define PRCM_ARMITVAL95TO64 (_PRCMU_BASE + 0x268) +#define PRCM_ARMITVAL127TO96 (_PRCMU_BASE + 0x26C) + +#define PRCM_HOSTACCESS_REQ (_PRCMU_BASE + 0x334) +#define PRCM_HOSTACCESS_REQ_HOSTACCESS_REQ 0x1 +#define ARM_WAKEUP_MODEM 0x1 + +#define PRCM_ARM_IT1_CLR (_PRCMU_BASE + 0x48C) +#define PRCM_ARM_IT1_VAL (_PRCMU_BASE + 0x494) +#define PRCM_HOLD_EVT (_PRCMU_BASE + 0x174) + +#define PRCM_MOD_AWAKE_STATUS (_PRCMU_BASE + 0x4A0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_COREPD_AWAKE BIT(0) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_AAPD_AWAKE BIT(1) +#define PRCM_MOD_AWAKE_STATUS_PRCM_MOD_VMODEM_OFF_ISO BIT(2) + +#define PRCM_ITSTATUS0 (_PRCMU_BASE + 0x148) +#define PRCM_ITSTATUS1 (_PRCMU_BASE + 0x150) +#define PRCM_ITSTATUS2 (_PRCMU_BASE + 0x158) +#define PRCM_ITSTATUS3 (_PRCMU_BASE + 0x160) +#define PRCM_ITSTATUS4 (_PRCMU_BASE + 0x168) +#define PRCM_ITSTATUS5 (_PRCMU_BASE + 0x484) +#define PRCM_ITCLEAR5 (_PRCMU_BASE + 0x488) +#define PRCM_ARMIT_MASKXP70_IT (_PRCMU_BASE + 0x1018) + +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) + +/* Level shifter and clamp control registers */ +#define PRCM_MMIP_LS_CLAMP_SET (_PRCMU_BASE + 0x420) +#define PRCM_MMIP_LS_CLAMP_CLR (_PRCMU_BASE + 0x424) + +/* PRCMU clock/PLL/reset registers */ +#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_CLKOCR (_PRCMU_BASE + 0x1CC) +#define PRCM_CLKOCR_CLKOUT0_REF_CLK (1 << 0) +#define PRCM_CLKOCR_CLKOUT0_MASK BITS(0, 13) +#define PRCM_CLKOCR_CLKOUT1_REF_CLK (1 << 16) +#define PRCM_CLKOCR_CLKOUT1_MASK BITS(16, 29) + +/* ePOD and memory power signal control registers */ +#define PRCM_EPOD_C_SET (_PRCMU_BASE + 0x410) +#define PRCM_SRAM_LS_SLEEP (_PRCMU_BASE + 0x304) + +/* Debug power control unit registers */ +#define PRCM_POWER_STATE_SET (_PRCMU_BASE + 0x254) + +/* Miscellaneous unit registers */ +#define PRCM_DSI_SW_RESET (_PRCMU_BASE + 0x324) +#define PRCM_GPIOCR (_PRCMU_BASE + 0x138) +#define PRCM_GPIOCR_DBG_STM_MOD_CMD1 0x800 +#define PRCM_GPIOCR_DBG_UARTMOD_CMD0 0x1 + +/* PRCMU HW semaphore */ +#define PRCM_SEM (_PRCMU_BASE + 0x400) +#define PRCM_SEM_PRCM_SEM BIT(0) + +#define PRCM_TCR (_PRCMU_BASE + 0x1C8) +#define PRCM_TCR_TENSEL_MASK BITS(0, 7) +#define PRCM_TCR_STOP_TIMERS BIT(16) +#define PRCM_TCR_DOZE_MODE BIT(17) + +#define PRCM_CLKOCR_CLKODIV0_SHIFT 0 +#define PRCM_CLKOCR_CLKODIV0_MASK BITS(0, 5) +#define PRCM_CLKOCR_CLKOSEL0_SHIFT 6 +#define PRCM_CLKOCR_CLKOSEL0_MASK BITS(6, 8) +#define PRCM_CLKOCR_CLKODIV1_SHIFT 16 +#define PRCM_CLKOCR_CLKODIV1_MASK BITS(16, 21) +#define PRCM_CLKOCR_CLKOSEL1_SHIFT 22 +#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) + +/* GPIOCR register */ +#define PRCM_GPIOCR_SPI2_SELECT BIT(23) + +#define PRCM_DDR_SUBSYS_APE_MINBW (_PRCMU_BASE + 0x438) +#define PRCM_CGATING_BYPASS (_PRCMU_BASE + 0x134) +#define PRCM_CGATING_BYPASS_ICN2 BIT(6) + +/* Miscellaneous unit registers */ +#define PRCM_RESOUTN_SET (_PRCMU_BASE + 0x214) +#define PRCM_RESOUTN_CLR (_PRCMU_BASE + 0x218) + +/* System reset register */ +#define PRCM_APE_SOFTRST (_PRCMU_BASE + 0x228) + +#endif /* __DB8500_PRCMU_REGS_H */ diff --git a/drivers/regulator/db8500-prcmu.c b/drivers/regulator/db8500-prcmu.c index 2bb8f451cc0..2d014a14436 100644 --- a/drivers/regulator/db8500-prcmu.c +++ b/drivers/regulator/db8500-prcmu.c @@ -13,7 +13,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/include/linux/mfd/db5500-prcmu.h b/include/linux/mfd/db5500-prcmu.h index f0977986402..9890687f582 100644 --- a/include/linux/mfd/db5500-prcmu.h +++ b/include/linux/mfd/db5500-prcmu.h @@ -5,21 +5,35 @@ * * U5500 PRCMU API. */ -#ifndef __MACH_PRCMU_U5500_H -#define __MACH_PRCMU_U5500_H +#ifndef __MFD_DB5500_PRCMU_H +#define __MFD_DB5500_PRCMU_H -#ifdef CONFIG_UX500_SOC_DB5500 +#ifdef CONFIG_MFD_DB5500_PRCMU void db5500_prcmu_early_init(void); - +int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state); +int db5500_prcmu_set_display_clocks(void); +int db5500_prcmu_disable_dsipll(void); +int db5500_prcmu_enable_dsipll(void); int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size); int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); +void db5500_prcmu_enable_wakeups(u32 wakeups); +int db5500_prcmu_request_clock(u8 clock, bool enable); +void db5500_prcmu_config_abb_event_readout(u32 abb_events); +void db5500_prcmu_get_abb_event_buffer(void __iomem **buf); +int prcmu_resetout(u8 resoutn, u8 state); +int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll); +int db5500_prcmu_config_esram0_deep_sleep(u8 state); +void db5500_prcmu_system_reset(u16 reset_code); +u16 db5500_prcmu_get_reset_code(void); +bool db5500_prcmu_is_ac_wake_requested(void); +int db5500_prcmu_set_arm_opp(u8 opp); +int db5500_prcmu_get_arm_opp(void); #else /* !CONFIG_UX500_SOC_DB5500 */ -static inline void db5500_prcmu_early_init(void) -{ -} +static inline void db5500_prcmu_early_init(void) {} static inline int db5500_prcmu_abb_read(u8 slave, u8 reg, u8 *value, u8 size) { @@ -31,15 +45,75 @@ static inline int db5500_prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size) return -ENOSYS; } -#endif /* CONFIG_UX500_SOC_DB5500 */ +static inline int db5500_prcmu_request_clock(u8 clock, bool enable) +{ + return 0; +} + +static inline int db5500_prcmu_set_display_clocks(void) +{ + return 0; +} + +static inline int db5500_prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int db5500_prcmu_enable_dsipll(void) +{ + return 0; +} -static inline int db5500_prcmu_config_abb_event_readout(u32 abb_events) +static inline int db5500_prcmu_config_esram0_deep_sleep(u8 state) { -#ifdef CONFIG_MACH_U5500_SIMULATOR return 0; -#else - return -1; -#endif } -#endif /* __MACH_PRCMU_U5500_H */ +static inline void db5500_prcmu_enable_wakeups(u32 wakeups) {} + +static inline int prcmu_resetout(u8 resoutn, u8 state) +{ + return 0; +} + +static inline int db5500_prcmu_set_epod(u16 epod_id, u8 epod_state) +{ + return 0; +} + +static inline void db5500_prcmu_get_abb_event_buffer(void __iomem **buf) {} +static inline void db5500_prcmu_config_abb_event_readout(u32 abb_events) {} + +static inline int db5500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) +{ + return 0; +} + +static inline void db5500_prcmu_system_reset(u16 reset_code) {} + +static inline u16 db5500_prcmu_get_reset_code(void) +{ + return 0; +} + +static inline bool db5500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db5500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db5500_prcmu_get_arm_opp(void) +{ + return 0; +} + + +#endif /* CONFIG_MFD_DB5500_PRCMU */ + +#endif /* __MFD_DB5500_PRCMU_H */ diff --git a/include/linux/mfd/db8500-prcmu.h b/include/linux/mfd/db8500-prcmu.h index 917dbcab701..60d27f7bfc1 100644 --- a/include/linux/mfd/db8500-prcmu.h +++ b/include/linux/mfd/db8500-prcmu.h @@ -11,7 +11,6 @@ #define __MFD_DB8500_PRCMU_H #include -#include /* This portion previously known as */ @@ -133,7 +132,7 @@ enum ap_pwrst { * @APEXECUTE_TO_APIDLE: Power state transition from ApExecute to ApIdle */ enum ap_pwrst_trans { - NO_TRANSITION = 0x00, + PRCMU_AP_NO_CHANGE = 0x00, APEXECUTE_TO_APSLEEP = 0x01, APIDLE_TO_APSLEEP = 0x02, /* To be removed */ PRCMU_AP_SLEEP = 0x01, @@ -145,54 +144,6 @@ enum ap_pwrst_trans { PRCMU_AP_DEEP_IDLE = 0x07, }; -/** - * enum ddr_pwrst - DDR power states definition - * @DDR_PWR_STATE_UNCHANGED: SDRAM and DDR controller state is unchanged - * @DDR_PWR_STATE_ON: - * @DDR_PWR_STATE_OFFLOWLAT: - * @DDR_PWR_STATE_OFFHIGHLAT: - */ -enum ddr_pwrst { - DDR_PWR_STATE_UNCHANGED = 0x00, - DDR_PWR_STATE_ON = 0x01, - DDR_PWR_STATE_OFFLOWLAT = 0x02, - DDR_PWR_STATE_OFFHIGHLAT = 0x03 -}; - -/** - * enum arm_opp - ARM OPP states definition - * @ARM_OPP_INIT: - * @ARM_NO_CHANGE: The ARM operating point is unchanged - * @ARM_100_OPP: The new ARM operating point is arm100opp - * @ARM_50_OPP: The new ARM operating point is arm50opp - * @ARM_MAX_OPP: Operating point is "max" (more than 100) - * @ARM_MAX_FREQ100OPP: Set max opp if available, else 100 - * @ARM_EXTCLK: The new ARM operating point is armExtClk - */ -enum arm_opp { - ARM_OPP_INIT = 0x00, - ARM_NO_CHANGE = 0x01, - ARM_100_OPP = 0x02, - ARM_50_OPP = 0x03, - ARM_MAX_OPP = 0x04, - ARM_MAX_FREQ100OPP = 0x05, - ARM_EXTCLK = 0x07 -}; - -/** - * enum ape_opp - APE OPP states definition - * @APE_OPP_INIT: - * @APE_NO_CHANGE: The APE operating point is unchanged - * @APE_100_OPP: The new APE operating point is ape100opp - * @APE_50_OPP: 50% - */ -enum ape_opp { - APE_OPP_INIT = 0x00, - APE_NO_CHANGE = 0x01, - APE_100_OPP = 0x02, - APE_50_OPP = 0x03 -}; - /** * enum hw_acc_state - State definition for hardware accelerator * @HW_NO_CHANGE: The hardware accelerator state must remain unchanged @@ -469,26 +420,6 @@ enum auto_enable { /* End of file previously known as prcmu-fw-defs_v1.h */ -/* PRCMU Wakeup defines */ -enum prcmu_wakeup_index { - PRCMU_WAKEUP_INDEX_RTC, - PRCMU_WAKEUP_INDEX_RTT0, - PRCMU_WAKEUP_INDEX_RTT1, - PRCMU_WAKEUP_INDEX_HSI0, - PRCMU_WAKEUP_INDEX_HSI1, - PRCMU_WAKEUP_INDEX_USB, - PRCMU_WAKEUP_INDEX_ABB, - PRCMU_WAKEUP_INDEX_ABB_FIFO, - PRCMU_WAKEUP_INDEX_ARM, - NUM_PRCMU_WAKEUP_INDICES -}; -#define PRCMU_WAKEUP(_name) (BIT(PRCMU_WAKEUP_INDEX_##_name)) - -/* PRCMU QoS APE OPP class */ -#define PRCMU_QOS_APE_OPP 1 -#define PRCMU_QOS_DDR_OPP 2 -#define PRCMU_QOS_DEFAULT_VALUE -1 - /** * enum hw_acc_dev - enum for hw accelerators * @HW_ACC_SVAMMDSP: for SVAMMDSP @@ -526,64 +457,6 @@ enum hw_acc_dev { NUM_HW_ACC }; -/* - * Ids for all EPODs (power domains) - * - EPOD_ID_SVAMMDSP: power domain for SVA MMDSP - * - EPOD_ID_SVAPIPE: power domain for SVA pipe - * - EPOD_ID_SIAMMDSP: power domain for SIA MMDSP - * - EPOD_ID_SIAPIPE: power domain for SIA pipe - * - EPOD_ID_SGA: power domain for SGA - * - EPOD_ID_B2R2_MCDE: power domain for B2R2 and MCDE - * - EPOD_ID_ESRAM12: power domain for ESRAM 1 and 2 - * - EPOD_ID_ESRAM34: power domain for ESRAM 3 and 4 - * - NUM_EPOD_ID: number of power domains - */ -#define EPOD_ID_SVAMMDSP 0 -#define EPOD_ID_SVAPIPE 1 -#define EPOD_ID_SIAMMDSP 2 -#define EPOD_ID_SIAPIPE 3 -#define EPOD_ID_SGA 4 -#define EPOD_ID_B2R2_MCDE 5 -#define EPOD_ID_ESRAM12 6 -#define EPOD_ID_ESRAM34 7 -#define NUM_EPOD_ID 8 - -/* - * state definition for EPOD (power domain) - * - EPOD_STATE_NO_CHANGE: The EPOD should remain unchanged - * - EPOD_STATE_OFF: The EPOD is switched off - * - EPOD_STATE_RAMRET: The EPOD is switched off with its internal RAM in - * retention - * - EPOD_STATE_ON_CLK_OFF: The EPOD is switched on, clock is still off - * - EPOD_STATE_ON: Same as above, but with clock enabled - */ -#define EPOD_STATE_NO_CHANGE 0x00 -#define EPOD_STATE_OFF 0x01 -#define EPOD_STATE_RAMRET 0x02 -#define EPOD_STATE_ON_CLK_OFF 0x03 -#define EPOD_STATE_ON 0x04 - -/* - * CLKOUT sources - */ -#define PRCMU_CLKSRC_CLK38M 0x00 -#define PRCMU_CLKSRC_ACLK 0x01 -#define PRCMU_CLKSRC_SYSCLK 0x02 -#define PRCMU_CLKSRC_LCDCLK 0x03 -#define PRCMU_CLKSRC_SDMMCCLK 0x04 -#define PRCMU_CLKSRC_TVCLK 0x05 -#define PRCMU_CLKSRC_TIMCLK 0x06 -#define PRCMU_CLKSRC_CLK009 0x07 -/* These are only valid for CLKOUT1: */ -#define PRCMU_CLKSRC_SIAMMDSPCLK 0x40 -#define PRCMU_CLKSRC_I2CCLK 0x41 -#define PRCMU_CLKSRC_MSP02CLK 0x42 -#define PRCMU_CLKSRC_ARMPLL_OBSCLK 0x43 -#define PRCMU_CLKSRC_HSIRXCLK 0x44 -#define PRCMU_CLKSRC_HSITXCLK 0x45 -#define PRCMU_CLKSRC_ARMCLKFIX 0x46 -#define PRCMU_CLKSRC_HDMICLK 0x47 - /* * Definitions for autonomous power management configuration. */ @@ -620,88 +493,12 @@ struct prcmu_auto_pm_config { u8 sva_policy; }; -/** - * enum ddr_opp - DDR OPP states definition - * @DDR_100_OPP: The new DDR operating point is ddr100opp - * @DDR_50_OPP: The new DDR operating point is ddr50opp - * @DDR_25_OPP: The new DDR operating point is ddr25opp - */ -enum ddr_opp { - DDR_100_OPP = 0x00, - DDR_50_OPP = 0x01, - DDR_25_OPP = 0x02, -}; - -/* - * Clock identifiers. - */ -enum prcmu_clock { - PRCMU_SGACLK, - PRCMU_UARTCLK, - PRCMU_MSP02CLK, - PRCMU_MSP1CLK, - PRCMU_I2CCLK, - PRCMU_SDMMCCLK, - PRCMU_SLIMCLK, - PRCMU_PER1CLK, - PRCMU_PER2CLK, - PRCMU_PER3CLK, - PRCMU_PER5CLK, - PRCMU_PER6CLK, - PRCMU_PER7CLK, - PRCMU_LCDCLK, - PRCMU_BMLCLK, - PRCMU_HSITXCLK, - PRCMU_HSIRXCLK, - PRCMU_HDMICLK, - PRCMU_APEATCLK, - PRCMU_APETRACECLK, - PRCMU_MCDECLK, - PRCMU_IPI2CCLK, - PRCMU_DSIALTCLK, - PRCMU_DMACLK, - PRCMU_B2R2CLK, - PRCMU_TVCLK, - PRCMU_SSPCLK, - PRCMU_RNGCLK, - PRCMU_UICCCLK, - PRCMU_NUM_REG_CLOCKS, - PRCMU_SYSCLK = PRCMU_NUM_REG_CLOCKS, - PRCMU_TIMCLK, -}; - -/* - * Definitions for controlling ESRAM0 in deep sleep. - */ -#define ESRAM0_DEEP_SLEEP_STATE_OFF 1 -#define ESRAM0_DEEP_SLEEP_STATE_RET 2 - -#ifdef CONFIG_MFD_DB8500_PRCMU -void __init prcmu_early_init(void); -int prcmu_set_display_clocks(void); -int prcmu_disable_dsipll(void); -int prcmu_enable_dsipll(void); -#else -static inline void __init prcmu_early_init(void) {} -#endif - #ifdef CONFIG_MFD_DB8500_PRCMU +void db8500_prcmu_early_init(void); int prcmu_set_rc_a2p(enum romcode_write); enum romcode_read prcmu_get_rc_p2a(void); enum ap_pwrst prcmu_get_xp70_current_state(void); -int prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); - -void prcmu_enable_wakeups(u32 wakeups); -static inline void prcmu_disable_wakeups(void) -{ - prcmu_enable_wakeups(0); -} - -void prcmu_config_abb_event_readout(u32 abb_events); -void prcmu_get_abb_event_buffer(void __iomem **buf); -int prcmu_set_arm_opp(u8 opp); -int prcmu_get_arm_opp(void); bool prcmu_has_arm_maxopp(void); bool prcmu_is_u8400(void); int prcmu_set_ape_opp(u8 opp); @@ -710,19 +507,14 @@ int prcmu_request_ape_opp_100_voltage(bool enable); int prcmu_release_usb_wakeup_state(void); int prcmu_set_ddr_opp(u8 opp); int prcmu_get_ddr_opp(void); -unsigned long prcmu_qos_get_cpufreq_opp_delay(void); -void prcmu_qos_set_cpufreq_opp_delay(unsigned long); /* NOTE! Use regulator framework instead */ int prcmu_set_hwacc(u16 hw_acc_dev, u8 state); -int prcmu_set_epod(u16 epod_id, u8 epod_state); void prcmu_configure_auto_pm(struct prcmu_auto_pm_config *sleep, struct prcmu_auto_pm_config *idle); bool prcmu_is_auto_pm_enabled(void); int prcmu_config_clkout(u8 clkout, u8 source, u8 div); -int prcmu_request_clock(u8 clock, bool enable); int prcmu_set_clock_divider(u8 clock, u8 divider); -int prcmu_config_esram0_deep_sleep(u8 state); int prcmu_config_hotdog(u8 threshold); int prcmu_config_hotmon(u8 low, u8 high); int prcmu_start_temp_sense(u16 cycles32k); @@ -732,14 +524,36 @@ int prcmu_abb_write(u8 slave, u8 reg, u8 *value, u8 size); void prcmu_ac_wake_req(void); void prcmu_ac_sleep_req(void); -void prcmu_system_reset(u16 reset_code); void prcmu_modem_reset(void); -bool prcmu_is_ac_wake_requested(void); void prcmu_enable_spi2(void); void prcmu_disable_spi2(void); +int prcmu_config_a9wdog(u8 num, bool sleep_auto_off); +int prcmu_enable_a9wdog(u8 id); +int prcmu_disable_a9wdog(u8 id); +int prcmu_kick_a9wdog(u8 id); +int prcmu_load_a9wdog(u8 id, u32 val); + +void db8500_prcmu_system_reset(u16 reset_code); +int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, bool keep_ap_pll); +void db8500_prcmu_enable_wakeups(u32 wakeups); +int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state); +int db8500_prcmu_request_clock(u8 clock, bool enable); +int db8500_prcmu_set_display_clocks(void); +int db8500_prcmu_disable_dsipll(void); +int db8500_prcmu_enable_dsipll(void); +void db8500_prcmu_config_abb_event_readout(u32 abb_events); +void db8500_prcmu_get_abb_event_buffer(void __iomem **buf); +int db8500_prcmu_config_esram0_deep_sleep(u8 state); +u16 db8500_prcmu_get_reset_code(void); +bool db8500_prcmu_is_ac_wake_requested(void); +int db8500_prcmu_set_arm_opp(u8 opp); +int db8500_prcmu_get_arm_opp(void); + #else /* !CONFIG_MFD_DB8500_PRCMU */ +static inline void db8500_prcmu_early_init(void) {} + static inline int prcmu_set_rc_a2p(enum romcode_write code) { return 0; @@ -755,34 +569,12 @@ static inline enum ap_pwrst prcmu_get_xp70_current_state(void) return AP_EXECUTE; } -static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, - bool keep_ap_pll) -{ - return 0; -} - -static inline void prcmu_enable_wakeups(u32 wakeups) {} - -static inline void prcmu_disable_wakeups(void) {} - -static inline void prcmu_config_abb_event_readout(u32 abb_events) {} - -static inline int prcmu_set_arm_opp(u8 opp) -{ - return 0; -} - -static inline int prcmu_get_arm_opp(void) -{ - return ARM_100_OPP; -} - -static bool prcmu_has_arm_maxopp(void) +static inline bool prcmu_has_arm_maxopp(void) { return false; } -static bool prcmu_is_u8400(void) +static inline bool prcmu_is_u8400(void) { return false; } @@ -817,13 +609,6 @@ static inline int prcmu_get_ddr_opp(void) return DDR_100_OPP; } -static inline unsigned long prcmu_qos_get_cpufreq_opp_delay(void) -{ - return 0; -} - -static inline void prcmu_qos_set_cpufreq_opp_delay(unsigned long n) {} - static inline int prcmu_set_hwacc(u16 hw_acc_dev, u8 state) { return 0; @@ -844,21 +629,11 @@ static inline int prcmu_config_clkout(u8 clkout, u8 source, u8 div) return 0; } -static inline int prcmu_request_clock(u8 clock, bool enable) -{ - return 0; -} - static inline int prcmu_set_clock_divider(u8 clock, u8 divider) { return 0; } -int prcmu_config_esram0_deep_sleep(u8 state) -{ - return 0; -} - static inline int prcmu_config_hotdog(u8 threshold) { return 0; @@ -893,86 +668,107 @@ static inline void prcmu_ac_wake_req(void) {} static inline void prcmu_ac_sleep_req(void) {} -static inline void prcmu_system_reset(u16 reset_code) {} - static inline void prcmu_modem_reset(void) {} -static inline bool prcmu_is_ac_wake_requested(void) +static inline int prcmu_enable_spi2(void) { - return false; + return 0; } -#ifndef CONFIG_UX500_SOC_DB5500 -static inline int prcmu_set_display_clocks(void) +static inline int prcmu_disable_spi2(void) { return 0; } -static inline int prcmu_disable_dsipll(void) +static inline void db8500_prcmu_system_reset(u16 reset_code) {} + +static inline int db8500_prcmu_set_power_state(u8 state, bool keep_ulp_clk, + bool keep_ap_pll) { return 0; } -static inline int prcmu_enable_dsipll(void) +static inline void db8500_prcmu_enable_wakeups(u32 wakeups) {} + +static inline int db8500_prcmu_set_epod(u16 epod_id, u8 epod_state) { return 0; } -#endif -static inline int prcmu_enable_spi2(void) +static inline int db8500_prcmu_request_clock(u8 clock, bool enable) { return 0; } -static inline int prcmu_disable_spi2(void) +static inline int db8500_prcmu_set_display_clocks(void) { return 0; } -#endif /* !CONFIG_MFD_DB8500_PRCMU */ +static inline int db8500_prcmu_disable_dsipll(void) +{ + return 0; +} + +static inline int db8500_prcmu_enable_dsipll(void) +{ + return 0; +} + +static inline int db8500_prcmu_config_esram0_deep_sleep(u8 state) +{ + return 0; +} + +static inline void db8500_prcmu_config_abb_event_readout(u32 abb_events) {} -#ifdef CONFIG_UX500_PRCMU_QOS_POWER -int prcmu_qos_requirement(int pm_qos_class); -int prcmu_qos_add_requirement(int pm_qos_class, char *name, s32 value); -int prcmu_qos_update_requirement(int pm_qos_class, char *name, s32 new_value); -void prcmu_qos_remove_requirement(int pm_qos_class, char *name); -int prcmu_qos_add_notifier(int prcmu_qos_class, - struct notifier_block *notifier); -int prcmu_qos_remove_notifier(int prcmu_qos_class, - struct notifier_block *notifier); -#else -static inline int prcmu_qos_requirement(int prcmu_qos_class) +static inline void db8500_prcmu_get_abb_event_buffer(void __iomem **buf) {} + +static inline u16 db8500_prcmu_get_reset_code(void) { return 0; } -static inline int prcmu_qos_add_requirement(int prcmu_qos_class, - char *name, s32 value) +static inline int prcmu_config_a9wdog(u8 num, bool sleep_auto_off) { return 0; } -static inline int prcmu_qos_update_requirement(int prcmu_qos_class, - char *name, s32 new_value) +static inline int prcmu_enable_a9wdog(u8 id) { return 0; } -static inline void prcmu_qos_remove_requirement(int prcmu_qos_class, char *name) +static inline int prcmu_disable_a9wdog(u8 id) { + return 0; } -static inline int prcmu_qos_add_notifier(int prcmu_qos_class, - struct notifier_block *notifier) +static inline int prcmu_kick_a9wdog(u8 id) { return 0; } -static inline int prcmu_qos_remove_notifier(int prcmu_qos_class, - struct notifier_block *notifier) + +static inline int prcmu_load_a9wdog(u8 id, u32 val) { return 0; } -#endif +static inline bool db8500_prcmu_is_ac_wake_requested(void) +{ + return 0; +} + +static inline int db8500_prcmu_set_arm_opp(u8 opp) +{ + return 0; +} + +static inline int db8500_prcmu_get_arm_opp(void) +{ + return 0; +} + +#endif /* !CONFIG_MFD_DB8500_PRCMU */ #endif /* __MFD_DB8500_PRCMU_H */ diff --git a/include/linux/mfd/dbx500-prcmu.h b/include/linux/mfd/dbx500-prcmu.h index 6c7584d69d8..bac942f959c 100644 --- a/include/linux/mfd/dbx500-prcmu.h +++ b/include/linux/mfd/dbx500-prcmu.h @@ -240,7 +240,7 @@ static inline int prcmu_set_power_state(u8 state, bool keep_ulp_clk, static inline int prcmu_set_epod(u16 epod_id, u8 epod_state) { if (machine_is_u5500()) - return db5500_prcmu_set_epod(epod_id, epod_state); + return -EINVAL; else return db8500_prcmu_set_epod(epod_id, epod_state); } @@ -295,7 +295,7 @@ int prcmu_get_ddr_opp(void); static inline int prcmu_set_arm_opp(u8 opp) { if (machine_is_u5500()) - return db5500_prcmu_set_arm_opp(opp); + return -EINVAL; else return db8500_prcmu_set_arm_opp(opp); } @@ -303,7 +303,7 @@ static inline int prcmu_set_arm_opp(u8 opp) static inline int prcmu_get_arm_opp(void) { if (machine_is_u5500()) - return db5500_prcmu_get_arm_opp(); + return -EINVAL; else return db8500_prcmu_get_arm_opp(); } @@ -362,7 +362,7 @@ static inline int prcmu_enable_dsipll(void) static inline int prcmu_config_esram0_deep_sleep(u8 state) { if (machine_is_u5500()) - return db5500_prcmu_config_esram0_deep_sleep(state); + return -EINVAL; else return db8500_prcmu_config_esram0_deep_sleep(state); } -- cgit v1.2.3-70-g09d2 From b958f7a7cbdfbf59ba61de7ebb9c59b0ee3a7967 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Sun, 30 Oct 2011 23:50:36 +0100 Subject: mfd: Fix missing abx500 header file updates I missed to include a patch adding the new silicon revision define CUT3P3 and remove the retired CUT0 versions of AB8500. Also delete the reference to the retired AB3550 from the header. Reported-by: Randy Dunlap Signed-off-by: Linus Walleij Signed-off-by: Samuel Ortiz --- include/linux/mfd/abx500.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mfd/abx500.h b/include/linux/mfd/abx500.h index 6d096e8b774..9970337ff04 100644 --- a/include/linux/mfd/abx500.h +++ b/include/linux/mfd/abx500.h @@ -34,11 +34,11 @@ #define AB5500_2_0 0x24 /* AB8500 CIDs*/ -#define AB8500_CUTEARLY 0x00 #define AB8500_CUT1P0 0x10 #define AB8500_CUT1P1 0x11 #define AB8500_CUT2P0 0x20 #define AB8500_CUT3P0 0x30 +#define AB8500_CUT3P3 0x33 /* * AB3100, EVENTA1, A2 and A3 event register flags -- cgit v1.2.3-70-g09d2