mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2024-08-27 19:29:37 +00:00
![Mark Brown](/assets/img/avatar_default.png)
Currently the MFD core supports remapping MFD cell interrupts using an irqdomain but only if the MFD is being instantiated using device tree and only if the device tree bindings use the pattern of registering IPs in the device tree with compatible properties. This will be actively harmful for drivers which support non-DT platforms and use this pattern for their DT bindings as it will mean that the core will silently change remapping behaviour and it is also limiting for drivers which don't do DT with this particular pattern. There is also a potential fragility if there are interrupts not associated with MFD cells and all the cells are omitted from the device tree for some reason. Instead change the code to take an IRQ domain as an optional argument, allowing drivers to take the decision about the parent domain for their interrupts. The one current user of this feature is ab8500-core, it has the domain lookup pushed out into the driver. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
177 lines
4 KiB
C
177 lines
4 KiB
C
/*
|
|
* tps65912-core.c -- TI TPS65912x
|
|
*
|
|
* Copyright 2011 Texas Instruments Inc.
|
|
*
|
|
* Author: Margarita Olaya Cabrera <magi@slimlogic.co.uk>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This driver is based on wm8350 implementation.
|
|
*/
|
|
|
|
#include <linux/module.h>
|
|
#include <linux/moduleparam.h>
|
|
#include <linux/init.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/mfd/core.h>
|
|
#include <linux/mfd/tps65912.h>
|
|
|
|
static struct mfd_cell tps65912s[] = {
|
|
{
|
|
.name = "tps65912-pmic",
|
|
},
|
|
};
|
|
|
|
int tps65912_set_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
|
|
{
|
|
u8 data;
|
|
int err;
|
|
|
|
mutex_lock(&tps65912->io_mutex);
|
|
|
|
err = tps65912->read(tps65912, reg, 1, &data);
|
|
if (err) {
|
|
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
|
|
goto out;
|
|
}
|
|
|
|
data |= mask;
|
|
err = tps65912->write(tps65912, reg, 1, &data);
|
|
if (err)
|
|
dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
|
|
|
|
out:
|
|
mutex_unlock(&tps65912->io_mutex);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tps65912_set_bits);
|
|
|
|
int tps65912_clear_bits(struct tps65912 *tps65912, u8 reg, u8 mask)
|
|
{
|
|
u8 data;
|
|
int err;
|
|
|
|
mutex_lock(&tps65912->io_mutex);
|
|
err = tps65912->read(tps65912, reg, 1, &data);
|
|
if (err) {
|
|
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
|
|
goto out;
|
|
}
|
|
|
|
data &= ~mask;
|
|
err = tps65912->write(tps65912, reg, 1, &data);
|
|
if (err)
|
|
dev_err(tps65912->dev, "Write to reg 0x%x failed\n", reg);
|
|
|
|
out:
|
|
mutex_unlock(&tps65912->io_mutex);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tps65912_clear_bits);
|
|
|
|
static inline int tps65912_read(struct tps65912 *tps65912, u8 reg)
|
|
{
|
|
u8 val;
|
|
int err;
|
|
|
|
err = tps65912->read(tps65912, reg, 1, &val);
|
|
if (err < 0)
|
|
return err;
|
|
|
|
return val;
|
|
}
|
|
|
|
static inline int tps65912_write(struct tps65912 *tps65912, u8 reg, u8 val)
|
|
{
|
|
return tps65912->write(tps65912, reg, 1, &val);
|
|
}
|
|
|
|
int tps65912_reg_read(struct tps65912 *tps65912, u8 reg)
|
|
{
|
|
int data;
|
|
|
|
mutex_lock(&tps65912->io_mutex);
|
|
|
|
data = tps65912_read(tps65912, reg);
|
|
if (data < 0)
|
|
dev_err(tps65912->dev, "Read from reg 0x%x failed\n", reg);
|
|
|
|
mutex_unlock(&tps65912->io_mutex);
|
|
return data;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tps65912_reg_read);
|
|
|
|
int tps65912_reg_write(struct tps65912 *tps65912, u8 reg, u8 val)
|
|
{
|
|
int err;
|
|
|
|
mutex_lock(&tps65912->io_mutex);
|
|
|
|
err = tps65912_write(tps65912, reg, val);
|
|
if (err < 0)
|
|
dev_err(tps65912->dev, "Write for reg 0x%x failed\n", reg);
|
|
|
|
mutex_unlock(&tps65912->io_mutex);
|
|
return err;
|
|
}
|
|
EXPORT_SYMBOL_GPL(tps65912_reg_write);
|
|
|
|
int tps65912_device_init(struct tps65912 *tps65912)
|
|
{
|
|
struct tps65912_board *pmic_plat_data = tps65912->dev->platform_data;
|
|
struct tps65912_platform_data *init_data;
|
|
int ret, dcdc_avs, value;
|
|
|
|
init_data = kzalloc(sizeof(struct tps65912_platform_data), GFP_KERNEL);
|
|
if (init_data == NULL)
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&tps65912->io_mutex);
|
|
dev_set_drvdata(tps65912->dev, tps65912);
|
|
|
|
dcdc_avs = (pmic_plat_data->is_dcdc1_avs << 0 |
|
|
pmic_plat_data->is_dcdc2_avs << 1 |
|
|
pmic_plat_data->is_dcdc3_avs << 2 |
|
|
pmic_plat_data->is_dcdc4_avs << 3);
|
|
if (dcdc_avs) {
|
|
tps65912->read(tps65912, TPS65912_I2C_SPI_CFG, 1, &value);
|
|
dcdc_avs |= value;
|
|
tps65912->write(tps65912, TPS65912_I2C_SPI_CFG, 1, &dcdc_avs);
|
|
}
|
|
|
|
ret = mfd_add_devices(tps65912->dev, -1,
|
|
tps65912s, ARRAY_SIZE(tps65912s),
|
|
NULL, 0, NULL);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
init_data->irq = pmic_plat_data->irq;
|
|
init_data->irq_base = pmic_plat_data->irq_base;
|
|
ret = tps65912_irq_init(tps65912, init_data->irq, init_data);
|
|
if (ret < 0)
|
|
goto err;
|
|
|
|
kfree(init_data);
|
|
return ret;
|
|
|
|
err:
|
|
kfree(init_data);
|
|
mfd_remove_devices(tps65912->dev);
|
|
kfree(tps65912);
|
|
return ret;
|
|
}
|
|
|
|
void tps65912_device_exit(struct tps65912 *tps65912)
|
|
{
|
|
mfd_remove_devices(tps65912->dev);
|
|
kfree(tps65912);
|
|
}
|
|
|
|
MODULE_AUTHOR("Margarita Olaya <magi@slimlogic.co.uk>");
|
|
MODULE_DESCRIPTION("TPS65912x chip family multi-function driver");
|
|
MODULE_LICENSE("GPL");
|