Merge branch 'for-mfd' of git://git.linaro.org/people/ljones/linux-3.0-ux500 into for-next
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
commit
dce7886e80
8 changed files with 1394 additions and 74 deletions
|
@ -320,6 +320,7 @@ static struct abx500_ops ab8500_ops = {
|
|||
.mask_and_set_register = ab8500_mask_and_set_register,
|
||||
.event_registers_startup_state_get = NULL,
|
||||
.startup_irq_enabled = NULL,
|
||||
.dump_all_banks = ab8500_dump_all_banks,
|
||||
};
|
||||
|
||||
static void ab8500_irq_lock(struct irq_data *data)
|
||||
|
@ -521,6 +522,7 @@ static irqreturn_t ab8500_irq(int irq, void *dev)
|
|||
int virq = ab8500_irq_get_virq(ab8500, line);
|
||||
|
||||
handle_nested_irq(virq);
|
||||
ab8500_debug_register_interrupt(line);
|
||||
value &= ~(1 << bit);
|
||||
|
||||
} while (value);
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -12,6 +12,7 @@
|
|||
#include <linux/interrupt.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/completion.h>
|
||||
#include <linux/regulator/consumer.h>
|
||||
|
@ -82,6 +83,11 @@
|
|||
/* This is used to not lose precision when dividing to get gain and offset */
|
||||
#define CALIB_SCALE 1000
|
||||
|
||||
/* Time in ms before disabling regulator */
|
||||
#define GPADC_AUDOSUSPEND_DELAY 1
|
||||
|
||||
#define CONVERSION_TIME 500 /* ms */
|
||||
|
||||
enum cal_channels {
|
||||
ADC_INPUT_VMAIN = 0,
|
||||
ADC_INPUT_BTEMP,
|
||||
|
@ -102,10 +108,10 @@ struct adc_cal_data {
|
|||
|
||||
/**
|
||||
* struct ab8500_gpadc - AB8500 GPADC device information
|
||||
* @chip_id ABB chip id
|
||||
* @dev: pointer to the struct device
|
||||
* @node: a list of AB8500 GPADCs, hence prepared for
|
||||
reentrance
|
||||
* @parent: pointer to the struct ab8500
|
||||
* @ab8500_gpadc_complete: pointer to the struct completion, to indicate
|
||||
* the completion of gpadc conversion
|
||||
* @ab8500_gpadc_lock: structure of type mutex
|
||||
|
@ -114,9 +120,9 @@ struct adc_cal_data {
|
|||
* @cal_data array of ADC calibration data structs
|
||||
*/
|
||||
struct ab8500_gpadc {
|
||||
u8 chip_id;
|
||||
struct device *dev;
|
||||
struct list_head node;
|
||||
struct ab8500 *parent;
|
||||
struct completion ab8500_gpadc_complete;
|
||||
struct mutex ab8500_gpadc_lock;
|
||||
struct regulator *regu;
|
||||
|
@ -282,8 +288,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
return -ENODEV;
|
||||
|
||||
mutex_lock(&gpadc->ab8500_gpadc_lock);
|
||||
|
||||
/* Enable VTVout LDO this is required for GPADC */
|
||||
regulator_enable(gpadc->regu);
|
||||
pm_runtime_get_sync(gpadc->dev);
|
||||
|
||||
/* Check if ADC is not busy, lock and proceed */
|
||||
do {
|
||||
|
@ -332,7 +339,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
EN_BUF | EN_ICHAR);
|
||||
break;
|
||||
case BTEMP_BALL:
|
||||
if (gpadc->chip_id >= AB8500_CUT3P0) {
|
||||
if (!is_ab8500_2p0_or_earlier(gpadc->parent)) {
|
||||
/* Turn on btemp pull-up on ABB 3.0 */
|
||||
ret = abx500_mask_and_set_register_interruptible(
|
||||
gpadc->dev,
|
||||
|
@ -344,7 +351,7 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
* Delay might be needed for ABB8500 cut 3.0, if not, remove
|
||||
* when hardware will be available
|
||||
*/
|
||||
msleep(1);
|
||||
usleep_range(1000, 1000);
|
||||
break;
|
||||
}
|
||||
/* Intentional fallthrough */
|
||||
|
@ -367,7 +374,8 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
goto out;
|
||||
}
|
||||
/* wait for completion of conversion */
|
||||
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete, 2*HZ)) {
|
||||
if (!wait_for_completion_timeout(&gpadc->ab8500_gpadc_complete,
|
||||
msecs_to_jiffies(CONVERSION_TIME))) {
|
||||
dev_err(gpadc->dev,
|
||||
"timeout: didn't receive GPADC conversion interrupt\n");
|
||||
ret = -EINVAL;
|
||||
|
@ -397,8 +405,10 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
dev_err(gpadc->dev, "gpadc_conversion: disable gpadc failed\n");
|
||||
goto out;
|
||||
}
|
||||
/* Disable VTVout LDO this is required for GPADC */
|
||||
regulator_disable(gpadc->regu);
|
||||
|
||||
pm_runtime_mark_last_busy(gpadc->dev);
|
||||
pm_runtime_put_autosuspend(gpadc->dev);
|
||||
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
|
||||
return (high_data << 8) | low_data;
|
||||
|
@ -412,7 +422,9 @@ int ab8500_gpadc_read_raw(struct ab8500_gpadc *gpadc, u8 channel)
|
|||
*/
|
||||
(void) abx500_set_register_interruptible(gpadc->dev, AB8500_GPADC,
|
||||
AB8500_GPADC_CTRL1_REG, DIS_GPADC);
|
||||
regulator_disable(gpadc->regu);
|
||||
|
||||
pm_runtime_put(gpadc->dev);
|
||||
|
||||
mutex_unlock(&gpadc->ab8500_gpadc_lock);
|
||||
dev_err(gpadc->dev,
|
||||
"gpadc_conversion: Failed to AD convert channel %d\n", channel);
|
||||
|
@ -571,6 +583,28 @@ static void ab8500_gpadc_read_calibration_data(struct ab8500_gpadc *gpadc)
|
|||
gpadc->cal_data[ADC_INPUT_VBAT].offset);
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_runtime_suspend(struct device *dev)
|
||||
{
|
||||
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
||||
|
||||
regulator_disable(gpadc->regu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_runtime_resume(struct device *dev)
|
||||
{
|
||||
struct ab8500_gpadc *gpadc = dev_get_drvdata(dev);
|
||||
|
||||
regulator_enable(gpadc->regu);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_runtime_idle(struct device *dev)
|
||||
{
|
||||
pm_runtime_suspend(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ab8500_gpadc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int ret = 0;
|
||||
|
@ -591,6 +625,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
}
|
||||
|
||||
gpadc->dev = &pdev->dev;
|
||||
gpadc->parent = dev_get_drvdata(pdev->dev.parent);
|
||||
mutex_init(&gpadc->ab8500_gpadc_lock);
|
||||
|
||||
/* Initialize completion used to notify completion of conversion */
|
||||
|
@ -607,14 +642,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
goto fail;
|
||||
}
|
||||
|
||||
/* Get Chip ID of the ABB ASIC */
|
||||
ret = abx500_get_chip_id(gpadc->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(gpadc->dev, "failed to get chip ID\n");
|
||||
goto fail_irq;
|
||||
}
|
||||
gpadc->chip_id = (u8) ret;
|
||||
|
||||
/* VTVout LDO used to power up ab8500-GPADC */
|
||||
gpadc->regu = regulator_get(&pdev->dev, "vddadc");
|
||||
if (IS_ERR(gpadc->regu)) {
|
||||
|
@ -622,6 +649,16 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
|
|||
dev_err(gpadc->dev, "failed to get vtvout LDO\n");
|
||||
goto fail_irq;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, gpadc);
|
||||
|
||||
regulator_enable(gpadc->regu);
|
||||
|
||||
pm_runtime_set_autosuspend_delay(gpadc->dev, GPADC_AUDOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(gpadc->dev);
|
||||
pm_runtime_set_active(gpadc->dev);
|
||||
pm_runtime_enable(gpadc->dev);
|
||||
|
||||
ab8500_gpadc_read_calibration_data(gpadc);
|
||||
list_add_tail(&gpadc->node, &ab8500_gpadc_list);
|
||||
dev_dbg(gpadc->dev, "probe success\n");
|
||||
|
@ -642,19 +679,34 @@ static int ab8500_gpadc_remove(struct platform_device *pdev)
|
|||
list_del(&gpadc->node);
|
||||
/* remove interrupt - completion of Sw ADC conversion */
|
||||
free_irq(gpadc->irq, gpadc);
|
||||
/* disable VTVout LDO that is being used by GPADC */
|
||||
regulator_put(gpadc->regu);
|
||||
|
||||
pm_runtime_get_sync(gpadc->dev);
|
||||
pm_runtime_disable(gpadc->dev);
|
||||
|
||||
regulator_disable(gpadc->regu);
|
||||
|
||||
pm_runtime_set_suspended(gpadc->dev);
|
||||
|
||||
pm_runtime_put_noidle(gpadc->dev);
|
||||
|
||||
kfree(gpadc);
|
||||
gpadc = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops ab8500_gpadc_pm_ops = {
|
||||
SET_RUNTIME_PM_OPS(ab8500_gpadc_runtime_suspend,
|
||||
ab8500_gpadc_runtime_resume,
|
||||
ab8500_gpadc_runtime_idle)
|
||||
};
|
||||
|
||||
static struct platform_driver ab8500_gpadc_driver = {
|
||||
.probe = ab8500_gpadc_probe,
|
||||
.remove = ab8500_gpadc_remove,
|
||||
.driver = {
|
||||
.name = "ab8500-gpadc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &ab8500_gpadc_pm_ops,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
@ -7,12 +7,73 @@
|
|||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/signal.h>
|
||||
#include <linux/power_supply.h>
|
||||
#include <linux/mfd/abx500.h>
|
||||
#include <linux/mfd/abx500/ab8500.h>
|
||||
#include <linux/mfd/abx500/ab8500-sysctrl.h>
|
||||
|
||||
static struct device *sysctrl_dev;
|
||||
|
||||
void ab8500_power_off(void)
|
||||
{
|
||||
sigset_t old;
|
||||
sigset_t all;
|
||||
static char *pss[] = {"ab8500_ac", "ab8500_usb"};
|
||||
int i;
|
||||
bool charger_present = false;
|
||||
union power_supply_propval val;
|
||||
struct power_supply *psy;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* If we have a charger connected and we're powering off,
|
||||
* reboot into charge-only mode.
|
||||
*/
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(pss); i++) {
|
||||
psy = power_supply_get_by_name(pss[i]);
|
||||
if (!psy)
|
||||
continue;
|
||||
|
||||
ret = psy->get_property(psy, POWER_SUPPLY_PROP_ONLINE, &val);
|
||||
|
||||
if (!ret && val.intval) {
|
||||
charger_present = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!charger_present)
|
||||
goto shutdown;
|
||||
|
||||
/* Check if battery is known */
|
||||
psy = power_supply_get_by_name("ab8500_btemp");
|
||||
if (psy) {
|
||||
ret = psy->get_property(psy, POWER_SUPPLY_PROP_TECHNOLOGY,
|
||||
&val);
|
||||
if (!ret && val.intval != POWER_SUPPLY_TECHNOLOGY_UNKNOWN) {
|
||||
printk(KERN_INFO
|
||||
"Charger \"%s\" is connected with known battery."
|
||||
" Rebooting.\n",
|
||||
pss[i]);
|
||||
machine_restart("charging");
|
||||
}
|
||||
}
|
||||
|
||||
shutdown:
|
||||
sigfillset(&all);
|
||||
|
||||
if (!sigprocmask(SIG_BLOCK, &all, &old)) {
|
||||
(void)ab8500_sysctrl_set(AB8500_STW4500CTRL1,
|
||||
AB8500_STW4500CTRL1_SWOFF |
|
||||
AB8500_STW4500CTRL1_SWRESET4500N);
|
||||
(void)sigprocmask(SIG_SETMASK, &old, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool valid_bank(u8 bank)
|
||||
{
|
||||
return ((bank == AB8500_SYS_CTRL1_BLOCK) ||
|
||||
|
@ -33,6 +94,7 @@ int ab8500_sysctrl_read(u16 reg, u8 *value)
|
|||
return abx500_get_register_interruptible(sysctrl_dev, bank,
|
||||
(u8)(reg & 0xFF), value);
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_sysctrl_read);
|
||||
|
||||
int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
|
||||
{
|
||||
|
@ -48,10 +110,40 @@ int ab8500_sysctrl_write(u16 reg, u8 mask, u8 value)
|
|||
return abx500_mask_and_set_register_interruptible(sysctrl_dev, bank,
|
||||
(u8)(reg & 0xFF), mask, value);
|
||||
}
|
||||
EXPORT_SYMBOL(ab8500_sysctrl_write);
|
||||
|
||||
static int ab8500_sysctrl_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ab8500_platform_data *plat;
|
||||
struct ab8500_sysctrl_platform_data *pdata;
|
||||
|
||||
sysctrl_dev = &pdev->dev;
|
||||
plat = dev_get_platdata(pdev->dev.parent);
|
||||
if (plat->pm_power_off)
|
||||
pm_power_off = ab8500_power_off;
|
||||
|
||||
pdata = plat->sysctrl;
|
||||
|
||||
if (pdata) {
|
||||
int ret, i, j;
|
||||
|
||||
for (i = AB8500_SYSCLKREQ1RFCLKBUF;
|
||||
i <= AB8500_SYSCLKREQ8RFCLKBUF; i++) {
|
||||
j = i - AB8500_SYSCLKREQ1RFCLKBUF;
|
||||
ret = ab8500_sysctrl_write(i, 0xff,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
dev_dbg(&pdev->dev,
|
||||
"Setting SysClkReq%dRfClkBuf 0x%X\n",
|
||||
j + 1,
|
||||
pdata->initial_req_buf_config[j]);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev,
|
||||
"unable to set sysClkReq%dRfClkBuf: "
|
||||
"%d\n", j + 1, ret);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -153,6 +153,22 @@ int abx500_startup_irq_enabled(struct device *dev, unsigned int irq)
|
|||
}
|
||||
EXPORT_SYMBOL(abx500_startup_irq_enabled);
|
||||
|
||||
void abx500_dump_all_banks(void)
|
||||
{
|
||||
struct abx500_ops *ops;
|
||||
struct device dummy_child = {0};
|
||||
struct abx500_device_entry *dev_entry;
|
||||
|
||||
list_for_each_entry(dev_entry, &abx500_list, list) {
|
||||
dummy_child.parent = dev_entry->dev;
|
||||
ops = &dev_entry->ops;
|
||||
|
||||
if ((ops != NULL) && (ops->dump_all_banks != NULL))
|
||||
ops->dump_all_banks(&dummy_child);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(abx500_dump_all_banks);
|
||||
|
||||
MODULE_AUTHOR("Mattias Wallin <mattias.wallin@stericsson.com>");
|
||||
MODULE_DESCRIPTION("ABX500 core driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
@ -306,6 +306,7 @@ int abx500_mask_and_set_register_interruptible(struct device *dev, u8 bank,
|
|||
int abx500_get_chip_id(struct device *dev);
|
||||
int abx500_event_registers_startup_state_get(struct device *dev, u8 *event);
|
||||
int abx500_startup_irq_enabled(struct device *dev, unsigned int irq);
|
||||
void abx500_dump_all_banks(void);
|
||||
|
||||
struct abx500_ops {
|
||||
int (*get_chip_id) (struct device *);
|
||||
|
@ -316,6 +317,7 @@ struct abx500_ops {
|
|||
int (*mask_and_set_register) (struct device *, u8, u8, u8, u8);
|
||||
int (*event_registers_startup_state_get) (struct device *, u8 *);
|
||||
int (*startup_irq_enabled) (struct device *, unsigned int);
|
||||
void (*dump_all_banks) (struct device *);
|
||||
};
|
||||
|
||||
int abx500_register_ops(struct device *core_dev, struct abx500_ops *ops);
|
||||
|
|
|
@ -37,6 +37,11 @@ static inline int ab8500_sysctrl_clear(u16 reg, u8 bits)
|
|||
return ab8500_sysctrl_write(reg, bits, 0);
|
||||
}
|
||||
|
||||
/* Configuration data for SysClkReq1RfClkBuf - SysClkReq8RfClkBuf */
|
||||
struct ab8500_sysctrl_platform_data {
|
||||
u8 initial_req_buf_config[8];
|
||||
};
|
||||
|
||||
/* Registers */
|
||||
#define AB8500_TURNONSTATUS 0x100
|
||||
#define AB8500_RESETSTATUS 0x101
|
||||
|
|
|
@ -270,10 +270,12 @@ struct regulator_reg_init;
|
|||
struct regulator_init_data;
|
||||
struct ab8500_gpio_platform_data;
|
||||
struct ab8500_codec_platform_data;
|
||||
struct ab8500_sysctrl_platform_data;
|
||||
|
||||
/**
|
||||
* struct ab8500_platform_data - AB8500 platform data
|
||||
* @irq_base: start of AB8500 IRQs, AB8500_NR_IRQS will be used
|
||||
* @pm_power_off: Should machine pm power off hook be registered or not
|
||||
* @init: board-specific initialization after detection of ab8500
|
||||
* @num_regulator_reg_init: number of regulator init registers
|
||||
* @regulator_reg_init: regulator init registers
|
||||
|
@ -282,6 +284,7 @@ struct ab8500_codec_platform_data;
|
|||
*/
|
||||
struct ab8500_platform_data {
|
||||
int irq_base;
|
||||
bool pm_power_off;
|
||||
void (*init) (struct ab8500 *);
|
||||
int num_regulator_reg_init;
|
||||
struct ab8500_regulator_reg_init *regulator_reg_init;
|
||||
|
@ -289,6 +292,7 @@ struct ab8500_platform_data {
|
|||
struct regulator_init_data *regulator;
|
||||
struct ab8500_gpio_platform_data *gpio;
|
||||
struct ab8500_codec_platform_data *codec;
|
||||
struct ab8500_sysctrl_platform_data *sysctrl;
|
||||
};
|
||||
|
||||
extern int ab8500_init(struct ab8500 *ab8500,
|
||||
|
@ -341,4 +345,12 @@ static inline int is_ab8500_2p0(struct ab8500 *ab)
|
|||
return (is_ab8500(ab) && (ab->chip_id == AB8500_CUT2P0));
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AB8500_DEBUG
|
||||
void ab8500_dump_all_banks(struct device *dev);
|
||||
void ab8500_debug_register_interrupt(int line);
|
||||
#else
|
||||
static inline void ab8500_dump_all_banks(struct device *dev) {}
|
||||
static inline void ab8500_debug_register_interrupt(int line) {}
|
||||
#endif
|
||||
|
||||
#endif /* MFD_AB8500_H */
|
||||
|
|
Loading…
Reference in a new issue